Snap for 10428683 from 94ec5a14cea628292aa957a95d5fa325e015072f to mainline-adbd-release
Change-Id: I5c3e057d50413c56a9c0e036edbde0d564aee05d
diff --git a/Android.bp b/Android.bp
index fab53d1..4097571 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,13 +83,14 @@
"android.hardware.radio-V1.4-java",
"android.hardware.radio-V1.5-java",
"android.hardware.radio-V1.6-java",
- "android.hardware.radio.config-V1-java",
- "android.hardware.radio.data-V1-java",
- "android.hardware.radio.messaging-V1-java",
- "android.hardware.radio.modem-V1-java",
- "android.hardware.radio.network-V1-java",
- "android.hardware.radio.sim-V1-java",
- "android.hardware.radio.voice-V1-java",
+ "android.hardware.radio.config-V2-java",
+ "android.hardware.radio.data-V2-java",
+ "android.hardware.radio.ims-V1-java",
+ "android.hardware.radio.messaging-V2-java",
+ "android.hardware.radio.modem-V2-java",
+ "android.hardware.radio.network-V2-java",
+ "android.hardware.radio.sim-V2-java",
+ "android.hardware.radio.voice-V2-java",
"voip-common",
"ims-common",
"unsupportedappusage",
diff --git a/OWNERS b/OWNERS
index 94509fe..a061cf0 100644
--- a/OWNERS
+++ b/OWNERS
@@ -8,15 +8,15 @@
jackyu@google.com
jayachandranc@google.com
linggm@google.com
-mtelang@google.com
+pmadapurmath@google.com
rgreenwalt@google.com
sarahchin@google.com
sasindran@google.com
tgunn@google.com
tjstuart@google.com
+tnd@google.com
xiaotonj@google.com
-
diff --git a/README.txt b/README.txt
index 9e40b77..1a44beb 100644
--- a/README.txt
+++ b/README.txt
@@ -15,7 +15,7 @@
implement in this directory and packages/services/Telephony. This IPC scheme
allows us to run public API code in the calling process, while the
telephony-related code runs in the privileged com.android.phone process. Such
-implementations include PhoneInterfaceManager, SubscriptionController and
+implementations include PhoneInterfaceManager, SubscriptionManagerService and
others.
The declaration of the com.android.phone process is in
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index c10eeb5..61e44a3 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,7 +23,7 @@
// Holds atoms to store on persist storage in case of power cycle or process crash.
// NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation.
-// Next id: 50
+// Next id: 70
message PersistAtoms {
/* Aggregated RAT usage during the call. */
repeated VoiceCallRatUsage voice_call_rat_usage = 1;
@@ -177,6 +177,60 @@
/* Timestamp of last network_requests_v2 pull. */
optional int64 network_requests_v2_pull_timestamp_millis = 51;
+
+ /* Unmetered networks information. */
+ repeated UnmeteredNetworks unmetered_networks = 52;
+
+ /* Outgoing Short Code SMS statistics and information. */
+ repeated OutgoingShortCodeSms outgoing_short_code_sms = 53;
+
+ /* Timestamp of last outgoing_short_code_sms pull. */
+ optional int64 outgoing_short_code_sms_pull_timestamp_millis = 54;
+
+ /* Number of time the user toggled the data switch feature since the last collection. */
+ optional int32 auto_data_switch_toggle_count = 55;
+
+ /** Snapshot of satellite controller. */
+ repeated SatelliteController satellite_controller = 58;
+
+ /* Timestamp of last satellite_controller pull. */
+ optional int64 satellite_controller_pull_timestamp_millis = 59;
+
+ /** Snapshot of satellite controller. */
+ repeated SatelliteSession satellite_session = 60;
+
+ /* Timestamp of last satellite_controller pull. */
+ optional int64 satellite_session_pull_timestamp_millis = 61;
+
+ /** Snapshot of satellite incoming datagram. */
+ repeated SatelliteIncomingDatagram satellite_incoming_datagram = 62;
+
+ /* Timestamp of last satellite_incoming_datagram pull. */
+ optional int64 satellite_incoming_datagram_pull_timestamp_millis = 63;
+
+ /** Snapshot of satellite outgoing datagram. */
+ repeated SatelliteOutgoingDatagram satellite_outgoing_datagram = 64;
+
+ /* Timestamp of last satellite_outgoing_datagram pull. */
+ optional int64 satellite_outgoing_datagram_pull_timestamp_millis = 65;
+
+ /** Snapshot of satellite provision datagram. */
+ repeated SatelliteProvision satellite_provision = 66;
+
+ /* Timestamp of last satellite_provision pull. */
+ optional int64 satellite_provision_pull_timestamp_millis = 67;
+
+ /** Snapshot of satellite SOS message recommender. */
+ repeated SatelliteSosMessageRecommender satellite_sos_message_recommender = 68;
+
+ /* Timestamp of last satellite_sos_message_recommender pull. */
+ optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69;
+
+ /* Consolidated emergency numbers list information. */
+ repeated EmergencyNumbersInfo emergency_numbers_info = 56;
+
+ /* Timestamp of last emergency number pull. */
+ optional int64 emergency_number_pull_timestamp_millis = 57;
}
// The canonical versions of the following enums live in:
@@ -187,10 +241,10 @@
// NOTE: StatsLog functions use int in place of enum
message VoiceCallSession {
+ reserved 4;
optional int32 bearer_at_start = 1;
optional int32 bearer_at_end = 2;
optional int32 direction = 3;
- optional int32 setup_duration = 4;
optional bool setup_failed = 5;
optional int32 disconnect_reason_code = 6;
optional int32 disconnect_extra_code = 7;
@@ -219,6 +273,9 @@
optional int32 rat_at_connected = 30;
optional bool is_multiparty = 31;
optional int32 call_duration = 32;
+ optional int32 last_known_rat = 33;
+ optional int32 fold_state = 34;
+
// Internal use only
optional int64 setup_begin_millis = 10001;
}
@@ -245,6 +302,11 @@
optional bool is_esim = 12;
optional int32 carrier_id = 13;
optional int64 message_id = 14;
+ optional int32 count = 15;
+ optional bool is_managed_profile = 16;
+
+ // Internal use only
+ optional int32 hashCode = 10001;
}
message OutgoingSms {
@@ -262,6 +324,13 @@
optional int64 message_id = 12;
optional int32 retry_id = 13;
optional int64 interval_millis = 14;
+ optional int32 count = 15;
+ optional int32 send_error_code = 16;
+ optional int32 network_error_code = 17;
+ optional bool is_managed_profile = 18;
+
+ // Internal use only
+ optional int32 hashCode = 10001;
}
message CarrierIdMismatch {
@@ -292,6 +361,8 @@
optional bool ongoing = 18;
optional int32 band_at_end = 19;
repeated int32 handover_failure_causes = 20;
+ repeated int32 handover_failure_rat = 21;
+ optional bool is_non_dds = 22;
}
message CellularServiceState {
@@ -305,6 +376,8 @@
optional int32 carrier_id = 8;
optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled
optional bool is_emergency_only = 10;
+ optional bool is_internet_pdn_up = 11;
+ optional int32 fold_state = 12;
// Internal use only
optional int64 last_used_millis = 10001;
@@ -498,3 +571,104 @@
optional int32 failed_reason = 4;
optional int32 count = 5;
}
+
+message UnmeteredNetworks {
+ optional int32 phone_id = 1;
+ optional int32 carrier_id = 2;
+ optional int64 unmetered_networks_bitmask = 3;
+}
+
+message OutgoingShortCodeSms {
+ optional int32 category = 1;
+ optional int32 xml_version = 2;
+ optional int32 short_code_sms_count = 3;
+}
+
+message SatelliteController {
+ optional int32 count_of_satellite_service_enablements_success = 1;
+ optional int32 count_of_satellite_service_enablements_fail = 2;
+ optional int32 count_of_outgoing_datagram_success = 3;
+ optional int32 count_of_outgoing_datagram_fail = 4;
+ optional int32 count_of_incoming_datagram_success = 5;
+ optional int32 count_of_incoming_datagram_fail = 6;
+ optional int32 count_of_datagram_type_sos_sms_success = 7;
+ optional int32 count_of_datagram_type_sos_sms_fail = 8;
+ optional int32 count_of_datagram_type_location_sharing_success = 9;
+ optional int32 count_of_datagram_type_location_sharing_fail = 10;
+ optional int32 count_of_provision_success = 11;
+ optional int32 count_of_provision_fail = 12;
+ optional int32 count_of_deprovision_success = 13;
+ optional int32 count_of_deprovision_fail = 14;
+ optional int32 total_service_uptime_sec = 15;
+ optional int32 total_battery_consumption_percent = 16;
+ optional int32 total_battery_charged_time_sec = 17;
+}
+
+message SatelliteSession {
+ optional int32 satellite_service_initialization_result = 1;
+ optional int32 satellite_technology = 2;
+ optional int32 count = 3;
+}
+
+message SatelliteIncomingDatagram {
+ optional int32 result_code = 1;
+ optional int32 datagram_size_bytes = 2;
+ optional int64 datagram_transfer_time_millis = 3;
+}
+
+message SatelliteOutgoingDatagram {
+ optional int32 datagram_type = 1;
+ optional int32 result_code = 2;
+ optional int32 datagram_size_bytes = 3;
+ optional int64 datagram_transfer_time_millis = 4;
+}
+
+message SatelliteProvision {
+ optional int32 result_code = 1;
+ optional int32 provisioning_time_sec = 2;
+ optional bool is_provision_request = 3;
+ optional bool is_canceled = 4;
+}
+
+message SatelliteSosMessageRecommender {
+ optional bool is_display_sos_message_sent = 1;
+ optional int32 count_of_timer_started = 2;
+ optional bool is_ims_registered = 3;
+ optional int32 cellular_service_state = 4;
+ optional int32 count = 5;
+}
+
+message EmergencyNumbersInfo {
+ enum ServiceCategory {
+ EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0;
+ EMERGENCY_SERVICE_CATEGORY_POLICE = 1;
+ EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2;
+ EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3;
+ EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4;
+ EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5;
+ EMERGENCY_SERVICE_CATEGORY_MIEC = 6;
+ EMERGENCY_SERVICE_CATEGORY_AIEC = 7;
+ }
+ enum Source {
+ EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0;
+ EMERGENCY_NUMBER_SOURCE_SIM = 1;
+ EMERGENCY_NUMBER_SOURCE_DATABASE = 2;
+ EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3;
+ EMERGENCY_NUMBER_SOURCE_DEFAULT = 4;
+ }
+ enum CallRoute {
+ EMERGENCY_CALL_ROUTE_UNKNOWN = 0;
+ EMERGENCY_CALL_ROUTE_EMERGENCY = 1;
+ EMERGENCY_CALL_ROUTE_NORMAL = 2;
+ }
+ optional bool is_db_version_ignored = 1;
+ optional int32 asset_version = 2;
+ optional int32 ota_version = 3;
+ optional string number = 4;
+ optional string country_iso = 5;
+ optional string mnc = 6;
+ optional CallRoute route = 7;
+ repeated string urns = 8;
+ repeated ServiceCategory service_categories = 9;
+ repeated Source sources = 10;
+}
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 6d3d711..b87728b 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -931,6 +931,9 @@
/** Data switch caused by CBRS switch. */
DATA_SWITCH_REASON_CBRS = 3;
+
+ /** Data switch caused by non-default SIM having better availability(e.g.signal). */
+ DATA_SWITCH_REASON_AUTO = 4;
}
/** The reason for data switch. */
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 972884a..b8de975 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
@@ -26,6 +28,7 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.telephony.Annotation.RadioPowerState;
+import android.telephony.BarringInfo;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
@@ -114,6 +117,17 @@
protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList();
protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
+ protected RegistrantList mEmergencyNetworkScanRegistrants = new RegistrantList();
+ protected RegistrantList mConnectionSetupFailureRegistrants = new RegistrantList();
+ protected RegistrantList mNotifyAnbrRegistrants = new RegistrantList();
+ protected RegistrantList mTriggerImsDeregistrationRegistrants = new RegistrantList();
+ protected RegistrantList mPendingSatelliteMessageCountRegistrants = new RegistrantList();
+ protected RegistrantList mNewSatelliteMessagesRegistrants = new RegistrantList();
+ protected RegistrantList mSatelliteMessagesTransferCompleteRegistrants = new RegistrantList();
+ protected RegistrantList mSatellitePointingInfoChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSatelliteModeChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSatelliteRadioTechnologyChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSatelliteProvisionStateChangedRegistrants = new RegistrantList();
@UnsupportedAppUsage
protected Registrant mGsmSmsRegistrant;
@@ -160,6 +174,8 @@
// Cache last emergency number list indication from radio
private final List<EmergencyNumber> mLastEmergencyNumberListIndication = new ArrayList<>();
+ // The last barring information received
+ protected BarringInfo mLastBarringInfo = new BarringInfo();
// Preferred network type received from PhoneFactory.
// This is used when establishing a connection to the
// vendor ril so it starts up in the correct mode.
@@ -900,6 +916,7 @@
|| mState == TelephonyManager.RADIO_POWER_UNAVAILABLE)
&& (oldState == TelephonyManager.RADIO_POWER_ON)) {
mOffOrNotAvailRegistrants.notifyRegistrants();
+ mLastBarringInfo = new BarringInfo();
}
}
}
@@ -918,6 +935,12 @@
}
}
+ /** {@inheritDoc} */
+ @Override
+ public @NonNull BarringInfo getLastBarringInfo() {
+ return mLastBarringInfo;
+ }
+
/**
* {@inheritDoc}
*/
@@ -1132,4 +1155,133 @@
@Override
public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
}
+
+ /**
+ * Register for Emergency network scan result.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ @Override
+ public void registerForEmergencyNetworkScan(Handler h, int what, Object obj) {
+ mEmergencyNetworkScanRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregister for Emergency network scan result.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ @Override
+ public void unregisterForEmergencyNetworkScan(Handler h) {
+ mEmergencyNetworkScanRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+ mConnectionSetupFailureRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForConnectionSetupFailure(Handler h) {
+ mConnectionSetupFailureRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForNotifyAnbr(Handler h, int what, Object obj) {
+ mNotifyAnbrRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForNotifyAnbr(Handler h) {
+ mNotifyAnbrRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {
+ mTriggerImsDeregistrationRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForTriggerImsDeregistration(Handler h) {
+ mTriggerImsDeregistrationRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForPendingSatelliteMessageCount(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mPendingSatelliteMessageCountRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) {
+ mPendingSatelliteMessageCountRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForNewSatelliteMessages(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mNewSatelliteMessagesRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForNewSatelliteMessages(@NonNull Handler h) {
+ mNewSatelliteMessagesRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSatelliteMessagesTransferComplete(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mSatelliteMessagesTransferCompleteRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) {
+ mSatelliteMessagesTransferCompleteRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSatellitePointingInfoChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mSatellitePointingInfoChangedRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) {
+ mSatellitePointingInfoChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSatelliteModeChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mSatelliteModeChangedRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSatelliteModeChanged(@NonNull Handler h) {
+ mSatelliteModeChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mSatelliteRadioTechnologyChangedRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) {
+ mSatelliteRadioTechnologyChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSatelliteProvisionStateChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mSatelliteProvisionStateChangedRegistrants.add(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {
+ mSatelliteProvisionStateChangedRegistrants.remove(h);
+ }
}
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 3fdc444..4de16cb 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -35,6 +35,7 @@
public static final int ERROR_CALLING_DISABLED = 5;
public static final int ERROR_TOO_MANY_CALLS = 6;
public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7;
+ public static final int ERROR_FDN_BLOCKED = 8;
public
CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallWaitingController.java b/src/java/com/android/internal/telephony/CallWaitingController.java
new file mode 100644
index 0000000..49940fc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallWaitingController.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the change of the user setting of the call waiting service
+ */
+public class CallWaitingController extends Handler {
+
+ public static final String LOG_TAG = "CallWaitingCtrl";
+ private static final boolean DBG = false; /* STOPSHIP if true */
+
+ // Terminal-based call waiting is not supported. */
+ public static final int TERMINAL_BASED_NOT_SUPPORTED = -1;
+ // Terminal-based call waiting is supported but not activated. */
+ public static final int TERMINAL_BASED_NOT_ACTIVATED = 0;
+ // Terminal-based call waiting is supported and activated. */
+ public static final int TERMINAL_BASED_ACTIVATED = 1;
+
+ private static final int EVENT_SET_CALL_WAITING_DONE = 1;
+ private static final int EVENT_GET_CALL_WAITING_DONE = 2;
+ private static final int EVENT_REGISTERED_TO_NETWORK = 3;
+
+ // Class to pack mOnComplete object passed by the caller
+ private static class Cw {
+ final boolean mEnable;
+ final Message mOnComplete;
+ final boolean mImsRegistered;
+
+ Cw(boolean enable, boolean imsRegistered, Message onComplete) {
+ mEnable = enable;
+ mOnComplete = onComplete;
+ mImsRegistered = imsRegistered;
+ }
+ }
+
+ @VisibleForTesting
+ public static final String PREFERENCE_TBCW = "terminal_based_call_waiting";
+ @VisibleForTesting
+ public static final String KEY_SUB_ID = "subId";
+ @VisibleForTesting
+ public static final String KEY_STATE = "state";
+ @VisibleForTesting
+ public static final String KEY_CS_SYNC = "cs_sync";
+
+ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
+ slotIndex);
+
+ private boolean mSupportedByImsService = false;
+ private boolean mValidSubscription = false;
+
+ // The user's last setting of terminal-based call waiting
+ private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED;
+
+ private int mSyncPreference = CALL_WAITING_SYNC_NONE;
+ private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ private boolean mCsEnabled = false;
+ private boolean mRegisteredForNetworkAttach = false;
+ private boolean mImsRegistered = false;
+
+ private final GsmCdmaPhone mPhone;
+ private final ServiceStateTracker mSST;
+ private final Context mContext;
+
+ // Constructors
+ public CallWaitingController(GsmCdmaPhone phone) {
+ mPhone = phone;
+ mSST = phone.getServiceStateTracker();
+ mContext = phone.getContext();
+ }
+
+ private void initialize() {
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+ if (ccm != null) {
+ // Callback directly handle carrier config change should be executed in handler thread
+ ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
+ } else {
+ loge("CarrierConfigLoader is not available.");
+ }
+
+ int phoneId = mPhone.getPhoneId();
+ int subId = mPhone.getSubId();
+ SharedPreferences sp =
+ mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+ mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+ mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE);
+
+ logi("initialize phoneId=" + phoneId
+ + ", lastSubId=" + mLastSubId + ", subId=" + subId
+ + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference
+ + ", csEnabled=" + mCsEnabled);
+ }
+
+ /**
+ * Returns the cached user setting.
+ *
+ * Possible values are
+ * {@link #TERMINAL_BASED_NOT_SUPPORTED},
+ * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and
+ * {@link #TERMINAL_BASED_ACTIVATED}.
+ *
+ * @param forCsOnly indicates the caller expects the result for CS calls only
+ */
+ @VisibleForTesting
+ public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+ if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+ return TERMINAL_BASED_NOT_SUPPORTED;
+ }
+ if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED;
+ return mCallWaitingState;
+ }
+
+ /**
+ * Serves the user's requests to interrogate the call waiting service
+ *
+ * @return true when terminal-based call waiting is supported, otherwise false
+ */
+ @VisibleForTesting
+ public synchronized boolean getCallWaiting(@Nullable Message onComplete) {
+ if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+ logi("getCallWaiting " + mCallWaitingState);
+
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ // Interrogate CW in CS network
+ if (!mCsEnabled) {
+ // skip interrogation if CS is not available and IMS is registered
+ if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+ Cw cw = new Cw(false, isImsRegistered(), onComplete);
+ Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+ mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+ return true;
+ }
+ }
+ }
+
+ if (mSyncPreference == CALL_WAITING_SYNC_NONE
+ || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+ || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+ || isSyncImsOnly()) {
+ sendGetCallWaitingResponse(onComplete);
+ return true;
+ } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+ || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+ Cw cw = new Cw(false, isImsRegistered(), onComplete);
+ Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+ mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Serves the user's requests to set the call waiting service
+ *
+ * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*.
+ * @return true when terminal-based call waiting is supported, otherwise false
+ */
+ @VisibleForTesting
+ public synchronized boolean setCallWaiting(boolean enable,
+ int serviceClass, @Nullable Message onComplete) {
+ if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+ if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false;
+
+ logi("setCallWaiting enable=" + enable + ", service=" + serviceClass);
+
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ // Enable CW in the CS network
+ if (!mCsEnabled && enable) {
+ if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+ Cw cw = new Cw(true, isImsRegistered(), onComplete);
+ Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+ mPhone.mCi.setCallWaiting(true, serviceClass, resp);
+ return true;
+ } else {
+ // CS network is not available, however, IMS is registered.
+ // Enabling the service in the CS network will be delayed.
+ registerForNetworkAttached();
+ }
+ }
+ }
+
+ if (mSyncPreference == CALL_WAITING_SYNC_NONE
+ || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+ || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+ || isSyncImsOnly()) {
+ updateState(
+ enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+
+ sendToTarget(onComplete, null, null);
+ return true;
+ } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+ || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+ Cw cw = new Cw(enable, isImsRegistered(), onComplete);
+ Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+ mPhone.mCi.setCallWaiting(enable, serviceClass, resp);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_SET_CALL_WAITING_DONE:
+ onSetCallWaitingDone((AsyncResult) msg.obj);
+ break;
+ case EVENT_GET_CALL_WAITING_DONE:
+ onGetCallWaitingDone((AsyncResult) msg.obj);
+ break;
+ case EVENT_REGISTERED_TO_NETWORK:
+ onRegisteredToNetwork();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private synchronized void onSetCallWaitingDone(AsyncResult ar) {
+ if (ar.userObj == null) {
+ // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+ if (DBG) logd("onSetCallWaitingDone to sync on network attached");
+ if (ar.exception == null) {
+ updateSyncState(true);
+ } else {
+ loge("onSetCallWaitingDone e=" + ar.exception);
+ }
+ return;
+ }
+
+ if (!(ar.userObj instanceof Cw)) {
+ // Unexpected state
+ if (DBG) logd("onSetCallWaitingDone unexpected result");
+ return;
+ }
+
+ if (DBG) logd("onSetCallWaitingDone");
+ Cw cw = (Cw) ar.userObj;
+
+ if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+ // do not synchronize service state between CS and IMS
+ sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+ return;
+ }
+
+ if (ar.exception == null) {
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+ updateSyncState(true);
+ }
+ updateState(
+ cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+ } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ if (cw.mImsRegistered) {
+ // IMS is registered. Do not notify error.
+ // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+ updateState(TERMINAL_BASED_ACTIVATED);
+ sendToTarget(cw.mOnComplete, null, null);
+ return;
+ }
+ }
+ sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+ }
+
+ private synchronized void onGetCallWaitingDone(AsyncResult ar) {
+ if (ar.userObj == null) {
+ // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+ if (DBG) logd("onGetCallWaitingDone to sync on network attached");
+ boolean enabled = false;
+ if (ar.exception == null) {
+ //resp[0]: 1 if enabled, 0 otherwise
+ //resp[1]: bitwise ORs of SERVICE_CLASS_* constants
+ int[] resp = (int[]) ar.result;
+ if (resp != null && resp.length > 1) {
+ enabled = (resp[0] == 1)
+ && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+ } else {
+ loge("onGetCallWaitingDone unexpected response");
+ }
+ } else {
+ loge("onGetCallWaitingDone e=" + ar.exception);
+ }
+ if (enabled) {
+ updateSyncState(true);
+ } else {
+ logi("onGetCallWaitingDone enabling CW service in CS network");
+ mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE,
+ obtainMessage(EVENT_SET_CALL_WAITING_DONE));
+ }
+ unregisterForNetworkAttached();
+ return;
+ }
+
+ if (!(ar.userObj instanceof Cw)) {
+ // Unexpected state
+ if (DBG) logd("onGetCallWaitingDone unexpected result");
+ return;
+ }
+
+ if (DBG) logd("onGetCallWaitingDone");
+ Cw cw = (Cw) ar.userObj;
+
+ if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+ // do not synchronize service state between CS and IMS
+ sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+ return;
+ }
+
+ if (ar.exception == null) {
+ int[] resp = (int[]) ar.result;
+ //resp[0]: 1 if enabled, 0 otherwise
+ //resp[1]: bitwise ORs of SERVICE_CLASS_
+ if (resp == null || resp.length < 2) {
+ logi("onGetCallWaitingDone unexpected response");
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ // no exception but unexpected response, local setting is preferred.
+ sendGetCallWaitingResponse(cw.mOnComplete);
+ } else {
+ sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+ }
+ return;
+ }
+
+ boolean enabled = resp[0] == 1
+ && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ updateSyncState(enabled);
+
+ if (!enabled && !cw.mImsRegistered) {
+ // IMS is not registered, change the local setting
+ logi("onGetCallWaitingDone CW in CS network is disabled.");
+ updateState(TERMINAL_BASED_NOT_ACTIVATED);
+ }
+
+ // return the user setting saved
+ sendGetCallWaitingResponse(cw.mOnComplete);
+ return;
+ }
+ updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+ } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+ // Got an exception
+ if (cw.mImsRegistered) {
+ // queryCallWaiting failed. However, IMS is registered. Do not notify error.
+ // return the user setting saved
+ logi("onGetCallWaitingDone get an exception, but IMS is registered");
+ sendGetCallWaitingResponse(cw.mOnComplete);
+ return;
+ }
+ }
+ sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+ }
+
+ private void sendToTarget(Message onComplete, Object result, Throwable exception) {
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, result, exception);
+ onComplete.sendToTarget();
+ }
+ }
+
+ private void sendGetCallWaitingResponse(Message onComplete) {
+ if (onComplete != null) {
+ int serviceClass = SERVICE_CLASS_NONE;
+ if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) {
+ serviceClass = SERVICE_CLASS_VOICE;
+ }
+ sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null);
+ }
+ }
+
+ private synchronized void onRegisteredToNetwork() {
+ if (mCsEnabled) return;
+
+ if (DBG) logd("onRegisteredToNetwork");
+
+ mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE,
+ obtainMessage(EVENT_GET_CALL_WAITING_DONE));
+ }
+
+ private synchronized void onCarrierConfigurationChanged(int slotIndex) {
+ if (slotIndex != mPhone.getPhoneId()) return;
+
+ int subId = mPhone.getSubId();
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ logi("onCarrierConfigChanged invalid subId=" + subId);
+
+ mValidSubscription = false;
+ unregisterForNetworkAttached();
+ return;
+ }
+
+ if (!updateCarrierConfig(subId, false /* ignoreSavedState */)) {
+ return;
+ }
+
+ logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled);
+
+ if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) {
+ if (!mCsEnabled) {
+ registerForNetworkAttached();
+ }
+ }
+ }
+
+ /**
+ * @param ignoreSavedState only used for test
+ * @return true when succeeded.
+ */
+ @VisibleForTesting
+ public boolean updateCarrierConfig(int subId, boolean ignoreSavedState) {
+ mValidSubscription = true;
+
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mContext,
+ subId,
+ KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
+ KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+ KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
+ if (b.isEmpty()) return false;
+
+ boolean supportsTerminalBased = false;
+ int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY);
+ if (services != null) {
+ for (int service : services) {
+ if (service == SUPPLEMENTARY_SERVICE_CW) {
+ supportsTerminalBased = true;
+ }
+ }
+ }
+ int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+ CALL_WAITING_SYNC_FIRST_CHANGE);
+ boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
+ int defaultState = supportsTerminalBased
+ ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED)
+ : TERMINAL_BASED_NOT_SUPPORTED;
+ int savedState = getSavedState(subId);
+
+ if (DBG) {
+ logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId()
+ + ", subId=" + subId + ", support=" + supportsTerminalBased
+ + ", sync=" + syncPreference + ", default=" + defaultState
+ + ", savedState=" + savedState);
+ }
+
+ int desiredState = savedState;
+
+ if (ignoreSavedState) {
+ desiredState = defaultState;
+ } else if ((mLastSubId != subId)
+ && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+ || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) {
+ desiredState = defaultState;
+ } else {
+ if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) {
+ desiredState = TERMINAL_BASED_NOT_SUPPORTED;
+ } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) {
+ desiredState = defaultState;
+ }
+ }
+
+ updateState(desiredState, syncPreference, ignoreSavedState);
+ return true;
+ }
+
+ private void updateState(int state) {
+ updateState(state, mSyncPreference, false);
+ }
+
+ private void updateState(int state, int syncPreference, boolean ignoreSavedState) {
+ int subId = mPhone.getSubId();
+
+ if (mLastSubId == subId
+ && mCallWaitingState == state
+ && mSyncPreference == syncPreference
+ && (!ignoreSavedState)) {
+ return;
+ }
+
+ int phoneId = mPhone.getPhoneId();
+
+ logi("updateState phoneId=" + phoneId
+ + ", subId=" + subId + ", state=" + state
+ + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState);
+
+ SharedPreferences sp =
+ mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(KEY_SUB_ID + phoneId, subId);
+ editor.putInt(KEY_STATE + subId, state);
+ editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
+ editor.apply();
+
+ mCallWaitingState = state;
+ mLastSubId = subId;
+ mSyncPreference = syncPreference;
+ if (mLastSubId != subId) {
+ mCsEnabled = false;
+ }
+
+ mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState);
+ }
+
+ private int getSavedState(int subId) {
+ SharedPreferences sp =
+ mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+ int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+
+ logi("getSavedState subId=" + subId + ", state=" + state);
+
+ return state;
+ }
+
+ private void updateSyncState(boolean enabled) {
+ int phoneId = mPhone.getPhoneId();
+
+ logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled);
+
+ mCsEnabled = enabled;
+ }
+
+ /**
+ * @return whether the service is enabled in the CS network
+ */
+ @VisibleForTesting
+ public boolean getSyncState() {
+ return mCsEnabled;
+ }
+
+ private boolean isCircuitSwitchedNetworkAvailable() {
+ logi("isCircuitSwitchedNetworkAvailable="
+ + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE));
+ return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE;
+ }
+
+ private boolean isImsRegistered() {
+ logi("isImsRegistered " + mImsRegistered);
+ return mImsRegistered;
+ }
+
+ /**
+ * Sets the registration state of IMS service.
+ */
+ public synchronized void setImsRegistrationState(boolean registered) {
+ logi("setImsRegistrationState prev=" + mImsRegistered
+ + ", new=" + registered);
+ mImsRegistered = registered;
+ }
+
+ private void registerForNetworkAttached() {
+ logi("registerForNetworkAttached");
+ if (mRegisteredForNetworkAttach) return;
+
+ mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+ mRegisteredForNetworkAttach = true;
+ }
+
+ private void unregisterForNetworkAttached() {
+ logi("unregisterForNetworkAttached");
+ if (!mRegisteredForNetworkAttach) return;
+
+ mSST.unregisterForNetworkAttached(this);
+ removeMessages(EVENT_REGISTERED_TO_NETWORK);
+ mRegisteredForNetworkAttach = false;
+ }
+
+ /**
+ * Sets whether the device supports the terminal-based call waiting.
+ * Only for test
+ */
+ @VisibleForTesting
+ public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) {
+ if (mSupportedByImsService == supported) return;
+
+ logi("setTerminalBasedCallWaitingSupported " + supported);
+
+ mSupportedByImsService = supported;
+
+ if (supported) {
+ initialize();
+ onCarrierConfigurationChanged(mPhone.getPhoneId());
+ } else {
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+ if (ccm != null && mCarrierConfigChangeListener != null) {
+ ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+ }
+ updateState(TERMINAL_BASED_NOT_SUPPORTED);
+ }
+ }
+
+ /**
+ * Notifies that the UE has attached to the network
+ * Only for test
+ */
+ @VisibleForTesting
+ public void notifyRegisteredToNetwork() {
+ sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK);
+ }
+
+ private boolean isSyncImsOnly() {
+ return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered);
+ }
+
+ /**
+ * Dump this instance into a readable format for dumpsys usage.
+ */
+ public void dump(PrintWriter printWriter) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.increaseIndent();
+ pw.println("CallWaitingController:");
+ pw.println(" mSupportedByImsService=" + mSupportedByImsService);
+ pw.println(" mValidSubscription=" + mValidSubscription);
+ pw.println(" mCallWaitingState=" + mCallWaitingState);
+ pw.println(" mSyncPreference=" + mSyncPreference);
+ pw.println(" mLastSubId=" + mLastSubId);
+ pw.println(" mCsEnabled=" + mCsEnabled);
+ pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach);
+ pw.println(" mImsRegistered=" + mImsRegistered);
+ pw.decreaseIndent();
+ }
+
+ private void loge(String msg) {
+ Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+ }
+
+ private void logi(String msg) {
+ Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+ }
+
+ private void logd(String msg) {
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 42a4f23..6d74c18 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -27,6 +27,7 @@
import android.os.RegistrantList;
import android.provider.Settings;
import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
import android.util.Log;
@@ -93,7 +94,11 @@
// ignore rebroadcast since carrier apps are direct boot aware.
return;
}
- sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState));
+ final int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ SubscriptionManager.INVALID_PHONE_INDEX);
+ if (mPhone.getPhoneId() == phoneId) {
+ sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState));
+ }
}
}
};
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 3e2baa5..863db93 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -287,16 +287,17 @@
return;
}
mLastAccessResetCarrierKey = now;
- int[] subIds = context.getSystemService(SubscriptionManager.class)
- .getSubscriptionIds(mPhoneId);
- if (subIds == null || subIds.length < 1) {
+
+ int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId);
return;
}
+
final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
- .createForSubscriptionId(subIds[0]);
+ .createForSubscriptionId(subId);
int carrierId = telephonyManager.getSimCarrierId();
- deleteCarrierInfoForImsiEncryption(context, subIds[0], carrierId);
+ deleteCarrierInfoForImsiEncryption(context, subId, carrierId);
Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index edf3d5f..beb6b26 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony;
-import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL;
-
import static java.nio.charset.StandardCharsets.UTF_8;
import android.app.AlarmManager;
@@ -32,7 +30,6 @@
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
-import android.provider.Telephony;
import android.telephony.CarrierConfigManager;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionManager;
@@ -122,13 +119,22 @@
mPhone = phone;
mContext = phone.getContext();
IntentFilter filter = new IntentFilter();
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX);
filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
.createForSubscriptionId(mPhone.getSubId());
+ CarrierConfigManager carrierConfigManager = mContext.getSystemService(
+ CarrierConfigManager.class);
+ // Callback which directly handle config change should be executed on handler thread
+ carrierConfigManager.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex == mPhone.getPhoneId()) {
+ Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex);
+ handleAlarmOrConfigChange();
+ }
+ });
}
private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() {
@@ -161,12 +167,6 @@
Log.d(LOG_TAG, "Handling reset intent: " + action);
sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
}
- } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
- Log.d(LOG_TAG, "Carrier Config changed: " + action);
- sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
- }
}
}
};
@@ -389,14 +389,23 @@
return false;
}
int subId = mPhone.getSubId();
- PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
- if (b == null) {
+ PersistableBundle b = null;
+ try {
+ b = carrierConfigManager.getConfigForSubId(subId,
+ CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT,
+ CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING,
+ CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "CarrierConfigLoader is not available.");
+ }
+ if (b == null || b.isEmpty()) {
return false;
}
+
mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
mAllowedOverMeteredNetwork = b.getBoolean(
- KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
+ CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) {
Log.d(LOG_TAG,
"Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 1e184bb..ab7ebc4 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -16,11 +16,8 @@
package com.android.internal.telephony;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
@@ -31,8 +28,6 @@
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN;
-import static com.android.internal.telephony.SubscriptionInfoUpdater.simStateString;
-
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,8 +49,6 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
-import android.os.Registrant;
-import android.os.RegistrantList;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -69,7 +62,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IntArray;
import android.util.LocalLog;
import android.util.Pair;
@@ -86,6 +78,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -111,6 +104,7 @@
private static final int PACKAGE_NOT_PRIVILEGED = 0;
private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG = 1;
private static final int PACKAGE_PRIVILEGED_FROM_SIM = 2;
+ private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE = 3;
// TODO(b/232273884): Turn feature on when find solution to handle the inter-carriers switching
/**
@@ -133,25 +127,6 @@
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
/**
- * Action to register a Registrant with this Tracker.
- * obj: Registrant that will be notified of Carrier Privileged UID changes.
- */
- private static final int ACTION_REGISTER_LISTENER = 1;
-
- /**
- * Action to unregister a Registrant with this Tracker.
- * obj: Handler used by the Registrant that will be removed.
- */
- private static final int ACTION_UNREGISTER_LISTENER = 2;
-
- /**
- * Action for tracking when Carrier Configs are updated.
- * arg1: Subscription Id for the Carrier Configs update being broadcast
- * arg2: Slot Index for the Carrier Configs update being broadcast
- */
- private static final int ACTION_CARRIER_CONFIG_CERTS_UPDATED = 3;
-
- /**
* Action for tracking when the Phone's SIM state changes.
* arg1: slotId that this Action applies to
* arg2: simState reported by this Broadcast
@@ -192,6 +167,14 @@
*/
private static final int ACTION_UICC_ACCESS_RULES_LOADED = 10;
+ /**
+ * Action to set the test override rule through {@link
+ * TelephonyManager#setCarrierServicePackageOverride}.
+ *
+ * <p>obj: String of the carrierServicePackage from method setCarrierServicePackageOverride.
+ */
+ private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11;
+
private final Context mContext;
private final Phone mPhone;
private final PackageManager mPackageManager;
@@ -201,7 +184,6 @@
private final TelephonyRegistryManager mTelephonyRegistryManager;
@NonNull private final LocalLog mLocalLog = new LocalLog(64);
- @NonNull private final RegistrantList mRegistrantList = new RegistrantList();
// Stores rules for Carrier Config-loaded rules
@NonNull private final List<UiccAccessRule> mCarrierConfigRules = new ArrayList<>();
// Stores rules for SIM-loaded rules.
@@ -211,6 +193,7 @@
// - Empty list indicates test override to simulate no rules (CC and UICC rules are ignored)
// - Non-empty list indicates test override with specific rules (CC and UICC rules are ignored)
@Nullable private List<UiccAccessRule> mTestOverrideRules = null;
+ @Nullable private String mTestOverrideCarrierServicePackage = null;
// Map of PackageName -> Certificate hashes for that Package
@NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
// Map of PackageName -> UIDs for that Package
@@ -300,19 +283,6 @@
if (action == null) return;
switch (action) {
- case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: {
- Bundle extras = intent.getExtras();
- int slotIndex = extras.getInt(EXTRA_SLOT_INDEX);
- int subId =
- extras.getInt(
- EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
- sendMessage(
- obtainMessage(
- ACTION_CARRIER_CONFIG_CERTS_UPDATED,
- subId,
- slotIndex));
- break;
- }
case TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED: // fall through
case TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED: {
Bundle extras = intent.getExtras();
@@ -375,13 +345,16 @@
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mCarrierConfigManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ // Callback is executed in handler thread and directly handles carrier config update
+ mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigUpdated(
+ subId, slotIndex));
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyRegistryManager =
(TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
IntentFilter certFilter = new IntentFilter();
- certFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
mContext.registerReceiver(mIntentReceiver, certFilter);
@@ -403,20 +376,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case ACTION_REGISTER_LISTENER: {
- handleRegisterListener((Registrant) msg.obj);
- break;
- }
- case ACTION_UNREGISTER_LISTENER: {
- handleUnregisterListener((Handler) msg.obj);
- break;
- }
- case ACTION_CARRIER_CONFIG_CERTS_UPDATED: {
- int subId = msg.arg1;
- int slotIndex = msg.arg2;
- handleCarrierConfigUpdated(subId, slotIndex);
- break;
- }
case ACTION_SIM_STATE_UPDATED: {
handleSimStateChanged(msg.arg1, msg.arg2);
break;
@@ -448,6 +407,11 @@
handleUiccAccessRulesLoaded();
break;
}
+ case ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE: {
+ String carrierServicePackage = (String) msg.obj;
+ handleSetTestOverrideCarrierServicePackage(carrierServicePackage);
+ break;
+ }
default: {
Rlog.e(TAG, "Received unknown msg type: " + msg.what);
break;
@@ -455,23 +419,6 @@
}
}
- private void handleRegisterListener(@NonNull Registrant registrant) {
- mRegistrantList.add(registrant);
- mPrivilegedPackageInfoLock.readLock().lock();
- try {
- // Old registrant callback still takes int[] as parameter, need conversion here
- int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids);
- registrant.notifyResult(
- Arrays.copyOf(uids, uids.length));
- } finally {
- mPrivilegedPackageInfoLock.readLock().unlock();
- }
- }
-
- private void handleUnregisterListener(@NonNull Handler handler) {
- mRegistrantList.remove(handler);
- }
-
private void handleCarrierConfigUpdated(int subId, int slotIndex) {
if (slotIndex != mPhone.getPhoneId()) return;
@@ -493,7 +440,10 @@
@NonNull
private List<UiccAccessRule> getCarrierConfigRules(int subId) {
- PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+ PersistableBundle carrierConfigs =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mContext, subId, KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+ // CarrierConfigManager#isConfigForIdentifiedCarrier can handle null or empty bundle
if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
return Collections.EMPTY_LIST;
}
@@ -529,7 +479,7 @@
SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS;
sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
mClearUiccRulesUptimeMillis);
- mLocalLog.log("SIM is gone, simState=" + simStateString(simState)
+ mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState)
+ ". Delay " + TimeUnit.MILLISECONDS.toSeconds(
CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules.");
} else {
@@ -614,10 +564,10 @@
List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
for (Signature signature : signatures) {
byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
- certs.add(IccUtils.bytesToHexString(sha1).toUpperCase());
+ certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
- certs.add(IccUtils.bytesToHexString(sha256).toUpperCase());
+ certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
}
mInstalledPackageCerts.put(pkg.packageName, certs);
@@ -731,12 +681,6 @@
mPrivilegedPackageInfoLock.readLock().lock();
try {
- // The obsoleted callback only care about UIDs
- if (carrierPrivilegesUidsChanged) {
- int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids);
- mRegistrantList.notifyResult(Arrays.copyOf(uids, uids.length));
- }
-
if (carrierPrivilegesPackageNamesChanged || carrierPrivilegesUidsChanged) {
mTelephonyRegistryManager.notifyCarrierPrivilegesChanged(
mPhone.getPhoneId(),
@@ -768,6 +712,7 @@
final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
switch (priv) {
case PACKAGE_PRIVILEGED_FROM_SIM:
+ case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
carrierServiceEligiblePackages.add(e.getKey());
// fallthrough
case PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG:
@@ -809,7 +754,9 @@
}
for (UiccAccessRule rule : mCarrierConfigRules) {
if (rule.matches(cert, pkgName)) {
- return PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
+ return pkgName.equals(mTestOverrideCarrierServicePackage)
+ ? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
+ : PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
}
}
}
@@ -882,33 +829,6 @@
}
/**
- * Registers the given Registrant with this tracker.
- *
- * <p>After being registered, the Registrant will be notified with the current Carrier
- * Privileged UIDs for this Phone.
- *
- * @deprecated Use {@link TelephonyManager#addCarrierPrivilegesListener} instead, which also
- * provides package names
- * <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT
- */
- @Deprecated
- public void registerCarrierPrivilegesListener(@NonNull Handler h, int what,
- @Nullable Object obj) {
- sendMessage(obtainMessage(ACTION_REGISTER_LISTENER, new Registrant(h, what, obj)));
- }
-
- /**
- * Unregisters the given listener with this tracker.
- *
- * @deprecated Use {@link TelephonyManager#removeCarrierPrivilegesListener} instead
- * <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT
- */
- @Deprecated
- public void unregisterCarrierPrivilegesListener(@NonNull Handler handler) {
- sendMessage(obtainMessage(ACTION_UNREGISTER_LISTENER, handler));
- }
-
- /**
* Set test carrier privilege rules which will override the actual rules on both Carrier Config
* and SIM.
*
@@ -921,6 +841,30 @@
sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
}
+ /**
+ * Override the carrier provisioning package, if it exists.
+ *
+ * <p>This API is to be used ONLY for testing, and requires the provided package to be carrier
+ * privileged. While this override is set, ONLY the specified package will be considered
+ * eligible to be bound as the carrier provisioning package, and any existing bindings will be
+ * terminated.
+ *
+ * @param carrierServicePackage the package to be used as the overridden carrier service
+ * package, or {@code null} to reset override
+ * @see TelephonyManager#setCarrierServicePackageOverride
+ */
+ public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) {
+ sendMessage(obtainMessage(
+ ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage));
+ }
+
+ private void handleSetTestOverrideCarrierServicePackage(
+ @Nullable String carrierServicePackage) {
+ mTestOverrideCarrierServicePackage = carrierServicePackage;
+ refreshInstalledPackageCache();
+ maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
+ }
+
private void handleSetTestOverrideRules(@Nullable String carrierPrivilegeRules) {
if (carrierPrivilegeRules == null) {
mTestOverrideRules = null;
@@ -1089,6 +1033,11 @@
String carrierServicePackageName = null;
for (ResolveInfo resolveInfo : carrierServiceResolveInfos) {
String packageName = getPackageName(resolveInfo);
+ if (mTestOverrideCarrierServicePackage != null
+ && !mTestOverrideCarrierServicePackage.equals(packageName)) {
+ continue;
+ }
+
if (simPrivilegedPackages.contains(packageName)) {
carrierServicePackageName = packageName;
break;
@@ -1098,11 +1047,4 @@
? new Pair<>(null, Process.INVALID_UID)
: new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName));
}
-
- @NonNull
- private static int[] intSetToArray(@NonNull Set<Integer> intSet) {
- IntArray converter = new IntArray(intSet.size());
- intSet.forEach(converter::add);
- return converter.toArray();
- }
}
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index ec6a5a0..8a9b3e3 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -43,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.metrics.CarrierIdMatchStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.util.TelephonyUtils;
@@ -54,6 +55,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* CarrierResolver identifies the subscription carrier and returns a canonical carrier Id
@@ -191,14 +193,14 @@
}
/**
- * This is triggered from SubscriptionInfoUpdater after sim state change.
+ * This is triggered from UiccController after sim state change.
* The sequence of sim loading would be
- * 1. ACTION_SUBINFO_CONTENT_CHANGE
+ * 1. OnSubscriptionsChangedListener
* 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
* /ACTION_SIM_APPLICATION_STATE_CHANGED
* 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
*
- * For SIM refresh either reset or init refresh type, SubscriptionInfoUpdater will re-trigger
+ * For SIM refresh either reset or init refresh type, UiccController will re-trigger
* carrier identification with sim loaded state. Framework today silently handle single file
* refresh type.
* TODO: check fileId from single file refresh, if the refresh file is IMSI, gid1 or other
@@ -547,7 +549,7 @@
// subscriptioninfo db to make sure we have correct carrier id set.
if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) {
// only persist carrier id to simInfo db when subId is valid.
- SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId());
+ SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId);
}
}
@@ -751,7 +753,8 @@
// Ideally we should do full string match. However due to SIM manufacture issues
// gid from some SIM might has garbage tail.
private boolean gidMatch(String gidFromSim, String gid) {
- return (gidFromSim != null) && gidFromSim.toLowerCase().startsWith(gid.toLowerCase());
+ return (gidFromSim != null) && gidFromSim.toLowerCase(Locale.ROOT)
+ .startsWith(gid.toLowerCase(Locale.ROOT));
}
private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index dfa53b3..960d794 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -200,7 +201,13 @@
}
}
- void updateForPhoneId(int phoneId, String simState) {
+ /**
+ * Update SIM state.
+ *
+ * @param phoneId The phone id.
+ * @param simState The legacy SIM state.
+ */
+ public void updateForPhoneId(int phoneId, @NonNull String simState) {
logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return;
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 64dc7ec..6b44998 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -19,10 +19,8 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -102,8 +100,34 @@
this.mSST = sst;
mTelephonyManager = mPhone.getContext().getSystemService(
TelephonyManager.class).createForSubscriptionId(mPhone.getSubId());
- phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ ccm.registerCarrierConfigChangeListener(
+ mPhone.getContext().getMainExecutor(),
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex != mPhone.getPhoneId()) return;
+
+ Rlog.d(LOG_TAG, "onCarrierConfigChanged: slotIndex=" + slotIndex
+ + ", subId=" + subId + ", carrierId=" + carrierId);
+
+ // Only get carrier configs used for EmergencyNetworkNotification
+ // and PrefNetworkNotification
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
+ CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+ CarrierConfigManager
+ .KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+ if (b.isEmpty()) return;
+
+ for (Map.Entry<Integer, NotificationType> entry :
+ mNotificationTypeMap.entrySet()) {
+ NotificationType notificationType = entry.getValue();
+ notificationType.setDelay(b);
+ }
+ handleConfigChanges();
+ });
+
// Listen for subscriber changes
SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
new OnSubscriptionsChangedListener(this.getLooper()) {
@@ -246,7 +270,7 @@
TelephonyManager tm = ((TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId());
- boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(context);
+ boolean isCarrierConfigEnabled = isCarrierConfigEnableNr();
boolean isRadioAccessFamilySupported = checkSupportedBitmask(
tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR);
boolean isNrNetworkTypeAllowed = checkSupportedBitmask(
@@ -261,15 +285,13 @@
return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed);
}
- private boolean isCarrierConfigEnableNr(Context context) {
- CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
- context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (carrierConfigManager == null) {
- Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: CarrierConfigManager is null");
- return false;
- }
- PersistableBundle config = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
- if (config == null) {
+ private boolean isCarrierConfigEnableNr() {
+ PersistableBundle config =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ if (config.isEmpty()) {
Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId());
return false;
}
@@ -348,21 +370,6 @@
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
- context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
- NotificationType notificationType = entry.getValue();
- notificationType.setDelay(b);
- }
- handleConfigChanges();
- }
- };
-
/**
* Post a notification to the NotificationManager for changing network type.
*/
@@ -376,6 +383,7 @@
Notification.Builder builder = getNotificationBuilder(notificationType);
// set some common attributes
builder.setWhen(System.currentTimeMillis())
+ .setShowWhen(true)
.setAutoCancel(true)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
.setColor(context.getResources().getColor(
@@ -590,7 +598,7 @@
.setContentTitle(title)
.setStyle(new Notification.BigTextStyle().bigText(details))
.setContentText(details)
- .setFlag(Notification.FLAG_NO_CLEAR, true)
+ .setOngoing(true)
.setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
}
}
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 2914ff9..3250cef 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -20,11 +20,8 @@
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
@@ -48,7 +45,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -102,51 +98,45 @@
/**
* This is a list of supported signals from CarrierSignalAgent
*/
- private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = new HashSet<>(Arrays.asList(
+ private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = Set.of(
TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
- TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE));
+ TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
- private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP =
- new HashMap<String, String>() {{
- put(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
- TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
- put(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
- TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
- put(TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
- TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
- put(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
- TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET);
- put(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
- TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
- }};
+ private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = Map.of(
+ TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
+ TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET,
+ TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP
.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
private final LocalLog mErrorLocalLog = new LocalLog(16);
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) log("CarrierSignalAgent receiver action: " + action);
- if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- loadCarrierConfig();
- }
- }
- };
-
private ConnectivityManager.NetworkCallback mNetworkCallback;
/** Constructor */
public CarrierSignalAgent(Phone phone) {
mPhone = phone;
+ CarrierConfigManager carrierConfigManager = mPhone.getContext().getSystemService(
+ CarrierConfigManager.class);
loadCarrierConfig();
- // reload configurations on CARRIER_CONFIG_CHANGED
- mPhone.getContext().registerReceiver(mReceiver,
- new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ carrierConfigManager.registerCarrierConfigChangeListener(
+ mPhone.getContext().getMainExecutor(),
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex == mPhone.getPhoneId()) {
+ loadCarrierConfig();
+ }
+ });
mPhone.getCarrierActionAgent().registerForCarrierAction(
CarrierActionAgent.CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, this,
EVENT_REGISTER_DEFAULT_NETWORK_AVAIL, null, false);
@@ -208,45 +198,47 @@
* load carrier config and cached the results into a hashMap action -> array list of components.
*/
private void loadCarrierConfig() {
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = null;
- if (configManager != null) {
- b = configManager.getConfigForSubId(mPhone.getSubId());
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
+ KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+ if (b.isEmpty()) {
+ return;
}
- if (b != null) {
- synchronized (mCachedWakeSignalConfigs) {
- log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
- Map<String, Set<ComponentName>> config = parseAndCache(
- b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
- // In some rare cases, up-to-date config could be fetched with delay and all signals
- // have already been delivered the receivers from the default carrier config.
- // To handle this raciness, we should notify those receivers (from old configs)
- // and reset carrier actions. This should be done before cached Config got purged
- // and written with the up-to-date value, Otherwise those receivers from the
- // old config might lingers without properly clean-up.
- if (!mCachedWakeSignalConfigs.isEmpty()
- && !config.equals(mCachedWakeSignalConfigs)) {
- if (VDBG) log("carrier config changed, reset receivers from old config");
- mPhone.getCarrierActionAgent().sendEmptyMessage(
- CarrierActionAgent.CARRIER_ACTION_RESET);
- }
- mCachedWakeSignalConfigs = config;
- }
- synchronized (mCachedNoWakeSignalConfigs) {
- log("Loading carrier config: "
- + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
- Map<String, Set<ComponentName>> config = parseAndCache(
- b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
- if (!mCachedNoWakeSignalConfigs.isEmpty()
- && !config.equals(mCachedNoWakeSignalConfigs)) {
- if (VDBG) log("carrier config changed, reset receivers from old config");
- mPhone.getCarrierActionAgent().sendEmptyMessage(
- CarrierActionAgent.CARRIER_ACTION_RESET);
- }
- mCachedNoWakeSignalConfigs = config;
+ synchronized (mCachedWakeSignalConfigs) {
+ log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+ Map<String, Set<ComponentName>> config = parseAndCache(
+ b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+ // In some rare cases, up-to-date config could be fetched with delay and all signals
+ // have already been delivered the receivers from the default carrier config.
+ // To handle this raciness, we should notify those receivers (from old configs)
+ // and reset carrier actions. This should be done before cached Config got purged
+ // and written with the up-to-date value, Otherwise those receivers from the
+ // old config might lingers without properly clean-up.
+ if (!mCachedWakeSignalConfigs.isEmpty()
+ && !config.equals(mCachedWakeSignalConfigs)) {
+ if (VDBG) log("carrier config changed, reset receivers from old config");
+ mPhone.getCarrierActionAgent().sendEmptyMessage(
+ CarrierActionAgent.CARRIER_ACTION_RESET);
}
+ mCachedWakeSignalConfigs = config;
+ }
+
+ synchronized (mCachedNoWakeSignalConfigs) {
+ log("Loading carrier config: "
+ + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+ Map<String, Set<ComponentName>> config = parseAndCache(
+ b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+ if (!mCachedNoWakeSignalConfigs.isEmpty()
+ && !config.equals(mCachedNoWakeSignalConfigs)) {
+ if (VDBG) log("carrier config changed, reset receivers from old config");
+ mPhone.getCarrierActionAgent().sendEmptyMessage(
+ CarrierActionAgent.CARRIER_ACTION_RESET);
+ }
+ mCachedNoWakeSignalConfigs = config;
}
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
new file mode 100644
index 0000000..82d4409
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.os.AsyncResult;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+import android.telephony.CellBroadcastIdRange;
+import android.telephony.SmsCbMessage;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+
+/**
+ * This class is to track the state to set cell broadcast config
+ */
+
+public final class CellBroadcastConfigTracker extends StateMachine {
+ private static final boolean DBG = Build.IS_DEBUGGABLE;
+
+ private static final int EVENT_REQUEST = 1;
+ private static final int EVENT_CONFIGURATION_DONE = 2;
+ private static final int EVENT_ACTIVATION_DONE = 3;
+ private static final int EVENT_RADIO_OFF = 4;
+ private static final int EVENT_SUBSCRIPTION_CHANGED = 5;
+
+ private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+ private static final int SMS_CB_CODE_SCHEME_MAX = 255;
+
+ // Cache of current cell broadcast id ranges of 3gpp
+ private List<CellBroadcastIdRange> mCbRanges3gpp = new CopyOnWriteArrayList<>();
+ // Cache of current cell broadcast id ranges of 3gpp2
+ private List<CellBroadcastIdRange> mCbRanges3gpp2 = new CopyOnWriteArrayList<>();
+ private Phone mPhone;
+ @VisibleForTesting
+ public int mSubId;
+ @VisibleForTesting
+ public final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ sendMessage(EVENT_SUBSCRIPTION_CHANGED);
+ }
+ };
+
+ /**
+ * The class is to present the request to set cell broadcast id ranges
+ */
+ private static class Request {
+ private final List<CellBroadcastIdRange> mCbRangesRequest3gpp =
+ new CopyOnWriteArrayList<>();
+ private final List<CellBroadcastIdRange> mCbRangesRequest3gpp2 =
+ new CopyOnWriteArrayList<>();
+ Consumer<Integer> mCallback;
+
+ Request(@NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) {
+ ranges.forEach(r -> {
+ if (r.getType() == SmsCbMessage.MESSAGE_FORMAT_3GPP) {
+ mCbRangesRequest3gpp.add(r);
+ } else {
+ mCbRangesRequest3gpp2.add(r);
+ }
+ });
+ mCallback = callback;
+ }
+
+ List<CellBroadcastIdRange> get3gppRanges() {
+ return mCbRangesRequest3gpp;
+ }
+
+ List<CellBroadcastIdRange> get3gpp2Ranges() {
+ return mCbRangesRequest3gpp2;
+ }
+
+ Consumer<Integer> getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public String toString() {
+ return "Request[mCbRangesRequest3gpp = " + mCbRangesRequest3gpp + ", "
+ + "mCbRangesRequest3gpp2 = " + mCbRangesRequest3gpp2 + ", "
+ + "mCallback = " + mCallback + "]";
+ }
+ }
+
+ /**
+ * The default state.
+ */
+ private class DefaultState extends State {
+ @Override
+ public void enter() {
+ mPhone.registerForRadioOffOrNotAvailable(getHandler(), EVENT_RADIO_OFF, null);
+ mPhone.getContext().getSystemService(SubscriptionManager.class)
+ .addOnSubscriptionsChangedListener(new HandlerExecutor(getHandler()),
+ mSubChangedListener);
+ }
+
+ @Override
+ public void exit() {
+ mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
+ mPhone.getContext().getSystemService(SubscriptionManager.class)
+ .removeOnSubscriptionsChangedListener(mSubChangedListener);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = HANDLED;
+ if (DBG) {
+ logd("DefaultState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_RADIO_OFF:
+ resetConfig();
+ break;
+ case EVENT_SUBSCRIPTION_CHANGED:
+ int subId = mPhone.getSubId();
+ if (mSubId != subId) {
+ log("SubId changed from " + mSubId + " to " + subId);
+ mSubId = subId;
+ resetConfig();
+ }
+ break;
+ default:
+ log("unexpected message!");
+ break;
+
+ }
+
+ return retVal;
+ }
+ }
+
+ private DefaultState mDefaultState = new DefaultState();
+
+ /*
+ * The idle state which does not have ongoing radio request.
+ */
+ private class IdleState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = NOT_HANDLED;
+ if (DBG) {
+ logd("IdleState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_REQUEST:
+ Request request = (Request) msg.obj;
+ if (DBG) {
+ logd("IdleState handle EVENT_REQUEST with request:" + request);
+ }
+ if (!mCbRanges3gpp.equals(request.get3gppRanges())) {
+ // set gsm config if the config is changed
+ setGsmConfig(request.get3gppRanges(), request);
+ transitionTo(mGsmConfiguringState);
+ } else if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) {
+ // set cdma config directly if no gsm config change but cdma config is
+ // changed
+ setCdmaConfig(request.get3gpp2Ranges(), request);
+ transitionTo(mCdmaConfiguringState);
+ } else {
+ logd("Do nothing as the requested ranges are same as now");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+ }
+ retVal = HANDLED;
+ break;
+ default:
+ break;
+ }
+ return retVal;
+ }
+ }
+ private IdleState mIdleState = new IdleState();
+
+ /*
+ * The state waiting for the result to set gsm config.
+ */
+ private class GsmConfiguringState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = NOT_HANDLED;
+ if (DBG) {
+ logd("GsmConfiguringState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_REQUEST:
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+ case EVENT_CONFIGURATION_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Request request = (Request) ar.userObj;
+ if (DBG) {
+ logd("GsmConfiguringState handle EVENT_CONFIGURATION_DONE with request:"
+ + request);
+ }
+ if (ar.exception == null) {
+ // set gsm activation and transit to gsm activating state
+ setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP,
+ !request.get3gppRanges().isEmpty(), request);
+ transitionTo(mGsmActivatingState);
+ } else {
+ logd("Failed to set gsm config");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
+ // transit to idle state on the failure case
+ transitionTo(mIdleState);
+ }
+ retVal = HANDLED;
+ break;
+ default:
+ break;
+ }
+ return retVal;
+ }
+ }
+ private GsmConfiguringState mGsmConfiguringState = new GsmConfiguringState();
+
+ /*
+ * The state waiting for the result to set gsm activation.
+ */
+ private class GsmActivatingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = NOT_HANDLED;
+ if (DBG) {
+ logd("GsmActivatingState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_REQUEST:
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+ case EVENT_ACTIVATION_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Request request = (Request) ar.userObj;
+ if (DBG) {
+ logd("GsmActivatingState handle EVENT_ACTIVATION_DONE with request:"
+ + request);
+ }
+ if (ar.exception == null) {
+ mCbRanges3gpp = request.get3gppRanges();
+ if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) {
+ // set cdma config and transit to cdma configuring state if the config
+ // is changed.
+ setCdmaConfig(request.get3gpp2Ranges(), request);
+ transitionTo(mCdmaConfiguringState);
+ } else {
+ logd("Done as no need to update ranges for 3gpp2");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+ // transit to idle state if there is no cdma config change
+ transitionTo(mIdleState);
+ }
+ } else {
+ logd("Failed to set gsm activation");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION);
+ // transit to idle state on the failure case
+ transitionTo(mIdleState);
+ }
+ retVal = HANDLED;
+ break;
+ default:
+ break;
+ }
+ return retVal;
+ }
+ }
+ private GsmActivatingState mGsmActivatingState = new GsmActivatingState();
+
+ /*
+ * The state waiting for the result to set cdma config.
+ */
+ private class CdmaConfiguringState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = NOT_HANDLED;
+ if (DBG) {
+ logd("CdmaConfiguringState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_REQUEST:
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+ case EVENT_CONFIGURATION_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Request request = (Request) ar.userObj;
+ if (DBG) {
+ logd("CdmaConfiguringState handle EVENT_ACTIVATION_DONE with request:"
+ + request);
+ }
+ if (ar.exception == null) {
+ // set cdma activation and transit to cdma activating state
+ setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
+ !request.get3gpp2Ranges().isEmpty(), request);
+ transitionTo(mCdmaActivatingState);
+ } else {
+ logd("Failed to set cdma config");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
+ // transit to idle state on the failure case
+ transitionTo(mIdleState);
+ }
+ retVal = HANDLED;
+ break;
+ default:
+ break;
+ }
+ return retVal;
+ }
+ }
+ private CdmaConfiguringState mCdmaConfiguringState = new CdmaConfiguringState();
+
+ /*
+ * The state waiting for the result to set cdma activation.
+ */
+ private class CdmaActivatingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = NOT_HANDLED;
+ if (DBG) {
+ logd("CdmaActivatingState message:" + msg.what);
+ }
+ switch (msg.what) {
+ case EVENT_REQUEST:
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+ case EVENT_ACTIVATION_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Request request = (Request) ar.userObj;
+ if (DBG) {
+ logd("CdmaActivatingState handle EVENT_ACTIVATION_DONE with request:"
+ + request);
+ }
+ if (ar.exception == null) {
+ mCbRanges3gpp2 = request.get3gpp2Ranges();
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+ } else {
+ logd("Failed to set cdma activation");
+ request.getCallback().accept(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION);
+ }
+ // transit to idle state anyway
+ transitionTo(mIdleState);
+ retVal = HANDLED;
+ break;
+ default:
+ break;
+ }
+ return retVal;
+ }
+ }
+ private CdmaActivatingState mCdmaActivatingState = new CdmaActivatingState();
+
+ private CellBroadcastConfigTracker(Phone phone) {
+ super("CellBroadcastConfigTracker-" + phone.getPhoneId());
+ init(phone);
+ }
+
+ private CellBroadcastConfigTracker(Phone phone, Handler handler) {
+ super("CellBroadcastConfigTracker-" + phone.getPhoneId(), handler);
+ init(phone);
+ }
+
+ private void init(Phone phone) {
+ logd("init");
+ mPhone = phone;
+ mSubId = mPhone.getSubId();
+
+ addState(mDefaultState);
+ addState(mIdleState, mDefaultState);
+ addState(mGsmConfiguringState, mDefaultState);
+ addState(mGsmActivatingState, mDefaultState);
+ addState(mCdmaConfiguringState, mDefaultState);
+ addState(mCdmaActivatingState, mDefaultState);
+ setInitialState(mIdleState);
+ }
+
+ /**
+ * create a CellBroadcastConfigTracker instance for the phone
+ */
+ public static CellBroadcastConfigTracker make(Phone phone, Handler handler,
+ boolean shouldStart) {
+ CellBroadcastConfigTracker tracker = handler == null
+ ? new CellBroadcastConfigTracker(phone)
+ : new CellBroadcastConfigTracker(phone, handler);
+ if (shouldStart) {
+ tracker.start();
+ }
+ return tracker;
+ }
+
+ /**
+ * Return current cell broadcast ranges.
+ */
+ @NonNull public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+ ranges.addAll(mCbRanges3gpp);
+ ranges.addAll(mCbRanges3gpp2);
+ return ranges;
+ }
+
+ /**
+ * Set reception of cell broadcast messages with the list of the given ranges.
+ */
+ public void setCellBroadcastIdRanges(
+ @NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) {
+ if (DBG) {
+ logd("setCellBroadcastIdRanges with ranges:" + ranges);
+ }
+ ranges = mergeRangesAsNeeded(ranges);
+ sendMessage(EVENT_REQUEST, new Request(ranges, callback));
+ }
+
+ /**
+ * Merge the overlapped CellBroadcastIdRanges in the list as needed
+ * @param ranges the list of CellBroadcastIdRanges
+ * @return the list of CellBroadcastIdRanges without overlapping
+ *
+ * @throws IllegalArgumentException if there is conflict of the ranges. For instance,
+ * the channel is enabled in some range, but disable in others.
+ */
+ @VisibleForTesting
+ public static @NonNull List<CellBroadcastIdRange> mergeRangesAsNeeded(
+ @NonNull List<CellBroadcastIdRange> ranges) throws IllegalArgumentException {
+ ranges.sort((r1, r2) -> r1.getType() != r2.getType() ? r1.getType() - r2.getType()
+ : (r1.getStartId() != r2.getStartId() ? r1.getStartId() - r2.getStartId()
+ : r2.getEndId() - r1.getEndId()));
+ final List<CellBroadcastIdRange> newRanges = new ArrayList<>();
+ ranges.forEach(r -> {
+ if (newRanges.isEmpty() || newRanges.get(newRanges.size() - 1).getType() != r.getType()
+ || newRanges.get(newRanges.size() - 1).getEndId() + 1 < r.getStartId()
+ || (newRanges.get(newRanges.size() - 1).getEndId() + 1 == r.getStartId()
+ && newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled())) {
+ newRanges.add(new CellBroadcastIdRange(r.getStartId(), r.getEndId(),
+ r.getType(), r.isEnabled()));
+ } else {
+ if (newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled()) {
+ throw new IllegalArgumentException("range conflict " + r);
+ }
+ if (r.getEndId() > newRanges.get(newRanges.size() - 1).getEndId()) {
+ CellBroadcastIdRange range = newRanges.get(newRanges.size() - 1);
+ newRanges.set(newRanges.size() - 1, new CellBroadcastIdRange(
+ range.getStartId(), r.getEndId(), range.getType(), range.isEnabled()));
+ }
+ }
+ });
+ return newRanges;
+ }
+
+ private void resetConfig() {
+ mCbRanges3gpp.clear();
+ mCbRanges3gpp2.clear();
+ }
+
+ private void setGsmConfig(List<CellBroadcastIdRange> ranges, Request request) {
+ if (DBG) {
+ logd("setGsmConfig with " + ranges);
+ }
+
+ SmsBroadcastConfigInfo[] configs = new SmsBroadcastConfigInfo[ranges.size()];
+ for (int i = 0; i < configs.length; i++) {
+ CellBroadcastIdRange r = ranges.get(i);
+ configs[i] = new SmsBroadcastConfigInfo(r.getStartId(), r.getEndId(),
+ SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, r.isEnabled());
+ }
+
+ Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request);
+ mPhone.mCi.setGsmBroadcastConfig(configs, response);
+ }
+
+ private void setCdmaConfig(List<CellBroadcastIdRange> ranges, Request request) {
+ if (DBG) {
+ logd("setCdmaConfig with " + ranges);
+ }
+
+ CdmaSmsBroadcastConfigInfo[] configs =
+ new CdmaSmsBroadcastConfigInfo[ranges.size()];
+ for (int i = 0; i < configs.length; i++) {
+ CellBroadcastIdRange r = ranges.get(i);
+ configs[i] = new CdmaSmsBroadcastConfigInfo(
+ r.getStartId(), r.getEndId(), 1, r.isEnabled());
+ }
+
+ Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request);
+ mPhone.mCi.setCdmaBroadcastConfig(configs, response);
+ }
+
+ private void setActivation(int type, boolean activate, Request request) {
+ if (DBG) {
+ logd("setActivation(" + type + "." + activate + ')');
+ }
+
+ Message response = obtainMessage(EVENT_ACTIVATION_DONE, request);
+
+ if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP) {
+ mPhone.mCi.setGsmBroadcastActivation(activate, response);
+ } else if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP2) {
+ mPhone.mCi.setCdmaBroadcastActivation(activate, response);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 19a585d..9cbd7a6 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.radio.V1_0.RegState;
import android.hardware.radio.V1_4.DataRegStateResult.VopsInfo.hidl_discriminator;
import android.hardware.radio.V1_6.RegStateResult.AccessTechnologySpecificInfo;
+import android.hardware.radio.network.RegState;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +35,7 @@
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
+import android.telephony.DataSpecificRegistrationInfo;
import android.telephony.LteVopsSupportInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
@@ -190,6 +191,8 @@
return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
case RegState.REG_ROAMING:
return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ case RegState.REG_EM:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY;
default:
return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
}
@@ -201,6 +204,7 @@
case RegState.NOT_REG_MT_SEARCHING_OP_EM:
case RegState.REG_DENIED_EM:
case RegState.UNKNOWN_EM:
+ case RegState.REG_EM:
return true;
case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
case RegState.REG_HOME:
@@ -500,6 +504,14 @@
final String rplmn = regResult.registeredPlmn;
final int reasonForDenial = regResult.reasonForDenial;
+ if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
+ && reasonForDenial
+ == android.hardware.radio.network.RegistrationFailCause.NONE) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"),
+ "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
+ }
+
int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
networkType = TelephonyManager.NETWORK_TYPE_LTE;
@@ -514,6 +526,8 @@
boolean isNrAvailable = false;
boolean isDcNrRestricted = false;
VopsSupportInfo vopsInfo = null;
+ int lteAttachResultType = 0;
+ int lteAttachExtraInfo = 0;
android.hardware.radio.network.AccessTechnologySpecificInfo info =
regResult.accessTechnologySpecificInfo;
@@ -532,6 +546,8 @@
vopsInfo = convertHalLteVopsSupportInfo(
info.getEutranInfo().lteVopsInfo.isVopsSupported,
info.getEutranInfo().lteVopsInfo.isEmcBearerSupported);
+ lteAttachResultType = info.getEutranInfo().lteAttachResultType;
+ lteAttachExtraInfo = info.getEutranInfo().extraInfo;
break;
case android.hardware.radio.network.AccessTechnologySpecificInfo.ngranNrVopsInfo:
vopsInfo = new NrVopsSupportInfo(info.getNgranNrVopsInfo().vopsSupported,
@@ -557,10 +573,26 @@
loge("Unknown domain passed to CellularNetworkService= " + domain);
// fall through
case NetworkRegistrationInfo.DOMAIN_PS:
- return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
- reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
- rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
- vopsInfo);
+ return new NetworkRegistrationInfo.Builder()
+ .setDomain(domain)
+ .setTransportType(transportType)
+ .setRegistrationState(regState)
+ .setAccessNetworkTechnology(networkType)
+ .setRejectCause(reasonForDenial)
+ .setEmergencyOnly(isEmergencyOnly)
+ .setAvailableServices(availableServices)
+ .setCellIdentity(cellIdentity)
+ .setRegisteredPlmn(rplmn)
+ .setDataSpecificInfo(
+ new DataSpecificRegistrationInfo.Builder(MAX_DATA_CALLS)
+ .setDcNrRestricted(isDcNrRestricted)
+ .setNrAvailable(isNrAvailable)
+ .setEnDcAvailable(isEndcAvailable)
+ .setVopsSupportInfo(vopsInfo)
+ .setLteAttachResultType(lteAttachResultType)
+ .setLteAttachExtraInfo(lteAttachExtraInfo)
+ .build())
+ .build();
}
}
@@ -582,6 +614,14 @@
networkType =
getNetworkTypeForCellIdentity(networkType, cellIdentity, mPhone.getCarrierId());
+ if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
+ && reasonForDenial
+ == android.hardware.radio.network.RegistrationFailCause.NONE) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"),
+ "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
+ }
+
// Conditional parameters for specific RANs
boolean cssSupported = false;
int roamingIndicator = 0;
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index 72bb6a3..e068c1c 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -341,7 +341,6 @@
return new CommandException(Error.RF_HARDWARE_ISSUE);
case RILConstants.NO_RF_CALIBRATION_INFO:
return new CommandException(Error.NO_RF_CALIBRATION_INFO);
-
default:
Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
return new CommandException(Error.INVALID_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 27cedfe..971e051 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -25,22 +25,31 @@
import android.os.Handler;
import android.os.Message;
import android.os.WorkSource;
+import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.BarringInfo;
import android.telephony.CarrierRestrictionRules;
import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.RadioAccessSpecifier;
import android.telephony.SignalThresholdInfo;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.NetworkSliceInfo;
import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.SimPhonebookRecord;
@@ -124,6 +133,15 @@
static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39;
static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM = 96;
+ /** IMS voice capability */
+ int IMS_MMTEL_CAPABILITY_VOICE = 1 << 0;
+ /** IMS video capability */
+ int IMS_MMTEL_CAPABILITY_VIDEO = 1 << 1;
+ /** IMS SMS capability */
+ int IMS_MMTEL_CAPABILITY_SMS = 1 << 2;
+ /** IMS RCS capabilities */
+ int IMS_RCS_CAPABILITIES = 1 << 3;
+
//***** Methods
/**
@@ -1773,6 +1791,17 @@
public void getDeviceIdentity(Message response);
/**
+ * Request the device IMEI / IMEI type / IMEISV
+ * "response" is ImeiInfo object that contains
+ * [0] ImeiType Indicates whether IMEI is of primary or secondary type
+ * [1] IMEI if GSM subscription is available
+ * [2] IMEISV if GSM subscription is available
+ *
+ * @param response Message
+ */
+ public void getImei(Message response);
+
+ /**
* Request the device MDN / H_SID / H_NID / MIN.
* "response" is const char **
* [0] is MDN if CDMA subscription is available
@@ -2086,10 +2115,15 @@
*
* Input parameters equivalent to TS 27.007 AT+CCHC command.
*
+ * Per spec SGP.22 V3.0, ES10 commands needs to be sent over command port of MEP-A. In order
+ * to close proper logical channel, should pass information about whether the logical channel
+ * was opened for sending ES10 commands or not.
+ *
* @param channel Channel id. Id of the channel to be closed.
+ * @param isEs10 Whether the logical channel is opened to perform ES10 operations.
* @param response Callback message.
*/
- public void iccCloseLogicalChannel(int channel, Message response);
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response);
/**
* Exchange APDUs with the SIM on a logical channel.
@@ -2105,11 +2139,12 @@
* @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
* is sent to the SIM.
* @param data Data to be sent with the APDU.
+ * @param isEs10Command whether APDU command is an ES10 command or a regular APDU
* @param response Callback message. response.obj.userObj will be
* an IccIoResult on success.
*/
- public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
- int p1, int p2, int p3, String data, Message response);
+ void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
+ int p1, int p2, int p3, String data, boolean isEs10Command, Message response);
/**
* Exchange APDUs with the SIM on a basic channel.
@@ -2186,11 +2221,21 @@
/**
* @return the radio hal version
+ * @deprecated use {@link #getHalVersion(int)}
*/
+ @Deprecated
default HalVersion getHalVersion() {
return HalVersion.UNKNOWN;
}
+ /**
+ * @param service indicate the service id to query.
+ * @return the hal version of a specific service
+ */
+ default HalVersion getHalVersion(@HalService int service) {
+ return HalVersion.UNKNOWN;
+ }
+
/**
* Sets user selected subscription at Modem.
*
@@ -2619,6 +2664,15 @@
default void getBarringInfo(Message result) {};
/**
+ * Returns the last barring information received.
+ *
+ * @return the last barring information.
+ */
+ default @Nullable BarringInfo getLastBarringInfo() {
+ return null;
+ };
+
+ /**
* Allocates a pdu session id
*
* AsyncResult.result is the allocated pdu session id
@@ -2747,6 +2801,54 @@
public void unregisterForSimPhonebookRecordsReceived(Handler h);
/**
+ * Registers for notifications of connection setup failure.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForConnectionSetupFailure(Handler h, int what, Object obj) {}
+
+ /**
+ * Unregisters for notifications of connection setup failure.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForConnectionSetupFailure(Handler h) {}
+
+ /**
+ * Registers for notifications when ANBR is received form the network.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForNotifyAnbr(Handler h, int what, Object obj) {}
+
+ /**
+ * Unregisters for notifications when ANBR is received form the network.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForNotifyAnbr(Handler h) {}
+
+ /**
+ * Registers for IMS deregistration trigger from modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {}
+
+ /**
+ * Unregisters for IMS deregistration trigger from modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForTriggerImsDeregistration(Handler h) {}
+
+ /**
* Set the UE's usage setting.
*
* @param result Callback message containing the success or failure status.
@@ -2761,4 +2863,417 @@
* @param result Callback message containing the usage setting (or a failure status).
*/
default void getUsageSetting(Message result) {}
+
+ /**
+ * Sets the emergency mode.
+ *
+ * @param emcMode Defines the radio emergency mode type.
+ * @param result Callback message containing the success or failure status.
+ */
+ default void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+ @Nullable Message result) {}
+
+ /**
+ * Triggers an emergency network scan.
+ *
+ * @param accessNetwork Contains the list of access network types to be prioritized
+ * during emergency scan. The 1st entry has the highest priority.
+ * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+ * full service scan or both.
+ * @param result Callback message containing the success or failure status.
+ */
+ default void triggerEmergencyNetworkScan(
+ @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+ @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {}
+
+ /**
+ * Cancels ongoing emergency network scan.
+ *
+ * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+ * If {@code true}, then the modem shall start the new scan from the beginning,
+ * otherwise the modem shall resume from the last search.
+ * @param result Callback message containing the success or failure status.
+ */
+ default void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {}
+
+ /**
+ * Exits ongoing emergency mode.
+ *
+ * @param result Callback message containing the success or failure status.
+ */
+ default void exitEmergencyMode(@Nullable Message result) {}
+
+ /**
+ * Registers for emergency network scan result.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForEmergencyNetworkScan(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for emergency network scan result.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForEmergencyNetworkScan(@NonNull Handler h) {}
+
+ /**
+ * Provides a list of SRVCC call information to radio
+ *
+ * @param srvccConnections the list of connections.
+ */
+ default void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {}
+
+ /**
+ * Updates the IMS registration information to the radio.
+ *
+ * @param state The current IMS registration state.
+ * @param imsRadioTech The type of underlying radio access network used.
+ * @param suggestedAction The suggested action for the radio to perform.
+ * @param capabilities IMS capabilities such as VOICE, VIDEO and SMS.
+ */
+ default void updateImsRegistrationInfo(int state,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ int capabilities, Message result) {}
+
+ /**
+ * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+ *
+ * @param token A nonce to identify the request.
+ * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+ * @param accessNetworkType The type of underlying radio access network used.
+ * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+ * mobile terminated use case eg. MO/MT call/SMS etc.
+ */
+ default void startImsTraffic(int token,
+ @MmTelFeature.ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection,
+ Message result) {}
+
+ /**
+ * Notifies IMS traffic has been stopped.
+ *
+ * @param token The token assigned by startImsTraffic.
+ */
+ default void stopImsTraffic(int token, Message result) {}
+
+ /**
+ * Triggers the UE initiated EPS fallback procedure.
+ *
+ * @param reason Specifies the reason for EPS fallback.
+ */
+ default void triggerEpsFallback(int reason, Message result) {}
+
+ /**
+ * Triggers radio to send ANBRQ message to the network.
+ *
+ * @param mediaType Media type is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond The bit rate requested by the opponent UE.
+ * @param result Callback message to receive the result.
+ */
+ default void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, Message result) {}
+
+ /**
+ * Set the UE's ability to accept/reject null ciphered and/or null integrity-protected
+ * connections.
+ *
+ * @param enabled true to allow null ciphered and/or null integrity-protected connections,
+ * false to disallow.
+ * @param result Callback message containing the success or failure status.
+ */
+ default void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) {}
+
+ /**
+ * Check whether null ciphering and/or null integrity-protected connections are allowed.
+ *
+ * @param result Callback message containing the success or failure status.
+ */
+ default void isNullCipherAndIntegrityEnabled(Message result) {}
+
+ /**
+ * Notifies the IMS call status to the modem.
+ *
+ * @param imsCallInfo The list of {@link ImsCallInfo}.
+ * @param result A callback to receive the response.
+ */
+ default void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) {}
+
+ /**
+ * Enables or disables N1 mode (access to 5G core network) in accordance with
+ * 3GPP TS 24.501 4.9.
+ * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode.
+ * @param result Callback message to receive the result.
+ */
+ default void setN1ModeEnabled(boolean enable, Message result) {}
+
+ /**
+ * Check whether N1 mode (access to 5G core network) is enabled or not.
+ * @param result Callback message to receive the result.
+ */
+ default void isN1ModeEnabled(Message result) {}
+
+ /**
+ * Get feature capabilities supported by satellite.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ default void getSatelliteCapabilities(Message result) {}
+
+ /**
+ * Turn satellite modem on/off.
+ *
+ * @param result Message that will be sent back to the requester
+ * @param on {@code true} for turning on.
+ * {@code false} for turning off.
+ */
+ default void setSatellitePower(Message result, boolean on) {}
+
+ /**
+ * Get satellite modem state.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ default void getSatellitePowerState(Message result) {}
+
+ /**
+ * Get satellite provision state.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ default void getSatelliteProvisionState(Message result) {}
+
+ /**
+ * Check whether satellite modem is supported by the device.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ default void isSatelliteSupported(Message result) {}
+
+ /**
+ * Provision the subscription with a satellite provider. This is needed to register the
+ * subscription if the provider allows dynamic registration.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param imei IMEI of the SIM associated with the satellite modem.
+ * @param msisdn MSISDN of the SIM associated with the satellite modem.
+ * @param imsi IMSI of the SIM associated with the satellite modem.
+ * @param features List of features to be provisioned.
+ */
+ default void provisionSatelliteService(
+ Message result, String imei, String msisdn, String imsi, int[] features) {}
+
+ /**
+ * Add contacts that are allowed to be used for satellite communication. This is applicable for
+ * incoming messages as well.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param contacts List of allowed contacts to be added.
+ */
+ default void addAllowedSatelliteContacts(Message result, String[] contacts) {}
+
+ /**
+ * Remove contacts that are allowed to be used for satellite communication. This is applicable
+ * for incoming messages as well.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param contacts List of allowed contacts to be removed.
+ */
+ default void removeAllowedSatelliteContacts(Message result, String[] contacts) {}
+
+ /**
+ * Send text messages.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param messages List of messages in text format to be sent.
+ * @param destination The recipient of the message.
+ * @param latitude The current latitude of the device.
+ * @param longitude The current longitude of the device. The location (i.e., latitude and
+ * longitude) of the device will be filled for emergency messages.
+ */
+ default void sendSatelliteMessages(Message result, String[] messages, String destination,
+ double latitude, double longitude) {}
+
+ /**
+ * Get pending messages.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void getPendingSatelliteMessages(Message result) {}
+
+ /**
+ * Get current satellite registration mode.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void getSatelliteMode(Message result) {}
+
+ /**
+ * Set the filter for what type of indication framework want to receive from modem.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param filterBitmask The filter bitmask identifying what type of indication Telephony
+ * framework wants to receive from modem.
+ */
+ default void setSatelliteIndicationFilter(Message result, int filterBitmask) {}
+
+ /**
+ * User started pointing to the satellite. Modem should continue to update the ponting input
+ * as user moves device.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void startSendingSatellitePointingInfo(Message result) {}
+
+ /**
+ * Stop sending satellite pointing info to the framework.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void stopSendingSatellitePointingInfo(Message result) {}
+
+ /**
+ * Get max number of characters per text message.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void getMaxCharactersPerSatelliteTextMessage(Message result) {}
+
+ /**
+ * Get whether satellite communication is allowed for the current location.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {}
+
+ /**
+ * Get the time after which the satellite will be visible.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ default void getTimeForNextSatelliteVisibility(Message result) {}
+
+ /**
+ * Registers for pending message count from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForPendingSatelliteMessageCount(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for pending message count from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) {}
+
+ /**
+ * Registers for new messages from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForNewSatelliteMessages(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for new messages from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForNewSatelliteMessages(@NonNull Handler h) {}
+
+ /**
+ * Registers for messages transfer complete from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForSatelliteMessagesTransferComplete(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for messages transfer complete from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) {}
+
+ /**
+ * Registers for pointing info changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForSatellitePointingInfoChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for pointing info changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) {}
+
+ /**
+ * Registers for mode changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForSatelliteModeChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for mode changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForSatelliteModeChanged(@NonNull Handler h) {}
+
+ /**
+ * Registers for radio technology changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for radio technology changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) {}
+
+ /**
+ * Registers for provision state changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ default void registerForSatelliteProvisionStateChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {}
+
+ /**
+ * Unregisters for provision state changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ default void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {}
}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index c60e5df..68fd6ab 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -27,6 +27,8 @@
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.MmTelFeature.ImsAudioHandler;
import android.util.Log;
import com.android.ims.internal.ConferenceParticipant;
@@ -139,6 +141,13 @@
* @param extensionData The extension data.
*/
public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData);
+
+ /**
+ * Indicates that the audio handler for this connection is changed.
+ *
+ * @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler}.
+ */
+ void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler);
}
/**
@@ -194,6 +203,8 @@
public void onReceivedDtmfDigit(char digit) {}
@Override
public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {}
+ @Override
+ public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) {}
}
public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -328,6 +339,41 @@
/* Instance Methods */
/**
+ * PhoneFactory Dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneFactoryProxy {
+ Phone getPhone(int index);
+ Phone getDefaultPhone();
+ Phone[] getPhones();
+ }
+
+ private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
+ @Override
+ public Phone getPhone(int index) {
+ return PhoneFactory.getPhone(index);
+ }
+
+ @Override
+ public Phone getDefaultPhone() {
+ return PhoneFactory.getDefaultPhone();
+ }
+
+ @Override
+ public Phone[] getPhones() {
+ return PhoneFactory.getPhones();
+ }
+ };
+
+ /**
+ * Overrides PhoneFactory dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
+ mPhoneFactoryProxy = proxy;
+ }
+
+ /**
* @return The telecom internal call ID associated with this connection. Only to be used for
* debugging purposes.
*/
@@ -590,14 +636,35 @@
*/
public void setEmergencyCallInfo(CallTracker ct) {
if (ct != null) {
- Phone phone = ct.getPhone();
- if (phone != null) {
- EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+ Phone currentPhone = ct.getPhone();
+ if (currentPhone != null) {
+ EmergencyNumberTracker tracker = currentPhone.getEmergencyNumberTracker();
if (tracker != null) {
EmergencyNumber num = tracker.getEmergencyNumber(mAddress);
+ Phone[] allPhones = mPhoneFactoryProxy.getPhones();
if (num != null) {
mIsEmergencyCall = true;
mEmergencyNumberInfo = num;
+ } else if (allPhones.length > 1) {
+ // If there are multiple active SIMs, check all instances:
+ boolean found = false;
+ for (Phone phone : allPhones) {
+ // If the current iteration was already checked, skip:
+ if (phone.getPhoneId() == currentPhone.getPhoneId()){
+ continue;
+ }
+ num = phone.getEmergencyNumberTracker()
+ .getEmergencyNumber(mAddress);
+ if (num != null){
+ found = true;
+ mIsEmergencyCall = true;
+ mEmergencyNumberInfo = num;
+ break;
+ }
+ }
+ if (!found){
+ Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null");
+ }
} else {
Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null");
}
@@ -1519,6 +1586,25 @@
}
/**
+ * Called to report audio mode changed for Voip.
+ * @param imsAudioHandler the received value to handle the audio for this IMS call.
+ */
+ public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) {
+ Rlog.i(TAG, "onAudioModeIsVoipChanged: conn imsAudioHandler " + imsAudioHandler);
+
+ boolean isVoip = imsAudioHandler == MmTelFeature.AUDIO_HANDLER_ANDROID;
+ if (isVoip == mAudioModeIsVoip) return;
+ mAudioModeIsVoip = isVoip;
+
+ Rlog.i(TAG, "onAudioModeIsVoipChanged: isVoip: " + isVoip
+ + "mAudioModeIsVoip:" + mAudioModeIsVoip);
+
+ for (Listener l : mListeners) {
+ l.onAudioModeIsVoipChanged(imsAudioHandler);
+ }
+ }
+
+ /**
* Called to report RTP header extensions received from the network.
* @param extensionData the received extension data.
*/
diff --git a/src/java/com/android/internal/telephony/DataIndication.java b/src/java/com/android/internal/telephony/DataIndication.java
index c16cbaa..205c4d8 100644
--- a/src/java/com/android/internal/telephony/DataIndication.java
+++ b/src/java/com/android/internal/telephony/DataIndication.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
@@ -53,9 +55,9 @@
*/
public void dataCallListChanged(int indicationType,
android.hardware.radio.data.SetupDataCallResult[] dcList) {
- mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_DATA, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList);
mRil.mDataCallListChangedRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -68,11 +70,12 @@
*/
public void keepaliveStatus(int indicationType,
android.hardware.radio.data.KeepaliveStatus halStatus) {
- mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_DATA, indicationType);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS, "handle=" + halStatus.sessionHandle
- + " code=" + halStatus.code);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(
+ RIL_UNSOL_KEEPALIVE_STATUS,
+ "handle=" + halStatus.sessionHandle + " code=" + halStatus.code);
}
KeepaliveStatus ks = new KeepaliveStatus(
@@ -86,11 +89,11 @@
* @param pco New PcoData
*/
public void pcoData(int indicationType, android.hardware.radio.data.PcoDataInfo pco) {
- mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_DATA, indicationType);
PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId, pco.contents);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -103,10 +106,10 @@
*/
public void unthrottleApn(int indicationType, android.hardware.radio.data.DataProfileInfo dpi)
throws RemoteException {
- mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_DATA, indicationType);
DataProfile response = RILUtils.convertToDataProfile(dpi);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response);
mRil.mApnUnthrottledRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -119,8 +122,10 @@
*/
public void slicingConfigChanged(int indicationType,
android.hardware.radio.data.SlicingConfig slicingConfig) throws RemoteException {
- mRil.processIndication(RIL.DATA_SERVICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
+ mRil.processIndication(HAL_SERVICE_DATA, indicationType);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
+ }
NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
mRil.mSlicingConfigChangedRegistrants.notifyRegistrants(
new AsyncResult(null, ret, null));
diff --git a/src/java/com/android/internal/telephony/DataResponse.java b/src/java/com/android/internal/telephony/DataResponse.java
index 7cfe13b..bef1da7 100644
--- a/src/java/com/android/internal/telephony/DataResponse.java
+++ b/src/java/com/android/internal/telephony/DataResponse.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.data.IRadioDataResponse;
@@ -51,7 +53,7 @@
* @param id The pdu session id allocated
*/
public void allocatePduSessionIdResponse(RadioResponseInfo responseInfo, int id) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
RadioResponse.sendMessageResponse(rr.mResult, id);
@@ -64,14 +66,14 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void cancelHandoverResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
@@ -80,7 +82,7 @@
*/
public void getDataCallListResponse(RadioResponseInfo responseInfo,
android.hardware.radio.data.SetupDataCallResult[] dataCallResultList) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr != null) {
ArrayList<DataCallResponse> response =
@@ -98,7 +100,7 @@
*/
public void getSlicingConfigResponse(RadioResponseInfo responseInfo,
android.hardware.radio.data.SlicingConfig slicingConfig) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr != null) {
NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
@@ -113,35 +115,35 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void releasePduSessionIdResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setDataAllowedResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setDataProfileResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setDataThrottlingResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setInitialAttachApnResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
@@ -150,7 +152,7 @@
*/
public void setupDataCallResponse(RadioResponseInfo responseInfo,
android.hardware.radio.data.SetupDataCallResult setupDataCallResult) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr != null) {
DataCallResponse response = RILUtils.convertHalDataCallResult(setupDataCallResult);
@@ -165,7 +167,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void startHandoverResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
}
/**
@@ -175,7 +177,7 @@
public void startKeepaliveResponse(RadioResponseInfo responseInfo,
android.hardware.radio.data.KeepaliveStatus keepaliveStatus) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr == null) return;
KeepaliveStatus ret = null;
@@ -214,7 +216,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
- RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
if (rr == null) return;
try {
diff --git a/src/java/com/android/internal/telephony/DebugService.java b/src/java/com/android/internal/telephony/DebugService.java
index 0b759b5..3341577 100644
--- a/src/java/com/android/internal/telephony/DebugService.java
+++ b/src/java/com/android/internal/telephony/DebugService.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import android.os.Build;
+
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -50,8 +52,16 @@
TelephonyMetrics.getInstance().dump(fd, pw, args);
return;
case "--saveatoms":
- log("Saving atoms..");
- PhoneFactory.getMetricsCollector().getAtomsStorage().flushAtoms();
+ if (Build.IS_DEBUGGABLE) {
+ log("Saving atoms..");
+ PhoneFactory.getMetricsCollector().flushAtomsStorage();
+ }
+ return;
+ case "--clearatoms":
+ if (Build.IS_DEBUGGABLE) {
+ log("Clearing atoms..");
+ PhoneFactory.getMetricsCollector().clearAtomsStorage();
+ }
return;
}
}
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index e4aff4c..e5a5c8f 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.telephony.Annotation;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
import android.telephony.BarringInfo;
@@ -32,9 +33,13 @@
import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
import android.telephony.TelephonyRegistryManager;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
import com.android.telephony.Rlog;
@@ -142,15 +147,28 @@
mTelephonyRegistryMgr.notifyCellInfoChanged(subId, cellInfo);
}
- public void notifyPreciseCallState(Phone sender) {
+ /**
+ * Notify precise call state of foreground, background and ringing call states.
+ *
+ * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId} for
+ * ringing, foreground & background calls.
+ * @param imsCallServiceTypes Array of IMS call service type for ringing, foreground &
+ * background calls.
+ * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls.
+ */
+ public void notifyPreciseCallState(Phone sender, String[] imsCallIds,
+ @Annotation.ImsCallServiceType int[] imsCallServiceTypes,
+ @Annotation.ImsCallType int[] imsCallTypes) {
Call ringingCall = sender.getRingingCall();
Call foregroundCall = sender.getForegroundCall();
Call backgroundCall = sender.getBackgroundCall();
+
if (ringingCall != null && foregroundCall != null && backgroundCall != null) {
- mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
- convertPreciseCallState(ringingCall.getState()),
+ int[] callStates = {convertPreciseCallState(ringingCall.getState()),
convertPreciseCallState(foregroundCall.getState()),
- convertPreciseCallState(backgroundCall.getState()));
+ convertPreciseCallState(backgroundCall.getState())};
+ mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
+ callStates, imsCallIds, imsCallServiceTypes, imsCallTypes);
}
}
@@ -223,6 +241,12 @@
}
@Override
+ public void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status) {
+ mTelephonyRegistryMgr.notifyMediaQualityStatusChanged(
+ sender.getPhoneId(), sender.getSubId(), status);
+ }
+
+ @Override
public void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity,
@NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
mTelephonyRegistryMgr.notifyRegistrationFailed(sender.getPhoneId(), sender.getSubId(),
@@ -262,6 +286,18 @@
sender.getSubId(), linkCapacityEstimateList);
}
+ @Override
+ public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type) {
+ mTelephonyRegistryMgr.notifyCallBackModeStarted(sender.getPhoneId(),
+ sender.getSubId(), type);
+ }
+
+ @Override
+ public void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
+ @EmergencyCallbackModeStopReason int reason) {
+ mTelephonyRegistryMgr.notifyCallbackModeStopped(sender.getPhoneId(),
+ sender.getSubId(), type, reason);
+ }
/**
* Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants
* for the public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 3d63a29..ecc6208 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -20,6 +20,7 @@
import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
@@ -669,7 +670,7 @@
LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN);
mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000);
- if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+ if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.NGRAN);
}
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index 886a899..567331d 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -18,11 +18,11 @@
import android.annotation.NonNull;
import android.os.Handler;
+import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
import android.telephony.AnomalyReporter;
-import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.IndentingPrintWriter;
@@ -45,8 +45,6 @@
* TelephonyDisplayInfo via {@link #getTelephonyDisplayInfo}.
*/
public class DisplayInfoController extends Handler {
- private static final String TAG = "DisplayInfoController";
-
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
@@ -66,22 +64,36 @@
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED)
);
+ /** Event for service state changed (roaming). */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
private final Phone mPhone;
private final NetworkTypeController mNetworkTypeController;
private final RegistrantList mTelephonyDisplayInfoChangedRegistrants = new RegistrantList();
- private TelephonyDisplayInfo mTelephonyDisplayInfo;
+ private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo;
+ private @NonNull ServiceState mServiceState;
public DisplayInfoController(Phone phone) {
mPhone = phone;
mLogTag = "DIC-" + mPhone.getPhoneId();
+ mTelephonyDisplayInfo = new TelephonyDisplayInfo(
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
mNetworkTypeController = new NetworkTypeController(phone, this);
mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ post(() -> {
+ mPhone.getServiceStateTracker()
+ .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+ updateTelephonyDisplayInfo();
+ });
}
/**
* @return the current TelephonyDisplayInfo
*/
- public TelephonyDisplayInfo getTelephonyDisplayInfo() {
+ public @NonNull TelephonyDisplayInfo getTelephonyDisplayInfo() {
return mTelephonyDisplayInfo;
}
@@ -90,12 +102,10 @@
* NetworkTypeController.
*/
public void updateTelephonyDisplayInfo() {
- NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- int dataNetworkType = nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
- : nri.getAccessNetworkTechnology();
- TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(dataNetworkType,
- mNetworkTypeController.getOverrideNetworkType());
+ TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(
+ mNetworkTypeController.getDataNetworkType(),
+ mNetworkTypeController.getOverrideNetworkType(),
+ mServiceState.getRoaming());
if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) {
logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to "
+ newDisplayInfo);
@@ -133,7 +143,7 @@
}
} catch (InvalidArgumentException e) {
logel(e.getMessage());
- AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a55"),
+ AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a56"),
e.getMessage(), mPhone.getCarrierId());
}
}
@@ -157,6 +167,17 @@
mTelephonyDisplayInfoChangedRegistrants.remove(h);
}
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ log("ServiceState updated, isRoaming=" + mServiceState.getRoaming());
+ updateTelephonyDisplayInfo();
+ break;
+ }
+ }
+
/**
* Log debug messages.
* @param s debug messages
diff --git a/src/java/com/android/internal/telephony/FdnUtils.java b/src/java/com/android/internal/telephony/FdnUtils.java
new file mode 100644
index 0000000..aa2bcfd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/FdnUtils.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.text.TextUtils;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+
+/**
+ * This is a basic utility class for common functions related to Fixed Dialing Numbers
+ * designed as per 3GPP 22.101.
+ */
+public class FdnUtils {
+ private static final boolean VDBG = false;
+ private static final String LOG_TAG = FdnUtils.class.getSimpleName();
+
+ /**
+ * The following function checks if dialed number is blocked due to FDN.
+ *
+ * @param phoneId The phone object id for which the FDN check is performed
+ * @param dialStr dialed phone number
+ * @param defaultCountryIso country ISO for the subscription associated with this phone
+ * @return {@code true} if dialStr is blocked due to FDN check.
+ */
+ public static boolean isNumberBlockedByFDN(int phoneId, String dialStr,
+ String defaultCountryIso) {
+ if (!isFdnEnabled(phoneId)) {
+ return false;
+ }
+
+ ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+ return !isFDN(dialStr, defaultCountryIso, fdnList);
+ }
+
+ /**
+ * Checks if FDN is enabled
+ * @param phoneId The phone object id for which the FDN check is performed
+ * @return {@code true} if FDN is enabled
+ */
+ public static boolean isFdnEnabled(int phoneId) {
+ UiccCardApplication app = getUiccCardApplication(phoneId);
+ if (app == null || (!app.getIccFdnAvailable())) {
+ return false;
+ }
+
+ return app.getIccFdnEnabled();
+ }
+
+ /**
+ * If FDN is enabled, check to see if the given supplementary service control strings are
+ * blocked due to FDN.
+ * @param phoneId The phone object id for which the FDN check is performed
+ * @param controlStrings control strings associated with the supplementary service request
+ * @param defaultCountryIso country ISO for the subscription associated with this phone
+ * @return {@code true} if the FDN list does not contain any of the control strings.
+ */
+ public static boolean isSuppServiceRequestBlockedByFdn(int phoneId,
+ ArrayList<String> controlStrings, String defaultCountryIso) {
+ if (!isFdnEnabled(phoneId)) {
+ return false;
+ }
+
+ ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+ for(String controlString : controlStrings) {
+ if(isFDN(controlString, defaultCountryIso, fdnList)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if dialStr is part of FDN list.
+ *
+ * @param fdnList List of all FDN records associated with a sim card
+ * @param dialStr dialed phone number
+ * @param defaultCountryIso country ISO for the subscription associated with this phone
+ * @return {@code true} if dialStr is present in the fdnList.
+ */
+ @VisibleForTesting
+ public static boolean isFDN(String dialStr, String defaultCountryIso,
+ ArrayList<AdnRecord> fdnList) {
+ if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) {
+ Rlog.w(LOG_TAG, "isFDN: unexpected null value");
+ return false;
+ }
+
+ // Parse the dialStr and convert it to E164 format
+ String dialStrE164 = null;
+ String dialStrNational = null;
+ final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso);
+ dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164);
+ dialStrNational = String.valueOf(phoneNumber.getNationalNumber());
+ } catch (NumberParseException ignored) {
+ Rlog.w(LOG_TAG, "isFDN: could not parse dialStr");
+ }
+
+ /**
+ * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber
+ * E.g.1: returns true if fdnNumber="123" and dialStr="12345"
+ * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345"
+ */
+ for (AdnRecord fdn: fdnList) {
+ String fdnNumber = fdn.getNumber();
+ if (TextUtils.isEmpty(fdnNumber)) {
+ continue;
+ }
+
+ if(!TextUtils.isEmpty(dialStrE164)) {
+ if(dialStrE164.startsWith(fdnNumber)) {
+ return true;
+ }
+ }
+
+ if(!TextUtils.isEmpty(dialStrNational)) {
+ if (dialStrNational.startsWith(fdnNumber)) {
+ return true;
+ }
+ }
+
+ if (dialStr.startsWith(fdnNumber)) {
+ return true;
+ }
+ }
+
+ if (VDBG) {
+ Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list");
+ }
+ return false;
+ }
+
+ private static ArrayList<AdnRecord> getFdnList(int phoneId) {
+ UiccCardApplication app = getUiccCardApplication(phoneId);
+ if (app == null) {
+ return null;
+ }
+
+ IccRecords iccRecords = app.getIccRecords();
+ if (iccRecords == null) {
+ return null;
+ }
+
+ AdnRecordCache adnRecordCache = iccRecords.getAdnCache();
+ if(adnRecordCache == null) {
+ return null;
+ }
+
+ return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN);
+ }
+
+ private static UiccCardApplication getUiccCardApplication(int phoneId) {
+ UiccProfile uiccProfile = UiccController.getInstance()
+ .getUiccProfileForPhone(phoneId);
+ if (uiccProfile == null) {
+ return null;
+ }
+
+ return uiccProfile.getApplication(UiccController.APP_FAM_3GPP);
+ }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
old mode 100755
new mode 100644
index 3293558..d76ee19
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -45,6 +45,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -182,11 +184,6 @@
mCi.unregisterForCallWaitingInfo(this);
// Prior to phone switch to GSM, if CDMA has any emergency call
// data will be in disabled state, after switching to GSM enable data.
- if (mIsInEmergencyCall) {
- if (!mPhone.isUsingNewDataStack()) {
- mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
- }
- }
} else {
mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
mPendingCallInEcm = false;
@@ -400,9 +397,6 @@
//CDMA
public void setIsInEmergencyCall() {
mIsInEmergencyCall = true;
- if (!mPhone.isUsingNewDataStack()) {
- mPhone.getDataEnabledSettings().setInternalDataEnabled(false);
- }
mPhone.notifyEmergencyCallRegistrants(true);
mPhone.sendEmergencyCallStateChange(true);
}
@@ -488,16 +482,29 @@
disableDataCallInEmergencyCall(dialString);
// In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
- if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
+ if (!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(),
mPendingMO.getEmergencyNumberInfo(),
- mPendingMO.hasKnownUserIntentEmergency(),
- clirMode, obtainCompleteMessage());
+ mPendingMO.hasKnownUserIntentEmergency(), clirMode,
+ obtainCompleteMessage());
+ } else if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ mPendingCallInEcm = true;
+ final int finalClirMode = clirMode;
+ Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(),
+ mPendingMO.getEmergencyNumberInfo(),
+ mPendingMO.hasKnownUserIntentEmergency(), finalClirMode,
+ obtainCompleteMessage());
+ }
+ };
+ EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete);
} else {
mPhone.exitEmergencyCallbackMode();
- mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
- mPendingCallClirMode=clirMode;
- mPendingCallInEcm=true;
+ mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
+ mPendingCallClirMode = clirMode;
+ mPendingCallInEcm = true;
}
}
@@ -967,6 +974,8 @@
} else {
newUnknownConnectionCdma = mConnections[i];
}
+ } else if (hangupWaitingCallSilently(i)) {
+ return;
}
}
}
@@ -1018,6 +1027,9 @@
if (mConnections[i].getCall() == mRingingCall) {
newRinging = mConnections[i];
+ if (hangupWaitingCallSilently(i)) {
+ return;
+ }
} // else something strange happened
hasNonHangupStateChanged = true;
} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
@@ -1754,9 +1766,6 @@
}
if (!inEcm) {
// Re-initiate data connection
- if (!mPhone.isUsingNewDataStack()) {
- mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
- }
mPhone.notifyEmergencyCallRegistrants(false);
}
mPhone.sendEmergencyCallStateChange(false);
@@ -1830,6 +1839,10 @@
}
private boolean isEmcRetryCause(int causeCode) {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ log("isEmcRetryCause AP based domain selection ignores the cause");
+ return false;
+ }
if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS ||
causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) {
return true;
@@ -1909,4 +1922,22 @@
public void cleanupCalls() {
pollCallsWhenSafe();
}
+
+ private boolean hangupWaitingCallSilently(int index) {
+ if (index < 0 || index >= mConnections.length) return false;
+
+ GsmCdmaConnection newRinging = mConnections[index];
+ if (newRinging == null) return false;
+
+ if ((mPhone.getTerminalBasedCallWaitingState(true)
+ == CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED)
+ && (newRinging.getState() == Call.State.WAITING)) {
+ Rlog.d(LOG_TAG, "hangupWaitingCallSilently");
+ newRinging.dispose();
+ mConnections[index] = null;
+ mCi.hangupWaitingOrBackground(obtainCompleteMessage());
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index b79bdef..e06520a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -478,7 +478,7 @@
* @return the corresponding value from {@link DisconnectCause}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- int disconnectCauseFromCode(int causeCode) {
+ public int disconnectCauseFromCode(int causeCode) {
/**
* See 22.001 Annex F.4 for mapping of cause codes
* to local tones
@@ -565,6 +565,14 @@
case CallFailCause.RADIO_OFF:
return DisconnectCause.POWER_OFF;
+ case CallFailCause.NO_VALID_SIM:
+ return DisconnectCause.ICC_ERROR;
+
+ case CallFailCause.LOCAL_NETWORK_NO_SERVICE:
+ // fallthrough
+ case CallFailCause.LOCAL_SERVICE_UNAVAILABLE:
+ return DisconnectCause.OUT_OF_SERVICE;
+
case CallFailCause.ACCESS_CLASS_BLOCKED:
case CallFailCause.ERROR_UNSPECIFIED:
case CallFailCause.NORMAL_CLEARING:
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index ff8412f..5eae061 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -16,6 +16,11 @@
package com.android.internal.telephony;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+
import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE;
import static com.android.internal.telephony.CommandException.Error.SIM_BUSY;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
@@ -32,6 +37,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -40,6 +46,7 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.SQLException;
+import android.hardware.radio.modem.ImeiInfo;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Build;
@@ -57,6 +64,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.preference.PreferenceManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Telephony;
import android.sysprop.TelephonyProperties;
@@ -64,11 +72,13 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
-import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.RadioPowerState;
+import android.telephony.AnomalyReporter;
import android.telephony.BarringInfo;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellBroadcastIdRange;
import android.telephony.CellIdentity;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.LinkCapacityEstimate;
@@ -82,7 +92,7 @@
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.UssdResponse;
-import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsCallProfile;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -94,17 +104,19 @@
import com.android.internal.telephony.data.AccessNetworksManager;
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.gsm.GsmMmiCode;
+import com.android.internal.telephony.gsm.SsData;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccCardStatus;
@@ -131,6 +143,9 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.UUID;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -157,6 +172,8 @@
// Key used to read/write the SIM IMSI used for storing the voice mail
private static final String VM_SIM_IMSI = "vm_sim_imsi_key";
/** List of Registrants to receive Supplementary Service Notifications. */
+ // Key used to read/write the current sub Id. Updated on SIM loaded.
+ public static final String CURR_SUBID = "curr_subid";
private RegistrantList mSsnRegistrants = new RegistrantList();
//CDMA
@@ -227,10 +244,17 @@
private final RegistrantList mVolteSilentRedialRegistrants = new RegistrantList();
private DialArgs mDialArgs = null;
-
+ private final RegistrantList mEmergencyDomainSelectedRegistrants = new RegistrantList();
private String mImei;
private String mImeiSv;
private String mVmNumber;
+ private int mImeiType = IMEI_TYPE_UNKNOWN;
+
+ @VisibleForTesting
+ public CellBroadcastConfigTracker mCellBroadcastConfigTracker =
+ CellBroadcastConfigTracker.make(this, null, true);
+
+ private boolean mIsNullCipherAndIntegritySupported = false;
// Create Cfu (Call forward unconditional) so that dialing number &
// mOnComplete (Message object passed by client) can be packed &
@@ -271,12 +295,11 @@
private CarrierKeyDownloadManager mCDM;
private CarrierInfoManager mCIM;
- private final SettingsObserver mSettingsObserver;
-
private final ImsManagerFactory mImsManagerFactory;
private final CarrierPrivilegesTracker mCarrierPrivilegesTracker;
private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
+ private final CallWaitingController mCallWaitingController;
// Constructors
@@ -316,10 +339,6 @@
mAccessNetworksManager = mTelephonyComponentFactory
.inject(AccessNetworksManager.class.getName())
.makeAccessNetworksManager(this, getLooper());
- if (!isUsingNewDataStack()) {
- mTransportManager = mTelephonyComponentFactory.inject(TransportManager.class.getName())
- .makeTransportManager(this);
- }
// SST/DSM depends on SSC, so SSC is instanced before SST/DSM
mSignalStrengthController = mTelephonyComponentFactory.inject(
SignalStrengthController.class.getName()).makeSignalStrengthController(this);
@@ -328,10 +347,6 @@
mEmergencyNumberTracker = mTelephonyComponentFactory
.inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
this, this.mCi);
- if (!isUsingNewDataStack()) {
- mDataEnabledSettings = mTelephonyComponentFactory
- .inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this);
- }
mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
.makeDeviceStateMonitor(this);
@@ -340,20 +355,9 @@
mDisplayInfoController = mTelephonyComponentFactory.inject(
DisplayInfoController.class.getName()).makeDisplayInfoController(this);
- if (isUsingNewDataStack()) {
- mDataNetworkController = mTelephonyComponentFactory.inject(
- DataNetworkController.class.getName())
- .makeDataNetworkController(this, getLooper());
- } else {
- // DcTracker uses ServiceStateTracker and DisplayInfoController so needs to be created
- // after they are instantiated
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- DcTracker dcTracker = mTelephonyComponentFactory.inject(DcTracker.class.getName())
- .makeDcTracker(this, transport);
- mDcTrackers.put(transport, dcTracker);
- mAccessNetworksManager.registerDataThrottler(dcTracker.getDataThrottler());
- }
- }
+ mDataNetworkController = mTelephonyComponentFactory.inject(
+ DataNetworkController.class.getName())
+ .makeDataNetworkController(this, getLooper());
mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
.makeCarrierResolver(this);
@@ -365,23 +369,22 @@
mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null);
+ mSST.getServiceStateStats().registerDataNetworkControllerCallback();
- // TODO: Remove SettingsObserver and provisioning events when DataEnabledSettings is removed
- mSettingsObserver = new SettingsObserver(context, this);
- mSettingsObserver.observe(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- EVENT_DEVICE_PROVISIONED_CHANGE);
- mSettingsObserver.observe(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
- EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE);
-
- SubscriptionController.getInstance().registerForUiccAppsEnabled(this,
- EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false);
+ mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback(
+ this::post) {
+ @Override
+ public void onUiccApplicationsEnabledChanged(int subId) {
+ reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES);
+ }
+ });
mLinkBandwidthEstimator = mTelephonyComponentFactory
.inject(LinkBandwidthEstimator.class.getName())
.makeLinkBandwidthEstimator(this);
+ mCallWaitingController = new CallWaitingController(this);
+
loadTtyMode();
CallManager.getInstance().registerPhone(this);
@@ -419,6 +422,17 @@
int newPreferredTtyMode = intent.getIntExtra(
TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
updateUiTtyMode(newPreferredTtyMode);
+ } else if (TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) {
+ if (mPhoneId == intent.getIntExtra(
+ SubscriptionManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_UNKNOWN);
+ if (simState == TelephonyManager.SIM_STATE_LOADED
+ && currentSlotSubIdChanged()) {
+ setNetworkSelectionModeAutomatic(null);
+ }
+ }
}
}
};
@@ -475,15 +489,20 @@
mCi.registerForLceInfo(this, EVENT_LINK_CAPACITY_CHANGED, null);
mCi.registerForCarrierInfoForImsiEncryption(this,
EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION, null);
+ mCi.registerForTriggerImsDeregistration(this, EVENT_IMS_DEREGISTRATION_TRIGGERED, null);
+ mCi.registerForNotifyAnbr(this, EVENT_TRIGGER_NOTIFY_ANBR, null);
IntentFilter filter = new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter,
android.Manifest.permission.MODIFY_PHONE_STATE, null, Context.RECEIVER_EXPORTED);
mCDM = new CarrierKeyDownloadManager(this);
mCIM = new CarrierInfoManager();
+
+ initializeCarrierApps();
}
private void initRatSpecific(int precisePhoneType) {
@@ -506,8 +525,12 @@
// This is needed to handle phone process crashes
mIsPhoneInEcmState = getInEcmMode();
if (mIsPhoneInEcmState) {
- // Send a message which will invoke handleExitEmergencyCallbackMode
- mCi.exitEmergencyCallbackMode(null);
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ EmergencyStateTracker.getInstance().exitEmergencyCallbackMode();
+ } else {
+ // Send a message which will invoke handleExitEmergencyCallbackMode
+ mCi.exitEmergencyCallbackMode(null);
+ }
}
mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
@@ -530,7 +553,7 @@
logd("update icc_operator_numeric=" + operatorNumeric);
tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric);
- SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
+ mSubscriptionManagerService.setMccMnc(getSubId(), operatorNumeric);
// Sets iso country property by retrieving from build-time system property
String iso = "";
@@ -542,7 +565,7 @@
logd("init: set 'gsm.sim.operator.iso-country' to iso=" + iso);
tm.setSimCountryIsoForPhone(mPhoneId, iso);
- SubscriptionController.getInstance().setCountryIso(iso, getSubId());
+ mSubscriptionManagerService.setCountryIso(getSubId(), iso);
// Updates MCC MNC device configuration information
logd("update mccmnc=" + operatorNumeric);
@@ -554,6 +577,31 @@
}
}
+ /**
+ * Initialize the carrier apps.
+ */
+ private void initializeCarrierApps() {
+ // Only perform on the default phone. There is no need to do it twice on the DSDS device.
+ if (mPhoneId != 0) return;
+
+ logd("initializeCarrierApps");
+ mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Remove this line after testing
+ if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) {
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ // If couldn't get current user ID, guess it's 0.
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ TelephonyManager.getDefault(),
+ userHandle != null ? userHandle.getIdentifier() : 0, mContext);
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ TelephonyManager.getDefault(), ActivityManager.getCurrentUser(), mContext);
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isPhoneTypeGsm() {
return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM;
@@ -680,11 +728,6 @@
}
@Override
- public TransportManager getTransportManager() {
- return mTransportManager;
- }
-
- @Override
public AccessNetworksManager getAccessNetworksManager() {
return mAccessNetworksManager;
}
@@ -736,83 +779,8 @@
}
@Override
- public PhoneConstants.DataState getDataConnectionState(String apnType) {
- PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED;
-
- if (mSST == null) {
- // Radio Technology Change is ongoing, dispose() and removeReferences() have
- // already been called
-
- ret = PhoneConstants.DataState.DISCONNECTED;
- } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
- && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
- (isPhoneTypeGsm() && !apnType.equals(ApnSetting.TYPE_EMERGENCY_STRING)))) {
- // If we're out of service, open TCP sockets may still work
- // but no data will flow
-
- // Emergency APN is available even in Out Of Service
- // Pass the actual State of EPDN
-
- ret = PhoneConstants.DataState.DISCONNECTED;
- } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
- int currentTransport = mAccessNetworksManager.getCurrentTransport(
- ApnSetting.getApnTypesBitmaskFromString(apnType));
- if (getDcTracker(currentTransport) != null) {
- switch (getDcTracker(currentTransport).getState(apnType)) {
- case CONNECTED:
- case DISCONNECTING:
- if (isDataSuspended()) {
- ret = PhoneConstants.DataState.SUSPENDED;
- } else {
- ret = PhoneConstants.DataState.CONNECTED;
- }
- break;
- case CONNECTING:
- ret = PhoneConstants.DataState.CONNECTING;
- break;
- default:
- ret = PhoneConstants.DataState.DISCONNECTED;
- }
- }
- }
-
- logd("getDataConnectionState apnType=" + apnType + " ret=" + ret);
- return ret;
- }
-
- @Override
public @DataActivityType int getDataActivityState() {
- if (isUsingNewDataStack()) {
- return getDataNetworkController().getDataActivity();
- }
- int ret = TelephonyManager.DATA_ACTIVITY_NONE;
-
- if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE
- && getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
- switch (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getActivity()) {
- case DATAIN:
- ret = TelephonyManager.DATA_ACTIVITY_IN;
- break;
-
- case DATAOUT:
- ret = TelephonyManager.DATA_ACTIVITY_OUT;
- break;
-
- case DATAINANDOUT:
- ret = TelephonyManager.DATA_ACTIVITY_INOUT;
- break;
-
- case DORMANT:
- ret = TelephonyManager.DATA_ACTIVITY_DORMANT;
- break;
-
- default:
- ret = TelephonyManager.DATA_ACTIVITY_NONE;
- break;
- }
- }
-
- return ret;
+ return getDataNetworkController().getDataActivity();
}
/**
@@ -831,7 +799,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyPreciseCallStateChangedP();
+ AsyncResult ar = new AsyncResult(null, this, null);
+ mPreciseCallStateRegistrants.notifyRegistrants(ar);
+
+ mNotifier.notifyPreciseCallState(this, null, null, null);
}
public void notifyNewRingingConnection(Connection c) {
@@ -1023,14 +994,6 @@
}
@Override
- public void dispose() {
- // Note: this API is currently never called. We are defining actions here in case
- // we need to dispose GsmCdmaPhone/Phone object.
- super.dispose();
- SubscriptionController.getInstance().unregisterForUiccAppsEnabled(this);
- }
-
- @Override
public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
if (isPhoneTypeGsm()) {
loge("enableEnhancedVoicePrivacy: not expected on GSM");
@@ -1434,8 +1397,7 @@
// emergency number list on another SIM, but is not on theirs. In this case we will use the
// emergency number list for this carrier's SIM only.
if (useOnlyDialedSimEccList) {
- isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString,
- true /* exactMatch */);
+ isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString);
logi("dial; isEmergency=" + isEmergency
+ " (based on this phone only); globalIsEmergency="
+ tm.isEmergencyNumber(dialString));
@@ -1444,6 +1406,12 @@
logi("dial; isEmergency=" + isEmergency + " (based on all phones)");
}
+ // Undetectable emergeny number indicated by new domain selection service
+ if (dialArgs.isEmergency) {
+ logi("dial; isEmergency=" + isEmergency + " (domain selection module)");
+ isEmergency = true;
+ }
+
/** Check if the call is Wireless Priority Service call */
boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS)
|| dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
@@ -1469,6 +1437,55 @@
boolean useImsForCall = useImsForCall(dialArgs)
&& (isWpsCall ? allowWpsOverIms : true);
+ Bundle extras = dialArgs.intentExtras;
+ if (extras != null && extras.containsKey(PhoneConstants.EXTRA_COMPARE_DOMAIN)) {
+ int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
+ if (!isEmergency && (!isMmiCode || isPotentialUssdCode)) {
+ if ((domain == DOMAIN_PS && !useImsForCall)
+ || (domain == DOMAIN_CS && useImsForCall)
+ || domain == DOMAIN_UNKNOWN || domain == DOMAIN_CS_PS) {
+ loge("[Anomaly] legacy-useImsForCall:" + useImsForCall
+ + ", NCDS-domain:" + domain);
+
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("bfae6c2e-ca2f-4121-b167-9cad26a3b353"),
+ "Domain selection results don't match. useImsForCall:"
+ + useImsForCall + ", NCDS-domain:" + domain);
+ }
+ }
+ extras.remove(PhoneConstants.EXTRA_COMPARE_DOMAIN);
+ }
+
+ // Only when the domain selection service is supported, EXTRA_DIAL_DOMAIN extra shall exist.
+ if (extras != null && extras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN)) {
+ int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
+ logi("dial domain=" + domain);
+ useImsForCall = false;
+ useImsForUt = false;
+ useImsForEmergency = false;
+ if (domain == DOMAIN_PS) {
+ if (isEmergency) {
+ useImsForEmergency = true;
+ } else if (!isMmiCode || isPotentialUssdCode) {
+ useImsForCall = true;
+ } else {
+ // should not reach here
+ loge("dial unexpected Ut domain selection, ignored");
+ }
+ } else if (domain == PhoneConstants.DOMAIN_NON_3GPP_PS) {
+ if (isEmergency) {
+ useImsForEmergency = true;
+ extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE,
+ String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN));
+ } else {
+ // should not reach here
+ loge("dial DOMAIN_NON_3GPP_PS should be used only for emergency calls");
+ }
+ }
+
+ extras.remove(PhoneConstants.EXTRA_DIAL_DOMAIN);
+ }
+
if (DBG) {
logi("useImsForCall=" + useImsForCall
+ ", useOnlyDialedSimEccList=" + useOnlyDialedSimEccList
@@ -1491,6 +1508,12 @@
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
+ // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN
+ if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) {
+ throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED,
+ "cannot dial number blocked by FDN");
+ }
+
// Bypass WiFi Only WFC check if this is an emergency call - we should still try to
// place over cellular if possible.
if (!isEmergency) {
@@ -1559,10 +1582,13 @@
}
if (DBG) logd("Trying (non-IMS) CS call");
if (isDialedNumberSwapped && isEmergency) {
- // Triggers ECM when CS call ends only for test emergency calls using
- // ril.test.emergencynumber.
- mIsTestingEmergencyCallbackMode = true;
- mCi.testingEmergencyCall();
+ // If domain selection is enabled, ECM testing is handled in EmergencyStateTracker
+ if (!DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ // Triggers ECM when CS call ends only for test emergency calls using
+ // ril.test.emergencynumber.
+ mIsTestingEmergencyCallbackMode = true;
+ mCi.testingEmergencyCall();
+ }
}
chosenPhoneConsumer.accept(this);
@@ -1688,6 +1714,13 @@
return true;
}
+ // Perform FDN check
+ if(FdnUtils.isNumberBlockedByFDN(mPhoneId, ussdRequest, getCountryIso())) {
+ sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+ wrappedCallback );
+ return true;
+ }
+
// Try over IMS if possible.
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
@@ -1787,7 +1820,7 @@
public void setRadioPower(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
- Phone.RADIO_POWER_REASON_USER);
+ TelephonyManager.RADIO_POWER_REASON_USER);
}
@Override
@@ -1797,6 +1830,11 @@
forceApply, reason);
}
+ @Override
+ public Set<Integer> getRadioPowerOffReasons() {
+ return mSST.getRadioPowerOffReasons();
+ }
+
private void storeVoiceMailNumber(String number) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
@@ -1946,6 +1984,11 @@
return mImei;
}
+ @Override
+ public int getImeiType() {
+ return mImeiType;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getEsn() {
@@ -2331,6 +2374,15 @@
@Override
public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
Message onComplete) {
+ // Perform FDN check
+ SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+ if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete);
@@ -2380,6 +2432,16 @@
int serviceClass,
int timerSeconds,
Message onComplete) {
+ // Perform FDN check
+ SsData.RequestType requestType = GsmMmiCode.cfActionToRequestType(commandInterfaceCFAction);
+ SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+ if(isRequestBlockedByFDN(requestType, serviceType)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason,
@@ -2433,6 +2495,15 @@
@Override
public void getCallBarring(String facility, String password, Message onComplete,
int serviceClass) {
+ // Perform FDN check
+ SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+ if (isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
@@ -2449,6 +2520,17 @@
@Override
public void setCallBarring(String facility, boolean lockState, String password,
Message onComplete, int serviceClass) {
+ // Perform FDN check
+ SsData.RequestType requestType = lockState ? SsData.RequestType.SS_ACTIVATION :
+ SsData.RequestType.SS_DEACTIVATION;
+ SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+ if (isRequestBlockedByFDN(requestType, serviceType)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
@@ -2472,6 +2554,18 @@
*/
public void changeCallBarringPassword(String facility, String oldPwd, String newPwd,
Message onComplete) {
+ // Perform FDN check
+ SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+ ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd(
+ SsData.RequestType.SS_REGISTRATION,
+ serviceType);
+ if(FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso())) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
if (isPhoneTypeGsm()) {
mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete);
} else {
@@ -2481,7 +2575,16 @@
@Override
public void getOutgoingCallerIdDisplay(Message onComplete) {
+ // Perform FDN check
+ if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIR)){
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
+
if (useSsOverIms(onComplete)) {
imsPhone.getOutgoingCallerIdDisplay(onComplete);
return;
@@ -2499,6 +2602,15 @@
@Override
public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
+ // Perform FDN check
+ SsData.RequestType requestType = GsmMmiCode.clirModeToRequestType(commandInterfaceCLIRMode);
+ if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_CLIR)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
@@ -2521,6 +2633,14 @@
@Override
public void queryCLIP(Message onComplete) {
+ // Perform FDN check
+ if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIP)){
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.queryCLIP(onComplete);
@@ -2539,6 +2659,16 @@
@Override
public void getCallWaiting(Message onComplete) {
+ // Perform FDN check
+ if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_WAIT)){
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
+ if (mCallWaitingController.getCallWaiting(onComplete)) return;
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.getCallWaiting(onComplete);
@@ -2580,6 +2710,18 @@
@Override
public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
+ // Perform FDN check
+ SsData.RequestType requestType = enable ? SsData.RequestType.SS_ACTIVATION :
+ SsData.RequestType.SS_DEACTIVATION;
+ if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_WAIT)) {
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+ onComplete.sendToTarget();
+ return;
+ }
+
+ if (mCallWaitingController.setCallWaiting(enable, serviceClass, onComplete)) return;
+
Phone imsPhone = mImsPhone;
if (useSsOverIms(onComplete)) {
imsPhone.setCallWaiting(enable, onComplete);
@@ -2610,6 +2752,23 @@
}
@Override
+ public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+ return mCallWaitingController.getTerminalBasedCallWaitingState(forCsOnly);
+ }
+
+ @Override
+ public void setTerminalBasedCallWaitingStatus(int state) {
+ if (mImsPhone != null) {
+ mImsPhone.setTerminalBasedCallWaitingStatus(state);
+ }
+ }
+
+ @Override
+ public void setTerminalBasedCallWaitingSupported(boolean supported) {
+ mCallWaitingController.setTerminalBasedCallWaitingSupported(supported);
+ }
+
+ @Override
public void getAvailableNetworks(Message response) {
if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
Message msg = obtainMessage(EVENT_GET_AVAILABLE_NETWORKS_DONE, response);
@@ -2672,25 +2831,12 @@
@Override
public boolean getDataRoamingEnabled() {
- if (isUsingNewDataStack()) {
- return getDataSettingsManager().isDataRoamingEnabled();
- }
- if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
- return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getDataRoamingEnabled();
- }
- return false;
+ return getDataSettingsManager().isDataRoamingEnabled();
}
@Override
public void setDataRoamingEnabled(boolean enable) {
- if (isUsingNewDataStack()) {
- getDataSettingsManager().setDataRoamingEnabled(enable);
- return;
- }
- if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
- getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setDataRoamingEnabledByUser(enable);
- }
+ getDataSettingsManager().setDataRoamingEnabled(enable);
}
@Override
@@ -2735,21 +2881,12 @@
}
/**
- * Whether data is enabled by user. Unlike isDataEnabled, this only
- * checks user setting stored in {@link android.provider.Settings.Global#MOBILE_DATA}
- * if not provisioning, or isProvisioningDataEnabled if provisioning.
+ * Whether data is enabled by user.
*/
@Override
public boolean isUserDataEnabled() {
- if (isUsingNewDataStack()) {
- return getDataSettingsManager().isDataEnabledForReason(
- TelephonyManager.DATA_ENABLED_REASON_USER);
- }
- if (mDataEnabledSettings.isProvisioning()) {
- return mDataEnabledSettings.isProvisioningDataEnabled();
- } else {
- return mDataEnabledSettings.isUserDataEnabled();
- }
+ return getDataSettingsManager().isDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_USER);
}
/**
@@ -2901,11 +3038,12 @@
private void handleRadioAvailable() {
mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
-
+ mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE));
mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
+ handleNullCipherEnabledChange();
startLceAfterRadioIsAvailable();
}
@@ -2951,7 +3089,21 @@
handleRadioAvailable();
}
break;
-
+ case EVENT_GET_DEVICE_IMEI_DONE :
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception != null || ar.result == null) {
+ loge("Exception received : " + ar.exception);
+ break;
+ }
+ ImeiInfo imeiInfo = (ImeiInfo) ar.result;
+ if (!TextUtils.isEmpty(imeiInfo.imei)) {
+ mImeiType = imeiInfo.type;
+ mImei = imeiInfo.imei;
+ mImeiSv = imeiInfo.svn;
+ } else {
+ // TODO Report telephony anomaly
+ }
+ break;
case EVENT_GET_DEVICE_IDENTITY_DONE:{
ar = (AsyncResult)msg.obj;
@@ -2959,8 +3111,10 @@
break;
}
String[] respId = (String[])ar.result;
- mImei = respId[0];
- mImeiSv = respId[1];
+ if (TextUtils.isEmpty(mImei)) {
+ mImei = respId[0];
+ mImeiSv = respId[1];
+ }
mEsn = respId[2];
mMeid = respId[3];
// some modems return all 0's instead of null/empty string when MEID is unavailable
@@ -2985,12 +3139,16 @@
logd("Event EVENT_MODEM_RESET Received" + " isInEcm = " + isInEcm()
+ " isPhoneTypeGsm = " + isPhoneTypeGsm() + " mImsPhone = " + mImsPhone);
if (isInEcm()) {
- if (isPhoneTypeGsm()) {
- if (mImsPhone != null) {
- mImsPhone.handleExitEmergencyCallbackMode();
- }
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ EmergencyStateTracker.getInstance().exitEmergencyCallbackMode();
} else {
- handleExitEmergencyCallbackMode(msg);
+ if (isPhoneTypeGsm()) {
+ if (mImsPhone != null) {
+ mImsPhone.handleExitEmergencyCallbackMode();
+ }
+ } else {
+ handleExitEmergencyCallbackMode(msg);
+ }
}
}
}
@@ -3283,24 +3441,9 @@
case EVENT_SET_CARRIER_DATA_ENABLED:
ar = (AsyncResult) msg.obj;
boolean enabled = (boolean) ar.result;
- if (isUsingNewDataStack()) {
- getDataSettingsManager().setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
- mContext.getOpPackageName());
- return;
- }
- mDataEnabledSettings.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
- enabled);
- break;
- case EVENT_DEVICE_PROVISIONED_CHANGE:
- if (!isUsingNewDataStack()) {
- mDataEnabledSettings.updateProvisionedChanged();
- }
- break;
- case EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE:
- if (!isUsingNewDataStack()) {
- mDataEnabledSettings.updateProvisioningDataEnabled();
- }
+ getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
+ mContext.getOpPackageName());
break;
case EVENT_GET_AVAILABLE_NETWORKS_DONE:
ar = (AsyncResult) msg.obj;
@@ -3375,12 +3518,64 @@
logd("EVENT_SUBSCRIPTIONS_CHANGED");
updateUsageSetting();
break;
+ case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
+ logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
+ ar = (AsyncResult) msg.obj;
+ if (ar == null || ar.exception == null) {
+ mIsNullCipherAndIntegritySupported = true;
+ return;
+ }
+ CommandException.Error error = ((CommandException) ar.exception).getCommandError();
+ mIsNullCipherAndIntegritySupported = !error.equals(
+ CommandException.Error.REQUEST_NOT_SUPPORTED);
+ break;
+ case EVENT_IMS_DEREGISTRATION_TRIGGERED:
+ logd("EVENT_IMS_DEREGISTRATION_TRIGGERED");
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ mImsPhone.triggerImsDeregistration(((int[]) ar.result)[0]);
+ } else {
+ Rlog.e(LOG_TAG, "Unexpected unsol with exception", ar.exception);
+ }
+ break;
+
+ case EVENT_TRIGGER_NOTIFY_ANBR:
+ logd("EVENT_TRIGGER_NOTIFY_ANBR");
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ if (mImsPhone != null) {
+ mImsPhone.triggerNotifyAnbr(((int[]) ar.result)[0], ((int[]) ar.result)[1],
+ ((int[]) ar.result)[2]);
+ }
+ }
+ break;
default:
super.handleMessage(msg);
}
}
+ /**
+ * Check if a different SIM is inserted at this slot from the last time. Storing last subId
+ * in SharedPreference for now to detect SIM change.
+ *
+ * @return {@code true} if current slot mapping changed; {@code false} otherwise.
+ */
+ private boolean currentSlotSubIdChanged() {
+ SharedPreferences sp =
+ PreferenceManager.getDefaultSharedPreferences(mContext);
+ int storedSubId = sp.getInt(CURR_SUBID + mPhoneId, -1);
+ boolean changed = storedSubId != getSubId();
+ if (changed) {
+ // Update stored subId
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(CURR_SUBID + mPhoneId, getSubId());
+ editor.apply();
+ }
+ Rlog.d(LOG_TAG, "currentSlotSubIdChanged: changed=" + changed);
+ return changed;
+ }
+
public UiccCardApplication getUiccCardApplication() {
if (isPhoneTypeGsm()) {
return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP);
@@ -3747,6 +3942,10 @@
//CDMA
private void handleEnterEmergencyCallbackMode(Message msg) {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM enter event.");
+ return;
+ }
if (DBG) {
Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()="
+ isInEcm());
@@ -3770,6 +3969,10 @@
//CDMA
private void handleExitEmergencyCallbackMode(Message msg) {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM exit event.");
+ return;
+ }
AsyncResult ar = (AsyncResult)msg.obj;
if (DBG) {
Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm="
@@ -3795,10 +3998,6 @@
// send an Intent
sendEmergencyCallbackModeChange();
- // Re-initiate data connection
- if (!isUsingNewDataStack()) {
- mDataEnabledSettings.setInternalDataEnabled(true);
- }
notifyEmergencyCallRegistrants(false);
}
mIsTestingEmergencyCallbackMode = false;
@@ -3816,6 +4015,7 @@
* otherwise, restart Ecm timer and notify apps the timer is restarted.
*/
public void handleTimerInEmergencyCallbackMode(int action) {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return;
switch(action) {
case CANCEL_ECM_TIMER:
removeCallbacks(mExitEcmRunnable);
@@ -4229,6 +4429,7 @@
@Override
public void setImsRegistrationState(boolean registered) {
mSST.setImsRegistrationState(registered);
+ mCallWaitingController.setImsRegistrationState(registered);
}
@Override
@@ -4290,6 +4491,13 @@
" mTelecomVoiceServiceStateOverride=" + mTelecomVoiceServiceStateOverride + "("
+ ServiceState.rilServiceStateToString(mTelecomVoiceServiceStateOverride)
+ ")");
+ pw.println(" mUiccApplicationsEnabled=" + mUiccApplicationsEnabled);
+ pw.flush();
+ try {
+ mCallWaitingController.dump(pw);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
pw.flush();
}
@@ -4409,7 +4617,7 @@
if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) {
return null;
}
- return subInfo.getCountryIso().toUpperCase();
+ return subInfo.getCountryIso().toUpperCase(Locale.ROOT);
}
public void notifyEcbmTimerReset(Boolean flag) {
@@ -4515,6 +4723,27 @@
mVolteSilentRedialRegistrants.notifyRegistrants(ar);
}
+ /** {@inheritDoc} */
+ @Override
+ public void registerForEmergencyDomainSelected(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mEmergencyDomainSelectedRegistrants.addUnique(h, what, obj);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unregisterForEmergencyDomainSelected(@NonNull Handler h) {
+ mEmergencyDomainSelectedRegistrants.remove(h);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void notifyEmergencyDomainSelected(@TransportType int transportType) {
+ logd("notifyEmergencyDomainSelected transportType=" + transportType);
+ mEmergencyDomainSelectedRegistrants.notifyRegistrants(
+ new AsyncResult(null, transportType, null));
+ }
+
/**
* Sets the SIM voice message waiting indicator records.
* @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
@@ -4675,6 +4904,8 @@
// If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing.
if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT
|| mUiccApplicationsEnabled == null) {
+ loge("reapplyUiccAppsEnablementIfNeeded: slot state="
+ + (slot != null ? slot.getCardState() : null));
return;
}
@@ -4687,18 +4918,24 @@
return;
}
- SubscriptionInfo info = SubscriptionController.getInstance().getSubInfoForIccId(
- IccUtils.stripTrailingFs(iccId));
+ SubscriptionInfo info = mSubscriptionManagerService
+ .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag())
+ .stream()
+ .filter(subInfo -> subInfo.getIccId().equals(IccUtils.stripTrailingFs(iccId)))
+ .findFirst()
+ .orElse(null);
+
+ logd("reapplyUiccAppsEnablementIfNeeded: retries=" + retries + ", subInfo=" + info);
// If info is null, it could be a new subscription. By default we enable it.
- boolean expectedValue = info == null ? true : info.areUiccApplicationsEnabled();
+ boolean expectedValue = info == null || info.areUiccApplicationsEnabled();
// If for any reason current state is different from configured state, re-apply the
// configured state.
if (expectedValue != mUiccApplicationsEnabled) {
mCi.enableUiccApplications(expectedValue, Message.obtain(
this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE,
- new Pair<Boolean, Integer>(expectedValue, retries)));
+ new Pair<>(expectedValue, retries)));
}
}
@@ -4745,24 +4982,7 @@
* @return Currently bound data service package names.
*/
public @NonNull List<String> getDataServicePackages() {
- if (isUsingNewDataStack()) {
- return getDataNetworkController().getDataServicePackages();
- }
- List<String> packages = new ArrayList<>();
- int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
-
- for (int transport : transports) {
- DcTracker dct = getDcTracker(transport);
- if (dct != null) {
- String pkg = dct.getDataServiceManager().getDataServicePackageName();
- if (!TextUtils.isEmpty(pkg)) {
- packages.add(pkg);
- }
- }
- }
-
- return packages;
+ return getDataNetworkController().getDataServicePackages();
}
private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(
@@ -4804,28 +5024,26 @@
boolean mIsVonrEnabledByCarrier =
config.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL);
-
- String result = SubscriptionController.getInstance().getSubscriptionProperty(
- getSubId(),
- SubscriptionManager.NR_ADVANCED_CALLING_ENABLED);
+ boolean mDefaultVonr =
+ config.getBoolean(CarrierConfigManager.KEY_VONR_ON_BY_DEFAULT_BOOL);
int setting = -1;
- if (result != null) {
- setting = Integer.parseInt(result);
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(getSubId());
+ if (subInfo != null) {
+ setting = subInfo.getNrAdvancedCallingEnabled();
}
logd("VoNR setting from telephony.db:"
+ setting
+ " ,vonr_enabled_bool:"
- + mIsVonrEnabledByCarrier);
+ + mIsVonrEnabledByCarrier
+ + " ,vonr_on_by_default_bool:"
+ + mDefaultVonr);
- if (!mIsVonrEnabledByCarrier) {
- mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
- } else if (setting == 1 || setting == -1) {
- mCi.setVoNrEnabled(true, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
- } else if (setting == 0) {
- mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
- }
+ boolean enbleVonr = mIsVonrEnabledByCarrier
+ && (setting == 1 || (setting == -1 && mDefaultVonr));
+ mCi.setVoNrEnabled(enbleVonr, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
}
private void updateCdmaRoamingSettingsAfterCarrierConfigChanged(PersistableBundle config) {
@@ -4887,4 +5105,49 @@
public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2);
}
+
+ /**
+ * Return current cell broadcast ranges.
+ */
+ public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+ return mCellBroadcastConfigTracker.getCellBroadcastIdRanges();
+ }
+
+ /**
+ * Set reception of cell broadcast messages with the list of the given ranges.
+ */
+ @Override
+ public void setCellBroadcastIdRanges(
+ @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) {
+ mCellBroadcastConfigTracker.setCellBroadcastIdRanges(ranges, callback);
+ }
+
+ /**
+ * The following function checks if supplementary service request is blocked due to FDN.
+ * @param requestType request type associated with the supplementary service
+ * @param serviceType supplementary service type
+ * @return {@code true} if request is blocked due to FDN.
+ */
+ private boolean isRequestBlockedByFDN(SsData.RequestType requestType,
+ SsData.ServiceType serviceType) {
+ ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(requestType, serviceType);
+ return FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso());
+ }
+
+ @Override
+ public void handleNullCipherEnabledChange() {
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, true)) {
+ logi("Not handling null cipher update. Feature disabled by DeviceConfig.");
+ return;
+ }
+ mCi.setNullCipherAndIntegrityEnabled(
+ getNullCipherAndIntegrityEnabledPreference(),
+ obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE));
+ }
+
+ @Override
+ public boolean isNullCipherAndIntegritySupported() {
+ return mIsNullCipherAndIntegritySupported;
+ }
}
diff --git a/src/java/com/android/internal/telephony/HalVersion.java b/src/java/com/android/internal/telephony/HalVersion.java
index f83d790..c05111b 100644
--- a/src/java/com/android/internal/telephony/HalVersion.java
+++ b/src/java/com/android/internal/telephony/HalVersion.java
@@ -25,6 +25,9 @@
*/
public class HalVersion implements Comparable<HalVersion> {
+ /** The HAL Version indicating that the version is unsupported */
+ public static final HalVersion UNSUPPORTED = new HalVersion(-2, -2);
+
/** The HAL Version indicating that the version is unknown or invalid */
public static final HalVersion UNKNOWN = new HalVersion(-1, -1);
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 742cc90..ab62aa4 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -39,6 +39,7 @@
import com.android.telephony.Rlog;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -337,7 +338,10 @@
}
efid = updateEfForIccType(efid);
- if (DBG) logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid).toUpperCase());
+ if (DBG) {
+ logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid)
+ .toUpperCase(Locale.ROOT));
+ }
checkThread();
Request loadRequest = new Request();
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 7a128c0..b7c7e7b 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -37,6 +37,7 @@
import com.android.telephony.Rlog;
import java.util.List;
+import java.util.Locale;
/**
* {@hide}
@@ -424,7 +425,7 @@
private MatrixCursor loadFromEf(int efType, int subId) {
if (DBG) log("loadFromEf: efType=0x" +
- Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
+ Integer.toHexString(efType).toUpperCase(Locale.ROOT) + ", subscription=" + subId);
List<AdnRecord> adnRecords = null;
try {
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index a329771..2d77631 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -176,6 +176,41 @@
mSmsPermissions = smsPermissions;
}
+ /**
+ * PhoneFactory Dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneFactoryProxy {
+ Phone getPhone(int index);
+ Phone getDefaultPhone();
+ Phone[] getPhones();
+ }
+
+ private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
+ @Override
+ public Phone getPhone(int index) {
+ return PhoneFactory.getPhone(index);
+ }
+
+ @Override
+ public Phone getDefaultPhone() {
+ return PhoneFactory.getDefaultPhone();
+ }
+
+ @Override
+ public Phone[] getPhones() {
+ return PhoneFactory.getPhones();
+ }
+ };
+
+ /**
+ * Overrides PhoneFactory dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
+ mPhoneFactoryProxy = proxy;
+ }
+
private void enforceNotOnHandlerThread(String methodName) {
if (Looper.myLooper() == mHandler.getLooper()) {
throw new RuntimeException("This method " + methodName + " will deadlock if called from"
@@ -457,11 +492,11 @@
*/
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
- boolean persistMessageForNonDefaultSmsApp, long messageId) {
+ boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
- messageId);
+ messageId, skipShortCodeCheck);
}
/**
@@ -481,6 +516,16 @@
SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, 0L /* messageId */);
}
+
+ private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+ int validityPeriod, boolean isForVvm, long messageId) {
+ sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+ persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod, isForVvm,
+ messageId, false);
+ }
+
/**
* Send a text based SMS.
*
@@ -527,12 +572,13 @@
* Any Other values including negative considered as Invalid Validity Period of the message.
* @param messageId An id that uniquely identifies the message requested to be sent.
* Used for logging and diagnostics purposes. The id may be 0.
+ * @param skipShortCodeCheck Skip check for short code type destination address.
*/
private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
- int validityPeriod, boolean isForVvm, long messageId) {
+ int validityPeriod, boolean isForVvm, long messageId, boolean skipShortCodeCheck) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr
+ " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
@@ -544,7 +590,7 @@
destAddr = filterDestAddress(destAddr);
mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
- priority, expectMore, validityPeriod, isForVvm, messageId);
+ priority, expectMore, validityPeriod, isForVvm, messageId, skipShortCodeCheck);
}
/**
@@ -625,9 +671,9 @@
}
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
- log("pdu: " + pdu +
- "\n format=" + format +
- "\n receivedIntent=" + receivedIntent);
+ log("pdu: " + IccUtils.bytesToHexString(pdu)
+ + "\n format=" + format
+ + "\n receivedIntent=" + receivedIntent);
}
mDispatchersController.injectSmsPdu(pdu, format, false /* isOverIms */,
result -> {
@@ -862,6 +908,7 @@
public String getSmscAddressFromIccEf(String callingPackage) {
if (!mSmsPermissions.checkCallingOrSelfCanGetSmscAddress(
callingPackage, "getSmscAddressFromIccEf")) {
+ loge("Caller do not have permission to call GetSmscAddress");
return null;
}
enforceNotOnHandlerThread("getSmscAddressFromIccEf");
@@ -883,6 +930,7 @@
public boolean setSmscAddressOnIccEf(String callingPackage, String smsc) {
if (!mSmsPermissions.checkCallingOrSelfCanSetSmscAddress(
callingPackage, "setSmscAddressOnIccEf")) {
+ loge("Caller do not have permission to call SetSmscAddress");
return false;
}
enforceNotOnHandlerThread("setSmscAddressOnIccEf");
@@ -1452,11 +1500,27 @@
return null;
}
- private void notifyIfOutgoingEmergencySms(String destAddr) {
+ @VisibleForTesting
+ public void notifyIfOutgoingEmergencySms(String destAddr) {
+ Phone[] allPhones = mPhoneFactoryProxy.getPhones();
EmergencyNumber emergencyNumber = mPhone.getEmergencyNumberTracker().getEmergencyNumber(
destAddr);
if (emergencyNumber != null) {
mPhone.notifyOutgoingEmergencySms(emergencyNumber);
+ } else if (allPhones.length > 1) {
+ // If there are multiple active SIMs, check all instances:
+ for (Phone phone : allPhones) {
+ // If the current iteration was already checked, skip:
+ if (phone.getPhoneId() == mPhone.getPhoneId()) {
+ continue;
+ }
+ emergencyNumber = phone.getEmergencyNumberTracker()
+ .getEmergencyNumber(destAddr);
+ if (emergencyNumber != null) {
+ mPhone.notifyOutgoingEmergencySms(emergencyNumber);
+ break;
+ }
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/ImsIndication.java b/src/java/com/android/internal/telephony/ImsIndication.java
new file mode 100644
index 0000000..00652f8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsIndication.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION;
+
+import android.hardware.radio.ims.IRadioImsIndication;
+import android.os.AsyncResult;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+
+/**
+ * Interface declaring unsolicited radio indications for IMS APIs.
+ */
+public class ImsIndication extends IRadioImsIndication.Stub {
+ private final RIL mRil;
+
+ public ImsIndication(RIL ril) {
+ mRil = ril;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioImsIndication.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioImsIndication.VERSION;
+ }
+
+ /**
+ * Fired by radio when any IMS traffic is not sent to network due to any failure
+ * on cellular networks.
+ *
+ * @param indicationType Type of radio indication.
+ * @param token The token provided by {@link #startImsTraffic}.
+ * @param failureInfo Connection failure information.
+ */
+ public void onConnectionSetupFailure(int indicationType, int token,
+ android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+ mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+ Object[] response = new Object[2];
+ response[0] = token;
+ response[1] = new ConnectionFailureInfo(
+ RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason),
+ failureInfo.causeCode, failureInfo.waitTimeMillis);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CONNECTION_SETUP_FAILURE, response);
+
+ mRil.mConnectionSetupFailureRegistrants.notifyRegistrants(
+ new AsyncResult(null, response, null));
+ }
+
+ /**
+ * Fired by radio when ANBR is received form the network.
+ *
+ * @param indicationType Type of radio indication.
+ * @param qosSessionId QoS session ID is used to identify media stream such as audio or video.
+ * @param imsdirection Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond The recommended bit rate for the UE
+ * for a specific logical channel and a specific direction by the network.
+ */
+ public void notifyAnbr(int indicationType, int qosSessionId, int imsdirection,
+ int bitsPerSecond) {
+ mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+ int[] response = new int[3];
+ response[0] = qosSessionId;
+ response[1] = imsdirection;
+ response[2] = bitsPerSecond;
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NOTIFY_ANBR, response);
+
+ mRil.mNotifyAnbrRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+ }
+
+ /**
+ * Requests IMS stack to perform graceful IMS deregistration before radio performing
+ * network detach in the events of SIM remove, refresh or and so on. The radio waits for
+ * the IMS deregistration, which will be notified by telephony via
+ * {@link IRadioIms#updateImsRegistrationInfo()}, or a certain timeout interval to start
+ * the network detach procedure.
+ *
+ * @param indicationType Type of radio indication.
+ * @param reason the reason why the deregistration is triggered.
+ */
+ public void triggerImsDeregistration(int indicationType, int reason) {
+ mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION, reason);
+
+ int[] response = new int[1];
+ response[0] = RILUtils.convertHalDeregistrationReason(reason);
+
+ mRil.mTriggerImsDeregistrationRegistrants.notifyRegistrants(
+ new AsyncResult(null, response, null));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ImsResponse.java b/src/java/com/android/internal/telephony/ImsResponse.java
new file mode 100644
index 0000000..1adc000
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsResponse.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.ims.feature.MmTelFeature.ImsTrafficSessionCallbackWrapper.INVALID_TOKEN;
+
+import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.ims.IRadioImsResponse;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+
+/**
+ * Interface declaring response functions to solicited radio requests for IMS APIs.
+ */
+public class ImsResponse extends IRadioImsResponse.Stub {
+ private final RIL mRil;
+
+ public ImsResponse(RIL ril) {
+ mRil = ril;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioImsResponse.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioImsResponse.VERSION;
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void setSrvccCallInfoResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void updateImsRegistrationInfoResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param failureInfo Failure information.
+ */
+ public void startImsTrafficResponse(RadioResponseInfo responseInfo,
+ android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_IMS, responseInfo);
+
+ if (rr != null) {
+ Object[] response = { new Integer(INVALID_TOKEN), null };
+ if (responseInfo.error == RadioError.NONE) {
+ if (failureInfo != null) {
+ response[1] = new ConnectionFailureInfo(
+ RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason),
+ failureInfo.causeCode, failureInfo.waitTimeMillis);
+ }
+ RadioResponse.sendMessageResponse(rr.mResult, response);
+ }
+ mRil.processResponseDone(rr, responseInfo, response);
+ }
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void stopImsTrafficResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void triggerEpsFallbackResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void sendAnbrQueryResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void updateImsCallStatusResponse(RadioResponseInfo info) {
+ RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 42fe7a7..90885fe 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.app.Activity;
import android.content.Context;
import android.os.Binder;
import android.os.Message;
@@ -43,7 +44,9 @@
import com.android.internal.telephony.util.SMSDispatcherUtil;
import com.android.telephony.Rlog;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -58,6 +61,7 @@
private static final String TAG = "ImsSmsDispatcher";
private static final int CONNECT_DELAY_MS = 5000; // 5 seconds;
+ public static final int MAX_SEND_RETRIES_OVER_IMS = MAX_SEND_RETRIES;
/**
* Creates FeatureConnector instances for ImsManager, used during testing to inject mock
@@ -72,6 +76,7 @@
FeatureConnector.Listener<ImsManager> listener, Executor executor);
}
+ public List<Integer> mMemoryAvailableNotifierList = new ArrayList<Integer>();
@VisibleForTesting
public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
@VisibleForTesting
@@ -140,6 +145,37 @@
private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
@Override
+ public void onMemoryAvailableResult(int token, @SendStatusResult int status,
+ int networkReasonCode) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ logd("onMemoryAvailableResult token=" + token + " status=" + status
+ + " networkReasonCode=" + networkReasonCode);
+ if (!mMemoryAvailableNotifierList.contains(token)) {
+ loge("onMemoryAvailableResult Invalid token");
+ return;
+ }
+ mMemoryAvailableNotifierList.remove(Integer.valueOf(token));
+
+ /**
+ * The Retrans flag is set and reset As per section 6.3.3.1.2 in TS 124011
+ * Note: Assuming that SEND_STATUS_ERROR_RETRY is sent in case of temporary failure
+ */
+ if (status == ImsSmsImplBase.SEND_STATUS_ERROR_RETRY) {
+ if (!mRPSmmaRetried) {
+ sendMessageDelayed(obtainMessage(EVENT_RETRY_SMMA), SEND_RETRY_DELAY);
+ mRPSmmaRetried = true;
+ } else {
+ mRPSmmaRetried = false;
+ }
+ } else {
+ mRPSmmaRetried = false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ @Override
public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
@SmsManager.Result int reason, int networkReasonCode) {
final long identity = Binder.clearCallingIdentity();
@@ -170,10 +206,19 @@
mTrackers.remove(token);
break;
case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
- if (tracker.mRetryCount < MAX_SEND_RETRIES) {
+ int maxRetryCountOverIms = getMaxRetryCountOverIms();
+ if (tracker.mRetryCount < getMaxSmsRetryCount()) {
+ if (maxRetryCountOverIms < getMaxSmsRetryCount()
+ && tracker.mRetryCount >= maxRetryCountOverIms) {
+ tracker.mRetryCount += 1;
+ mTrackers.remove(token);
+ fallbackToPstn(tracker);
+ break;
+ }
tracker.mRetryCount += 1;
sendMessageDelayed(
- obtainMessage(EVENT_SEND_RETRY, tracker), SEND_RETRY_DELAY);
+ obtainMessage(EVENT_SEND_RETRY, tracker),
+ getSmsRetryDelayValue());
} else {
tracker.onFailed(mContext, reason, networkReasonCode);
mTrackers.remove(token);
@@ -193,6 +238,7 @@
SmsConstants.FORMAT_3GPP2.equals(getFormat()),
status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK,
reason,
+ networkReasonCode,
tracker.mMessageId,
tracker.isFromDefaultSmsApplication(mContext),
tracker.getInterval());
@@ -248,6 +294,10 @@
mappedResult =
ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED;
break;
+ case Activity.RESULT_OK:
+ // class2 message saving to SIM operation is in progress, defer ack
+ // until saving to SIM is success/failure
+ return;
default:
mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
break;
@@ -263,7 +313,7 @@
} catch (ImsException e) {
loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
}
- }, true /* ignoreClass */, true /* isOverIms */);
+ }, true /* ignoreClass */, true /* isOverIms */, token);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -277,6 +327,10 @@
logd("SMS retry..");
sendSms((SmsTracker) msg.obj);
break;
+ case EVENT_RETRY_SMMA:
+ logd("SMMA Notification retry..");
+ onMemoryAvailable();
+ break;
default:
super.handleMessage(msg);
}
@@ -295,6 +349,9 @@
mImsManager = manager;
setListeners();
mIsImsServiceUp = true;
+
+ /* set ImsManager */
+ mSmsDispatchersController.setImsManager(mImsManager);
}
}
@@ -309,6 +366,9 @@
synchronized (mLock) {
mImsManager = null;
mIsImsServiceUp = false;
+
+ /* unset ImsManager */
+ mSmsDispatchersController.setImsManager(null);
}
}
}, this::post);
@@ -394,6 +454,81 @@
}
@Override
+ public int getMaxSmsRetryCount() {
+ int retryCount = MAX_SEND_RETRIES;
+ CarrierConfigManager mConfigManager;
+
+ mConfigManager = (CarrierConfigManager) mContext.getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+
+ if (mConfigManager != null) {
+ PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+ getSubId());
+
+ if (carrierConfig != null) {
+ retryCount = carrierConfig.getInt(
+ CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_COUNT_INT);
+ }
+ }
+
+ Rlog.d(TAG, "Retry Count: " + retryCount);
+
+ return retryCount;
+ }
+
+ /**
+ * Returns the number of times SMS can be sent over IMS
+ *
+ * @return retry count over Ims from carrier configuration
+ */
+ @VisibleForTesting
+ public int getMaxRetryCountOverIms() {
+ int retryCountOverIms = MAX_SEND_RETRIES_OVER_IMS;
+ CarrierConfigManager mConfigManager;
+
+ mConfigManager = (CarrierConfigManager) mContext.getSystemService(Context
+ .CARRIER_CONFIG_SERVICE);
+
+ if (mConfigManager != null) {
+ PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+ getSubId());
+
+
+ if (carrierConfig != null) {
+ retryCountOverIms = carrierConfig.getInt(
+ CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT);
+ }
+ }
+
+ Rlog.d(TAG, "Retry Count Over Ims: " + retryCountOverIms);
+
+ return retryCountOverIms;
+ }
+
+ @Override
+ public int getSmsRetryDelayValue() {
+ int retryDelay = SEND_RETRY_DELAY;
+ CarrierConfigManager mConfigManager;
+
+ mConfigManager = (CarrierConfigManager) mContext.getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+
+ if (mConfigManager != null) {
+ PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+ getSubId());
+
+ if (carrierConfig != null) {
+ retryDelay = carrierConfig.getInt(
+ CarrierConfigManager.ImsSms.KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT);
+ }
+ }
+
+ Rlog.d(TAG, "Retry delay: " + retryDelay);
+
+ return retryDelay;
+ }
+
+ @Override
protected boolean shouldBlockSmsForEcbm() {
// We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA
// SMS.
@@ -416,10 +551,43 @@
}
@Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message,
+ statusReportRequested, smsHeader, priority, validityPeriod, messageRef);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message,
+ statusReportRequested, messageRef);
+ }
+
+ @Override
protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly);
}
+ /**
+ * Send the Memory Available Event to the ImsService
+ */
+ public void onMemoryAvailable() {
+ logd("onMemoryAvailable ");
+ int token = mNextToken.incrementAndGet();
+ try {
+ logd("onMemoryAvailable: token = " + token);
+ mMemoryAvailableNotifierList.add(token);
+ getImsManager().onMemoryAvailable(token);
+ } catch (ImsException e) {
+ loge("onMemoryAvailable failed: " + e.getMessage());
+ if (mMemoryAvailableNotifierList.contains(token)) {
+ mMemoryAvailableNotifierList.remove(Integer.valueOf(token));
+ }
+ }
+ }
+
@Override
public void sendSms(SmsTracker tracker) {
logd("sendSms: "
@@ -489,6 +657,7 @@
@VisibleForTesting
public void fallbackToPstn(SmsTracker tracker) {
+ tracker.mMessageRef = nextMessageRef();
mSmsDispatchersController.sendRetrySms(tracker);
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index eeda1f9..9166719 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -45,7 +45,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.SQLException;
@@ -53,11 +52,10 @@
import android.os.AsyncResult;
import android.os.Build;
import android.os.Bundle;
+import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerWhitelistManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -89,7 +87,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -116,38 +113,6 @@
protected static final boolean DBG = true;
protected static final boolean VDBG = false; // STOPSHIP if true, logs user data
- /** Query projection for checking for duplicate message segments. */
- private static final String[] PDU_DELETED_FLAG_PROJECTION = {
- "pdu",
- "deleted"
- };
-
- /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
- private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING =
- new HashMap<Integer, Integer>() {{
- put(PDU_COLUMN, 0);
- put(DELETED_FLAG_COLUMN, 1);
- }};
-
- /** Query projection for combining concatenated message segments. */
- private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
- "pdu",
- "sequence",
- "destination_port",
- "display_originating_addr",
- "date"
- };
-
- /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
- private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING =
- new HashMap<Integer, Integer>() {{
- put(PDU_COLUMN, 0);
- put(SEQUENCE_COLUMN, 1);
- put(DESTINATION_PORT_COLUMN, 2);
- put(DISPLAY_ADDRESS_COLUMN, 3);
- put(DATE_COLUMN, 4);
- }};
-
public static final int PDU_COLUMN = 0;
public static final int SEQUENCE_COLUMN = 1;
public static final int DESTINATION_PORT_COLUMN = 2;
@@ -161,6 +126,34 @@
public static final int DELETED_FLAG_COLUMN = 10;
public static final int SUBID_COLUMN = 11;
+ /** Query projection for checking for duplicate message segments. */
+ private static final String[] PDU_DELETED_FLAG_PROJECTION = {
+ "pdu",
+ "deleted"
+ };
+
+ /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+ private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING = Map.of(
+ PDU_COLUMN, 0,
+ DELETED_FLAG_COLUMN, 1);
+
+ /** Query projection for combining concatenated message segments. */
+ private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
+ "pdu",
+ "sequence",
+ "destination_port",
+ "display_originating_addr",
+ "date"
+ };
+
+ /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+ private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING = Map.of(
+ PDU_COLUMN, 0,
+ SEQUENCE_COLUMN, 1,
+ DESTINATION_PORT_COLUMN, 2,
+ DISPLAY_ADDRESS_COLUMN, 3,
+ DATE_COLUMN, 4);
+
public static final String SELECT_BY_ID = "_id=?";
/** New SMS received as an AsyncResult. */
@@ -190,6 +183,7 @@
/** BroadcastReceiver timed out waiting for an intent */
public static final int EVENT_RECEIVER_TIMEOUT = 9;
+
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
@@ -294,8 +288,8 @@
* @param storageMonitor the SmsStorageMonitor to check for storage availability
*/
protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
- Phone phone) {
- super(name);
+ Phone phone, Looper looper) {
+ super(name, looper);
mContext = context;
mStorageMonitor = storageMonitor;
@@ -506,7 +500,6 @@
case EVENT_RETURN_TO_IDLE:
// already in idle state; ignore
return HANDLED;
-
case EVENT_BROADCAST_COMPLETE:
case EVENT_START_ACCEPTING_SMS:
default:
@@ -545,7 +538,8 @@
case EVENT_INJECT_SMS:
// handle new injected SMS
- handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */);
+ handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */,
+ msg.arg2 /* token */);
sendMessage(EVENT_RETURN_TO_IDLE);
return HANDLED;
@@ -668,7 +662,6 @@
}
}
}
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleNewSms(AsyncResult ar) {
if (ar.exception != null) {
@@ -679,7 +672,7 @@
int result;
try {
SmsMessage sms = (SmsMessage) ar.result;
- result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED);
+ result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED, 0 /*unused*/);
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
result = RESULT_SMS_DISPATCH_FAILURE;
@@ -698,7 +691,7 @@
* @param ar is the AsyncResult that has the SMS PDU to be injected.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void handleInjectSms(AsyncResult ar, boolean isOverIms) {
+ private void handleInjectSms(AsyncResult ar, boolean isOverIms, int token) {
int result;
SmsDispatchersController.SmsInjectionCallback callback = null;
try {
@@ -710,7 +703,7 @@
} else {
@SmsSource int smsSource =
isOverIms ? SOURCE_INJECTED_FROM_IMS : SOURCE_INJECTED_FROM_UNKNOWN;
- result = dispatchMessage(sms.mWrappedSmsMessage, smsSource);
+ result = dispatchMessage(sms.mWrappedSmsMessage, smsSource, token);
}
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
@@ -731,7 +724,7 @@
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
- private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource) {
+ private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource, int token) {
// If sms is null, there was a parsing error.
if (smsb == null) {
loge("dispatchSmsMessage: message is null");
@@ -745,20 +738,7 @@
return Intents.RESULT_SMS_HANDLED;
}
- // onlyCore indicates if the device is in cryptkeeper
- boolean onlyCore = false;
- try {
- onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))
- .isOnlyCoreApps();
- } catch (RemoteException e) {
- }
- if (onlyCore) {
- // Device is unable to receive SMS in encrypted state
- log("Received a short message in encrypted state. Rejecting.");
- return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED;
- }
-
- int result = dispatchMessageRadioSpecific(smsb, smsSource);
+ int result = dispatchMessageRadioSpecific(smsb, smsSource, token);
// In case of error, add to metrics. This is not required in case of success, as the
// data will be tracked when the message is processed (processMessagePart).
@@ -780,7 +760,7 @@
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb,
- @SmsSource int smsSource);
+ @SmsSource int smsSource, int token);
/**
* Send an acknowledge message to the SMSC.
@@ -880,7 +860,6 @@
* <code>RESULT_SMS_DISPATCH_FAILURE</code><br>
* <code>RESULT_SMS_NULL_PDU</code><br>
* <code>RESULT_SMS_NULL_MESSAGE</code><br>
- * <code>RESULT_SMS_RECEIVED_WHILE_ENCRYPTED</code><br>
* <code>RESULT_SMS_DATABASE_ERROR</code><br>
* <code>RESULT_SMS_INVALID_URI</code><br>
*/
@@ -1164,7 +1143,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void showNewMessageNotification() {
// Do not show the notification on non-FBE devices.
- if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
+ if (!StorageManager.isFileEncrypted()) {
return;
}
log("Show new message notification.");
@@ -1458,12 +1437,14 @@
intent.putExtra("messageId", messageId);
}
+ UserHandle userHandle = null;
if (destPort == -1) {
intent.setAction(Intents.SMS_DELIVER_ACTION);
// Direct the intent to only the default SMS app. If we can't find a default SMS app
// then sent it to all broadcast receivers.
- // We are deliberately delivering to the primary user's default SMS App.
- ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
+ userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, subId);
+ ComponentName componentName = SmsApplication.getDefaultSmsApplicationAsUser(mContext,
+ true, userHandle);
if (componentName != null) {
// Deliver SMS message only to this receiver.
intent.setComponent(componentName);
@@ -1487,9 +1468,12 @@
intent.setComponent(null);
}
+ if (userHandle == null) {
+ userHandle = UserHandle.SYSTEM;
+ }
Bundle options = handleSmsWhitelisting(intent.getComponent(), isClass0);
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
- AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, UserHandle.SYSTEM, subId);
+ AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, userHandle, subId);
}
/**
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
old mode 100755
new mode 100644
index 4e0f452..de854fa
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.UserHandle;
import android.sysprop.TelephonyProperties;
import android.telephony.CellInfo;
import android.telephony.ServiceState;
@@ -486,20 +487,12 @@
String countryIso = "";
String countryIsoDebugInfo = "empty as default";
- // For time zone detection we want the best geographical match we can get, which may differ
- // from the countryIso.
- String timeZoneCountryIso = null;
- String timeZoneCountryIsoDebugInfo = null;
-
if (!TextUtils.isEmpty(mOperatorNumeric)) {
MccMnc mccMnc = MccMnc.fromOperatorNumeric(mOperatorNumeric);
if (mccMnc != null) {
- countryIso = MccTable.countryCodeForMcc(mccMnc.mcc);
+ countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
countryIsoDebugInfo = "OperatorNumeric(" + mOperatorNumeric
- + "): MccTable.countryCodeForMcc(\"" + mccMnc.mcc + "\")";
- timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
- timeZoneCountryIsoDebugInfo =
- "OperatorNumeric: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")";
+ + "): MccTable.geoCountryCodeForMccMnc(\"" + mccMnc + "\")";
} else {
loge("updateLocale: Can't get country from operator numeric. mOperatorNumeric = "
+ mOperatorNumeric);
@@ -509,15 +502,19 @@
// If for any reason we can't get country from operator numeric, try to get it from cell
// info.
if (TextUtils.isEmpty(countryIso)) {
+ // Find the most prevalent MCC from surrounding cell towers.
String mcc = getMccFromCellInfo();
if (mcc != null) {
countryIso = MccTable.countryCodeForMcc(mcc);
countryIsoDebugInfo = "CellInfo: MccTable.countryCodeForMcc(\"" + mcc + "\")";
+ // Some MCC+MNC combinations are known to be used in countries other than those
+ // that the MCC alone would suggest. Do a second pass of nearby cells that match
+ // the most frequently observed MCC to see if this could be one of those cases.
MccMnc mccMnc = getMccMncFromCellInfo(mcc);
if (mccMnc != null) {
- timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
- timeZoneCountryIsoDebugInfo =
+ countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
+ countryIsoDebugInfo =
"CellInfo: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")";
}
}
@@ -526,8 +523,6 @@
if (mCountryOverride != null) {
countryIso = mCountryOverride;
countryIsoDebugInfo = "mCountryOverride = \"" + mCountryOverride + "\"";
- timeZoneCountryIso = countryIso;
- timeZoneCountryIsoDebugInfo = countryIsoDebugInfo;
}
if (!mPhone.isRadioOn()) {
@@ -564,11 +559,13 @@
intent.putExtra(TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY,
getLastKnownCountryIso());
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
- mPhone.getContext().sendBroadcast(intent);
+ mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
}
// Pass the geographical country information to the telephony time zone detection code.
+ String timeZoneCountryIso = countryIso;
+ String timeZoneCountryIsoDebugInfo = countryIsoDebugInfo;
boolean isTestMcc = false;
if (!TextUtils.isEmpty(mOperatorNumeric)) {
// For a test cell (MCC 001), the NitzStateMachine requires handleCountryDetected("") in
@@ -579,11 +576,6 @@
timeZoneCountryIsoDebugInfo = "Test cell: " + mOperatorNumeric;
}
}
- if (timeZoneCountryIso == null) {
- // After this timeZoneCountryIso may still be null.
- timeZoneCountryIso = countryIso;
- timeZoneCountryIsoDebugInfo = "Defaulted: " + countryIsoDebugInfo;
- }
log("updateLocale: timeZoneCountryIso = " + timeZoneCountryIso
+ ", timeZoneCountryIsoDebugInfo = " + timeZoneCountryIsoDebugInfo);
diff --git a/src/java/com/android/internal/telephony/MessagingIndication.java b/src/java/com/android/internal/telephony/MessagingIndication.java
index 86e30b0..5814e3d 100644
--- a/src/java/com/android/internal/telephony/MessagingIndication.java
+++ b/src/java/com/android/internal/telephony/MessagingIndication.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CDMA_NEW_SMS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS;
@@ -47,9 +49,9 @@
*/
public void cdmaNewSms(int indicationType,
android.hardware.radio.messaging.CdmaSmsMessage msg) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
SmsMessage sms = new SmsMessage(RILUtils.convertHalCdmaSmsMessage(msg));
if (mRil.mCdmaSmsRegistrant != null) {
@@ -63,9 +65,9 @@
* @param indicationType Type of radio indication
*/
public void cdmaRuimSmsStorageFull(int indicationType) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
if (mRil.mIccSmsFullRegistrant != null) {
mRil.mIccSmsFullRegistrant.notifyRegistrant();
@@ -82,11 +84,11 @@
* BTS as coded in 3GPP 23.041 Section 9.4.2.2
*/
public void newBroadcastSms(int indicationType, byte[] data) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
- IccUtils.bytesToHexString(data));
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogvRet(
+ RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(data));
}
if (mRil.mGsmBroadcastSmsRegistrant != null) {
@@ -101,8 +103,8 @@
* The PDU starts with the SMSC address per TS 27.005 (+CMT:)
*/
public void newSms(int indicationType, byte[] pdu) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
if (mRil.mGsmSmsRegistrant != null) {
@@ -117,9 +119,9 @@
* @param recordNumber Record number on the SIM
*/
public void newSmsOnSim(int indicationType, int recordNumber) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
if (mRil.mSmsOnSimRegistrant != null) {
mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null));
@@ -133,9 +135,9 @@
* The PDU starts with the SMSC address per TS 27.005 (+CMT:)
*/
public void newSmsStatusReport(int indicationType, byte[] pdu) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
if (mRil.mSmsStatusRegistrant != null) {
mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pdu, null));
@@ -149,9 +151,9 @@
* @param indicationType Type of radio indication
*/
public void simSmsStorageFull(int indicationType) {
- mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
if (mRil.mIccSmsFullRegistrant != null) {
mRil.mIccSmsFullRegistrant.notifyRegistrant();
diff --git a/src/java/com/android/internal/telephony/MessagingResponse.java b/src/java/com/android/internal/telephony/MessagingResponse.java
index 3dc1d1a..19211e1 100644
--- a/src/java/com/android/internal/telephony/MessagingResponse.java
+++ b/src/java/com/android/internal/telephony/MessagingResponse.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.messaging.IRadioMessagingResponse;
@@ -36,7 +38,7 @@
private void responseSms(RadioResponseInfo responseInfo,
android.hardware.radio.messaging.SendSmsResult sms) {
- RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
if (rr != null) {
long messageId = RIL.getOutgoingSmsMessageId(rr.mResult);
@@ -62,35 +64,35 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void acknowledgeIncomingGsmSmsWithPduResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void acknowledgeLastIncomingGsmSmsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void deleteSmsOnRuimResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void deleteSmsOnSimResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
@@ -99,7 +101,7 @@
*/
public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo,
android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configs) {
- RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
if (rr != null) {
int[] ret;
@@ -148,7 +150,7 @@
*/
public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo,
android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configs) {
- RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
if (rr != null) {
ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<>();
@@ -168,14 +170,14 @@
* @param smsc Short Message Service Center address on the device
*/
public void getSmscAddressResponse(RadioResponseInfo responseInfo, String smsc) {
- RadioResponse.responseString(RIL.MESSAGING_SERVICE, mRil, responseInfo, smsc);
+ RadioResponse.responseString(HAL_SERVICE_MESSAGING, mRil, responseInfo, smsc);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void reportSmsMemoryStatusResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
@@ -227,35 +229,35 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCdmaBroadcastActivationResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setGsmBroadcastActivationResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setGsmBroadcastConfigResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setSmscAddressResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
}
/**
@@ -263,7 +265,7 @@
* @param index record index where the CDMA SMS message is stored
*/
public void writeSmsToRuimResponse(RadioResponseInfo responseInfo, int index) {
- RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+ RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
}
/**
@@ -271,7 +273,7 @@
* @param index record index where the message is stored
*/
public void writeSmsToSimResponse(RadioResponseInfo responseInfo, int index) {
- RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+ RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
}
@Override
diff --git a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
index 5932f9e..dce65af 100644
--- a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
+++ b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
@@ -23,12 +23,14 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import java.time.Instant;
@@ -152,9 +154,6 @@
* @return {@code true} if the SMS message has been processed as a missed incoming call SMS.
*/
private boolean processSms(@NonNull SmsMessage message) {
- long missedCallTime = 0;
- String callerId = null;
-
String[] smsPatterns = mCarrierConfig.getStringArray(CarrierConfigManager
.KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY);
if (smsPatterns == null || smsPatterns.length == 0) {
@@ -162,76 +161,91 @@
return false;
}
- for (String smsPattern : smsPatterns) {
- Pattern pattern;
- try {
- pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES);
- } catch (PatternSyntaxException e) {
- Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms "
- + "pattern: " + smsPattern + ", e=" + e);
- continue;
- }
-
- Matcher matcher = pattern.matcher(message.getMessageBody());
- String year = null, month = null, day = null, hour = null, minute = null;
- if (matcher.find()) {
- try {
- month = matcher.group(SMS_MONTH_TAG);
- day = matcher.group(SMS_DAY_TAG);
- hour = matcher.group(SMS_HOUR_TAG);
- minute = matcher.group(SMS_MINUTE_TAG);
- if (VDBG) {
- Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour
- + ", minute=" + minute);
+ boolean result = false;
+ String[] missedCallMsgs = splitCalls(message.getMessageBody());
+ if (missedCallMsgs != null && missedCallMsgs.length > 0) {
+ for (String parsedMsg : missedCallMsgs) {
+ long missedCallTime = 0;
+ String callerId = null;
+ for (String smsPattern : smsPatterns) {
+ Pattern pattern;
+ try {
+ pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES);
+ } catch (PatternSyntaxException e) {
+ Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms "
+ + "pattern: " + smsPattern + ", e=" + e);
+ continue;
}
- } catch (IllegalArgumentException e) {
- if (VDBG) {
- Rlog.v(TAG, "One of the critical date field is missing. Using the "
- + "current time for missed incoming call.");
- }
- missedCallTime = System.currentTimeMillis();
- }
- // Year is an optional field.
- try {
- year = matcher.group(SMS_YEAR_TAG);
- } catch (IllegalArgumentException e) {
- if (VDBG) Rlog.v(TAG, "Year is missing.");
- }
-
- try {
- if (missedCallTime == 0) {
- missedCallTime = getEpochTime(year, month, day, hour, minute);
- if (missedCallTime == 0) {
- Rlog.e(TAG, "Can't get the time. Use the current time.");
+ Matcher matcher = pattern.matcher(parsedMsg);
+ String year = null, month = null, day = null, hour = null, minute = null;
+ if (matcher.find()) {
+ try {
+ month = matcher.group(SMS_MONTH_TAG);
+ day = matcher.group(SMS_DAY_TAG);
+ hour = matcher.group(SMS_HOUR_TAG);
+ minute = matcher.group(SMS_MINUTE_TAG);
+ if (VDBG) {
+ Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour
+ + ", minute=" + minute);
+ }
+ } catch (IllegalArgumentException e) {
+ if (VDBG) {
+ Rlog.v(TAG, "One of the critical date field is missing. Using the "
+ + "current time for missed incoming call.");
+ }
missedCallTime = System.currentTimeMillis();
}
+
+ // Year is an optional field.
+ try {
+ year = matcher.group(SMS_YEAR_TAG);
+ } catch (IllegalArgumentException e) {
+ if (VDBG) Rlog.v(TAG, "Year is missing.");
+ }
+
+ try {
+ if (missedCallTime == 0) {
+ missedCallTime = getEpochTime(year, month, day, hour, minute);
+ if (missedCallTime == 0) {
+ Rlog.e(TAG, "Can't get the time. Use the current time.");
+ missedCallTime = System.currentTimeMillis();
+ }
+ }
+
+ if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime);
+ } catch (Exception e) {
+ Rlog.e(TAG, "Can't get the time for missed incoming call");
+ }
+
+ try {
+ callerId = matcher.group(SMS_CALLER_ID_TAG);
+ if (VDBG) Rlog.v(TAG, "caller id=" + callerId);
+ } catch (IllegalArgumentException e) {
+ Rlog.d(TAG, "Caller id is not provided or can't be parsed.");
+ }
+ createMissedIncomingCallEvent(missedCallTime, callerId);
+ result = true;
+ break;
}
-
- if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime);
- } catch (Exception e) {
- Rlog.e(TAG, "Can't get the time for missed incoming call");
}
-
- try {
- callerId = matcher.group(SMS_CALLER_ID_TAG);
- if (VDBG) Rlog.v(TAG, "caller id=" + callerId);
- } catch (IllegalArgumentException e) {
- Rlog.d(TAG, "Caller id is not provided or can't be parsed.");
- }
- createMissedIncomingCallEvent(missedCallTime, callerId);
- return true;
}
}
-
- Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern.");
- return false;
+ if (!result) {
+ Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern.");
+ }
+ return result;
}
- // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle.
- private static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
- return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
- String.valueOf(phone.getFullIccSerialNumber()));
+ private String[] splitCalls(String messageBody) {
+ String[] messages = null;
+ if (messageBody != null) {
+ messages = messageBody.split("(\\n|\\s\\n)" + "(\\n|\\s\\n)");
+ Rlog.d(TAG,
+ "splitTheMultipleCalls no of calls = " + ((messages != null) ? messages.length
+ : 0));
+ }
+ return messages;
}
/**
@@ -260,4 +274,18 @@
tm.addNewIncomingCall(makePstnPhoneAccountHandle(mPhone), bundle);
}
}
-}
+
+ // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle.
+ private PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
+ SubscriptionManager subscriptionManager =
+ (SubscriptionManager) phone.getContext().getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ UserHandle userHandle = subscriptionManager.getSubscriptionUserHandle(phone.getSubId());
+ if (userHandle != null) {
+ return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+ String.valueOf(phone.getSubId()), userHandle);
+ }
+ return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+ String.valueOf(phone.getSubId()));
+ }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java
index 4266a75..a20e748 100644
--- a/src/java/com/android/internal/telephony/MockModem.java
+++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -16,6 +16,14 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +42,7 @@
private static final String BIND_IRADIODATA = "android.telephony.mockmodem.iradiodata";
private static final String BIND_IRADIONETWORK = "android.telephony.mockmodem.iradionetwork";
private static final String BIND_IRADIOVOICE = "android.telephony.mockmodem.iradiovoice";
+ private static final String BIND_IRADIOIMS = "android.telephony.mockmodem.iradioims";
private static final String BIND_IRADIOCONFIG = "android.telephony.mockmodem.iradioconfig";
private static final String PHONE_ID = "phone_id";
@@ -42,7 +51,7 @@
static final int RADIOCONFIG_SERVICE = RIL.MAX_SERVICE_IDX + 1;
static final int BINDER_RETRY_MILLIS = 3 * 100;
- static final int BINDER_MAX_RETRY = 3;
+ static final int BINDER_MAX_RETRY = 10;
private Context mContext;
private String mServiceName;
@@ -54,6 +63,7 @@
private IBinder mDataBinder;
private IBinder mNetworkBinder;
private IBinder mVoiceBinder;
+ private IBinder mImsBinder;
private IBinder mConfigBinder;
private ServiceConnection mModemServiceConnection;
private ServiceConnection mSimServiceConnection;
@@ -61,9 +71,11 @@
private ServiceConnection mDataServiceConnection;
private ServiceConnection mNetworkServiceConnection;
private ServiceConnection mVoiceServiceConnection;
+ private ServiceConnection mImsServiceConnection;
private ServiceConnection mConfigServiceConnection;
private byte mPhoneId;
+ private String mTag;
MockModem(Context context, String serviceName) {
this(context, serviceName, 0);
@@ -71,6 +83,7 @@
MockModem(Context context, String serviceName, int phoneId) {
mPhoneId = (byte) phoneId;
+ mTag = TAG + "-" + mPhoneId;
mContext = context;
String[] componentInfo = serviceName.split("/", 2);
mPackageName = componentInfo[0];
@@ -86,20 +99,22 @@
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
- Rlog.d(TAG, "IRadio " + getModuleName(mService) + " - onServiceConnected");
+ Rlog.d(mTag, "IRadio " + getModuleName(mService) + " - onServiceConnected");
- if (mService == RIL.MODEM_SERVICE) {
+ if (mService == HAL_SERVICE_MODEM) {
mModemBinder = binder;
- } else if (mService == RIL.SIM_SERVICE) {
+ } else if (mService == HAL_SERVICE_SIM) {
mSimBinder = binder;
- } else if (mService == RIL.MESSAGING_SERVICE) {
+ } else if (mService == HAL_SERVICE_MESSAGING) {
mMessagingBinder = binder;
- } else if (mService == RIL.DATA_SERVICE) {
+ } else if (mService == HAL_SERVICE_DATA) {
mDataBinder = binder;
- } else if (mService == RIL.NETWORK_SERVICE) {
+ } else if (mService == HAL_SERVICE_NETWORK) {
mNetworkBinder = binder;
- } else if (mService == RIL.VOICE_SERVICE) {
+ } else if (mService == HAL_SERVICE_VOICE) {
mVoiceBinder = binder;
+ } else if (mService == HAL_SERVICE_IMS) {
+ mImsBinder = binder;
} else if (mService == RADIOCONFIG_SERVICE) {
mConfigBinder = binder;
}
@@ -107,20 +122,22 @@
@Override
public void onServiceDisconnected(ComponentName name) {
- Rlog.d(TAG, "IRadio " + getModuleName(mService) + " - onServiceDisconnected");
+ Rlog.d(mTag, "IRadio " + getModuleName(mService) + " - onServiceDisconnected");
- if (mService == RIL.MODEM_SERVICE) {
+ if (mService == HAL_SERVICE_MODEM) {
mModemBinder = null;
- } else if (mService == RIL.SIM_SERVICE) {
+ } else if (mService == HAL_SERVICE_SIM) {
mSimBinder = null;
- } else if (mService == RIL.MESSAGING_SERVICE) {
+ } else if (mService == HAL_SERVICE_MESSAGING) {
mMessagingBinder = null;
- } else if (mService == RIL.DATA_SERVICE) {
+ } else if (mService == HAL_SERVICE_DATA) {
mDataBinder = null;
- } else if (mService == RIL.NETWORK_SERVICE) {
+ } else if (mService == HAL_SERVICE_NETWORK) {
mNetworkBinder = null;
- } else if (mService == RIL.VOICE_SERVICE) {
+ } else if (mService == HAL_SERVICE_VOICE) {
mVoiceBinder = null;
+ } else if (mService == HAL_SERVICE_IMS) {
+ mImsBinder = null;
} else if (mService == RADIOCONFIG_SERVICE) {
mConfigBinder = null;
}
@@ -138,7 +155,7 @@
Intent intent = new Intent();
intent.setComponent(new ComponentName(mPackageName, mServiceName));
- intent.setAction(actionName);
+ intent.setAction(actionName + phoneId);
intent.putExtra(PHONE_ID, phoneId);
status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
@@ -148,18 +165,20 @@
/** waitForBinder */
public IBinder getServiceBinder(int service) {
switch (service) {
- case RIL.MODEM_SERVICE:
+ case HAL_SERVICE_MODEM:
return mModemBinder;
- case RIL.SIM_SERVICE:
+ case HAL_SERVICE_SIM:
return mSimBinder;
- case RIL.MESSAGING_SERVICE:
+ case HAL_SERVICE_MESSAGING:
return mMessagingBinder;
- case RIL.DATA_SERVICE:
+ case HAL_SERVICE_DATA:
return mDataBinder;
- case RIL.NETWORK_SERVICE:
+ case HAL_SERVICE_NETWORK:
return mNetworkBinder;
- case RIL.VOICE_SERVICE:
+ case HAL_SERVICE_VOICE:
return mVoiceBinder;
+ case HAL_SERVICE_IMS:
+ return mImsBinder;
case RADIOCONFIG_SERVICE:
return mConfigBinder;
default:
@@ -183,95 +202,109 @@
boolean status =
bindModuleToMockModemService(BIND_IRADIOCONFIG, mConfigServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Config bind fail");
+ Rlog.d(mTag, "IRadio Config bind fail");
mConfigServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Config is bound");
+ Rlog.d(mTag, "IRadio Config is bound");
}
- } else if (service == RIL.MODEM_SERVICE) {
+ } else if (service == HAL_SERVICE_MODEM) {
if (mModemBinder == null) {
- mModemServiceConnection = new MockModemConnection(RIL.MODEM_SERVICE);
+ mModemServiceConnection = new MockModemConnection(HAL_SERVICE_MODEM);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIOMODEM, mModemServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Modem bind fail");
+ Rlog.d(mTag, "IRadio Modem bind fail");
mModemServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Modem is bound");
+ Rlog.d(mTag, "IRadio Modem is bound");
}
- } else if (service == RIL.SIM_SERVICE) {
+ } else if (service == HAL_SERVICE_SIM) {
if (mSimBinder == null) {
- mSimServiceConnection = new MockModemConnection(RIL.SIM_SERVICE);
+ mSimServiceConnection = new MockModemConnection(HAL_SERVICE_SIM);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIOSIM, mSimServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Sim bind fail");
+ Rlog.d(mTag, "IRadio Sim bind fail");
mSimServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Sim is bound");
+ Rlog.d(mTag, "IRadio Sim is bound");
}
- } else if (service == RIL.MESSAGING_SERVICE) {
+ } else if (service == HAL_SERVICE_MESSAGING) {
if (mMessagingBinder == null) {
- mMessagingServiceConnection = new MockModemConnection(RIL.MESSAGING_SERVICE);
+ mMessagingServiceConnection = new MockModemConnection(HAL_SERVICE_MESSAGING);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIOMESSAGING, mMessagingServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Messaging bind fail");
+ Rlog.d(mTag, "IRadio Messaging bind fail");
mMessagingServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Messaging is bound");
+ Rlog.d(mTag, "IRadio Messaging is bound");
}
- } else if (service == RIL.DATA_SERVICE) {
+ } else if (service == HAL_SERVICE_DATA) {
if (mDataBinder == null) {
- mDataServiceConnection = new MockModemConnection(RIL.DATA_SERVICE);
+ mDataServiceConnection = new MockModemConnection(HAL_SERVICE_DATA);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIODATA, mDataServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Data bind fail");
+ Rlog.d(mTag, "IRadio Data bind fail");
mDataServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Data is bound");
+ Rlog.d(mTag, "IRadio Data is bound");
}
- } else if (service == RIL.NETWORK_SERVICE) {
+ } else if (service == HAL_SERVICE_NETWORK) {
if (mNetworkBinder == null) {
- mNetworkServiceConnection = new MockModemConnection(RIL.NETWORK_SERVICE);
+ mNetworkServiceConnection = new MockModemConnection(HAL_SERVICE_NETWORK);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIONETWORK, mNetworkServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Network bind fail");
+ Rlog.d(mTag, "IRadio Network bind fail");
mNetworkServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Network is bound");
+ Rlog.d(mTag, "IRadio Network is bound");
}
- } else if (service == RIL.VOICE_SERVICE) {
+ } else if (service == HAL_SERVICE_VOICE) {
if (mVoiceBinder == null) {
- mVoiceServiceConnection = new MockModemConnection(RIL.VOICE_SERVICE);
+ mVoiceServiceConnection = new MockModemConnection(HAL_SERVICE_VOICE);
boolean status =
bindModuleToMockModemService(
mPhoneId, BIND_IRADIOVOICE, mVoiceServiceConnection);
if (!status) {
- Rlog.d(TAG, "IRadio Voice bind fail");
+ Rlog.d(mTag, "IRadio Voice bind fail");
mVoiceServiceConnection = null;
}
} else {
- Rlog.d(TAG, "IRadio Voice is bound");
+ Rlog.d(mTag, "IRadio Voice is bound");
+ }
+ } else if (service == HAL_SERVICE_IMS) {
+ if (mImsBinder == null) {
+ mImsServiceConnection = new MockModemConnection(HAL_SERVICE_IMS);
+
+ boolean status =
+ bindModuleToMockModemService(
+ mPhoneId, BIND_IRADIOIMS, mImsServiceConnection);
+ if (!status) {
+ Rlog.d(TAG, "IRadio Ims bind fail");
+ mImsServiceConnection = null;
+ }
+ } else {
+ Rlog.d(TAG, "IRadio Ims is bound");
}
}
}
@@ -284,49 +317,56 @@
mContext.unbindService(mConfigServiceConnection);
mConfigServiceConnection = null;
mConfigBinder = null;
- Rlog.d(TAG, "unbind IRadio Config");
+ Rlog.d(mTag, "unbind IRadio Config");
}
- } else if (service == RIL.MODEM_SERVICE) {
+ } else if (service == HAL_SERVICE_MODEM) {
if (mModemServiceConnection != null) {
mContext.unbindService(mModemServiceConnection);
mModemServiceConnection = null;
mModemBinder = null;
- Rlog.d(TAG, "unbind IRadio Modem");
+ Rlog.d(mTag, "unbind IRadio Modem");
}
- } else if (service == RIL.SIM_SERVICE) {
+ } else if (service == HAL_SERVICE_SIM) {
if (mSimServiceConnection != null) {
mContext.unbindService(mSimServiceConnection);
mSimServiceConnection = null;
mSimBinder = null;
- Rlog.d(TAG, "unbind IRadio Sim");
+ Rlog.d(mTag, "unbind IRadio Sim");
}
- } else if (service == RIL.MESSAGING_SERVICE) {
+ } else if (service == HAL_SERVICE_MESSAGING) {
if (mMessagingServiceConnection != null) {
mContext.unbindService(mMessagingServiceConnection);
mMessagingServiceConnection = null;
mMessagingBinder = null;
- Rlog.d(TAG, "unbind IRadio Messaging");
+ Rlog.d(mTag, "unbind IRadio Messaging");
}
- } else if (service == RIL.DATA_SERVICE) {
+ } else if (service == HAL_SERVICE_DATA) {
if (mDataServiceConnection != null) {
mContext.unbindService(mDataServiceConnection);
mDataServiceConnection = null;
mDataBinder = null;
- Rlog.d(TAG, "unbind IRadio Data");
+ Rlog.d(mTag, "unbind IRadio Data");
}
- } else if (service == RIL.NETWORK_SERVICE) {
+ } else if (service == HAL_SERVICE_NETWORK) {
if (mNetworkServiceConnection != null) {
mContext.unbindService(mNetworkServiceConnection);
mNetworkServiceConnection = null;
mNetworkBinder = null;
- Rlog.d(TAG, "unbind IRadio Network");
+ Rlog.d(mTag, "unbind IRadio Network");
}
- } else if (service == RIL.VOICE_SERVICE) {
+ } else if (service == HAL_SERVICE_VOICE) {
if (mVoiceServiceConnection != null) {
mContext.unbindService(mVoiceServiceConnection);
mVoiceServiceConnection = null;
mVoiceBinder = null;
- Rlog.d(TAG, "unbind IRadio Voice");
+ Rlog.d(mTag, "unbind IRadio Voice");
+ }
+ } else if (service == HAL_SERVICE_IMS) {
+ if (mImsServiceConnection != null) {
+ mContext.unbindService(mImsServiceConnection);
+ mImsServiceConnection = null;
+ mImsBinder = null;
+ Rlog.d(TAG, "unbind IRadio Ims");
}
}
}
@@ -337,18 +377,20 @@
private String getModuleName(int service) {
switch (service) {
- case RIL.MODEM_SERVICE:
+ case HAL_SERVICE_MODEM:
return "modem";
- case RIL.SIM_SERVICE:
+ case HAL_SERVICE_SIM:
return "sim";
- case RIL.MESSAGING_SERVICE:
+ case HAL_SERVICE_MESSAGING:
return "messaging";
- case RIL.DATA_SERVICE:
+ case HAL_SERVICE_DATA:
return "data";
- case RIL.NETWORK_SERVICE:
+ case HAL_SERVICE_NETWORK:
return "network";
- case RIL.VOICE_SERVICE:
+ case HAL_SERVICE_VOICE:
return "voice";
+ case HAL_SERVICE_IMS:
+ return "ims";
case RADIOCONFIG_SERVICE:
return "config";
default:
diff --git a/src/java/com/android/internal/telephony/ModemIndication.java b/src/java/com/android/internal/telephony/ModemIndication.java
index 8baa5fd..0ee40bb 100644
--- a/src/java/com/android/internal/telephony/ModemIndication.java
+++ b/src/java/com/android/internal/telephony/ModemIndication.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
@@ -44,11 +46,11 @@
*/
public void hardwareConfigChanged(int indicationType,
android.hardware.radio.modem.HardwareConfig[] configs) {
- mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
mRil.mHardwareConfigChangeRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -62,9 +64,9 @@
* restart" that explains the cause of the modem restart
*/
public void modemReset(int indicationType, String reason) {
- mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
mRil.writeMetricsModemRestartEvent(reason);
mRil.mModemResetRegistrants.notifyRegistrants(new AsyncResult(null, reason, null));
@@ -78,11 +80,11 @@
*/
public void radioCapabilityIndication(int indicationType,
android.hardware.radio.modem.RadioCapability radioCapability) {
- mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
RadioCapability response = RILUtils.convertHalRadioCapability(radioCapability, mRil);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -94,12 +96,12 @@
* @param radioState Current radio state
*/
public void radioStateChanged(int indicationType, int radioState) {
- mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
int state = RILUtils.convertHalRadioState(radioState);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: "
- + state);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogMore(
+ RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + state);
}
mRil.setRadioState(state, false /* forceNotifyRegistrants */);
@@ -110,9 +112,9 @@
* @param indicationType Type of radio indication
*/
public void rilConnected(int indicationType) {
- mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
// Initial conditions
mRil.setRadioPower(false, null);
diff --git a/src/java/com/android/internal/telephony/ModemResponse.java b/src/java/com/android/internal/telephony/ModemResponse.java
index 6e44ddc..bd04d16 100644
--- a/src/java/com/android/internal/telephony/ModemResponse.java
+++ b/src/java/com/android/internal/telephony/ModemResponse.java
@@ -16,9 +16,12 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.modem.IRadioModemResponse;
+import android.hardware.radio.modem.ImeiInfo;
import android.os.SystemClock;
import android.telephony.ActivityStatsTechSpecificInfo;
import android.telephony.AnomalyReporter;
@@ -51,7 +54,7 @@
* @param responseInfo Response info struct containing response type, serial number and error.
*/
public void enableModemResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
@@ -59,7 +62,7 @@
* @param version String containing version string for log reporting
*/
public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) {
- RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, version);
+ RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, version);
}
/**
@@ -72,7 +75,21 @@
public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
String imeisv, String esn, String meid) {
RadioResponse.responseStrings(
- RIL.MODEM_SERVICE, mRil, responseInfo, imei, imeisv, esn, meid);
+ HAL_SERVICE_MODEM, mRil, responseInfo, imei, imeisv, esn, meid);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param imeiInfo object containing ImeiType, device IMEI and IMEISV
+ */
+ public void getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo) {
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ RadioResponse.sendMessageResponse(rr.mResult, imeiInfo);
+ }
+ mRil.processResponseDone(rr, responseInfo, imeiInfo);
+ }
}
/**
@@ -81,7 +98,7 @@
*/
public void getHardwareConfigResponse(RadioResponseInfo responseInfo,
android.hardware.radio.modem.HardwareConfig[] config) {
- RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
if (rr != null) {
ArrayList<HardwareConfig> ret = RILUtils.convertHalHardwareConfigList(config);
@@ -98,7 +115,7 @@
*/
public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
android.hardware.radio.modem.ActivityStatsInfo activityInfo) {
- RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
if (rr != null) {
ModemActivityInfo ret = null;
@@ -141,7 +158,7 @@
* @param isEnabled whether the modem stack is enabled.
*/
public void getModemStackStatusResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
- RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
@@ -157,7 +174,7 @@
*/
public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
android.hardware.radio.modem.RadioCapability radioCapability) {
- RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
if (rr != null) {
RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -179,42 +196,42 @@
* @param result String containing the contents of the NV item
*/
public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) {
- RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, result);
+ RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, result);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void nvResetConfigResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void nvWriteItemResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void requestShutdownResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendDeviceStateResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
}
/**
@@ -223,7 +240,7 @@
*/
public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
android.hardware.radio.modem.RadioCapability radioCapability) {
- RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
if (rr != null) {
RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -238,7 +255,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error.
*/
public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
mRil.mLastRadioPowerResult = responseInfo.error;
if (responseInfo.error == RadioError.RF_HARDWARE_ISSUE) {
AnomalyReporter.reportAnomaly(
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 3f5c23c..0acae4b 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -33,10 +33,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -54,6 +52,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.ArrayUtils;
import java.lang.annotation.Retention;
@@ -83,7 +83,6 @@
private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4;
private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5;
private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8;
@VisibleForTesting
public static final int EVENT_RADIO_STATE_CHANGED = 9;
@@ -121,19 +120,15 @@
private static final int PRIMARY_SUB_REMOVED_IN_GROUP = 7;
protected final Context mContext;
- protected final SubscriptionController mSubController;
+ private final SubscriptionManagerService mSubscriptionManagerService;
+
// Keep a record of active primary (non-opportunistic) subscription list.
@NonNull private List<Integer> mPrimarySubList = new ArrayList<>();
/** The singleton instance. */
protected static MultiSimSettingController sInstance = null;
- // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. The reason of keeping
- // a local variable instead of calling SubscriptionInfoUpdater#isSubInfoInitialized is, there
- // might be a race condition that we receive EVENT_SUBSCRIPTION_INFO_CHANGED first, then
- // EVENT_ALL_SUBSCRIPTIONS_LOADED. And calling SubscriptionInfoUpdater#isSubInfoInitialized
- // will make us handle EVENT_SUBSCRIPTION_INFO_CHANGED unexpectedly and causing us to believe
- // the SIMs are newly inserted instead of being initialized.
+ // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED.
private boolean mSubInfoInitialized = false;
// mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot.
@@ -158,19 +153,6 @@
private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
- int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- notifyCarrierConfigChanged(phoneId, subId);
- }
- }
- };
-
private static class DataSettingsControllerCallback extends DataSettingsManagerCallback {
private final Phone mPhone;
@@ -218,10 +200,10 @@
/**
* Init instance of MultiSimSettingController.
*/
- public static MultiSimSettingController init(Context context, SubscriptionController sc) {
+ public static MultiSimSettingController init(Context context) {
synchronized (MultiSimSettingController.class) {
if (sInstance == null) {
- sInstance = new MultiSimSettingController(context, sc);
+ sInstance = new MultiSimSettingController(context);
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
@@ -230,9 +212,9 @@
}
@VisibleForTesting
- public MultiSimSettingController(Context context, SubscriptionController sc) {
+ public MultiSimSettingController(Context context) {
mContext = context;
- mSubController = sc;
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
// Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
final int phoneCount = ((TelephonyManager) mContext.getSystemService(
@@ -245,8 +227,12 @@
mIsAskEverytimeSupportedForSms = mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support);
- context.registerReceiver(mIntentReceiver, new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+ // Listener callback is executed on handler thread to directly handle config change
+ ccm.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ onCarrierConfigChanged(slotIndex, subId));
}
/**
@@ -303,7 +289,7 @@
case EVENT_USER_DATA_ENABLED: {
int subId = msg.arg1;
boolean enable = msg.arg2 != 0;
- onUserDataEnabled(subId, enable);
+ onUserDataEnabled(subId, enable, true);
break;
}
case EVENT_ROAMING_DATA_ENABLED: {
@@ -325,11 +311,6 @@
case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
onDefaultDataSettingChanged();
break;
- case EVENT_CARRIER_CONFIG_CHANGED:
- int phoneId = msg.arg1;
- int subId = msg.arg2;
- onCarrierConfigChanged(phoneId, subId);
- break;
case EVENT_MULTI_SIM_CONFIG_CHANGED:
int activeModems = (int) ((AsyncResult) msg.obj).result;
onMultiSimConfigChanged(activeModems);
@@ -352,17 +333,21 @@
* If user is enabling a non-default non-opportunistic subscription, make it default
* data subscription.
*/
- protected void onUserDataEnabled(int subId, boolean enable) {
- if (DBG) log("onUserDataEnabled");
+ private void onUserDataEnabled(int subId, boolean enable, boolean setDefaultData) {
+ if (DBG) log("[onUserDataEnabled] subId=" + subId + " enable=" + enable +
+ " setDefaultData=" + setDefaultData);
// Make sure MOBILE_DATA of subscriptions in same group are synced.
setUserDataEnabledForGroup(subId, enable);
+ SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
+ int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
+
// If user is enabling a non-default non-opportunistic subscription, make it default.
- if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId)
- && enable && mSubController.isActiveSubId(subId)) {
- android.provider.Settings.Global.putInt(mContext.getContentResolver(),
- SETTING_USER_PREF_DATA_SUB, subId);
- mSubController.setDefaultDataSubId(subId);
+ if (defaultDataSubId != subId && subInfo != null && !subInfo.isOpportunistic() && enable
+ && subInfo.isActive() && setDefaultData) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ SETTING_USER_PREF_DATA_SUB, subId);
+ mSubscriptionManagerService.setDefaultDataSubId(subId);
}
}
@@ -373,8 +358,8 @@
if (DBG) log("onRoamingDataEnabled");
setRoamingDataEnabledForGroup(subId, enable);
- // Also inform SubscriptionController as it keeps another copy of user setting.
- mSubController.setDataRoaming(enable ? 1 : 0, subId);
+ // Also inform SubscriptionManagerService as it keeps another copy of user setting.
+ mSubscriptionManagerService.setDataRoaming(enable ? 1 : 0, subId);
}
/**
@@ -419,14 +404,6 @@
reEvaluateAll();
}
- /**
- * Called when carrier config changes on any phone.
- */
- @VisibleForTesting
- public void notifyCarrierConfigChanged(int phoneId, int subId) {
- obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget();
- }
-
private void onCarrierConfigChanged(int phoneId, int subId) {
log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
@@ -438,14 +415,12 @@
// being specified in it. So here we do additional check to make sur we don't miss the
// subId.
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- int[] subIds = mSubController.getSubId(phoneId);
- if (!ArrayUtils.isEmpty(subIds)) {
- CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService(
- mContext.CARRIER_CONFIG_SERVICE);
- if (cm != null && cm.getConfigForSubId(subIds[0]) != null) {
- loge("onCarrierConfigChanged with invalid subId while subd "
- + subIds[0] + " is active and its config is loaded");
- subId = subIds[0];
+ subId = SubscriptionManager.getSubscriptionId(phoneId);
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class);
+ if (cm != null && cm.getConfigForSubId(subId) != null) {
+ loge("onCarrierConfigChanged with invalid subId while subId "
+ + subId + " is active and its config is loaded");
}
}
}
@@ -459,7 +434,7 @@
*/
@VisibleForTesting
public boolean isCarrierConfigLoadedForAllSub() {
- int[] activeSubIds = mSubController.getActiveSubIdList(false);
+ int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(false);
for (int activeSubId : activeSubIds) {
boolean isLoaded = false;
for (int configLoadedSub : mCarrierConfigLoadedSubIds) {
@@ -526,7 +501,7 @@
private void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
if (DBG) log("onSubscriptionGroupChanged");
- List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
+ List<SubscriptionInfo> infoList = mSubscriptionManagerService.getSubscriptionsInGroup(
groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag());
if (infoList == null || infoList.isEmpty()) return;
@@ -535,24 +510,36 @@
int refSubId = infoList.get(0).getSubscriptionId();
for (SubscriptionInfo info : infoList) {
int subId = info.getSubscriptionId();
- if (mSubController.isActiveSubId(subId) && !mSubController.isOpportunistic(subId)) {
+ if (info.isActive() && !info.isOpportunistic()) {
refSubId = subId;
break;
}
}
+
if (DBG) log("refSubId is " + refSubId);
boolean enable = false;
try {
enable = GlobalSettingsHelper.getBoolean(
mContext, Settings.Global.MOBILE_DATA, refSubId);
- onUserDataEnabled(refSubId, enable);
} catch (SettingNotFoundException exception) {
//pass invalid refSubId to fetch the single-sim setting
enable = GlobalSettingsHelper.getBoolean(
mContext, Settings.Global.MOBILE_DATA, INVALID_SUBSCRIPTION_ID, enable);
- onUserDataEnabled(refSubId, enable);
}
+ boolean setDefaultData = true;
+ List<SubscriptionInfo> activeSubList = mSubscriptionManagerService
+ .getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ for (SubscriptionInfo activeInfo : activeSubList) {
+ if (!(groupUuid.equals(activeInfo.getGroupUuid()))) {
+ // Do not set refSubId as defaultDataSubId if there are other active
+ // subscriptions which does not belong to this groupUuid
+ setDefaultData = false;
+ break;
+ }
+ }
+ onUserDataEnabled(refSubId, enable, setDefaultData);
enable = false;
try {
@@ -566,8 +553,7 @@
onRoamingDataEnabled(refSubId, enable);
}
- // Sync settings in subscription database..
- mSubController.syncGroupedSetting(refSubId);
+ mSubscriptionManagerService.syncGroupedSetting(refSubId);
}
/**
@@ -589,21 +575,24 @@
if (!isReadyToReevaluate()) return;
- List<SubscriptionInfo> activeSubInfos = mSubController
+ List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService
.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
mContext.getAttributionTag());
if (ArrayUtils.isEmpty(activeSubInfos)) {
mPrimarySubList.clear();
- if (DBG) log("[updateDefaultValues] No active sub. Setting default to INVALID sub.");
- mSubController.setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mSubController.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mSubController.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (DBG) log("updateDefaults: No active sub. Setting default to INVALID sub.");
+ mSubscriptionManagerService.setDefaultDataSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mSubscriptionManagerService.setDefaultVoiceSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mSubscriptionManagerService.setDefaultSmsSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
return;
}
int change = updatePrimarySubListAndGetChangeType(activeSubInfos);
- if (DBG) log("[updateDefaultValues] change: " + change);
+ if (DBG) log("updateDefaultValues: change: " + change);
if (change == PRIMARY_SUB_NO_CHANGE) return;
// If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG
@@ -615,33 +604,35 @@
|| ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
.getActiveModemCount() == 1)) {
int subId = mPrimarySubList.get(0);
- if (DBG) log("[updateDefaultValues] to only primary sub " + subId);
- mSubController.setDefaultDataSubId(subId);
- mSubController.setDefaultVoiceSubId(subId);
- mSubController.setDefaultSmsSubId(subId);
+ if (DBG) log("updateDefaultValues: to only primary sub " + subId);
+ mSubscriptionManagerService.setDefaultDataSubId(subId);
+ mSubscriptionManagerService.setDefaultVoiceSubId(subId);
+ mSubscriptionManagerService.setDefaultSmsSubId(subId);
sendDefaultSubConfirmedNotification(subId);
return;
}
- if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList);
+ if (DBG) log("updateDefaultValues: records: " + mPrimarySubList);
+
+ boolean dataSelected, voiceSelected, smsSelected;
// Update default data subscription.
- if (DBG) log("[updateDefaultValues] Update default data subscription");
- boolean dataSelected = updateDefaultValue(mPrimarySubList,
- mSubController.getDefaultDataSubId(),
- (newValue -> mSubController.setDefaultDataSubId(newValue)));
+ if (DBG) log("updateDefaultValues: Update default data subscription");
+ dataSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultDataSubId(),
+ mSubscriptionManagerService::setDefaultDataSubId);
// Update default voice subscription.
- if (DBG) log("[updateDefaultValues] Update default voice subscription");
- boolean voiceSelected = updateDefaultValue(mPrimarySubList,
- mSubController.getDefaultVoiceSubId(),
- (newValue -> mSubController.setDefaultVoiceSubId(newValue)));
+ if (DBG) log("updateDefaultValues: Update default voice subscription");
+ voiceSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultVoiceSubId(),
+ mSubscriptionManagerService::setDefaultVoiceSubId);
// Update default sms subscription.
- if (DBG) log("[updateDefaultValues] Update default sms subscription");
- boolean smsSelected = updateDefaultValue(mPrimarySubList,
- mSubController.getDefaultSmsSubId(),
- (newValue -> mSubController.setDefaultSmsSubId(newValue)),
+ if (DBG) log("updateDefaultValues: Update default sms subscription");
+ smsSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultSmsSubId(),
+ mSubscriptionManagerService::setDefaultSmsSubId,
mIsAskEverytimeSupportedForSms);
boolean autoFallbackEnabled = mContext.getResources().getBoolean(
@@ -693,7 +684,9 @@
// any previous primary subscription becomes inactive, we consider it
for (int subId : prevPrimarySubList) {
if (mPrimarySubList.contains(subId)) continue;
- if (!mSubController.isActiveSubId(subId)) {
+ SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
+
+ if (subInfo == null || !subInfo.isActive()) {
for (int currentSubId : mPrimarySubList) {
if (areSubscriptionsInSameGroup(currentSubId, subId)) {
return PRIMARY_SUB_REMOVED_IN_GROUP;
@@ -701,10 +694,10 @@
}
return PRIMARY_SUB_REMOVED;
}
- if (!mSubController.isOpportunistic(subId)) {
+ if (!subInfo.isOpportunistic()) {
// Should never happen.
- loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId "
- + subId);
+ loge("[updatePrimarySubListAndGetChangeType]: missing active primary "
+ + "subId " + subId);
}
}
return PRIMARY_SUB_MARKED_OPPT;
@@ -729,6 +722,7 @@
@TelephonyManager.DefaultSubscriptionSelectType
int simSelectDialogType = getSimSelectDialogType(
change, dataSelected, voiceSelected, smsSelected);
+ log("sendSubChangeNotificationIfNeeded: simSelectDialogType=" + simSelectDialogType);
SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change);
if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE
@@ -760,6 +754,12 @@
boolean voiceSelected, boolean smsSelected) {
int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
+ // Do not show preference selection dialog during SuW as there is fullscreen activity to
+ // choose preference.
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ return dialogType;
+ }
// If a primary subscription is removed and only one is left active, ask user
// for preferred sub selection if any default setting is not set.
// If another primary subscription is added or default data is not selected, ask
@@ -768,12 +768,8 @@
&& (!dataSelected || !smsSelected || !voiceSelected)) {
dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
} else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change)
- || (change == PRIMARY_SUB_INITIALIZED && !dataSelected
- && Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 0) != 0))) {
+ || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) {
// If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again.
- // In default DSDS devices, do not show data selection dialog during SuW as there is
- // fullscreen activity to choose data preference.
dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
}
@@ -801,9 +797,12 @@
// If a dual CDMA SIM combination warning is needed.
if (phone != null && phone.isCdmaSubscriptionAppPresent()) {
cdmaPhoneCount++;
- String simName = mSubController.getActiveSubscriptionInfo(
- subId, mContext.getOpPackageName(), mContext.getAttributionTag())
- .getDisplayName().toString();
+ String simName = null;
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null) {
+ simName = subInfo.getDisplayName();
+ }
if (TextUtils.isEmpty(simName)) {
// Fall back to carrier name.
simName = phone.getCarrierName();
@@ -828,23 +827,21 @@
protected void disableDataForNonDefaultNonOpportunisticSubscriptions() {
if (!isReadyToReevaluate()) return;
- int defaultDataSub = mSubController.getDefaultDataSubId();
+ int defaultDataSub = mSubscriptionManagerService.getDefaultDataSubId();
for (Phone phone : PhoneFactory.getPhones()) {
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(phone.getSubId());
+ boolean isOpportunistic = subInfo != null && subInfo.isOpportunistic();
if (phone.getSubId() != defaultDataSub
&& SubscriptionManager.isValidSubscriptionId(phone.getSubId())
- && !mSubController.isOpportunistic(phone.getSubId())
+ && !isOpportunistic
&& phone.isUserDataEnabled()
&& !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) {
log("setting data to false on " + phone.getSubId());
- if (phone.isUsingNewDataStack()) {
- phone.getDataSettingsManager().setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false,
- mContext.getOpPackageName());
- } else {
- phone.getDataEnabledSettings().setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
- }
+ phone.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false,
+ mContext.getOpPackageName());
}
}
}
@@ -854,9 +851,13 @@
|| !SubscriptionManager.isUsableSubscriptionId(subId2)) return false;
if (subId1 == subId2) return true;
- ParcelUuid groupUuid1 = mSubController.getGroupUuid(subId1);
- ParcelUuid groupUuid2 = mSubController.getGroupUuid(subId2);
- return groupUuid1 != null && groupUuid1.equals(groupUuid2);
+ SubscriptionInfoInternal subInfo1 =
+ mSubscriptionManagerService.getSubscriptionInfoInternal(subId1);
+ SubscriptionInfoInternal subInfo2 =
+ mSubscriptionManagerService.getSubscriptionInfoInternal(subId2);
+ return subInfo1 != null && subInfo2 != null
+ && !TextUtils.isEmpty(subInfo1.getGroupUuid())
+ && subInfo1.getGroupUuid().equals(subInfo2.getGroupUuid());
}
/**
@@ -865,28 +866,30 @@
*/
protected void setUserDataEnabledForGroup(int subId, boolean enable) {
log("setUserDataEnabledForGroup subId " + subId + " enable " + enable);
- List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
- mSubController.getGroupUuid(subId), mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ List<SubscriptionInfo> infoList = null;
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null && !subInfo.getGroupUuid().isEmpty()) {
+ infoList = mSubscriptionManagerService.getSubscriptionsInGroup(
+ ParcelUuid.fromString(subInfo.getGroupUuid()), mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ }
if (infoList == null) return;
for (SubscriptionInfo info : infoList) {
int currentSubId = info.getSubscriptionId();
// TODO: simplify when setUserDataEnabled becomes singleton
- if (mSubController.isActiveSubId(currentSubId)) {
+ if (info.isActive()) {
// For active subscription, call setUserDataEnabled through DataSettingsManager.
- Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId));
+ Phone phone = PhoneFactory.getPhone(mSubscriptionManagerService
+ .getPhoneId(currentSubId));
// If enable is true and it's not opportunistic subscription, we don't enable it,
// as there can't be two
if (phone != null) {
- if (phone.isUsingNewDataStack()) {
- phone.getDataSettingsManager().setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, enable,
- mContext.getOpPackageName());
- } else {
- phone.getDataEnabledSettings().setUserDataEnabled(enable, false);
- }
+ phone.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, enable,
+ mContext.getOpPackageName());
}
} else {
// For inactive subscription, directly write into global settings.
@@ -901,11 +904,12 @@
* are synced.
*/
private void setRoamingDataEnabledForGroup(int subId, boolean enable) {
- SubscriptionController subController = SubscriptionController.getInstance();
- List<SubscriptionInfo> infoList = subController.getSubscriptionsInGroup(
- mSubController.getGroupUuid(subId), mContext.getOpPackageName(),
- mContext.getAttributionTag());
-
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null || subInfo.getGroupUuid().isEmpty()) return;
+ List<SubscriptionInfo> infoList = SubscriptionManagerService.getInstance()
+ .getSubscriptionsInGroup(ParcelUuid.fromString(subInfo.getGroupUuid()),
+ mContext.getOpPackageName(), mContext.getAttributionTag());
if (infoList == null) return;
for (SubscriptionInfo info : infoList) {
@@ -958,16 +962,16 @@
// subscription gets deactivated or removed, we need to automatically disable the grouped
// opportunistic subscription, which will be marked isGroupDisabled as true by SubController.
private void deactivateGroupedOpportunisticSubscriptionIfNeeded() {
- if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
-
- List<SubscriptionInfo> opptSubList = mSubController.getOpportunisticSubscriptions(
- mContext.getOpPackageName(), mContext.getAttributionTag());
+ List<SubscriptionInfo> opptSubList = mSubscriptionManagerService.getAllSubInfoList(
+ mContext.getOpPackageName(), mContext.getAttributionTag()).stream()
+ .filter(SubscriptionInfo::isOpportunistic)
+ .collect(Collectors.toList());
if (ArrayUtils.isEmpty(opptSubList)) return;
for (SubscriptionInfo info : opptSubList) {
- if (info.isGroupDisabled() && mSubController.isActiveSubId(info.getSubscriptionId())) {
- log("[deactivateGroupedOpptSubIfNeeded] "
+ if (info.isGroupDisabled() && info.isActive()) {
+ log("deactivateGroupedOpportunisticSubscriptionIfNeeded: "
+ "Deactivating grouped opportunistic subscription "
+ info.getSubscriptionId());
deactivateSubscription(info);
@@ -997,47 +1001,55 @@
// would be selected as preferred voice/data/sms SIM.
private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected,
boolean voiceSelected, boolean smsSelected) {
- // In Single SIM case or if there are no activated subs available, no need to update. EXIT.
- if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return;
+ // In Single SIM case or if there are no activated subs available, no need to update.
+ // EXIT.
+ if ((primarySubList.isEmpty()) || (mSubscriptionManagerService
+ .getActiveSubInfoCountMax() == 1)) {
+ return;
+ }
if (!isRadioAvailableOnAllSubs()) {
log("Radio is in Invalid state, Ignore Updating User Preference!!!");
return;
}
- final int defaultDataSubId = mSubController.getDefaultDataSubId();
+ final int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
- if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = "
- + mSubController.getDefaultVoiceSubId() +
- " sms = " + mSubController.getDefaultSmsSubId());
+ if (DBG) {
+ log("updateUserPreferences: dds = " + defaultDataSubId + " voice = "
+ + mSubscriptionManagerService.getDefaultVoiceSubId()
+ + " sms = " + mSubscriptionManagerService.getDefaultSmsSubId());
+ }
int autoDefaultSubId = primarySubList.get(0);
if ((primarySubList.size() == 1) && !smsSelected) {
- mSubController.setDefaultSmsSubId(autoDefaultSubId);
+ mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId);
}
if ((primarySubList.size() == 1) && !voiceSelected) {
- mSubController.setDefaultVoiceSubId(autoDefaultSubId);
+ mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId);
}
int userPrefDataSubId = getUserPrefDataSubIdFromDB();
- if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
- + " next active subId " + autoDefaultSubId);
+ log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
+ + " next active subId " + autoDefaultSubId);
// If earlier user selected DDS is now available, set that as DDS subId.
- if (primarySubList.contains(userPrefDataSubId) &&
- SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) &&
- (defaultDataSubId != userPrefDataSubId)) {
- mSubController.setDefaultDataSubId(userPrefDataSubId);
+ if (primarySubList.contains(userPrefDataSubId)
+ && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
+ && (defaultDataSubId != userPrefDataSubId)) {
+ mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
} else if (!dataSelected) {
- mSubController.setDefaultDataSubId(autoDefaultSubId);
+ mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
}
-
- if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() +
- " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " +
- mSubController.getDefaultSmsSubId());
+ if (DBG) {
+ log("updateUserPreferences: after dds = "
+ + mSubscriptionManagerService.getDefaultDataSubId() + " voice = "
+ + mSubscriptionManagerService.getDefaultVoiceSubId() + " sms = "
+ + mSubscriptionManagerService.getDefaultSmsSubId());
+ }
}
private int getUserPrefDataSubIdFromDB() {
@@ -1061,10 +1073,8 @@
// existing phone instance.
Phone[] phones = PhoneFactory.getPhones();
for (int i = mCallbacksCount; i < phones.length; i++) {
- if (phones[i].isUsingNewDataStack()) {
- phones[i].getDataSettingsManager().registerCallback(
- new DataSettingsControllerCallback(phones[i], this::post));
- }
+ phones[i].getDataSettingsManager().registerCallback(
+ new DataSettingsControllerCallback(phones[i], this::post));
}
mCallbacksCount = phones.length;
}
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index 17d906e..7f9ff79 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -16,14 +16,17 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_PRL_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_REGISTRATION_FAILED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
@@ -39,6 +42,7 @@
import android.telephony.BarringInfo;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhysicalChannelConfig;
@@ -72,7 +76,7 @@
public void barringInfoChanged(int indicationType,
android.hardware.radio.network.CellIdentity cellIdentity,
android.hardware.radio.network.BarringInfo[] barringInfos) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
if (cellIdentity == null || barringInfos == null) {
reportAnomaly(UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"),
@@ -84,7 +88,7 @@
BarringInfo cbi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity),
RILUtils.convertHalBarringInfoList(barringInfos));
- mRil.mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, cbi, null));
+ mRil.notifyBarringInfoChanged(cbi);
}
/**
@@ -93,11 +97,11 @@
* @param version PRL version after PRL changes
*/
public void cdmaPrlChanged(int indicationType, int version) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
int[] response = new int[]{version};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
mRil.mCdmaPrlChangedRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -109,9 +113,9 @@
*/
public void cellInfoList(int indicationType,
android.hardware.radio.network.CellInfo[] records) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList(records);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -122,11 +126,11 @@
*/
public void currentLinkCapacityEstimate(int indicationType,
android.hardware.radio.network.LinkCapacityEstimate lce) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
if (mRil.mLceInfoRegistrants != null) {
mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -140,27 +144,34 @@
*/
public void currentPhysicalChannelConfigs(int indicationType,
android.hardware.radio.network.PhysicalChannelConfig[] configs) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
List<PhysicalChannelConfig> response = new ArrayList<>(configs.length);
try {
for (android.hardware.radio.network.PhysicalChannelConfig config : configs) {
PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
+ int band = PhysicalChannelConfig.BAND_UNKNOWN;
switch (config.band.getTag()) {
case android.hardware.radio.network.PhysicalChannelConfigBand.geranBand:
- builder.setBand(config.band.getGeranBand());
+ band = config.band.getGeranBand();
break;
case android.hardware.radio.network.PhysicalChannelConfigBand.utranBand:
- builder.setBand(config.band.getUtranBand());
+ band = config.band.getUtranBand();
break;
case android.hardware.radio.network.PhysicalChannelConfigBand.eutranBand:
- builder.setBand(config.band.getEutranBand());
+ band = config.band.getEutranBand();
break;
case android.hardware.radio.network.PhysicalChannelConfigBand.ngranBand:
- builder.setBand(config.band.getNgranBand());
+ band = config.band.getNgranBand();
break;
default:
mRil.riljLoge("Unsupported band type " + config.band.getTag());
}
+ if (band == PhysicalChannelConfig.BAND_UNKNOWN) {
+ mRil.riljLoge("Unsupported unknown band.");
+ return;
+ } else {
+ builder.setBand(band);
+ }
response.add(builder.setCellConnectionStatus(
RILUtils.convertHalCellConnectionStatus(config.status))
.setDownlinkChannelNumber(config.downlinkChannelNumber)
@@ -178,7 +189,7 @@
mRil.riljLoge("Invalid PhysicalChannelConfig " + iae);
return;
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -191,13 +202,13 @@
*/
public void currentSignalStrength(int indicationType,
android.hardware.radio.network.SignalStrength signalStrength) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
SignalStrength ss = mRil.fixupSignalStrength10(ssInitial);
// Note this is set to "verbose" because it happens frequently
- if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+ if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
if (mRil.mSignalStrengthRegistrant != null) {
mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
@@ -209,9 +220,9 @@
* @param indicationType Type of radio indication
*/
public void imsNetworkStateChanged(int indicationType) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants();
}
@@ -223,11 +234,11 @@
*/
public void networkScanResult(int indicationType,
android.hardware.radio.network.NetworkScanResult result) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(result.networkInfos);
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
@@ -236,9 +247,9 @@
* @param indicationType Type of radio indication
*/
public void networkStateChanged(int indicationType) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
mRil.mNetworkStateRegistrants.notifyRegistrants();
}
@@ -256,9 +267,9 @@
*/
public void nitzTimeReceived(int indicationType, String nitzTime,
@ElapsedRealtimeLong long receivedTimeMs, long ageMs) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
// Ignore the NITZ if receivedTimeMs or ageMs is not a valid time.
// e.g. receivedTimeMs is non-positive, ageMs is negative or greater than receivedTimeMs.
@@ -280,7 +291,7 @@
boolean ignoreNitz = TelephonyProperties.ignore_nitz().orElse(false);
if (ignoreNitz) {
- if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
+ if (mRil.isLogOrTrace()) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
} else {
if (mRil.mNITZTimeRegistrant != null) {
mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult(null, result, null));
@@ -303,23 +314,32 @@
public void registrationFailed(int indicationType,
android.hardware.radio.network.CellIdentity cellIdentity, String chosenPlmn,
@NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity);
if (ci == null || TextUtils.isEmpty(chosenPlmn)
|| (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0
|| (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0
|| causeCode < 0 || additionalCauseCode < 0
|| (causeCode == Integer.MAX_VALUE && additionalCauseCode == Integer.MAX_VALUE)) {
- reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb4"),
+ reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb5"),
"Invalid registrationFailed indication");
- mRil.riljLoge("Invalid registrationFailed indication");
+ mRil.riljLoge("Invalid registrationFailed indication (ci is null)=" + (ci == null)
+ + " (chosenPlmn is empty)=" + TextUtils.isEmpty(chosenPlmn)
+ + " (is CS/PS)=" + ((domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0)
+ + " (only CS/PS)=" + ((domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0)
+ + " (causeCode)=" + causeCode
+ + " (additionalCauseCode)=" + additionalCauseCode);
return;
}
+ RegistrationFailedEvent registrationFailedEvent = new RegistrationFailedEvent(
+ ci, chosenPlmn, domain, causeCode, additionalCauseCode);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogMore(RIL_UNSOL_REGISTRATION_FAILED, registrationFailedEvent.toString());
+ }
mRil.mRegistrationFailedRegistrant.notifyRegistrant(
- new AsyncResult(null, new RegistrationFailedEvent(
- ci, chosenPlmn, domain, causeCode, additionalCauseCode), null));
+ new AsyncResult(null, registrationFailedEvent, null));
}
/**
@@ -328,9 +348,9 @@
* @param state Bitmask of restricted state as defined by PhoneRestrictedState
*/
public void restrictedStateChanged(int indicationType, int state) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
+ if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
if (mRil.mRestrictedStateRegistrant != null) {
mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult(null, state, null));
@@ -344,7 +364,7 @@
*/
public void suppSvcNotify(int indicationType,
android.hardware.radio.network.SuppSvcNotification suppSvcNotification) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
SuppServiceNotification notification = new SuppServiceNotification();
notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
@@ -353,7 +373,9 @@
notification.type = suppSvcNotification.type;
notification.number = suppSvcNotification.number;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+ }
if (mRil.mSsnRegistrant != null) {
mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult(null, notification, null));
@@ -366,16 +388,37 @@
* @param rat Current new voice rat
*/
public void voiceRadioTechChanged(int indicationType, int rat) {
- mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
int[] response = new int[] {rat};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+ }
mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
}
+ /**
+ * Emergency Scan Results.
+ * @param indicationType Type of radio indication
+ * @param result the result of the Emergency Network Scan
+ */
+ public void emergencyNetworkScanResult(int indicationType,
+ android.hardware.radio.network.EmergencyRegResult result) {
+ mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
+
+ EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(result);
+
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT, response);
+ }
+
+ mRil.mEmergencyNetworkScanRegistrants.notifyRegistrants(
+ new AsyncResult(null, response, null));
+ }
+
@Override
public String getInterfaceHash() {
return IRadioNetworkIndication.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 3535678..91cd4ec 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -16,13 +16,10 @@
package com.android.internal.telephony;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
@@ -30,7 +27,6 @@
import android.os.PersistableBundle;
import android.os.RegistrantList;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
@@ -58,9 +54,6 @@
private final int mTransportType;
private final Phone mPhone;
-
- private final CarrierConfigManager mCarrierConfigManager;
-
// Registrants who listens registration state change callback from this class.
private final RegistrantList mRegStateChangeRegistrants = new RegistrantList();
@@ -72,22 +65,6 @@
private NetworkServiceConnection mServiceConnection;
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
- && mPhone.getPhoneId() == intent.getIntExtra(
- CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
- // We should wait for carrier config changed event because the target binding
- // package name can come from the carrier config. Note that we still get this event
- // even when SIM is absent.
- logd("Carrier config changed. Try to bind network service.");
- sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE);
- }
- }
- };
-
public NetworkRegistrationManager(@TransportType int transportType, Phone phone) {
mTransportType = transportType;
mPhone = phone;
@@ -96,19 +73,20 @@
? "C" : "I") + "-" + mPhone.getPhoneId();
mTag = "NRM" + tagSuffix;
- mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
+ CarrierConfigManager ccm = phone.getContext().getSystemService(CarrierConfigManager.class);
+ // Callback directly calls rebindService and should be executed in handler thread
+ ccm.registerCarrierConfigChangeListener(
+ this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex == phone.getPhoneId()) {
+ // We should wait for carrier config changed event because the target
+ // binding package name can come from the carrier config. Note that
+ // we still get this event even when SIM is absent.
+ logd("Carrier config changed. Try to bind network service.");
+ rebindService();
+ }
+ });
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- try {
- Context contextAsUser = phone.getContext().createPackageContextAsUser(
- phone.getContext().getPackageName(), 0, UserHandle.ALL);
- contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
- null /* broadcastPermission */, null);
- } catch (PackageManager.NameNotFoundException e) {
- loge("Package name not found: " + e.getMessage());
- }
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_BIND_NETWORK_SERVICE, null);
@@ -333,9 +311,10 @@
// Read package name from resource overlay
packageName = mPhone.getContext().getResources().getString(resourceId);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+ if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
packageName = b.getString(carrierConfig, packageName);
}
@@ -367,15 +346,17 @@
// Read class name from resource overlay
className = mPhone.getContext().getResources().getString(resourceId);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+ if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
className = b.getString(carrierConfig, className);
}
return className;
}
+
private void logd(String msg) {
Rlog.d(mTag, msg);
}
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index d9f70fd..b1eb926 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -16,12 +16,15 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.network.IRadioNetworkResponse;
import android.os.AsyncResult;
import android.telephony.BarringInfo;
import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
import android.telephony.LinkCapacityEstimate;
import android.telephony.RadioAccessSpecifier;
import android.telephony.SignalStrength;
@@ -57,7 +60,7 @@
int halRadioAccessFamilyBitmap) {
int networkTypeBitmask = RILUtils.convertHalNetworkTypeBitMask(halRadioAccessFamilyBitmap);
mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, networkTypeBitmask);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, networkTypeBitmask);
}
/**
@@ -65,7 +68,7 @@
* @param bandModes List of RadioBandMode listing supported modes
*/
public void getAvailableBandModesResponse(RadioResponseInfo responseInfo, int[] bandModes) {
- RadioResponse.responseIntArrayList(RIL.NETWORK_SERVICE, mRil, responseInfo,
+ RadioResponse.responseIntArrayList(HAL_SERVICE_NETWORK, mRil, responseInfo,
RILUtils.primitiveArrayToArrayList(bandModes));
}
@@ -75,7 +78,7 @@
*/
public void getAvailableNetworksResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.OperatorInfo[] networkInfos) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
ArrayList<OperatorInfo> ret = new ArrayList<>();
@@ -98,7 +101,7 @@
public void getBarringInfoResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.CellIdentity cellIdentity,
android.hardware.radio.network.BarringInfo[] barringInfos) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
BarringInfo bi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity),
@@ -118,7 +121,7 @@
* @param type CdmaRoamingType defined in types.hal
*/
public void getCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo, int type) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, type);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, type);
}
/**
@@ -127,7 +130,7 @@
*/
public void getCellInfoListResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.CellInfo[] cellInfo) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(cellInfo);
@@ -144,7 +147,7 @@
*/
public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.RegStateResult dataRegResponse) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
@@ -161,7 +164,7 @@
*/
public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo,
boolean isRegistered, int ratFamily) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, isRegistered ? 1 : 0,
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, isRegistered ? 1 : 0,
ratFamily == android.hardware.radio.RadioTechnologyFamily.THREE_GPP
? PhoneConstants.PHONE_TYPE_GSM : PhoneConstants.PHONE_TYPE_CDMA);
}
@@ -171,7 +174,7 @@
* @param selection false for automatic selection, true for manual selection
*/
public void getNetworkSelectionModeResponse(RadioResponseInfo responseInfo, boolean selection) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, selection ? 1 : 0);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, selection ? 1 : 0);
}
/**
@@ -183,7 +186,7 @@
public void getOperatorResponse(RadioResponseInfo responseInfo, String longName,
String shortName, String numeric) {
RadioResponse.responseStrings(
- RIL.NETWORK_SERVICE, mRil, responseInfo, longName, shortName, numeric);
+ HAL_SERVICE_NETWORK, mRil, responseInfo, longName, shortName, numeric);
}
/**
@@ -192,7 +195,7 @@
*/
public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.SignalStrength signalStrength) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength);
@@ -209,7 +212,7 @@
*/
public void getSystemSelectionChannelsResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.RadioAccessSpecifier[] halSpecifiers) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
ArrayList<RadioAccessSpecifier> specifiers = new ArrayList<>();
@@ -229,7 +232,7 @@
* @param rat Current voice RAT
*/
public void getVoiceRadioTechnologyResponse(RadioResponseInfo responseInfo, int rat) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, rat);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, rat);
}
/**
@@ -238,7 +241,7 @@
*/
public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.RegStateResult voiceRegResponse) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
RadioResponse.sendMessageResponse(rr.mResult, voiceRegResponse);
@@ -254,7 +257,7 @@
*/
public void isNrDualConnectivityEnabledResponse(RadioResponseInfo responseInfo,
boolean isEnabled) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
@@ -270,7 +273,7 @@
*/
public void pullLceDataResponse(RadioResponseInfo responseInfo,
android.hardware.radio.network.LceDataInfo lceInfo) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo);
@@ -285,105 +288,105 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setAllowedNetworkTypesBitmapResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setBandModeResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setBarringPasswordResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setIndicationFilterResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setLocationUpdatesResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setNetworkSelectionModeAutomaticResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setNetworkSelectionModeManualResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setNrDualConnectivityStateResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setSignalStrengthReportingCriteriaResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setSuppServiceNotificationsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial number and error.
*/
public void setSystemSelectionChannelsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
NetworkScanResult nsr = null;
if (responseInfo.error == RadioError.NONE) {
@@ -399,7 +402,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void stopNetworkScanResponse(RadioResponseInfo responseInfo) {
- RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
NetworkScanResult nsr = null;
if (responseInfo.error == RadioError.NONE) {
@@ -417,14 +420,14 @@
*/
public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo,
int retriesRemaining) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, retriesRemaining);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, retriesRemaining);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setUsageSettingResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
/**
@@ -433,7 +436,91 @@
*/
public void getUsageSettingResponse(RadioResponseInfo responseInfo,
/* @TelephonyManager.UsageSetting */ int usageSetting) {
- RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, usageSetting);
+ RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, usageSetting);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param regState the current registration state of the modem.
+ */
+ public void setEmergencyModeResponse(RadioResponseInfo responseInfo,
+ android.hardware.radio.network.EmergencyRegResult regState) {
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+ if (rr != null) {
+ EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(regState);
+ if (responseInfo.error == RadioError.NONE) {
+ RadioResponse.sendMessageResponse(rr.mResult, response);
+ }
+ mRil.processResponseDone(rr, responseInfo, response);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void triggerEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void exitEmergencyModeResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void cancelEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void setNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param isEnabled Indicates whether null cipher and integrity is enabled, indicating
+ * potentially unencrypted communication
+ */
+ public void isNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo,
+ boolean isEnabled) {
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+ }
+ mRil.processResponseDone(rr, responseInfo, isEnabled);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param isEnabled Indicates whether N1 mode is enabled or not.
+ */
+ public void isN1ModeEnabledResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+ }
+ mRil.processResponseDone(rr, responseInfo, isEnabled);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ */
+ public void setN1ModeEnabledResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
}
@Override
@@ -445,4 +532,5 @@
public int getInterfaceVersion() {
return IRadioNetworkResponse.VERSION;
}
+
}
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index ea68cba..7567566 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -42,6 +42,8 @@
import android.telephony.TelephonyScanManager;
import android.util.Log;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -195,7 +197,7 @@
public static Set<String> getAllowedMccMncsForLocationRestrictedScan(Context context) {
final long token = Binder.clearCallingIdentity();
try {
- return SubscriptionController.getInstance()
+ return SubscriptionManagerService.getInstance()
.getAvailableSubscriptionInfoList(context.getOpPackageName(),
context.getAttributionTag()).stream()
.flatMap(NetworkScanRequestTracker::getAllowableMccMncsFromSubscriptionInfo)
@@ -472,21 +474,21 @@
notifyMessenger(nsri, notifyMsg,
rilErrorToScanError(nsr.scanError), nsr.networkInfos);
if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
- deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+ deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
}
} else {
if (nsr.networkInfos != null) {
notifyMessenger(nsri, notifyMsg,
rilErrorToScanError(nsr.scanError), nsr.networkInfos);
}
- deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+ deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
}
} else {
logEmptyResultOrException(ar);
- deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+ deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
}
}
@@ -514,6 +516,7 @@
Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
return;
}
+ nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
if (ar.exception == null && ar.result != null) {
deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
} else {
@@ -526,7 +529,6 @@
Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
}
}
- nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
}
// Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 2260d34..beebf22 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -28,20 +30,16 @@
import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;
import android.text.TextUtils;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.data.DataUtils;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
@@ -55,6 +53,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -86,31 +85,35 @@
/** Stop all timers and go to current state. */
public static final int EVENT_UPDATE = 0;
/** Quit after processing all existing messages. */
- public static final int EVENT_QUIT = 1;
- private static final int EVENT_DATA_RAT_CHANGED = 2;
- private static final int EVENT_NR_STATE_CHANGED = 3;
- private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
- private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5;
- private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
- private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
- private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
- private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
- private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
- private static final int EVENT_INITIALIZE = 12;
- private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
- private static final int EVENT_PCO_DATA_CHANGED = 14;
- private static final int EVENT_BANDWIDTH_CHANGED = 15;
- private static final int EVENT_UPDATE_NR_ADVANCED_STATE = 16;
- private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 17;
+ private static final int EVENT_QUIT = 1;
+ /** Initialize all events. */
+ private static final int EVENT_INITIALIZE = 2;
+ /** Event for service state changed (data rat, bandwidth, NR state, NR frequency, etc). */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 3;
+ /** Event for physical link status changed. */
+ private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 4;
+ /** Event for physical channel config indications turned on/off. */
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 5;
+ /** Event for carrier configs changed. */
+ private static final int EVENT_CARRIER_CONFIG_CHANGED = 6;
+ /** Event for primary timer expired. If a secondary timer exists, it will begin afterwards. */
+ private static final int EVENT_PRIMARY_TIMER_EXPIRED = 7;
+ /** Event for secondary timer expired. */
+ private static final int EVENT_SECONDARY_TIMER_EXPIRED = 8;
+ /** Event for radio off or unavailable. */
+ private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 9;
+ /** Event for preferred network mode changed. */
+ private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 10;
+ /** Event for physical channel configs changed. */
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 11;
+ /** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */
+ private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12;
private static final String[] sEvents = new String[EVENT_DEVICE_IDLE_MODE_CHANGED + 1];
static {
sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
sEvents[EVENT_QUIT] = "EVENT_QUIT";
- sEvents[EVENT_DATA_RAT_CHANGED] = "EVENT_DATA_RAT_CHANGED";
- sEvents[EVENT_NR_STATE_CHANGED] = "EVENT_NR_STATE_CHANGED";
- sEvents[EVENT_NR_FREQUENCY_CHANGED] = "EVENT_NR_FREQUENCY_CHANGED";
+ sEvents[EVENT_SERVICE_STATE_CHANGED] = "EVENT_SERVICE_STATE_CHANGED";
sEvents[EVENT_PHYSICAL_LINK_STATUS_CHANGED] = "EVENT_PHYSICAL_LINK_STATUS_CHANGED";
sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED] =
"EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED";
@@ -121,26 +124,15 @@
sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
- sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED";
- sEvents[EVENT_BANDWIDTH_CHANGED] = "EVENT_BANDWIDTH_CHANGED";
- sEvents[EVENT_UPDATE_NR_ADVANCED_STATE] = "EVENT_UPDATE_NR_ADVANCED_STATE";
sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED";
}
- private final Phone mPhone;
- private final DisplayInfoController mDisplayInfoController;
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private final @NonNull Phone mPhone;
+ private final @NonNull DisplayInfoController mDisplayInfoController;
+ private final @NonNull BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
- case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
- if (intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_PHONE_INDEX) == mPhone.getPhoneId()
- && !intent.getBooleanExtra(
- CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
- sendMessage(EVENT_CARRIER_CONFIG_CHANGED);
- }
- break;
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
sendMessage(EVENT_DEVICE_IDLE_MODE_CHANGED);
break;
@@ -148,19 +140,33 @@
}
};
- private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
- private String mLteEnhancedPattern = "";
- private int mOverrideNetworkType;
+ private final @NonNull CarrierConfigManager.CarrierConfigChangeListener
+ mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ // CarrierConfigChangeListener wouldn't send notification on device unlock
+ if (slotIndex == mPhone.getPhoneId()) {
+ sendMessage(EVENT_CARRIER_CONFIG_CHANGED);
+ }
+ }
+ };
+
+ private @NonNull Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
+ private @NonNull String mLteEnhancedPattern = "";
+ private @Annotation.OverrideNetworkType int mOverrideNetworkType;
private boolean mIsPhysicalChannelConfigOn;
private boolean mIsPrimaryTimerActive;
private boolean mIsSecondaryTimerActive;
- private boolean mIsTimerResetEnabledForLegacyStateRRCIdle;
+ private boolean mIsTimerResetEnabledForLegacyStateRrcIdle;
private int mLtePlusThresholdBandwidth;
private int mNrAdvancedThresholdBandwidth;
- private int[] mAdditionalNrAdvancedBandsList;
- private String mPrimaryTimerState;
- private String mSecondaryTimerState;
- private String mPreviousState;
+ private boolean mIncludeLteForNrAdvancedThresholdBandwidth;
+ private @NonNull int[] mAdditionalNrAdvancedBandsList;
+ private @NonNull String mPrimaryTimerState;
+ private @NonNull String mSecondaryTimerState;
+ private @NonNull String mPreviousState;
private @LinkStatus int mPhysicalLinkStatus;
private boolean mIsPhysicalChannelConfig16Supported;
private boolean mIsNrAdvancedAllowedByPco = false;
@@ -169,6 +175,13 @@
private boolean mEnableNrAdvancedWhileRoaming = true;
private boolean mIsDeviceIdleMode = false;
+ private @Nullable DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
+ private @Nullable DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
+
+ // Cached copies below to prevent race conditions
+ private @NonNull ServiceState mServiceState;
+ private @Nullable List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
/**
* NetworkTypeController constructor.
*
@@ -181,13 +194,22 @@
mDisplayInfoController = displayInfoController;
mOverrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
mIsPhysicalChannelConfigOn = true;
- addState(mDefaultState);
- addState(mLegacyState, mDefaultState);
- addState(mIdleState, mDefaultState);
- addState(mLteConnectedState, mDefaultState);
- addState(mNrConnectedState, mDefaultState);
- setInitialState(mDefaultState);
+ mPrimaryTimerState = "";
+ mSecondaryTimerState = "";
+ mPreviousState = "";
+ DefaultState defaultState = new DefaultState();
+ addState(defaultState);
+ addState(mLegacyState, defaultState);
+ addState(mIdleState, defaultState);
+ addState(mLteConnectedState, defaultState);
+ addState(mNrConnectedState, defaultState);
+ addState(mNrConnectedAdvancedState, defaultState);
+ setInitialState(defaultState);
start();
+
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+
sendMessage(EVENT_INITIALIZE);
}
@@ -200,10 +222,21 @@
}
/**
- * @return True if either the primary or secondary 5G hysteresis timer is active,
- * and false if neither are.
+ * @return The current data network type, used to create TelephonyDisplayInfo in
+ * DisplayInfoController.
*/
- public boolean is5GHysteresisActive() {
+ public @Annotation.NetworkType int getDataNetworkType() {
+ NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+ : nri.getAccessNetworkTechnology();
+ }
+
+ /**
+ * @return {@code true} if either the primary or secondary 5G icon timers are active,
+ * and {@code false} if neither are.
+ */
+ public boolean areAnyTimersActive() {
return mIsPrimaryTimerActive || mIsSecondaryTimerActive;
}
@@ -214,133 +247,101 @@
EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
mPhone.registerForPhysicalChannelConfig(getHandler(),
EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
- mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
- EVENT_DATA_RAT_CHANGED, null);
- mPhone.getServiceStateTracker().registerForBandwidthChanged(
- getHandler(), EVENT_BANDWIDTH_CHANGED, null);
+ mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
+ EVENT_SERVICE_STATE_CHANGED, null);
mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
TelephonyManager.class).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
- mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
- EVENT_NR_STATE_CHANGED, null);
- mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
- EVENT_NR_FREQUENCY_CHANGED, null);
mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(),
EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null);
IntentFilter filter = new IntentFilter();
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
- if (!mPhone.isUsingNewDataStack()) {
- mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null);
- }
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ ccm.registerCarrierConfigChangeListener(Runnable::run, mCarrierConfigChangeListener);
}
private void unRegisterForAllEvents() {
mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
mPhone.unregisterForPreferredNetworkTypeChanged(getHandler());
- mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler());
- mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
- mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
+ mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
mPhone.getContext().unregisterReceiver(mIntentReceiver);
- if (!mPhone.isUsingNewDataStack()) {
- mPhone.mCi.unregisterForPcoData(getHandler());
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ if (mCarrierConfigChangeListener != null) {
+ ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
}
}
private void parseCarrierConfigs() {
- String nrIconConfiguration = CarrierConfigManager.getDefaultConfig().getString(
- CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
- String overrideTimerRule = CarrierConfigManager.getDefaultConfig().getString(
- CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
- String overrideSecondaryTimerRule = CarrierConfigManager.getDefaultConfig().getString(
- CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
- mLteEnhancedPattern = CarrierConfigManager.getDefaultConfig().getString(
- CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
- mIsTimerResetEnabledForLegacyStateRRCIdle =
- CarrierConfigManager.getDefaultConfig().getBoolean(
- CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
- mLtePlusThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt(
- CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
- mNrAdvancedThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt(
- CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT);
- mEnableNrAdvancedWhileRoaming = CarrierConfigManager.getDefaultConfig().getBoolean(
- CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
-
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle config = CarrierConfigManager.getDefaultConfig();
+ CarrierConfigManager configManager =
+ mPhone.getContext().getSystemService(CarrierConfigManager.class);
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
if (b != null) {
- if (b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING) != null) {
- nrIconConfiguration = b.getString(
- CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
- }
- if (b.getString(CarrierConfigManager
- .KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING) != null) {
- overrideTimerRule = b.getString(
- CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
- }
- if (b.getString(CarrierConfigManager
- .KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING) != null) {
- overrideSecondaryTimerRule = b.getString(
- CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
- }
- if (b.getString(CarrierConfigManager
- .KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING) != null) {
- mLteEnhancedPattern = b.getString(
- CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
- }
- mIsTimerResetEnabledForLegacyStateRRCIdle = b.getBoolean(
- CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
- mLtePlusThresholdBandwidth = b.getInt(
- CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT,
- mLtePlusThresholdBandwidth);
- mNrAdvancedThresholdBandwidth = b.getInt(
- CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT,
- mNrAdvancedThresholdBandwidth);
- mAdditionalNrAdvancedBandsList = b.getIntArray(
- CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
- mNrAdvancedCapablePcoId = b.getInt(
- CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
- if (mNrAdvancedCapablePcoId > 0 && mPhone.isUsingNewDataStack()) {
- mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
- new DataNetworkControllerCallback(getHandler()::post) {
- @Override
- public void onNrAdvancedCapableByPcoChanged(
- boolean nrAdvancedCapable) {
- log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
- mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
- sendMessage(EVENT_UPDATE_NR_ADVANCED_STATE);
- }
- });
- }
- mEnableNrAdvancedWhileRoaming = b.getBoolean(
- CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
- mIsUsingUserDataForRrcDetection = b.getBoolean(
- CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
- if (!mIsPhysicalChannelConfig16Supported || mIsUsingUserDataForRrcDetection) {
- if (mPhone.isUsingNewDataStack()) {
- mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
- new DataNetworkControllerCallback(getHandler()::post) {
- @Override
- public void onPhysicalLinkStatusChanged(
- @LinkStatus int status) {
- sendMessage(obtainMessage(
- EVENT_PHYSICAL_LINK_STATUS_CHANGED,
- new AsyncResult(null, status, null)));
- }});
- } else {
- mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .registerForPhysicalLinkStatusChanged(getHandler(),
- EVENT_PHYSICAL_LINK_STATUS_CHANGED);
- }
- }
+ config = b;
}
}
+ mLteEnhancedPattern = config.getString(
+ CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
+ mIsTimerResetEnabledForLegacyStateRrcIdle = config.getBoolean(
+ CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
+ mLtePlusThresholdBandwidth = config.getInt(
+ CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
+ mNrAdvancedThresholdBandwidth = config.getInt(
+ CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT);
+ mIncludeLteForNrAdvancedThresholdBandwidth = config.getBoolean(
+ CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL);
+ mEnableNrAdvancedWhileRoaming = config.getBoolean(
+ CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
+ mAdditionalNrAdvancedBandsList = config.getIntArray(
+ CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
+ mNrAdvancedCapablePcoId = config.getInt(
+ CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
+ if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) {
+ mNrAdvancedCapableByPcoChangedCallback =
+ new DataNetworkControllerCallback(getHandler()::post) {
+ @Override
+ public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) {
+ log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
+ mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
+ sendMessage(EVENT_UPDATE);
+ }
+ };
+ mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+ mNrAdvancedCapableByPcoChangedCallback);
+ } else if (mNrAdvancedCapablePcoId == 0 && mNrAdvancedCapableByPcoChangedCallback != null) {
+ mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+ mNrAdvancedCapableByPcoChangedCallback);
+ mNrAdvancedCapableByPcoChangedCallback = null;
+ }
+ mIsUsingUserDataForRrcDetection = config.getBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
+ if (!isUsingPhysicalChannelConfigForRrcDetection()) {
+ if (mNrPhysicalLinkStatusChangedCallback == null) {
+ mNrPhysicalLinkStatusChangedCallback =
+ new DataNetworkControllerCallback(getHandler()::post) {
+ @Override
+ public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
+ sendMessage(obtainMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ new AsyncResult(null, status, null)));
+ }
+ };
+ mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+ mNrPhysicalLinkStatusChangedCallback);
+ }
+ } else if (mNrPhysicalLinkStatusChangedCallback != null) {
+ mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+ mNrPhysicalLinkStatusChangedCallback);
+ mNrPhysicalLinkStatusChangedCallback = null;
+ }
+ String nrIconConfiguration = config.getString(
+ CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
+ String overrideTimerRule = config.getString(
+ CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
+ String overrideSecondaryTimerRule = config.getString(
+ CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
}
@@ -349,7 +350,7 @@
if (!TextUtils.isEmpty(icons)) {
// Format: "STATE:ICON,STATE2:ICON2"
for (String pair : icons.trim().split(",")) {
- String[] kv = (pair.trim().toLowerCase()).split(":");
+ String[] kv = (pair.trim().toLowerCase(Locale.ROOT)).split(":");
if (kv.length != 2) {
if (DBG) loge("Invalid 5G icon configuration, config = " + pair);
continue;
@@ -376,7 +377,7 @@
if (!TextUtils.isEmpty(timers)) {
// Format: "FROM_STATE,TO_STATE,DURATION;FROM_STATE_2,TO_STATE_2,DURATION_2"
for (String triple : timers.trim().split(";")) {
- String[] kv = (triple.trim().toLowerCase()).split(",");
+ String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
if (kv.length != 3) {
if (DBG) loge("Invalid 5G icon timer configuration, config = " + triple);
continue;
@@ -402,7 +403,7 @@
if (!TextUtils.isEmpty(secondaryTimers)) {
// Format: "PRIMARY_STATE,TO_STATE,DURATION;PRIMARY_STATE_2,TO_STATE_2,DURATION_2"
for (String triple : secondaryTimers.trim().split(";")) {
- String[] kv = (triple.trim().toLowerCase()).split(",");
+ String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
if (kv.length != 3) {
if (DBG) {
loge("Invalid 5G icon secondary timer configuration, config = " + triple);
@@ -444,7 +445,7 @@
int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
int dataNetworkType = getDataNetworkType();
boolean nrNsa = isLte(dataNetworkType)
- && mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
+ && mServiceState.getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
boolean nrSa = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR;
// NR display is not accurate when physical channel config notifications are off
@@ -475,7 +476,7 @@
keys.add(STATE_CONNECTED_NR_ADVANCED);
}
} else {
- switch (mPhone.getServiceState().getNrState()) {
+ switch (mServiceState.getNrState()) {
case NetworkRegistrationInfo.NR_STATE_CONNECTED:
if (isNrAdvanced()) {
keys.add(STATE_CONNECTED_NR_ADVANCED);
@@ -505,9 +506,9 @@
private @Annotation.OverrideNetworkType int getLteDisplayType() {
int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
if ((getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
- || mPhone.getServiceState().isUsingCarrierAggregation())
- && (IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
- > mLtePlusThresholdBandwidth)) {
+ || mServiceState.isUsingCarrierAggregation())
+ && IntStream.of(mServiceState.getCellBandwidths()).sum()
+ > mLtePlusThresholdBandwidth) {
value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA;
}
if (isLteEnhancedAvailable()) {
@@ -521,8 +522,8 @@
return false;
}
Pattern stringPattern = Pattern.compile(mLteEnhancedPattern);
- for (String opName : new String[] {mPhone.getServiceState().getOperatorAlphaLongRaw(),
- mPhone.getServiceState().getOperatorAlphaShortRaw()}) {
+ for (String opName : new String[] {mServiceState.getOperatorAlphaLongRaw(),
+ mServiceState.getOperatorAlphaShortRaw()}) {
if (!TextUtils.isEmpty(opName)) {
Matcher matcher = stringPattern.matcher(opName);
if (matcher.find()) {
@@ -542,8 +543,6 @@
if (DBG) log("DefaultState: process " + getEventName(msg.what));
switch (msg.what) {
case EVENT_UPDATE:
- case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
- if (DBG) log("Reset timers since preferred network mode changed.");
resetAllTimers();
transitionToCurrentState();
break;
@@ -555,26 +554,15 @@
break;
case EVENT_INITIALIZE:
// The reason that we do it here is because some of the works below requires
- // other modules (e.g. DcTracker, ServiceStateTracker), which is not created
- // yet when NetworkTypeController is created.
+ // other modules (e.g. DataNetworkController, ServiceStateTracker), which is not
+ // created yet when NetworkTypeController is created.
registerForAllEvents();
parseCarrierConfigs();
break;
- case EVENT_DATA_RAT_CHANGED:
- case EVENT_NR_STATE_CHANGED:
- case EVENT_NR_FREQUENCY_CHANGED:
- case EVENT_PCO_DATA_CHANGED:
- case EVENT_UPDATE_NR_ADVANCED_STATE:
- // ignored
- break;
- case EVENT_BANDWIDTH_CHANGED:
- // Update in case of LTE/LTE+ switch
- updateOverrideNetworkType();
- break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- if (isUsingPhysicalChannelConfigForRrcDetection()) {
- mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
- }
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ transitionToCurrentState();
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
@@ -616,6 +604,20 @@
resetAllTimers();
transitionTo(mLegacyState);
break;
+ case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
+ if (DBG) log("Reset timers since preferred network mode changed.");
+ resetAllTimers();
+ transitionToCurrentState();
+ break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+ }
+ transitionToCurrentState();
+ break;
case EVENT_DEVICE_IDLE_MODE_CHANGED:
PowerManager pm = mPhone.getContext().getSystemService(PowerManager.class);
mIsDeviceIdleMode = pm.isDeviceIdleMode();
@@ -635,8 +637,6 @@
}
}
- private final DefaultState mDefaultState = new DefaultState();
-
/**
* Device does not have NR available, due to any of the below reasons:
* <ul>
@@ -664,11 +664,19 @@
public boolean processMessage(Message msg) {
if (DBG) log("LegacyState: process " + getEventName(msg.what));
updateTimers();
- int rat = getDataNetworkType();
switch (msg.what) {
- case EVENT_DATA_RAT_CHANGED:
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ // fallthrough
+ case EVENT_UPDATE:
+ int rat = getDataNetworkType();
if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
- transitionTo(mNrConnectedState);
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ } else {
+ transitionTo(mNrConnectedState);
+ }
} else if (isLte(rat) && isNrNotRestricted()) {
transitionWithTimerTo(isPhysicalLinkActive()
? mLteConnectedState : mIdleState);
@@ -681,35 +689,22 @@
}
mIsNrRestricted = isNrRestricted();
break;
- case EVENT_NR_STATE_CHANGED:
- if (isNrConnected()) {
- transitionTo(mNrConnectedState);
- } else if (isLte(rat) && isNrNotRestricted()) {
- transitionWithTimerTo(isPhysicalLinkActive()
- ? mLteConnectedState : mIdleState);
- } else if (isLte(rat) && isNrRestricted()) {
- updateOverrideNetworkType();
- }
- mIsNrRestricted = isNrRestricted();
- break;
- case EVENT_NR_FREQUENCY_CHANGED:
- // ignored
- break;
case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
- if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+ if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
resetAllTimers();
}
}
- // Update in case of LTE/LTE+ switch
- updateOverrideNetworkType();
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkStatus = (int) ar.result;
- if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+ if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
resetAllTimers();
updateOverrideNetworkType();
@@ -751,52 +746,52 @@
if (DBG) log("IdleState: process " + getEventName(msg.what));
updateTimers();
switch (msg.what) {
- case EVENT_DATA_RAT_CHANGED:
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ // fallthrough
+ case EVENT_UPDATE:
int rat = getDataNetworkType();
- if (rat == TelephonyManager.NETWORK_TYPE_NR) {
- transitionTo(mNrConnectedState);
+ if (rat == TelephonyManager.NETWORK_TYPE_NR
+ || (isLte(rat) && isNrConnected())) {
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ } else {
+ transitionTo(mNrConnectedState);
+ }
} else if (!isLte(rat) || !isNrNotRestricted()) {
transitionWithTimerTo(mLegacyState);
- }
- break;
- case EVENT_NR_STATE_CHANGED:
- if (isNrConnected()) {
- transitionTo(mNrConnectedState);
- } else if (!isNrNotRestricted()) {
- transitionWithTimerTo(mLegacyState);
- }
- break;
- case EVENT_NR_FREQUENCY_CHANGED:
- // ignore
- break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- if (isUsingPhysicalChannelConfigForRrcDetection()) {
- mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
- if (isNrNotRestricted()) {
- // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
- if (isPhysicalLinkActive()) {
- transitionWithTimerTo(mLteConnectedState);
- break;
- }
+ } else {
+ if (isPhysicalLinkActive()) {
+ transitionWithTimerTo(mLteConnectedState);
} else {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
+ // Update in case the override network type changed
+ updateOverrideNetworkType();
}
}
- // Update in case of LTE/LTE+ switch
- updateOverrideNetworkType();
+ break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+ if (isPhysicalLinkActive()) {
+ transitionWithTimerTo(mLteConnectedState);
+ } else {
+ log("Reevaluating state due to link status changed.");
+ sendMessage(EVENT_UPDATE);
+ }
+ }
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkStatus = (int) ar.result;
- if (isNrNotRestricted()) {
- // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
- if (isPhysicalLinkActive()) {
- transitionWithTimerTo(mLteConnectedState);
- }
+ if (isPhysicalLinkActive()) {
+ transitionWithTimerTo(mLteConnectedState);
} else {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
+ log("Reevaluating state due to link status changed.");
+ sendMessage(EVENT_UPDATE);
}
break;
default:
@@ -835,52 +830,52 @@
if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
updateTimers();
switch (msg.what) {
- case EVENT_DATA_RAT_CHANGED:
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ // fallthrough
+ case EVENT_UPDATE:
int rat = getDataNetworkType();
- if (rat == TelephonyManager.NETWORK_TYPE_NR) {
- transitionTo(mNrConnectedState);
+ if (rat == TelephonyManager.NETWORK_TYPE_NR
+ || (isLte(rat) && isNrConnected())) {
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ } else {
+ transitionTo(mNrConnectedState);
+ }
} else if (!isLte(rat) || !isNrNotRestricted()) {
transitionWithTimerTo(mLegacyState);
- }
- break;
- case EVENT_NR_STATE_CHANGED:
- if (isNrConnected()) {
- transitionTo(mNrConnectedState);
- } else if (!isNrNotRestricted()) {
- transitionWithTimerTo(mLegacyState);
- }
- break;
- case EVENT_NR_FREQUENCY_CHANGED:
- // ignore
- break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- if (isUsingPhysicalChannelConfigForRrcDetection()) {
- mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
- if (isNrNotRestricted()) {
- // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
- if (!isPhysicalLinkActive()) {
- transitionWithTimerTo(mIdleState);
- break;
- }
+ } else {
+ if (!isPhysicalLinkActive()) {
+ transitionWithTimerTo(mIdleState);
} else {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
+ // Update in case the override network type changed
+ updateOverrideNetworkType();
}
}
- // Update in case of LTE/LTE+ switch
- updateOverrideNetworkType();
+ break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+ if (!isPhysicalLinkActive()) {
+ transitionWithTimerTo(mIdleState);
+ } else {
+ log("Reevaluating state due to link status changed.");
+ sendMessage(EVENT_UPDATE);
+ }
+ }
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkStatus = (int) ar.result;
- if (isNrNotRestricted()) {
- // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
- if (!isPhysicalLinkActive()) {
- transitionWithTimerTo(mIdleState);
- }
+ if (!isPhysicalLinkActive()) {
+ transitionWithTimerTo(mIdleState);
} else {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
+ log("Reevaluating state due to link status changed.");
+ sendMessage(EVENT_UPDATE);
}
break;
default:
@@ -904,28 +899,35 @@
* Device is connected to 5G NR as the primary or secondary cell.
*/
private final class NrConnectedState extends State {
- private boolean mIsNrAdvanced = false;
-
@Override
public void enter() {
- if (DBG) log("Entering NrConnectedState(" + getName() + ")");
+ if (DBG) log("Entering NrConnectedState");
updateTimers();
updateOverrideNetworkType();
if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
- mIsNrAdvanced = isNrAdvanced();
mPreviousState = getName();
}
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) log("NrConnectedState(" + getName() + "): process " + getEventName(msg.what));
+ if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
updateTimers();
- int rat = getDataNetworkType();
switch (msg.what) {
- case EVENT_DATA_RAT_CHANGED:
- if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
- updateOverrideNetworkType();
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ // fallthrough
+ case EVENT_UPDATE:
+ int rat = getDataNetworkType();
+ if (rat == TelephonyManager.NETWORK_TYPE_NR
+ || (isLte(rat) && isNrConnected())) {
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ } else {
+ // Update in case the override network type changed
+ updateOverrideNetworkType();
+ }
} else if (isLte(rat) && isNrNotRestricted()) {
transitionWithTimerTo(isPhysicalLinkActive()
? mLteConnectedState : mIdleState);
@@ -933,37 +935,21 @@
transitionWithTimerTo(mLegacyState);
}
break;
- case EVENT_NR_STATE_CHANGED:
- if (isLte(rat) && isNrNotRestricted()) {
- transitionWithTimerTo(isPhysicalLinkActive()
- ? mLteConnectedState : mIdleState);
- } else if (rat != TelephonyManager.NETWORK_TYPE_NR && !isNrConnected()) {
- transitionWithTimerTo(mLegacyState);
- }
- break;
- case EVENT_PCO_DATA_CHANGED:
- handlePcoData((AsyncResult) msg.obj);
- break;
- case EVENT_UPDATE_NR_ADVANCED_STATE:
- updateNrAdvancedState();
- break;
- case EVENT_NR_FREQUENCY_CHANGED:
case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
- updateNrAdvancedState();
+ // Check NR advanced in case NR advanced bands were added
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ }
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkStatus = (int) ar.result;
- if (!isNrConnected()) {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
- }
- break;
- case EVENT_BANDWIDTH_CHANGED:
- updateNrAdvancedState();
break;
default:
return NOT_HANDLED;
@@ -976,55 +962,94 @@
@Override
public String getName() {
- return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
- }
-
- private void updateNrAdvancedState() {
- if (!isNrConnected()) {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
- return;
- }
- if (!isNrAdvanced()) {
- if (DBG) log("updateNrAdvancedState: CONNECTED_NR_ADVANCED -> CONNECTED");
- transitionWithTimerTo(mNrConnectedState);
- } else {
- if (DBG) log("updateNrAdvancedState: CONNECTED -> CONNECTED_NR_ADVANCED");
- transitionTo(mNrConnectedState);
- }
- mIsNrAdvanced = isNrAdvanced();
- }
-
- private void handlePcoData(AsyncResult ar) {
- if (ar.exception != null) {
- loge("PCO_DATA exception: " + ar.exception);
- return;
- }
- PcoData pcodata = (PcoData) ar.result;
- if (pcodata == null) {
- return;
- }
- log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata);
- DcTracker dcTracker = mPhone.getDcTracker(
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- DataConnection dc =
- dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null;
- ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null;
- if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT)
- && mNrAdvancedCapablePcoId > 0
- && pcodata.pcoId == mNrAdvancedCapablePcoId
- ) {
- log("EVENT_PCO_DATA_CHANGED: NR_ADVANCED is allowed by PCO. length:"
- + pcodata.contents.length + ",value: " + Arrays.toString(pcodata.contents));
- mIsNrAdvancedAllowedByPco = pcodata.contents.length > 0
- && pcodata.contents[pcodata.contents.length - 1] == 1;
- updateNrAdvancedState();
- }
+ return STATE_CONNECTED;
}
}
private final NrConnectedState mNrConnectedState = new NrConnectedState();
+ /**
+ * Device is connected to 5G NR as the primary cell and the data rate is higher than
+ * the generic 5G data rate.
+ */
+ private final class NrConnectedAdvancedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log("Entering NrConnectedAdvancedState");
+ updateTimers();
+ updateOverrideNetworkType();
+ if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+ mPreviousState = getName();
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what));
+ updateTimers();
+ switch (msg.what) {
+ case EVENT_SERVICE_STATE_CHANGED:
+ mServiceState = mPhone.getServiceStateTracker().getServiceState();
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ // fallthrough
+ case EVENT_UPDATE:
+ int rat = getDataNetworkType();
+ if (rat == TelephonyManager.NETWORK_TYPE_NR
+ || (isLte(rat) && isNrConnected())) {
+ if (isNrAdvanced()) {
+ // Update in case the override network type changed
+ updateOverrideNetworkType();
+ } else {
+ if (rat == TelephonyManager.NETWORK_TYPE_NR && mOverrideNetworkType
+ != TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
+ // manually override network type after data rat changes since
+ // timer will prevent it from being updated
+ mOverrideNetworkType =
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
+ }
+ transitionWithTimerTo(mNrConnectedState);
+ }
+ } else if (isLte(rat) && isNrNotRestricted()) {
+ transitionWithTimerTo(isPhysicalLinkActive()
+ ? mLteConnectedState : mIdleState);
+ } else {
+ transitionWithTimerTo(mLegacyState);
+ }
+ break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ mPhysicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+ }
+ // Check NR advanced in case NR advanced bands were removed
+ if (!isNrAdvanced()) {
+ transitionWithTimerTo(mNrConnectedState);
+ }
+ break;
+ case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ mPhysicalLinkStatus = (int) ar.result;
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+ mPreviousState = getName();
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public String getName() {
+ return STATE_CONNECTED_NR_ADVANCED;
+ }
+ }
+
+ private final NrConnectedAdvancedState mNrConnectedAdvancedState =
+ new NrConnectedAdvancedState();
+
private void transitionWithTimerTo(IState destState) {
String destName = destState.getName();
if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName);
@@ -1062,22 +1087,23 @@
private void transitionToCurrentState() {
int dataRat = getDataNetworkType();
IState transitionState;
- if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) {
- transitionState = mNrConnectedState;
- mPreviousState = isNrAdvanced() ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
+ if (dataRat == TelephonyManager.NETWORK_TYPE_NR || (isLte(dataRat) && isNrConnected())) {
+ if (isNrAdvanced()) {
+ transitionState = mNrConnectedAdvancedState;
+ } else {
+ transitionState = mNrConnectedState;
+ }
} else if (isLte(dataRat) && isNrNotRestricted()) {
if (isPhysicalLinkActive()) {
transitionState = mLteConnectedState;
- mPreviousState = STATE_NOT_RESTRICTED_RRC_CON;
} else {
transitionState = mIdleState;
- mPreviousState = STATE_NOT_RESTRICTED_RRC_IDLE;
}
} else {
transitionState = mLegacyState;
- mPreviousState = isNrRestricted() ? STATE_RESTRICTED : STATE_LEGACY;
}
if (!transitionState.equals(getCurrentState())) {
+ mPreviousState = getCurrentState().getName();
transitionTo(transitionState);
} else {
updateOverrideNetworkType();
@@ -1103,6 +1129,8 @@
removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
mIsPrimaryTimerActive = false;
mPrimaryTimerState = "";
+ transitionToCurrentState();
+ return;
}
if (mIsSecondaryTimerActive && !mSecondaryTimerState.equals(currentState)) {
@@ -1114,18 +1142,25 @@
removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
mIsSecondaryTimerActive = false;
mSecondaryTimerState = "";
+ transitionToCurrentState();
+ return;
}
if (mIsPrimaryTimerActive || mIsSecondaryTimerActive) {
if (currentState.equals(STATE_CONNECTED_NR_ADVANCED)) {
if (DBG) log("Reset timers since state is NR_ADVANCED.");
resetAllTimers();
- }
-
- int rat = getDataNetworkType();
- if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
- if (DBG) log("Reset timers since 2G and 3G don't need NR timers.");
+ } else if (currentState.equals(STATE_CONNECTED)
+ && !mPrimaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)
+ && !mSecondaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)) {
+ if (DBG) log("Reset non-NR_ADVANCED timers since state is NR_CONNECTED");
resetAllTimers();
+ } else {
+ int rat = getDataNetworkType();
+ if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
+ if (DBG) log("Reset timers since 2G and 3G don't need NR timers.");
+ resetAllTimers();
+ }
}
}
}
@@ -1222,17 +1257,15 @@
}
private boolean isNrConnected() {
- return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+ return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
}
private boolean isNrNotRestricted() {
- return mPhone.getServiceState().getNrState()
- == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
+ return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
}
private boolean isNrRestricted() {
- return mPhone.getServiceState().getNrState()
- == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
+ return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
}
/**
@@ -1247,15 +1280,24 @@
// Check if NR advanced is enabled when the device is roaming. Some carriers disable it
// while the device is roaming.
- if (mPhone.getServiceState().getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
+ if (mServiceState.getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
return false;
}
+ int bandwidths = 0;
+ if (mPhone.getServiceStateTracker().getPhysicalChannelConfigList() != null) {
+ bandwidths = mPhone.getServiceStateTracker().getPhysicalChannelConfigList()
+ .stream()
+ .filter(config -> mIncludeLteForNrAdvancedThresholdBandwidth
+ || config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR)
+ .map(PhysicalChannelConfig::getCellBandwidthDownlinkKhz)
+ .mapToInt(Integer::intValue)
+ .sum();
+ }
+
// Check if meeting minimum bandwidth requirement. For most carriers, there is no minimum
// bandwidth requirement and mNrAdvancedThresholdBandwidth is 0.
- if (mNrAdvancedThresholdBandwidth > 0
- && IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
- < mNrAdvancedThresholdBandwidth) {
+ if (mNrAdvancedThresholdBandwidth > 0 && bandwidths < mNrAdvancedThresholdBandwidth) {
return false;
}
@@ -1265,18 +1307,15 @@
}
private boolean isNrMmwave() {
- return mPhone.getServiceState().getNrFrequencyRange()
- == ServiceState.FREQUENCY_RANGE_MMWAVE;
+ return mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE;
}
private boolean isAdditionalNrAdvancedBand() {
- List<PhysicalChannelConfig> physicalChannelConfigList =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList)
- || physicalChannelConfigList == null) {
+ || mPhysicalChannelConfigs == null) {
return false;
}
- for (PhysicalChannelConfig item : physicalChannelConfigList) {
+ for (PhysicalChannelConfig item : mPhysicalChannelConfigs) {
if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR
&& ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) {
return true;
@@ -1295,19 +1334,10 @@
}
private int getPhysicalLinkStatusFromPhysicalChannelConfig() {
- List<PhysicalChannelConfig> physicalChannelConfigList =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty())
+ return (mPhysicalChannelConfigs == null || mPhysicalChannelConfigs.isEmpty())
? DataCallResponse.LINK_STATUS_DORMANT : DataCallResponse.LINK_STATUS_ACTIVE;
}
- private int getDataNetworkType() {
- NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
- : nri.getAccessNetworkTechnology();
- }
-
private String getEventName(int event) {
try {
return sEvents[event];
@@ -1354,16 +1384,16 @@
pw.println("mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn);
pw.println("mIsPrimaryTimerActive=" + mIsPrimaryTimerActive);
pw.println("mIsSecondaryTimerActive=" + mIsSecondaryTimerActive);
- pw.println("mIsTimerRestEnabledForLegacyStateRRCIdle="
- + mIsTimerResetEnabledForLegacyStateRRCIdle);
+ pw.println("mIsTimerResetEnabledForLegacyStateRrcIdle="
+ + mIsTimerResetEnabledForLegacyStateRrcIdle);
pw.println("mLtePlusThresholdBandwidth=" + mLtePlusThresholdBandwidth);
pw.println("mNrAdvancedThresholdBandwidth=" + mNrAdvancedThresholdBandwidth);
+ pw.println("mAdditionalNrAdvancedBandsList="
+ + Arrays.toString(mAdditionalNrAdvancedBandsList));
pw.println("mPrimaryTimerState=" + mPrimaryTimerState);
pw.println("mSecondaryTimerState=" + mSecondaryTimerState);
pw.println("mPreviousState=" + mPreviousState);
- pw.println("mPhysicalLinkStatus=" + mPhysicalLinkStatus);
- pw.println("mAdditionalNrAdvancedBandsList="
- + Arrays.toString(mAdditionalNrAdvancedBandsList));
+ pw.println("mPhysicalLinkStatus=" + DataUtils.linkStatusToString(mPhysicalLinkStatus));
pw.println("mIsPhysicalChannelConfig16Supported=" + mIsPhysicalChannelConfig16Supported);
pw.println("mIsNrAdvancedAllowedByPco=" + mIsNrAdvancedAllowedByPco);
pw.println("mNrAdvancedCapablePcoId=" + mNrAdvancedCapablePcoId);
diff --git a/src/java/com/android/internal/telephony/NitzData.java b/src/java/com/android/internal/telephony/NitzData.java
index 6b19e08..8430585 100644
--- a/src/java/com/android/internal/telephony/NitzData.java
+++ b/src/java/com/android/internal/telephony/NitzData.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
+import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
@@ -39,9 +40,6 @@
private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
- /* Time stamp after 19 January 2038 is not supported under 32 bit */
- private static final int MAX_NITZ_YEAR = 2037;
-
private static final Pattern NITZ_SPLIT_PATTERN = Pattern.compile("[/:,+-]");
// Stored For logging / debugging only.
@@ -77,22 +75,37 @@
try {
String[] nitzSubs = NITZ_SPLIT_PATTERN.split(nitz);
- int year = 2000 + Integer.parseInt(nitzSubs[0]);
- if (year > MAX_NITZ_YEAR) {
- if (ServiceStateTracker.DBG) {
- Rlog.e(LOG_TAG, "NITZ year: " + year + " exceeds limit, skip NITZ time update");
- }
- return null;
+ int year = Integer.parseInt(nitzSubs[0]);
+ if (year < 1 || year > 99) {
+ // 0 > year > 99 imply an invalid string.
+ //
+ // At the time of this comment (year 2023), a zero year is considered invalid and
+ // assumed to be the result of invalid data being converted to zero in the code that
+ // turns the binary NITZ into a string. For the next few decades at least, Android
+ // devices should not need to interpret zero. Hopefully, NITZ will be replaced by
+ // the time that's not true, or folks dealing the Y2K1 issue can handle it.
+ //
+ // DateTimeException is also thrown by LocalDateTime below if the values are out of
+ // range and will be handled in the catch block.
+ throw new DateTimeException("Invalid NITZ year == 0");
}
+ // Values < {current year} could be considered invalid but are used in test code, so no
+ // window is applied to adjust low values < {current year} with "+ 2100" (and would also
+ // need to consider zero as valid). Code that processes the NitzData is in a better
+ // position to log and discard obviously invalid NITZ signals from past years.
+ year += 2000;
+
int month = Integer.parseInt(nitzSubs[1]);
int date = Integer.parseInt(nitzSubs[2]);
int hour = Integer.parseInt(nitzSubs[3]);
int minute = Integer.parseInt(nitzSubs[4]);
int second = Integer.parseInt(nitzSubs[5]);
- /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
- * offset as well (which we won't worry about until later) */
+ // NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
+ // offset as well (which we won't worry about until later).
+ // The LocalDateTime.of() will throw DateTimeException for values outside the allowed
+ // range for the Gregorian calendar.
long epochMillis = LocalDateTime.of(year, month, date, hour, minute, second)
.toInstant(ZoneOffset.UTC)
.toEpochMilli();
diff --git a/src/java/com/android/internal/telephony/NitzSignal.java b/src/java/com/android/internal/telephony/NitzSignal.java
index 889fe95..2619f3d 100644
--- a/src/java/com/android/internal/telephony/NitzSignal.java
+++ b/src/java/com/android/internal/telephony/NitzSignal.java
@@ -19,7 +19,7 @@
import android.annotation.DurationMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
-import android.os.TimestampedValue;
+import android.app.time.UnixEpochTime;
import java.time.Duration;
import java.util.Objects;
@@ -88,13 +88,12 @@
}
/**
- * Creates a {@link android.os.TimestampedValue} containing the UTC time as the number of
- * milliseconds since the start of the Unix epoch. The reference time is the time according to
- * the elapsed realtime clock when that would have been the time, accounting for receipt time
- * and age.
+ * Creates a {@link UnixEpochTime} containing the UTC time as the number of milliseconds since
+ * the start of the Unix epoch. The reference time is the time according to the elapsed realtime
+ * clock when that would have been the time, accounting for receipt time and age.
*/
- public TimestampedValue<Long> createTimeSignal() {
- return new TimestampedValue<>(
+ public UnixEpochTime createTimeSignal() {
+ return new UnixEpochTime(
getAgeAdjustedElapsedRealtimeMillis(),
getNitzData().getCurrentTimeInMillis());
}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 4afccd1..4e62d20 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions;
@@ -24,6 +26,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.hardware.radio.modem.ImeiInfo;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Build;
@@ -35,17 +38,21 @@
import android.os.RegistrantList;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
import android.preference.PreferenceManager;
import android.sysprop.TelephonyProperties;
import android.telecom.VideoProfile;
import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.SrvccState;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellBroadcastIdRange;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
@@ -60,14 +67,15 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
+import android.telephony.TelephonyManager.HalService;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.satellite.SatelliteDatagram;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
-import android.util.SparseArray;
import android.util.Xml;
import com.android.ims.ImsCall;
@@ -80,15 +88,17 @@
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.data.DataSettingsManager;
import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyConstants;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccFileHandler;
@@ -172,7 +182,8 @@
protected static final int EVENT_RADIO_ON = 5;
protected static final int EVENT_GET_BASEBAND_VERSION_DONE = 6;
protected static final int EVENT_USSD = 7;
- protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8;
+ @VisibleForTesting
+ public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8;
private static final int EVENT_GET_SIM_STATUS_DONE = 11;
protected static final int EVENT_SET_CALL_FORWARD_DONE = 12;
protected static final int EVENT_GET_CALL_FORWARD_DONE = 13;
@@ -222,11 +233,8 @@
// Radio state change
protected static final int EVENT_RADIO_STATE_CHANGED = 47;
protected static final int EVENT_SET_CARRIER_DATA_ENABLED = 48;
- protected static final int EVENT_DEVICE_PROVISIONED_CHANGE = 49;
- protected static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = 50;
protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE = 51;
- private static final int EVENT_ALL_DATA_DISCONNECTED = 52;
protected static final int EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED = 53;
protected static final int EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED = 54;
protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE = 55;
@@ -239,8 +247,12 @@
protected static final int EVENT_SUBSCRIPTIONS_CHANGED = 62;
protected static final int EVENT_GET_USAGE_SETTING_DONE = 63;
protected static final int EVENT_SET_USAGE_SETTING_DONE = 64;
+ protected static final int EVENT_IMS_DEREGISTRATION_TRIGGERED = 65;
+ protected static final int EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE = 66;
+ protected static final int EVENT_GET_DEVICE_IMEI_DONE = 67;
+ protected static final int EVENT_TRIGGER_NOTIFY_ANBR = 68;
- protected static final int EVENT_LAST = EVENT_SET_USAGE_SETTING_DONE;
+ protected static final int EVENT_LAST = EVENT_TRIGGER_NOTIFY_ANBR;
// For shared prefs.
private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -267,6 +279,10 @@
// Integer used to let the calling application know that the we are ignoring auto mode switch.
private static final int ALREADY_IN_AUTO_SELECTION = 1;
+ public static final String PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED =
+ "pref_null_cipher_and_integrity_enabled";
+ private final TelephonyAdminReceiver m2gAdminUpdater;
+
/**
* This method is invoked when the Phone exits Emergency Callback Mode.
*/
@@ -303,11 +319,6 @@
public CommandsInterface mCi;
protected int mVmCount = 0;
private boolean mDnsCheckDisabled;
- // Data connection trackers. For each transport type (e.g. WWAN, WLAN), there will be a
- // corresponding DcTracker. The WWAN DcTracker is for cellular data connections while
- // WLAN DcTracker is for IWLAN data connection. For IWLAN legacy mode, only one (WWAN) DcTracker
- // will be created.
- protected final SparseArray<DcTracker> mDcTrackers = new SparseArray<>();
protected DataNetworkController mDataNetworkController;
/* Used for dispatching signals to configured carrier apps */
protected CarrierSignalAgent mCarrierSignalAgent;
@@ -340,15 +351,13 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
- TelephonyTester mTelephonyTester;
+ private TelephonyTester mTelephonyTester;
private String mName;
private final String mActionDetached;
private final String mActionAttached;
protected DeviceStateMonitor mDeviceStateMonitor;
protected DisplayInfoController mDisplayInfoController;
- protected TransportManager mTransportManager;
protected AccessNetworksManager mAccessNetworksManager;
- protected DataEnabledSettings mDataEnabledSettings;
// Used for identify the carrier of current subscription
protected CarrierResolver mCarrierResolver;
protected SignalStrengthController mSignalStrengthController;
@@ -399,7 +408,7 @@
public static final String EXTRA_KEY_ALERT_SHOW = "alertShow";
public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage";
- private final RegistrantList mPreciseCallStateRegistrants = new RegistrantList();
+ protected final RegistrantList mPreciseCallStateRegistrants = new RegistrantList();
private final RegistrantList mHandoverRegistrants = new RegistrantList();
@@ -428,8 +437,6 @@
protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList();
- private final RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
-
private final RegistrantList mCellInfoRegistrants = new RegistrantList();
private final RegistrantList mRedialRegistrants = new RegistrantList();
@@ -449,6 +456,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final Context mContext;
+ protected SubscriptionManagerService mSubscriptionManagerService;
+
/**
* PhoneNotifier is an abstraction for all system-wide
* state change notification. DefaultPhoneNotifier is
@@ -474,9 +483,9 @@
protected LinkBandwidthEstimator mLinkBandwidthEstimator;
- /** The flag indicating using the new data stack or not. */
- // This flag and the old data stack code will be deleted in Android 14.
- private final boolean mNewDataStackEnabled;
+ public static final int IMEI_TYPE_UNKNOWN = -1;
+ public static final int IMEI_TYPE_PRIMARY = ImeiInfo.ImeiType.PRIMARY;
+ public static final int IMEI_TYPE_SECONDARY = ImeiInfo.ImeiType.SECONDARY;
public IccRecords getIccRecords() {
return mIccRecords.get();
@@ -580,10 +589,6 @@
.makeAppSmsManager(context);
mLocalLog = new LocalLog(64);
- if (TelephonyUtils.IS_DEBUGGABLE) {
- mTelephonyTester = new TelephonyTester(this);
- }
-
setUnitTestMode(unitTestMode);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -619,13 +624,17 @@
// Initialize SMS stats
mSmsStats = new SmsStats(this);
- mNewDataStackEnabled = !mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_force_disable_telephony_new_data_stack);
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+ m2gAdminUpdater = new TelephonyAdminReceiver(context, this);
if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
return;
}
+ if (TelephonyUtils.IS_DEBUGGABLE) {
+ mTelephonyTester = new TelephonyTester(this);
+ }
+
// Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
mTelephonyComponentFactory = telephonyComponentFactory;
mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
@@ -814,11 +823,6 @@
break;
}
- case EVENT_ALL_DATA_DISCONNECTED:
- if (areAllDataDisconnected()) {
- mAllDataDisconnectedRegistrants.notifyRegistrants();
- }
- break;
case EVENT_GET_USAGE_SETTING_DONE:
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
@@ -871,7 +875,11 @@
return null;
}
- public void notifySrvccState(Call.SrvccState state) {
+ /**
+ * Notifies the change of the SRVCC state.
+ * @param state the new SRVCC state.
+ */
+ public void notifySrvccState(@SrvccState int state) {
}
public void registerForSilentRedial(Handler h, int what, Object obj) {
@@ -894,6 +902,9 @@
Call.SrvccState srvccState = Call.SrvccState.NONE;
if (ret != null && ret.length != 0) {
int state = ret[0];
+ if (imsPhone != null) {
+ imsPhone.notifySrvccState(state);
+ }
switch(state) {
case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
srvccState = Call.SrvccState.STARTED;
@@ -906,11 +917,6 @@
break;
case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
srvccState = Call.SrvccState.COMPLETED;
- if (imsPhone != null) {
- imsPhone.notifySrvccState(srvccState);
- } else {
- Rlog.d(LOG_TAG, "HANDOVER_COMPLETED: mImsPhone null");
- }
break;
case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
@@ -985,17 +991,6 @@
}
/**
- * Subclasses of Phone probably want to replace this with a
- * version scoped to their packages
- */
- protected void notifyPreciseCallStateChangedP() {
- AsyncResult ar = new AsyncResult(null, this, null);
- mPreciseCallStateRegistrants.notifyRegistrants(ar);
-
- mNotifier.notifyPreciseCallState(this);
- }
-
- /**
* Notifies when a Handover happens due to SRVCC or Silent Redial
*/
public void registerForHandoverStateChanged(Handler h, int what, Object obj) {
@@ -1442,8 +1437,8 @@
@UnsupportedAppUsage
public void setNetworkSelectionModeAutomatic(Message response) {
Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode");
- // we don't want to do this unecesarily - it acutally causes
- // the radio to repeate network selection and is costly
+ // we don't want to do this unnecessarily - it actually causes
+ // the radio to repeat network selection and is costly
// first check if we're already in automatic mode
Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC);
msg.obj = response;
@@ -1476,6 +1471,7 @@
nsm.operatorAlphaShort = "";
if (doAutomatic) {
+ Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - set network selection auto");
Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
mCi.setNetworkSelectionModeAutomatic(msg);
} else {
@@ -1932,7 +1928,7 @@
public boolean isRadioOffForThermalMitigation() {
ServiceStateTracker sst = getServiceStateTracker();
return sst != null && sst.getRadioPowerOffReasons()
- .contains(Phone.RADIO_POWER_REASON_THERMAL);
+ .contains(TelephonyManager.RADIO_POWER_REASON_THERMAL);
}
/**
@@ -1951,13 +1947,6 @@
}
/**
- * @return The instance of transport manager.
- */
- public TransportManager getTransportManager() {
- return null;
- }
-
- /**
* @return The instance of access networks manager.
*/
public AccessNetworksManager getAccessNetworksManager() {
@@ -2326,6 +2315,12 @@
if (!mIsCarrierNrSupported) {
allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR;
}
+ if (m2gAdminUpdater.isCellular2gDisabled()) {
+ logd("SubId " + getSubId()
+ + " disabling 2g in getEffectiveAllowedNetworkTypes according to admin user "
+ + "restriction");
+ allowedNetworkTypes &= ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
+ }
logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: "
+ TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
return allowedNetworkTypes;
@@ -2401,17 +2396,18 @@
* Loads the allowed network type from subscription database.
*/
public void loadAllowedNetworksFromSubscriptionDatabase() {
- // Try to load ALLOWED_NETWORK_TYPES from SIMINFO.
- if (SubscriptionController.getInstance() == null) {
- return;
+ String result = null;
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(getSubId());
+ if (subInfo != null) {
+ result = subInfo.getAllowedNetworkTypesForReasons();
}
- String result = SubscriptionController.getInstance().getSubscriptionProperty(
- getSubId(),
- SubscriptionManager.ALLOWED_NETWORK_TYPES);
// After fw load network type from DB, do unlock if subId is valid.
- mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId(getSubId());
- if (result == null) {
+ mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId(
+ getSubId());
+
+ if (TextUtils.isEmpty(result)) {
return;
}
@@ -2421,14 +2417,14 @@
try {
// Format: "REASON=VALUE,REASON2=VALUE2"
for (String pair : result.trim().split(",")) {
- String[] networkTypesValues = (pair.trim().toLowerCase()).split("=");
+ String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("=");
if (networkTypesValues.length != 2) {
Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
continue;
}
int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]);
long value = Long.parseLong(networkTypesValues[1]);
- if (key != INVALID_ALLOWED_NETWORK_TYPES
+ if (TelephonyManager.isValidAllowedNetworkTypesReason(key)
&& value != INVALID_ALLOWED_NETWORK_TYPES) {
synchronized (mAllowedNetworkTypesForReasons) {
mAllowedNetworkTypesForReasons.put(key, value);
@@ -2466,7 +2462,15 @@
}
}
- private String convertAllowedNetworkTypeMapIndexToDbName(int reason) {
+ /**
+ * Convert the allowed network types reason to string.
+ *
+ * @param reason The allowed network types reason.
+ *
+ * @return The converted string.
+ */
+ public static String convertAllowedNetworkTypeMapIndexToDbName(
+ @TelephonyManager.AllowedNetworkTypesReason int reason) {
switch (reason) {
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
return ALLOWED_NETWORK_TYPES_TEXT_USER;
@@ -2477,7 +2481,10 @@
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G;
default:
- return Integer.toString(INVALID_ALLOWED_NETWORK_TYPES);
+ throw new IllegalArgumentException(
+ "No DB name conversion available for allowed network type reason: " + reason
+ + ". Did you forget to add an ALLOWED_NETWORK_TYPE_TEXT entry for"
+ + " a new reason?");
}
}
@@ -2977,6 +2984,9 @@
// This property is used to handle phone process crashes, and is the same for CDMA and IMS
// phones
protected static boolean getInEcmMode() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ return EmergencyStateTracker.getInstance().isInEcm();
+ }
return TelephonyProperties.in_ecm_mode().orElse(false);
}
@@ -2986,6 +2996,9 @@
* emergency operator.
*/
public boolean isInEcm() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ return EmergencyStateTracker.getInstance().isInEcm();
+ }
return mIsPhoneInEcmState;
}
@@ -2994,6 +3007,9 @@
}
public boolean isInCdmaEcm() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ return EmergencyStateTracker.getInstance().isInCdmaEcm();
+ }
return getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA && isInEcm()
&& (mImsPhone == null || !mImsPhone.isInImsEcm());
}
@@ -3730,18 +3746,11 @@
* @return true if there is a matching DUN APN.
*/
public boolean hasMatchedTetherApnSetting() {
- if (isUsingNewDataStack()) {
- NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (nrs != null) {
- return getDataNetworkController().getDataProfileManager()
- .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
- }
- return false;
- }
- if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
- return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .hasMatchedTetherApnSetting();
+ NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nrs != null) {
+ return getDataNetworkController().getDataProfileManager()
+ .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
}
return false;
}
@@ -3752,31 +3761,10 @@
* @return {@code true} if internet data is allowed to be established.
*/
public boolean isDataAllowed() {
- if (isUsingNewDataStack()) {
- return getDataNetworkController().isInternetDataAllowed();
- }
- return isDataAllowed(ApnSetting.TYPE_DEFAULT, null);
+ return getDataNetworkController().isInternetDataAllowed();
}
/**
- * Report on whether data connectivity is allowed.
- *
- * @param apnType APN type
- * @param reasons The reasons that data can/can't be established. This is an output param.
- * @return True if data is allowed to be established
- */
- public boolean isDataAllowed(@ApnType int apnType, DataConnectionReasons reasons) {
- if (mAccessNetworksManager != null) {
- int transport = mAccessNetworksManager.getCurrentTransport(apnType);
- if (getDcTracker(transport) != null) {
- return getDcTracker(transport).isDataAllowed(reasons);
- }
- }
- return false;
- }
-
-
- /**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns.
*/
public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
@@ -3932,16 +3920,6 @@
return null;
}
- /**
- * Get the current for the default apn DataState. No change notification
- * exists at this interface -- use
- * {@link android.telephony.PhoneStateListener} instead.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public PhoneConstants.DataState getDataConnectionState() {
- return getDataConnectionState(ApnSetting.TYPE_DEFAULT_STRING);
- }
-
public void notifyCallForwardingIndicator() {
}
@@ -4117,14 +4095,7 @@
*/
@UnsupportedAppUsage
public int getSubId() {
- if (SubscriptionController.getInstance() == null) {
- // TODO b/78359408 getInstance sometimes returns null in Treehugger tests, which causes
- // flakiness. Even though we haven't seen this crash in the wild we should keep this
- // check in until we've figured out the root cause.
- Rlog.e(LOG_TAG, "SubscriptionController.getInstance = null! Returning default subId");
- return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- }
- return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhoneId);
+ return mSubscriptionManagerService.getSubId(mPhoneId);
}
/**
@@ -4420,6 +4391,7 @@
// When radio capability switch is done, query IMEI value and update it in Phone objects
// to make it in sync with the IMEI value currently used by Logical-Modem.
if (capabilitySwitched) {
+ mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE));
mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
}
}
@@ -4438,7 +4410,12 @@
}
private int getResolvedUsageSetting(int subId) {
- SubscriptionInfo subInfo = SubscriptionController.getInstance().getSubscriptionInfo(subId);
+ SubscriptionInfo subInfo = null;
+ SubscriptionInfoInternal subInfoInternal = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ if (subInfoInternal != null) {
+ subInfo = subInfoInternal.toSubscriptionInfo();
+ }
if (subInfo == null
|| subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) {
@@ -4621,42 +4598,6 @@
return false;
}
- /**
- * @return True if all data connections are disconnected.
- */
- public boolean areAllDataDisconnected() {
- if (mAccessNetworksManager != null) {
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- if (getDcTracker(transport) != null
- && !getDcTracker(transport).areAllDataDisconnected()) {
- return false;
- }
- }
- }
- return true;
- }
-
- public void registerForAllDataDisconnected(Handler h, int what) {
- mAllDataDisconnectedRegistrants.addUnique(h, what, null);
- if (mAccessNetworksManager != null) {
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- if (getDcTracker(transport) != null
- && !getDcTracker(transport).areAllDataDisconnected()) {
- getDcTracker(transport).registerForAllDataDisconnected(
- this, EVENT_ALL_DATA_DISCONNECTED);
- }
- }
- }
- }
-
- public void unregisterForAllDataDisconnected(Handler h) {
- mAllDataDisconnectedRegistrants.remove(h);
- }
-
- public DataEnabledSettings getDataEnabledSettings() {
- return mDataEnabledSettings;
- }
-
@UnsupportedAppUsage
public IccSmsInterfaceManager getIccSmsInterfaceManager(){
return null;
@@ -4787,16 +4728,6 @@
return isEmergencyCallOnly;
}
- /**
- * Get data connection tracker based on the transport type
- *
- * @param transportType Transport type defined in AccessNetworkConstants.TransportType
- * @return The data connection tracker. Null if not found.
- */
- public @Nullable DcTracker getDcTracker(int transportType) {
- return mDcTrackers.get(transportType);
- }
-
// Return true if either CSIM or RUIM app is present. By default it returns false.
public boolean isCdmaSubscriptionAppPresent() {
return false;
@@ -4820,10 +4751,24 @@
* Get the HAL version.
*
* @return the current HalVersion
+ *
+ * @deprecated Use {@link #getHalVersion(int service)} instead.
*/
+ @Deprecated
public HalVersion getHalVersion() {
+ return getHalVersion(HAL_SERVICE_RADIO);
+ }
+
+ /**
+ * Get the HAL version with a specific service.
+ *
+ * @param service the service id to query
+ * @return the current HalVersion for a specific service
+ *
+ */
+ public HalVersion getHalVersion(@HalService int service) {
if (mCi != null && mCi instanceof RIL) {
- return ((RIL) mCi).getHalVersion();
+ return ((RIL) mCi).getHalVersion(service);
}
return RIL.RADIO_HAL_VERSION_UNKNOWN;
}
@@ -4967,11 +4912,600 @@
}
/**
- * @return {@code true} if using the new telephony data stack.
+ * Returns the user's last setting for terminal-based call waiting
+ * @param forCsOnly indicates the caller expects the result for CS calls only
*/
- // This flag and the old data stack code will be deleted in Android 14.
- public boolean isUsingNewDataStack() {
- return mNewDataStackEnabled;
+ public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+ return CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+ }
+
+ /**
+ * Notifies the change of the user setting of the terminal-based call waiting service
+ * to IMS service.
+ */
+ public void setTerminalBasedCallWaitingStatus(int state) {
+ }
+
+ /**
+ * Notifies that the IMS service connected supports the terminal-based call waiting service
+ */
+ public void setTerminalBasedCallWaitingSupported(boolean supported) {
+ }
+
+ /**
+ * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+ *
+ * @param token A nonce to identify the request.
+ * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+ * @param accessNetworkType The type of the radio access network used.
+ * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+ * mobile terminated use case eg. MO/MT call/SMS etc.
+ * @param response is callback message.
+ */
+ public void startImsTraffic(int token,
+ @MmTelFeature.ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) {
+ mCi.startImsTraffic(token, trafficType, accessNetworkType, trafficDirection, response);
+ }
+
+ /**
+ * Notifies IMS traffic has been stopped.
+ *
+ * @param token The token assigned by startImsTraffic.
+ * @param response is callback message.
+ */
+ public void stopImsTraffic(int token, Message response) {
+ mCi.stopImsTraffic(token, response);
+ }
+
+ /**
+ * Register for notifications of connection setup failure
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+ mCi.registerForConnectionSetupFailure(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications of connection setup failure
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForConnectionSetupFailure(Handler h) {
+ mCi.unregisterForConnectionSetupFailure(h);
+ }
+
+ /**
+ * Triggers the UE initiated EPS fallback procedure.
+ *
+ * @param reason specifies the reason for EPS fallback.
+ * @param response is callback message.
+ */
+ public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) {
+ mCi.triggerEpsFallback(reason, response);
+ }
+
+ /**
+ * Notifies the recommended bit rate for the indicated logical channel and direction.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond The recommended bit rate for the UE for a specific logical channel and
+ * a specific direction by NW.
+ */
+ public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ }
+
+ /**
+ * Sets the emergency mode
+ *
+ * @param emcMode The radio emergency mode type.
+ * @param result Callback message.
+ */
+ public void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+ @Nullable Message result) {
+ mCi.setEmergencyMode(emcMode, result);
+ }
+
+ /**
+ * Triggers an emergency network scan.
+ *
+ * @param accessNetwork Contains the list of access network types to be prioritized
+ * during emergency scan. The 1st entry has the highest priority.
+ * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+ * full service scan or any scan.
+ * @param result Callback message.
+ */
+ public void triggerEmergencyNetworkScan(
+ @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+ @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {
+ mCi.triggerEmergencyNetworkScan(accessNetwork, scanType, result);
+ }
+
+ /**
+ * Cancels ongoing emergency network scan
+ * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+ * If {@code true}, then the modem shall start the new scan from the beginning,
+ * otherwise the modem shall resume from the last search.
+ * @param result Callback message.
+ */
+ public void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {
+ mCi.cancelEmergencyNetworkScan(resetScan, result);
+ }
+
+ /**
+ * Exits ongoing emergency mode
+ * @param result Callback message.
+ */
+ public void exitEmergencyMode(@Nullable Message result) {
+ mCi.exitEmergencyMode(result);
+ }
+
+ /**
+ * Registers for emergency network scan result.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForEmergencyNetworkScan(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ mCi.registerForEmergencyNetworkScan(h, what, obj);
+ }
+
+ /**
+ * Unregisters for emergency network scan result.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEmergencyNetworkScan(@NonNull Handler h) {
+ mCi.unregisterForEmergencyNetworkScan(h);
+ }
+
+ /**
+ * Notifies that IMS deregistration is triggered.
+ *
+ * @param reason the reason why the deregistration is triggered.
+ */
+ public void triggerImsDeregistration(
+ @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
+ if (mImsPhone != null) {
+ mImsPhone.triggerImsDeregistration(reason);
+ }
+ }
+
+ /**
+ * Registers for the domain selected for emergency calls.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForEmergencyDomainSelected(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ }
+
+ /**
+ * Unregisters for the domain selected for emergency calls.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEmergencyDomainSelected(@NonNull Handler h) {
+ }
+
+ /**
+ * Notifies the domain selected.
+ *
+ * @param transportType The preferred transport type.
+ */
+ public void notifyEmergencyDomainSelected(
+ @AccessNetworkConstants.TransportType int transportType) {
+ }
+
+ /**
+ * @return Telephony tester instance.
+ */
+ public @Nullable TelephonyTester getTelephonyTester() {
+ return mTelephonyTester;
+ }
+
+ /**
+ * @return User handle associated with the phone's subscription id. {@code null} if subscription
+ * is invalid or not found.
+ */
+ @Nullable
+ public UserHandle getUserHandle() {
+ int subId = getSubId();
+
+ UserHandle userHandle = null;
+ try {
+ SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+ if (subManager != null) {
+ userHandle = subManager.getSubscriptionUserHandle(subId);
+ }
+ } catch (IllegalArgumentException ex) {
+ loge("getUserHandle: ex=" + ex);
+ }
+
+ return userHandle;
+ }
+
+ /**
+ * Checks if the context user is a managed profile.
+ *
+ * Note that this applies specifically to <em>managed</em> profiles.
+ *
+ * @return whether the context user is a managed profile.
+ */
+ public boolean isManagedProfile() {
+ UserHandle userHandle = getUserHandle();
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userHandle == null || userManager == null) return false;
+ return userManager.isManagedProfile(userHandle.getIdentifier());
+ }
+
+ /**
+ * @return global null cipher and integrity enabled preference
+ */
+ public boolean getNullCipherAndIntegrityEnabledPreference() {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return sp.getBoolean(PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true);
+ }
+
+ /**
+ * @return whether or not this Phone interacts with a modem that supports the null cipher
+ * and integrity feature.
+ */
+ public boolean isNullCipherAndIntegritySupported() {
+ return false;
+ }
+
+ /**
+ * Override to implement handling of an update to the enablement of the null cipher and
+ * integrity preference.
+ * {@see #PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED}
+ */
+ public void handleNullCipherEnabledChange() {
+ }
+
+ /**
+ * Notifies the IMS call status to the modem.
+ *
+ * @param imsCallInfo The list of {@link ImsCallInfo}.
+ * @param response A callback to receive the response.
+ */
+ public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message response) {
+ mCi.updateImsCallStatus(imsCallInfo, response);
+ }
+
+ /**
+ * Enables or disables N1 mode (access to 5G core network) in accordance with
+ * 3GPP TS 24.501 4.9.
+ * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode.
+ * @param result Callback message to receive the result.
+ */
+ public void setN1ModeEnabled(boolean enable, Message result) {
+ mCi.setN1ModeEnabled(enable, result);
+ }
+
+ /**
+ * Check whether N1 mode (access to 5G core network) is enabled or not.
+ * @param result Callback message to receive the result.
+ */
+ public void isN1ModeEnabled(Message result) {
+ mCi.isN1ModeEnabled(result);
+ }
+
+ /**
+ * Return current cell broadcast ranges.
+ */
+ public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+ return new ArrayList<>();
+ }
+
+ /**
+ * Set reception of cell broadcast messages with the list of the given ranges.
+ */
+ public void setCellBroadcastIdRanges(
+ @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) {
+ callback.accept(TelephonyManager.CELL_BROADCAST_RESULT_UNSUPPORTED);
+ }
+
+ /**
+ * Start receiving satellite position updates.
+ * This can be called by the pointing UI when the user starts pointing to the satellite.
+ * Modem should continue to report the pointing input as the device or satellite moves.
+ *
+ * @param result The Message to send to result of the operation to.
+ **/
+ public void startSatellitePositionUpdates(Message result) {
+ mCi.startSendingSatellitePointingInfo(result);
+ }
+
+ /**
+ * Stop receiving satellite position updates.
+ * This can be called by the pointing UI when the user stops pointing to the satellite.
+ *
+ * @param result The Message to send to result of the operation to.
+ **/
+ public void stopSatellitePositionUpdates(Message result) {
+ mCi.stopSendingSatellitePointingInfo(result);
+ }
+
+ /**
+ * Get maximum number of characters per text message on satellite.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void getMaxCharactersPerSatelliteTextMessage(Message result) {
+ mCi.getMaxCharactersPerSatelliteTextMessage(result);
+ }
+
+ /**
+ * Power on or off the satellite modem.
+ * @param result The Message to send the result of the operation to.
+ * @param powerOn {@code true} to power on the satellite modem and {@code false} to power off.
+ */
+ public void setSatellitePower(Message result, boolean powerOn) {
+ mCi.setSatellitePower(result, powerOn);
+ }
+
+ /**
+ * Check whether the satellite modem is powered on.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void isSatellitePowerOn(Message result) {
+ mCi.getSatellitePowerState(result);
+ }
+
+ /**
+ * Check whether the satellite service is supported on the device.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void isSatelliteSupported(Message result) {
+ mCi.isSatelliteSupported(result);
+ }
+
+ /**
+ * Check whether the satellite modem is provisioned.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void isSatelliteProvisioned(Message result) {
+ mCi.getSatelliteProvisionState(result);
+ }
+
+ /**
+ * Get the satellite capabilities.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void getSatelliteCapabilities(Message result) {
+ mCi.getSatelliteCapabilities(result);
+ }
+
+ /**
+ * Registers for pointing info changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatellitePositionInfoChanged(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ //TODO: Rename CommandsInterface and other modules when updating HAL APIs.
+ mCi.registerForSatellitePointingInfoChanged(h, what, obj);
+ }
+
+ /**
+ * Unregisters for pointing info changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatellitePositionInfoChanged(@NonNull Handler h) {
+ //TODO: Rename CommandsInterface and other modules when updating HAL APIs.
+ mCi.unregisterForSatellitePointingInfoChanged(h);
+ }
+
+ /**
+ * Registers for datagrams delivered events from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteDatagramsDelivered(@NonNull Handler h,
+ int what, @Nullable Object obj) {
+ //TODO: Remove.
+ mCi.registerForSatelliteMessagesTransferComplete(h, what, obj);
+ }
+
+ /**
+ * Unregisters for datagrams delivered events from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatelliteDatagramsDelivered(@NonNull Handler h) {
+ //TODO: Remove.
+ mCi.unregisterForSatelliteMessagesTransferComplete(h);
+ }
+
+ /**
+ * Provision the subscription with a satellite provider.
+ * This is needed to register the device/subscription if the provider allows dynamic
+ * registration.
+ *
+ * @param result Callback message to receive the result.
+ * @param token The token of the device/subscription to be provisioned.
+ */
+ public void provisionSatelliteService(Message result, String token) {
+ // TODO: update parameters in HAL
+ // mCi.provisionSatelliteService(result, token);
+ }
+
+ /**
+ * Deprovision the device/subscription with a satellite provider.
+ * This is needed to unregister the device/subscription if the provider allows dynamic
+ * registration.
+ * If provisioning is in progress for the given SIM, cancel the request.
+ * If there is no request in progress, deprovision the given SIM.
+ *
+ * @param result Callback message to receive the result.
+ * @param token The token of the device/subscription to be deprovisioned.
+ */
+ public void deprovisionSatelliteService(Message result, String token) {
+ //TODO (b/266126070): add implementation.
+ }
+
+ /**
+ * Register for a satellite provision state changed event.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteProvisionStateChanged(Handler h, int what, Object obj) {
+ mCi.registerForSatelliteProvisionStateChanged(h, what, obj);
+ }
+
+ /**
+ * Unregister for a satellite provision state changed event.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatelliteProvisionStateChanged(Handler h) {
+ mCi.unregisterForSatelliteProvisionStateChanged(h);
+ }
+
+ /**
+ * Get the list of provisioned satellite features.
+ *
+ * @param result Callback message to receive the result.
+ */
+ public void getProvisionedSatelliteFeatures(Message result) {
+ //TODO (b/266126070): add implementation.
+ }
+
+ /**
+ * Registers for satellite state changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteModemStateChanged(@NonNull Handler h, int what,
+ @Nullable Object obj) {
+ mCi.registerForSatelliteModeChanged(h, what, obj);
+ }
+
+ /**
+ * Unregisters for satellite state changed from satellite modem.
+ *
+ * @param h Handler to be removed from registrant list.
+ */
+ public void unregisterForSatelliteModemStateChanged(@NonNull Handler h) {
+ mCi.unregisterForSatelliteModeChanged(h);
+ }
+
+ /**
+ * Registers for pending datagram count info from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForPendingDatagramCount(@NonNull Handler h, int what,
+ @Nullable Object obj) {
+ mCi.registerForPendingSatelliteMessageCount(h, what, obj);
+ }
+
+ /**
+ * Unregisters for pending datagram count info from satellite modem.
+ *
+ * @param h Handler to be removed from registrant list.
+ */
+ public void unregisterForPendingDatagramCount(@NonNull Handler h) {
+ mCi.unregisterForPendingSatelliteMessageCount(h);
+ }
+
+ /**
+ * Register to receive incoming datagrams over satellite.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteDatagramsReceived(@NonNull Handler h, int what,
+ @Nullable Object obj) {
+ // TODO: rename
+ mCi.registerForNewSatelliteMessages(h, what, obj);
+ }
+
+ /**
+ * Unregister to stop receiving incoming datagrams over satellite.
+ *
+ * @param h Handler to be removed from registrant list.
+ */
+ public void unregisterForSatelliteDatagramsReceived(@NonNull Handler h) {
+ // TODO: rename
+ mCi.unregisterForNewSatelliteMessages(h);
+ }
+
+ /**
+ * Poll pending datagrams over satellite.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void pollPendingSatelliteDatagrams(Message result) {
+ //mCi.pollPendingSatelliteDatagrams(result);
+ }
+
+ /**
+ * Send datagram over satellite.
+ * @param result The Message to send the result of the operation to.
+ * @param datagram Datagram to send over satellite.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ */
+ public void sendSatelliteDatagram(Message result, SatelliteDatagram datagram,
+ boolean needFullScreenPointingUI) {
+ //mCi.sendSatelliteDatagram(result, datagram);
+ }
+
+ /**
+ * Check whether satellite communication is allowed for the current location.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {
+ mCi.isSatelliteCommunicationAllowedForCurrentLocation(result);
+ }
+
+ /**
+ * Get the time after which the satellite will be visible.
+ * @param result The Message to send the result of the operation to.
+ */
+ public void requestTimeForNextSatelliteVisibility(Message result) {
+ mCi.getTimeForNextSatelliteVisibility(result);
+ }
+
+ /**
+ * Start callback mode
+ * @param type for callback mode entry.
+ */
+ public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) {
+ Rlog.d(LOG_TAG, "startCallbackMode:type=" + type);
+ mNotifier.notifyCallbackModeStarted(this, type);
+ }
+
+ /**
+ * Stop callback mode
+ * @param type for callback mode exit.
+ * @param reason for stopping callback mode.
+ */
+ public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
+ @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+ Rlog.d(LOG_TAG, "stopCallbackMode:type=" + type + ", reason=" + reason);
+ mNotifier.notifyCallbackModeStopped(this, type, reason);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -4996,7 +5530,7 @@
pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
pw.println(" getUnitTestMode()=" + getUnitTestMode());
pw.println(" getState()=" + getState());
- pw.println(" getIccSerialNumber()=" + getIccSerialNumber());
+ pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber()));
pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
@@ -5009,7 +5543,6 @@
pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
pw.println(" isInEmergencySmsMode=" + isInEmergencySmsMode());
pw.println(" isEcmCanceledForEmergency=" + isEcmCanceledForEmergency());
- pw.println(" isUsingNewDataStack=" + isUsingNewDataStack());
pw.println(" service state=" + getServiceState());
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
@@ -5025,16 +5558,6 @@
pw.println("++++++++++++++++++++++++++++++++");
}
- if (mAccessNetworksManager != null) {
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- if (getDcTracker(transport) != null) {
- getDcTracker(transport).dump(fd, pw, args);
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
- }
- }
- }
-
if (mDataNetworkController != null) {
try {
mDataNetworkController.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 05c325e..8b95824 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -35,6 +35,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;
import java.util.HashMap;
@@ -71,6 +72,7 @@
private TelephonyManager mTelephonyManager;
private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
/**
* Init method to instantiate the object
@@ -122,6 +124,21 @@
}
}
+ // If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2.
+ private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
+ final PhoneCapability staticCapability) {
+ boolean enableVirtualDsda = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enable_virtual_dsda);
+
+ if (staticCapability.getLogicalModemList().size() > 1 && enableVirtualDsda) {
+ return new PhoneCapability.Builder(staticCapability)
+ .setMaxActiveVoiceSubscriptions(2)
+ .build();
+ } else {
+ return staticCapability;
+ }
+ }
+
/**
* Static method to get instance.
*/
@@ -180,6 +197,8 @@
ar = (AsyncResult) msg.obj;
if (ar != null && ar.exception == null) {
mStaticCapability = (PhoneCapability) ar.result;
+ mStaticCapability =
+ maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
notifyCapabilityChanged();
} else {
log(msg.what + " failure. Not getting phone capability." + ar.exception);
@@ -366,7 +385,7 @@
// eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the
// second phone. This loop does nothing if numOfActiveModems is increasing.
for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) {
- SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
+ SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId);
subInfoCleared = true;
mPhones[phoneId].mCi.onSlotActiveStatusChange(
SubscriptionManager.isValidPhoneId(phoneId));
@@ -400,7 +419,7 @@
+ "setting VOICE & SMS subId to -1 (No Preference)");
//Set the default VOICE subId to -1 ("No Preference")
- SubscriptionController.getInstance().setDefaultVoiceSubId(
+ SubscriptionManagerService.getInstance().setDefaultVoiceSubId(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
//TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042)
@@ -465,34 +484,46 @@
* @return true if the modem service is set successfully, false otherwise.
*/
public boolean setModemService(String serviceName) {
- if (mRadioConfig == null || mPhones[0] == null) {
- return false;
- }
-
log("setModemService: " + serviceName);
boolean statusRadioConfig = false;
boolean statusRil = false;
final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
+ final boolean isAllowedForBoot =
+ SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
- // Check for ALLOW_MOCK_MODEM_PROPERTY on user builds
- if (isAllowed || DEBUG) {
- if (serviceName != null) {
+ // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds
+ if (isAllowed || isAllowedForBoot || DEBUG) {
+ if (mRadioConfig != null) {
statusRadioConfig = mRadioConfig.setModemService(serviceName);
-
- //TODO: consider multi-sim case (b/210073692)
- statusRil = mPhones[0].mCi.setModemService(serviceName);
- } else {
- statusRadioConfig = mRadioConfig.setModemService(null);
-
- //TODO: consider multi-sim case
- statusRil = mPhones[0].mCi.setModemService(null);
}
- return statusRadioConfig && statusRil;
+ if (!statusRadioConfig) {
+ loge("setModemService: switching modem service for radioconfig fail");
+ return false;
+ }
+
+ for (int i = 0; i < getPhoneCount(); i++) {
+ if (mPhones[i] != null) {
+ statusRil = mPhones[i].mCi.setModemService(serviceName);
+ }
+
+ if (!statusRil) {
+ loge("setModemService: switch modem for radio " + i + " fail");
+
+ // Disconnect the switched service
+ mRadioConfig.setModemService(null);
+ for (int t = 0; t < i; t++) {
+ mPhones[t].mCi.setModemService(null);
+ }
+ return false;
+ }
+ }
} else {
loge("setModemService is not allowed");
return false;
}
+
+ return true;
}
/**
@@ -500,7 +531,6 @@
* @return the service name of the connected service.
*/
public String getModemService() {
- //TODO: consider multi-sim case
if (mPhones[0] == null) {
return "";
}
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 3361b74..57a375b 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE;
@@ -29,7 +31,6 @@
import android.content.pm.PackageManager;
import android.net.LocalServerSocket;
import android.os.Build;
-import android.os.HandlerThread;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.provider.Settings;
@@ -50,6 +51,7 @@
import com.android.internal.telephony.imsphone.ImsPhoneFactory;
import com.android.internal.telephony.metrics.MetricsCollector;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.util.IndentingPrintWriter;
@@ -83,8 +85,7 @@
private static IntentBroadcaster sIntentBroadcaster;
private static @Nullable EuiccController sEuiccController;
private static @Nullable EuiccCardController sEuiccCardController;
-
- static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
+ private static SubscriptionManagerService sSubscriptionManagerService;
@UnsupportedAppUsage
static private boolean sMadeDefaults = false;
@@ -117,6 +118,7 @@
synchronized (sLockProxyPhones) {
if (!sMadeDefaults) {
sContext = context;
+
// create the telephony device controller.
TelephonyDevController.create();
@@ -179,7 +181,7 @@
if (numPhones > 0) {
final RadioConfig radioConfig = RadioConfig.make(context,
- sCommandsInterfaces[0].getHalVersion());
+ sCommandsInterfaces[0].getHalVersion(HAL_SERVICE_RADIO));
sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig,
sCommandsInterfaces[0]);
} else {
@@ -194,12 +196,12 @@
// call getInstance()
sUiccController = UiccController.make(context);
- Rlog.i(LOG_TAG, "Creating SubscriptionController");
- TelephonyComponentFactory.getInstance().inject(SubscriptionController.class.
- getName()).initSubscriptionController(context);
+ Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
+ sSubscriptionManagerService = new SubscriptionManagerService(context,
+ Looper.myLooper());
+
TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
- getName()).initMultiSimSettingController(context,
- SubscriptionController.getInstance());
+ getName()).initMultiSimSettingController(context);
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_EUICC)) {
@@ -231,14 +233,6 @@
sMadeDefaults = true;
- Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
- HandlerThread pfhandlerThread = new HandlerThread("PhoneFactoryHandlerThread");
- pfhandlerThread.start();
- sSubInfoRecordUpdater = TelephonyComponentFactory.getInstance().inject(
- SubscriptionInfoUpdater.class.getName()).
- makeSubscriptionInfoUpdater(pfhandlerThread.
- getLooper(), context, SubscriptionController.getInstance());
-
// Only bring up IMS if the device supports having an IMS stack.
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_IMS)) {
@@ -376,10 +370,6 @@
}
}
- public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() {
- return sSubInfoRecordUpdater;
- }
-
/**
* Get the network factory associated with a given phone ID.
* @param phoneId the phone id
@@ -416,7 +406,6 @@
* @param phoneId The phone's id.
* @return the preferred network mode bitmask that should be set.
*/
- // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int calculatePreferredNetworkType(int phoneId) {
if (getPhone(phoneId) == null) {
@@ -433,7 +422,7 @@
/* Gets the default subscription */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int getDefaultSubscription() {
- return SubscriptionController.getInstance().getDefaultSubId();
+ return SubscriptionManagerService.getInstance().getDefaultSubId();
}
/* Returns User SMS Prompt property, enabled or not */
@@ -461,19 +450,7 @@
}
/**
- * Request a refresh of the embedded subscription list.
- *
- * @param cardId the card ID of the eUICC.
- * @param callback Optional callback to execute after the refresh completes. Must terminate
- * quickly as it will be called from SubscriptionInfoUpdater's handler thread.
- */
- public static void requestEmbeddedSubscriptionInfoListRefresh(
- int cardId, @Nullable Runnable callback) {
- sSubInfoRecordUpdater.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback);
- }
-
- /**
- * Get a the SmsController.
+ * Get the instance of {@link SmsController}.
*/
public static SmsController getSmsController() {
synchronized (sLockProxyPhones) {
@@ -576,21 +553,14 @@
pw.decreaseIndent();
pw.println("++++++++++++++++++++++++++++++++");
- pw.println("SubscriptionController:");
- pw.increaseIndent();
- try {
- SubscriptionController.getInstance().dump(fd, pw, args);
- } catch (Exception e) {
- e.printStackTrace();
- }
pw.flush();
pw.decreaseIndent();
pw.println("++++++++++++++++++++++++++++++++");
- pw.println("SubInfoRecordUpdater:");
+ pw.println("sRadioHalCapabilities:");
pw.increaseIndent();
try {
- sSubInfoRecordUpdater.dump(fd, pw, args);
+ sRadioHalCapabilities.dump(fd, pw, args);
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 5b4d5e5..32c0c73 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -36,9 +35,9 @@
import com.android.internal.telephony.PhoneConstants.DataState;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
/**
@@ -211,16 +210,6 @@
static final String REASON_DATA_UNTHROTTLED = "dataUnthrottled";
static final String REASON_TRAFFIC_DESCRIPTORS_UPDATED = "trafficDescriptorsUpdated";
- // Reasons for Radio being powered off
- int RADIO_POWER_REASON_USER = 0;
- int RADIO_POWER_REASON_THERMAL = 1;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"RADIO_POWER_REASON_"},
- value = {
- RADIO_POWER_REASON_USER,
- RADIO_POWER_REASON_THERMAL})
- public @interface RadioPowerReason {}
-
// Used for band mode selection methods
static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
static final int BM_EURO_BAND = RILConstants.BAND_MODE_EURO;
@@ -593,8 +582,9 @@
* getServiceState().getState() will not change immediately after this call.
* registerForServiceStateChanged() to find out when the
* request is complete. This will set the reason for radio power state as {@link
- * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
- * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+ * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+ * requested radio power state will actually be set.
+ * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
* for details.
*
* @param power true means "on", false means "off".
@@ -620,8 +610,9 @@
* getServiceState().getState() will not change immediately after this call.
* registerForServiceStateChanged() to find out when the
* request is complete. This will set the reason for radio power state as {@link
- * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
- * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+ * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+ * requested radio power state will actually be set.
+ * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
* for details.
*
* @param power true means "on", false means "off".
@@ -638,7 +629,7 @@
default void setRadioPower(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
- RADIO_POWER_REASON_USER);
+ TelephonyManager.RADIO_POWER_REASON_USER);
}
/**
@@ -656,11 +647,19 @@
* @param power true means "on", false means "off".
* @param reason RadioPowerReason constant defining the reason why the radio power was set.
*/
- default void setRadioPowerForReason(boolean power, @RadioPowerReason int reason) {
+ default void setRadioPowerForReason(boolean power,
+ @TelephonyManager.RadioPowerReason int reason) {
setRadioPowerForReason(power, false, false, false, reason);
}
/**
+ * @return reasons for powering off radio.
+ */
+ default Set<Integer> getRadioPowerOffReasons() {
+ return new HashSet<>();
+ }
+
+ /**
* Sets the radio power on/off state with option to specify whether it's for emergency call
* (off is sometimes called "airplane mode") and option to set the reason for setting the power
* state. Current state can be gotten via {@link #getServiceState()}.
@@ -686,7 +685,7 @@
*/
default void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply,
- @RadioPowerReason int reason) {}
+ @TelephonyManager.RadioPowerReason int reason) {}
/**
* Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned
@@ -1036,6 +1035,10 @@
String getImei();
/**
+ * Retrieves IMEI type for phones.
+ */
+ int getImeiType();
+ /**
* Retrieves the IccPhoneBookInterfaceManager of the Phone
*/
public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager();
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 701a157..20d6702 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.telephony.Annotation;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
import android.telephony.BarringInfo;
@@ -31,8 +32,11 @@
import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
import java.util.List;
@@ -78,7 +82,10 @@
void notifyCellInfo(Phone sender, List<CellInfo> cellInfo);
- void notifyPreciseCallState(Phone sender);
+ /** Send a notification that precise call state changed. */
+ void notifyPreciseCallState(Phone sender, String[] imsCallIds,
+ @Annotation.ImsCallServiceType int[] imsCallServiceTypes,
+ @Annotation.ImsCallType int[] imsCallTypes);
void notifyDisconnectCause(Phone sender, int cause, int preciseCause);
@@ -113,6 +120,9 @@
/** Notify of a change to the call quality of an active foreground call. */
void notifyCallQualityChanged(Phone sender, CallQuality callQuality, int callNetworkType);
+ /** Notify of a change to the media quality status of an active foreground call. */
+ void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status);
+
/** Notify registration failed */
void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity,
@NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
@@ -132,4 +142,11 @@
/** Notify link capacity estimate has changed. */
void notifyLinkCapacityEstimateChanged(Phone sender,
List<LinkCapacityEstimate> linkCapacityEstimateList);
+
+ /** Notify callback mode started. */
+ void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type);
+
+ /** Notify callback mode stopped. */
+ void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
+ @EmergencyCallbackModeStopReason int reason);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index b1dfa5f..d30a73c 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -27,22 +27,29 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.TelephonyServiceManager.ServiceRegisterer;
-import android.os.UserHandle;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
+import android.text.TextUtils;
import android.util.EventLog;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.SIMRecords;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.telephony.Rlog;
+import java.util.ArrayList;
+import java.util.List;
+
public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
private static final String TAG = "PhoneSubInfoController";
private static final boolean DBG = true;
@@ -147,14 +154,13 @@
public String getSubscriberIdForSubscriber(int subId, String callingPackage,
String callingFeatureId) {
String message = "getSubscriberIdForSubscriber";
-
- enforceCallingPackage(callingPackage, Binder.getCallingUid(), message);
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
long identity = Binder.clearCallingIdentity();
boolean isActive;
try {
- isActive = SubscriptionController.getInstance().isActiveSubId(subId, callingPackage,
- callingFeatureId);
+ isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId,
+ callingPackage, callingFeatureId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -168,7 +174,12 @@
}
identity = Binder.clearCallingIdentity();
try {
- return SubscriptionController.getInstance().getImsiPrivileged(subId);
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null && !TextUtils.isEmpty(subInfo.getImsi())) {
+ return subInfo.getImsi();
+ }
+ return null;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -316,28 +327,6 @@
"Requires MODIFY_PHONE_STATE");
}
- /**
- * Make sure the caller is the calling package itself
- *
- * @throws SecurityException if the caller is not the calling package
- */
- private void enforceCallingPackage(String callingPackage, int callingUid, String message) {
- int packageUid = -1;
- PackageManager pm = mContext.createContextAsUser(
- UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
- if (pm != null) {
- try {
- packageUid = pm.getPackageUid(callingPackage, 0);
- } catch (PackageManager.NameNotFoundException e) {
- // packageUid is -1
- }
- }
- if (packageUid != callingUid) {
- throw new SecurityException(message + ": Package " + callingPackage
- + " does not belong to " + callingUid);
- }
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getDefaultSubscription() {
return PhoneFactory.getDefaultSubscription();
@@ -359,6 +348,34 @@
}
/**
+ * Fetches the IMS private user identity (EF_IMPI) based on subscriptionId.
+ *
+ * @param subId subscriptionId
+ * @return IMPI (IMS private user identity) of type string.
+ * @throws IllegalArgumentException if the subscriptionId is not valid
+ * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ public String getImsPrivateUserIdentity(int subId, String callingPackage,
+ String callingFeatureId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid SubscriptionID = " + subId);
+ }
+ if (!TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(mContext,
+ callingPackage, callingFeatureId, "getImsPrivateUserIdentity")) {
+ throw (new SecurityException("No permissions to the caller"));
+ }
+ Phone phone = getPhone(subId);
+ assert phone != null;
+ IsimRecords isim = phone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimImpi();
+ } else {
+ throw new IllegalStateException("ISIM is not loaded");
+ }
+ }
+
+ /**
* get the Isim Domain based on subId
*/
public String getIsimDomain(int subId) {
@@ -389,6 +406,42 @@
}
/**
+ * Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId
+ *
+ * @param subId subscriptionId
+ * @param callingPackage package name of the caller
+ * @param callingFeatureId feature Id of the caller
+ * @return List of public user identities of type android.net.Uri or empty list if
+ * EF_IMPU is not available.
+ * @throws IllegalArgumentException if the subscriptionId is not valid
+ * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage,
+ String callingFeatureId) {
+ if (TelephonyPermissions.
+ checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber(
+ mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) {
+ Phone phone = getPhone(subId);
+ assert phone != null;
+ IsimRecords isimRecords = phone.getIsimRecords();
+ if (isimRecords != null) {
+ String[] impus = isimRecords.getIsimImpu();
+ List<Uri> impuList = new ArrayList<>();
+ for (String impu : impus) {
+ if (impu != null && impu.trim().length() > 0) {
+ impuList.add(Uri.parse(impu));
+ }
+ }
+ return impuList;
+ }
+ throw new IllegalStateException("ISIM is not loaded");
+ } else {
+ throw new IllegalArgumentException("Invalid SubscriptionID = " + subId);
+ }
+ }
+
+ /**
* get the Isim Ist based on subId
*/
public String getIsimIst(int subId) throws RemoteException {
@@ -418,6 +471,29 @@
});
}
+ /**
+ * Returns the USIM service table that fetched from EFUST elementary field that are loaded
+ * based on the appType.
+ */
+ public String getSimServiceTable(int subId, int appType) throws RemoteException {
+ return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSimServiceTable",
+ (phone) -> {
+ UiccPort uiccPort = phone.getUiccPort();
+ if (uiccPort == null || uiccPort.getUiccProfile() == null) {
+ loge("getSimServiceTable(): uiccPort or uiccProfile is null");
+ return null;
+ }
+ UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType(
+ appType);
+ if (uiccApp == null) {
+ loge("getSimServiceTable(): no app with specified apptype="
+ + appType);
+ return null;
+ }
+ return ((SIMRecords)uiccApp.getIccRecords()).getSimServiceTable();
+ });
+ }
+
@Override
public String getIccSimChallengeResponse(int subId, int appType, int authType, String data,
String callingPackage, String callingFeatureId) throws RemoteException {
@@ -438,7 +514,9 @@
}
if (authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM
- && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) {
+ && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA
+ && authType != UiccCardApplication.AUTH_CONTEXT_GBA_BOOTSTRAP
+ && authType != UiccCardApplication.AUTHTYPE_GBA_NAF_KEY_EXTERNAL) {
loge("getIccSimChallengeResponse() unsupported authType: " + authType);
return null;
}
@@ -491,7 +569,7 @@
if (phone != null) {
return callMethodHelper.callMethod(phone);
} else {
- loge(message + " phone is null for Subscription:" + subId);
+ if (VDBG) loge(message + " phone is null for Subscription:" + subId);
return null;
}
} finally {
@@ -581,6 +659,37 @@
}
}
+ /**
+ * Returns SIP URI or tel URI of the Public Service Identity of the SM-SC fetched from
+ * EF_PSISMSC elementary field as defined in Section 4.5.9 (3GPP TS 31.102).
+ * @throws IllegalStateException in case if phone or UiccApplication is not available.
+ */
+ public Uri getSmscIdentity(int subId, int appType) throws RemoteException {
+ Uri smscIdentityUri = callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSmscIdentity",
+ (phone) -> {
+ try {
+ String smscIdentity = null;
+ UiccPort uiccPort = phone.getUiccPort();
+ UiccCardApplication uiccApp =
+ uiccPort.getUiccProfile().getApplicationByType(
+ appType);
+ smscIdentity = (uiccApp != null) ? uiccApp.getIccRecords().getSmscIdentity()
+ : null;
+ if (TextUtils.isEmpty(smscIdentity)) {
+ return Uri.EMPTY;
+ }
+ return Uri.parse(smscIdentity);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getSmscIdentity(): Exception = " + ex);
+ return null;
+ }
+ });
+ if (smscIdentityUri == null) {
+ throw new IllegalStateException("Telephony service error");
+ }
+ return smscIdentityUri;
+ }
+
private void log(String s) {
Rlog.d(TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 76f0041..ed9982e 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,7 +28,6 @@
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.telephony.RadioAccessFamily;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -48,8 +47,10 @@
@VisibleForTesting
static final int EVENT_START_RC_RESPONSE = 2;
private static final int EVENT_APPLY_RC_RESPONSE = 3;
- private static final int EVENT_FINISH_RC_RESPONSE = 4;
- private static final int EVENT_TIMEOUT = 5;
+ @VisibleForTesting
+ public static final int EVENT_FINISH_RC_RESPONSE = 4;
+ @VisibleForTesting
+ public static final int EVENT_TIMEOUT = 5;
@VisibleForTesting
public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 6;
@@ -157,34 +158,6 @@
logd("Constructor - Exit");
}
- public void registerForAllDataDisconnected(int subId, Handler h, int what) {
- int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- mPhones[phoneId].registerForAllDataDisconnected(h, what);
- }
- }
-
- public void unregisterForAllDataDisconnected(int subId, Handler h) {
- int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- mPhones[phoneId].unregisterForAllDataDisconnected(h);
- }
- }
-
-
- public boolean areAllDataDisconnected(int subId) {
- int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- return mPhones[phoneId].areAllDataDisconnected();
- } else {
- // if we can't find a phone for the given subId, it is disconnected.
- return true;
- }
- }
-
/**
* Get phone radio type and access technology.
*
@@ -241,6 +214,7 @@
clearTransaction();
// Keep a wake lock until we finish radio capability changed
+ logd("Acquiring wake lock for setting radio capability");
mWakeLock.acquire();
return doSetRadioCapabilities(rafs);
@@ -386,19 +360,25 @@
}
}
RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
- if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
+ // Added exception condition to continue to mark as transaction fail case.
+ // Checking session validity during exception is not valid
+ if (ar.exception == null
+ && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) {
logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
+ " rc=" + rc);
return;
}
mRadioAccessFamilyStatusCounter--;
- int id = rc.getPhoneId();
+ //rc.getPhoneId() moved to avoid Null Pointer Exception, since when exception occurs
+ //its expected rc is null.
if (ar.exception != null) {
- logd("onStartRadioCapabilityResponse: Error response session=" + rc.getSession());
- logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
- mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+ logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
+ //mSetRadioAccessFamilyStatus will be set anyway to SET_RC_STATUS_FAIL
+ // if either of them fail at issueFinish() method below,i.e. both phone id count
+ // is set to SET_RC_STATUS_FAIL.
mTransactionFailed = true;
} else {
+ int id = rc.getPhoneId();
logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED;
}
@@ -510,13 +490,20 @@
* @param msg obj field isa RadioCapability
*/
void onFinishRadioCapabilityResponse(Message msg) {
- RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
- if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
- logd("onFinishRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
- + " rc=" + rc);
- return;
- }
synchronized (mSetRadioAccessFamilyStatus) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
+ // Added exception condition on finish to continue to revert if exception occurred.
+ // Checking session validity during exception is not valid
+ if (ar.exception == null
+ && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) {
+ logd("onFinishRadioCapabilityResponse: Ignore session="
+ + mRadioCapabilitySessionId + " rc=" + rc);
+ return;
+ }
+ if (ar.exception != null) {
+ logd("onFinishRadioCapabilityResponse got exception=" + ar.exception);
+ }
logd(" onFinishRadioCapabilityResponse mRadioAccessFamilyStatusCounter="
+ mRadioAccessFamilyStatusCounter);
mRadioAccessFamilyStatusCounter--;
@@ -604,7 +591,6 @@
clearTransaction();
} else {
intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
-
// now revert.
mTransactionFailed = false;
RadioAccessFamily[] rafs = new RadioAccessFamily[mPhones.length];
@@ -631,6 +617,7 @@
}
if (isWakeLockHeld()) {
+ logd("clearTransaction:checking wakelock held and releasing");
mWakeLock.release();
}
}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 49e8297..5ecdfcb 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -16,6 +16,15 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
import static com.android.internal.telephony.RILConstants.*;
import android.annotation.NonNull;
@@ -41,10 +50,13 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.WorkSource;
import android.provider.Settings;
import android.sysprop.TelephonyProperties;
+import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.BarringInfo;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellInfo;
import android.telephony.CellSignalStrengthCdma;
@@ -54,6 +66,7 @@
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
@@ -66,18 +79,24 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyHistogram;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
import android.telephony.TelephonyManager.PrefNetworkMode;
import android.telephony.data.DataProfile;
import android.telephony.data.NetworkSliceInfo;
import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaInformationRecords;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
import com.android.internal.telephony.metrics.ModemRestartStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
@@ -91,8 +110,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
@@ -132,6 +153,9 @@
private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
/** @hide */
+ public static final HalVersion RADIO_HAL_VERSION_UNSUPPORTED = HalVersion.UNSUPPORTED;
+
+ /** @hide */
public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
/** @hide */
@@ -158,8 +182,11 @@
/** @hide */
public static final HalVersion RADIO_HAL_VERSION_2_0 = new HalVersion(2, 0);
- // IRadio version
- private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
+ /** @hide */
+ public static final HalVersion RADIO_HAL_VERSION_2_1 = new HalVersion(2, 1);
+
+ // Hal version
+ private Map<Integer, HalVersion> mHalVersion = new HashMap<>();
//***** Instance Variables
@@ -197,15 +224,9 @@
private static final String PROPERTY_IS_VONR_ENABLED = "persist.radio.is_vonr_enabled_";
- static final int RADIO_SERVICE = 0;
- static final int DATA_SERVICE = 1;
- static final int MESSAGING_SERVICE = 2;
- static final int MODEM_SERVICE = 3;
- static final int NETWORK_SERVICE = 4;
- static final int SIM_SERVICE = 5;
- static final int VOICE_SERVICE = 6;
- static final int MIN_SERVICE_IDX = RADIO_SERVICE;
- static final int MAX_SERVICE_IDX = VOICE_SERVICE;
+ public static final int MIN_SERVICE_IDX = HAL_SERVICE_RADIO;
+
+ public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS;
/**
* An array of sets that records if services are disabled in the HAL for a specific phone ID
@@ -232,6 +253,8 @@
private volatile IRadio mRadioProxy = null;
private DataResponse mDataResponse;
private DataIndication mDataIndication;
+ private ImsResponse mImsResponse;
+ private ImsIndication mImsIndication;
private MessagingResponse mMessagingResponse;
private MessagingIndication mMessagingIndication;
private ModemResponse mModemResponse;
@@ -361,11 +384,11 @@
case EVENT_AIDL_PROXY_DEAD:
int aidlService = msg.arg1;
- AtomicLong obj = (AtomicLong) msg.obj;
- riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msg.obj
+ long msgCookie = (long) msg.obj;
+ riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie
+ ", service = " + serviceToString(aidlService) + ", cookie = "
+ mServiceCookies.get(aidlService));
- if (obj.get() == mServiceCookies.get(aidlService).get()) {
+ if (msgCookie == mServiceCookies.get(aidlService).get()) {
mIsRadioProxyInitialized = false;
resetProxyAndRequestList(aidlService);
}
@@ -412,8 +435,8 @@
public void serviceDied(long cookie) {
// Deal with service going away
riljLog("serviceDied");
- mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, RADIO_SERVICE,
- 0 /* ignored arg2 */, cookie));
+ mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+ HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
}
}
@@ -427,8 +450,11 @@
public void linkToDeath(IBinder service) throws RemoteException {
if (service != null) {
+ riljLog("Linked to death for service " + serviceToString(mService));
mBinder = service;
mBinder.linkToDeath(this, (int) mServiceCookies.get(mService).incrementAndGet());
+ } else {
+ riljLoge("Unable to link to death for service " + serviceToString(mService));
}
}
@@ -443,13 +469,13 @@
public void binderDied() {
riljLog("Service " + serviceToString(mService) + " has died.");
mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_AIDL_PROXY_DEAD, mService,
- 0 /* ignored arg2 */, mServiceCookies.get(mService)));
+ 0 /* ignored arg2 */, mServiceCookies.get(mService).get()));
unlinkToDeath();
}
}
private synchronized void resetProxyAndRequestList(int service) {
- if (service == RADIO_SERVICE) {
+ if (service == HAL_SERVICE_RADIO) {
mRadioProxy = null;
} else {
mServiceProxies.get(service).clear();
@@ -466,7 +492,7 @@
// Clear request list on close
clearRequestList(RADIO_NOT_AVAILABLE, false);
- if (service == RADIO_SERVICE) {
+ if (service == HAL_SERVICE_RADIO) {
getRadioProxy(null);
} else {
getRadioServiceProxy(service, null);
@@ -495,13 +521,13 @@
// Disable HIDL service
if (mRadioProxy != null) {
riljLog("Disable HIDL service");
- mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId);
+ mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
}
mMockModem.bindAllMockModemService();
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
- if (service == RADIO_SERVICE) continue;
+ if (service == HAL_SERVICE_RADIO) continue;
int retryCount = 0;
IBinder binder;
@@ -536,14 +562,26 @@
if ((serviceName == null) || (!serviceBound)) {
if (serviceBound) riljLog("Unbinding to MockModemService");
- if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) {
- mDisabledRadioServices.get(RADIO_SERVICE).clear();
+ if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
+ mDisabledRadioServices.get(HAL_SERVICE_RADIO).clear();
}
if (mMockModem != null) {
- mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
mMockModem = null;
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+ if (service == HAL_SERVICE_RADIO) {
+ if (isRadioVersion2_0()) {
+ mHalVersion.put(service, RADIO_HAL_VERSION_2_0);
+ } else {
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+ }
+ } else {
+ if (isRadioServiceSupported(service)) {
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+ } else {
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
+ }
+ }
resetProxyAndRequestList(service);
}
}
@@ -585,7 +623,10 @@
/** Returns a {@link IRadio} instance or null if the service is not available. */
@VisibleForTesting
public synchronized IRadio getRadioProxy(Message result) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) return null;
+ if (mHalVersion.containsKey(HAL_SERVICE_RADIO)
+ && mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ return null;
+ }
if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null;
if (!mIsCellularSupported) {
if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
@@ -602,14 +643,14 @@
}
try {
- if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) {
+ if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
+ " is disabled");
} else {
try {
mRadioProxy = android.hardware.radio.V1_6.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_6;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_6);
} catch (NoSuchElementException e) {
}
@@ -617,7 +658,7 @@
try {
mRadioProxy = android.hardware.radio.V1_5.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_5;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_5);
} catch (NoSuchElementException e) {
}
}
@@ -626,7 +667,7 @@
try {
mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_4;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_4);
} catch (NoSuchElementException e) {
}
}
@@ -635,7 +676,7 @@
try {
mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_3;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_3);
} catch (NoSuchElementException e) {
}
}
@@ -644,7 +685,7 @@
try {
mRadioProxy = android.hardware.radio.V1_2.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_2;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_2);
} catch (NoSuchElementException e) {
}
}
@@ -653,7 +694,7 @@
try {
mRadioProxy = android.hardware.radio.V1_1.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_1;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_1);
} catch (NoSuchElementException e) {
}
}
@@ -662,7 +703,7 @@
try {
mRadioProxy = android.hardware.radio.V1_0.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_0;
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_0);
} catch (NoSuchElementException e) {
}
}
@@ -671,11 +712,11 @@
if (!mIsRadioProxyInitialized) {
mIsRadioProxyInitialized = true;
mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
- mServiceCookies.get(RADIO_SERVICE).incrementAndGet());
+ mServiceCookies.get(HAL_SERVICE_RADIO).incrementAndGet());
mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
}
} else {
- mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId);
+ mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
riljLoge("getRadioProxy: set mRadioProxy for "
+ HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
}
@@ -700,29 +741,32 @@
/**
* Returns a {@link RadioDataProxy}, {@link RadioMessagingProxy}, {@link RadioModemProxy},
- * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy}, or an empty {@link RadioServiceProxy}
- * if the service is not available.
+ * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy},
+ * {@link RadioImsProxy}, or null if the service is not available.
*/
@NonNull
public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass,
Message result) {
if (serviceClass == RadioDataProxy.class) {
- return (T) getRadioServiceProxy(DATA_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_DATA, result);
}
if (serviceClass == RadioMessagingProxy.class) {
- return (T) getRadioServiceProxy(MESSAGING_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_MESSAGING, result);
}
if (serviceClass == RadioModemProxy.class) {
- return (T) getRadioServiceProxy(MODEM_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_MODEM, result);
}
if (serviceClass == RadioNetworkProxy.class) {
- return (T) getRadioServiceProxy(NETWORK_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_NETWORK, result);
}
if (serviceClass == RadioSimProxy.class) {
- return (T) getRadioServiceProxy(SIM_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_SIM, result);
}
if (serviceClass == RadioVoiceProxy.class) {
- return (T) getRadioServiceProxy(VOICE_SERVICE, result);
+ return (T) getRadioServiceProxy(HAL_SERVICE_VOICE, result);
+ }
+ if (serviceClass == RadioImsProxy.class) {
+ return (T) getRadioServiceProxy(HAL_SERVICE_IMS, result);
}
riljLoge("getRadioServiceProxy: unrecognized " + serviceClass);
return null;
@@ -730,12 +774,15 @@
/**
* Returns a {@link RadioServiceProxy}, which is empty if the service is not available.
- * For RADIO_SERVICE, use {@link #getRadioProxy} instead, as this will always return null.
+ * For HAL_SERVICE_RADIO, use {@link #getRadioProxy} instead, as this will always return null.
*/
@VisibleForTesting
@NonNull
public synchronized RadioServiceProxy getRadioServiceProxy(int service, Message result) {
if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
+ if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) {
+ return mServiceProxies.get(service);
+ }
if (!mIsCellularSupported) {
if (RILJ_LOGV) riljLog("getRadioServiceProxy: Not calling getService(): wifi-only");
if (result != null) {
@@ -752,168 +799,190 @@
}
try {
- if (mDisabledRadioServices.get(service).contains(mPhoneId)) {
+ if (mMockModem == null && mDisabledRadioServices.get(service).contains(mPhoneId)) {
riljLoge("getRadioServiceProxy: " + serviceToString(service) + " for "
+ HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
} else {
IBinder binder;
switch (service) {
- case DATA_SERVICE:
+ case HAL_SERVICE_DATA:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.data.IRadioData.DESCRIPTOR + "/"
+ HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(DATA_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_DATA);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioDataProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioDataProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.data.IRadioData.Stub.asInterface(
- binder));
+ binder)));
}
break;
- case MESSAGING_SERVICE:
+ case HAL_SERVICE_MESSAGING:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR
+ "/" + HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(MESSAGING_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_MESSAGING);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioMessagingProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioMessagingProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.messaging.IRadioMessaging.Stub
- .asInterface(binder));
+ .asInterface(binder)));
}
break;
- case MODEM_SERVICE:
+ case HAL_SERVICE_MODEM:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.modem.IRadioModem.DESCRIPTOR + "/"
+ HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(MODEM_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_MODEM);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioModemProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioModemProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.modem.IRadioModem.Stub
- .asInterface(binder));
+ .asInterface(binder)));
}
break;
- case NETWORK_SERVICE:
+ case HAL_SERVICE_NETWORK:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.network.IRadioNetwork.DESCRIPTOR + "/"
+ HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(NETWORK_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_NETWORK);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioNetworkProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioNetworkProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.network.IRadioNetwork.Stub
- .asInterface(binder));
+ .asInterface(binder)));
}
break;
- case SIM_SERVICE:
+ case HAL_SERVICE_SIM:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.sim.IRadioSim.DESCRIPTOR + "/"
+ HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(SIM_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_SIM);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioSimProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioSimProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.sim.IRadioSim.Stub
- .asInterface(binder));
+ .asInterface(binder)));
}
break;
- case VOICE_SERVICE:
+ case HAL_SERVICE_VOICE:
if (mMockModem == null) {
binder = ServiceManager.waitForDeclaredService(
android.hardware.radio.voice.IRadioVoice.DESCRIPTOR + "/"
+ HIDL_SERVICE_NAME[mPhoneId]);
} else {
- binder = mMockModem.getServiceBinder(VOICE_SERVICE);
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_VOICE);
}
if (binder != null) {
- mRadioVersion = RADIO_HAL_VERSION_2_0;
- ((RadioVoiceProxy) serviceProxy).setAidl(mRadioVersion,
+ mHalVersion.put(service, ((RadioVoiceProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
android.hardware.radio.voice.IRadioVoice.Stub
- .asInterface(binder));
+ .asInterface(binder)));
+ }
+ break;
+ case HAL_SERVICE_IMS:
+ if (mMockModem == null) {
+ binder = ServiceManager.waitForDeclaredService(
+ android.hardware.radio.ims.IRadioIms.DESCRIPTOR + "/"
+ + HIDL_SERVICE_NAME[mPhoneId]);
+ } else {
+ binder = mMockModem.getServiceBinder(HAL_SERVICE_IMS);
+ }
+ if (binder != null) {
+ mHalVersion.put(service, ((RadioImsProxy) serviceProxy).setAidl(
+ mHalVersion.get(service),
+ android.hardware.radio.ims.IRadioIms.Stub
+ .asInterface(binder)));
}
break;
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_6;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_6);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_6.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_5;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_5);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_5.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_4;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_4);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_4.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_3;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_3);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_3.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_2;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_2);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_2.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_1;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_1);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_1.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
}
}
- if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+ if (serviceProxy.isEmpty()
+ && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
try {
- mRadioVersion = RADIO_HAL_VERSION_1_0;
- serviceProxy.setHidl(mRadioVersion,
+ mHalVersion.put(service, RADIO_HAL_VERSION_1_0);
+ serviceProxy.setHidl(mHalVersion.get(service),
android.hardware.radio.V1_0.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true));
} catch (NoSuchElementException e) {
@@ -923,57 +992,65 @@
if (!serviceProxy.isEmpty()) {
if (serviceProxy.isAidl()) {
switch (service) {
- case DATA_SERVICE:
+ case HAL_SERVICE_DATA:
mDeathRecipients.get(service).linkToDeath(
((RadioDataProxy) serviceProxy).getAidl().asBinder());
((RadioDataProxy) serviceProxy).getAidl().setResponseFunctions(
mDataResponse, mDataIndication);
break;
- case MESSAGING_SERVICE:
+ case HAL_SERVICE_MESSAGING:
mDeathRecipients.get(service).linkToDeath(
((RadioMessagingProxy) serviceProxy).getAidl().asBinder());
((RadioMessagingProxy) serviceProxy).getAidl().setResponseFunctions(
mMessagingResponse, mMessagingIndication);
break;
- case MODEM_SERVICE:
+ case HAL_SERVICE_MODEM:
mDeathRecipients.get(service).linkToDeath(
((RadioModemProxy) serviceProxy).getAidl().asBinder());
((RadioModemProxy) serviceProxy).getAidl().setResponseFunctions(
mModemResponse, mModemIndication);
break;
- case NETWORK_SERVICE:
+ case HAL_SERVICE_NETWORK:
mDeathRecipients.get(service).linkToDeath(
((RadioNetworkProxy) serviceProxy).getAidl().asBinder());
((RadioNetworkProxy) serviceProxy).getAidl().setResponseFunctions(
mNetworkResponse, mNetworkIndication);
break;
- case SIM_SERVICE:
+ case HAL_SERVICE_SIM:
mDeathRecipients.get(service).linkToDeath(
((RadioSimProxy) serviceProxy).getAidl().asBinder());
((RadioSimProxy) serviceProxy).getAidl().setResponseFunctions(
mSimResponse, mSimIndication);
break;
- case VOICE_SERVICE:
+ case HAL_SERVICE_VOICE:
mDeathRecipients.get(service).linkToDeath(
((RadioVoiceProxy) serviceProxy).getAidl().asBinder());
((RadioVoiceProxy) serviceProxy).getAidl().setResponseFunctions(
mVoiceResponse, mVoiceIndication);
break;
+ case HAL_SERVICE_IMS:
+ mDeathRecipients.get(service).linkToDeath(
+ ((RadioImsProxy) serviceProxy).getAidl().asBinder());
+ ((RadioImsProxy) serviceProxy).getAidl().setResponseFunctions(
+ mImsResponse, mImsIndication);
+ break;
}
} else {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ if (mHalVersion.get(service)
+ .greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
throw new AssertionError("serviceProxy shouldn't be HIDL with HAL 2.0");
}
if (!mIsRadioProxyInitialized) {
mIsRadioProxyInitialized = true;
serviceProxy.getHidl().linkToDeath(mRadioProxyDeathRecipient,
- mServiceCookies.get(service).incrementAndGet());
+ mServiceCookies.get(HAL_SERVICE_RADIO).incrementAndGet());
serviceProxy.getHidl().setResponseFunctions(
mRadioResponse, mRadioIndication);
}
}
} else {
mDisabledRadioServices.get(service).add(mPhoneId);
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
+ HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
}
@@ -1002,7 +1079,7 @@
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
if (active) {
// Try to connect to RIL services and set response functions.
- if (service == RADIO_SERVICE) {
+ if (service == HAL_SERVICE_RADIO) {
getRadioProxy(null);
} else {
getRadioServiceProxy(service, null);
@@ -1043,7 +1120,11 @@
mRadioBugDetector = new RadioBugDetector(context, mPhoneId);
}
try {
- if (isRadioVersion2_0()) mRadioVersion = RADIO_HAL_VERSION_2_0;
+ if (isRadioVersion2_0()) {
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_2_0);
+ } else {
+ mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_UNKNOWN);
+ }
} catch (SecurityException ex) {
/* TODO(b/211920208): instead of the following workaround (guessing if we're in a test
* based on proxies being populated), mock ServiceManager to not throw
@@ -1059,6 +1140,8 @@
mRadioIndication = new RadioIndication(this);
mDataResponse = new DataResponse(this);
mDataIndication = new DataIndication(this);
+ mImsResponse = new ImsResponse(this);
+ mImsIndication = new ImsIndication(this);
mMessagingResponse = new MessagingResponse(this);
mMessagingIndication = new MessagingIndication(this);
mModemResponse = new ModemResponse(this);
@@ -1072,19 +1155,33 @@
mRilHandler = new RilHandler();
mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
- if (service != RADIO_SERVICE) {
+ if (service != HAL_SERVICE_RADIO) {
+ try {
+ if (isRadioServiceSupported(service)) {
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+ } else {
+ mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
+ }
+ } catch (SecurityException ex) {
+ /* TODO(b/211920208): instead of the following workaround (guessing if
+ * we're in a test based on proxies being populated), mock ServiceManager
+ * to not throw SecurityException and return correct value based on what
+ * HAL we're testing. */
+ if (proxies == null) throw ex;
+ }
mDeathRecipients.put(service, new BinderServiceDeathRecipient(service));
}
mDisabledRadioServices.put(service, new HashSet<>());
mServiceCookies.put(service, new AtomicLong(0));
}
if (proxies == null) {
- mServiceProxies.put(DATA_SERVICE, new RadioDataProxy());
- mServiceProxies.put(MESSAGING_SERVICE, new RadioMessagingProxy());
- mServiceProxies.put(MODEM_SERVICE, new RadioModemProxy());
- mServiceProxies.put(NETWORK_SERVICE, new RadioNetworkProxy());
- mServiceProxies.put(SIM_SERVICE, new RadioSimProxy());
- mServiceProxies.put(VOICE_SERVICE, new RadioVoiceProxy());
+ mServiceProxies.put(HAL_SERVICE_DATA, new RadioDataProxy());
+ mServiceProxies.put(HAL_SERVICE_MESSAGING, new RadioMessagingProxy());
+ mServiceProxies.put(HAL_SERVICE_MODEM, new RadioModemProxy());
+ mServiceProxies.put(HAL_SERVICE_NETWORK, new RadioNetworkProxy());
+ mServiceProxies.put(HAL_SERVICE_SIM, new RadioSimProxy());
+ mServiceProxies.put(HAL_SERVICE_VOICE, new RadioVoiceProxy());
+ mServiceProxies.put(HAL_SERVICE_IMS, new RadioImsProxy());
} else {
mServiceProxies = proxies;
}
@@ -1113,7 +1210,7 @@
// Set radio callback; needed to set RadioIndication callback (should be done after
// wakelock stuff is initialized above as callbacks are received on separate binder threads)
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
- if (service == RADIO_SERVICE) {
+ if (service == HAL_SERVICE_RADIO) {
getRadioProxy(null);
} else {
if (proxies == null) {
@@ -1121,30 +1218,62 @@
getRadioServiceProxy(service, null);
}
}
- }
- if (RILJ_LOGD) {
- riljLog("Radio HAL version: " + mRadioVersion);
+ if (RILJ_LOGD) {
+ riljLog("HAL version of " + serviceToString(service)
+ + ": " + mHalVersion.get(service));
+ }
}
}
private boolean isRadioVersion2_0() {
- final String[] serviceNames = new String[] {
- android.hardware.radio.data.IRadioData.DESCRIPTOR,
- android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR,
- android.hardware.radio.modem.IRadioModem.DESCRIPTOR,
- android.hardware.radio.network.IRadioNetwork.DESCRIPTOR,
- android.hardware.radio.sim.IRadioSim.DESCRIPTOR,
- android.hardware.radio.voice.IRadioVoice.DESCRIPTOR,
- };
- for (String serviceName : serviceNames) {
- if (ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) {
+ for (int service = HAL_SERVICE_DATA; service <= MAX_SERVICE_IDX; service++) {
+ if (isRadioServiceSupported(service)) {
return true;
}
}
return false;
}
+ private boolean isRadioServiceSupported(int service) {
+ String serviceName = "";
+
+ if (service == HAL_SERVICE_RADIO) {
+ return true;
+ }
+
+ switch (service) {
+ case HAL_SERVICE_DATA:
+ serviceName = android.hardware.radio.data.IRadioData.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_MESSAGING:
+ serviceName = android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_MODEM:
+ serviceName = android.hardware.radio.modem.IRadioModem.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_NETWORK:
+ serviceName = android.hardware.radio.network.IRadioNetwork.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_SIM:
+ serviceName = android.hardware.radio.sim.IRadioSim.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_VOICE:
+ serviceName = android.hardware.radio.voice.IRadioVoice.DESCRIPTOR;
+ break;
+ case HAL_SERVICE_IMS:
+ serviceName = android.hardware.radio.ims.IRadioIms.DESCRIPTOR;
+ break;
+ }
+
+ if (!serviceName.equals("")
+ && ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) {
+ return true;
+ }
+
+ return false;
+ }
+
private boolean isRadioBugDetectionEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_RADIO_BUG_DETECTION, 1) != 0;
@@ -1162,6 +1291,8 @@
private void addRequest(RILRequest rr) {
acquireWakeLock(rr, FOR_WAKELOCK);
+ Trace.asyncTraceForTrackBegin(
+ Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial);
synchronized (mRequestList) {
rr.mStartTimeMs = SystemClock.elapsedRealtime();
mRequestList.append(rr.mSerial, rr);
@@ -1202,7 +1333,7 @@
try {
simProxy.getIccCardStatus(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getIccCardStatus", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getIccCardStatus", e);
}
}
}
@@ -1249,7 +1380,7 @@
simProxy.supplyIccPinForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPinForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPinForApp", e);
}
}
}
@@ -1276,7 +1407,7 @@
RILUtils.convertNullToEmptyString(newPin),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPukForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPukForApp", e);
}
}
}
@@ -1302,7 +1433,7 @@
simProxy.supplyIccPin2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPin2ForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPin2ForApp", e);
}
}
}
@@ -1329,7 +1460,7 @@
RILUtils.convertNullToEmptyString(newPin2),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPuk2ForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPuk2ForApp", e);
}
}
}
@@ -1357,7 +1488,7 @@
RILUtils.convertNullToEmptyString(newPin),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPinForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPinForApp", e);
}
}
}
@@ -1385,7 +1516,7 @@
RILUtils.convertNullToEmptyString(newPin2),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPin2ForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPin2ForApp", e);
}
}
}
@@ -1407,7 +1538,7 @@
RILUtils.convertNullToEmptyString(netpin));
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(
- NETWORK_SERVICE, "supplyNetworkDepersonalization", e);
+ HAL_SERVICE_NETWORK, "supplyNetworkDepersonalization", e);
}
}
}
@@ -1417,7 +1548,7 @@
Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, result,
mRILDefaultWorkSource);
@@ -1430,7 +1561,7 @@
simProxy.supplySimDepersonalization(rr.mSerial, persoType,
RILUtils.convertNullToEmptyString(controlKey));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "supplySimDepersonalization", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplySimDepersonalization", e);
}
} else {
if (PersoSubState.PERSOSUBSTATE_SIM_NETWORK == persoType) {
@@ -1462,7 +1593,7 @@
try {
voiceProxy.getCurrentCalls(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCurrentCalls", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCurrentCalls", e);
}
}
}
@@ -1478,7 +1609,7 @@
public void enableModem(boolean enable, Message result) {
RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
if (modemProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+ if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result, mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -1489,7 +1620,8 @@
try {
modemProxy.enableModem(rr.mSerial, enable);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "enableModem", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM,
+ "enableModem", e);
}
} else {
if (RILJ_LOGV) riljLog("enableModem: not supported.");
@@ -1506,7 +1638,7 @@
Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, result,
mRILDefaultWorkSource);
@@ -1518,7 +1650,8 @@
try {
networkProxy.setSystemSelectionChannels(rr.mSerial, specifiers);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSystemSelectionChannels", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "setSystemSelectionChannels", e);
}
} else {
if (RILJ_LOGV) riljLog("setSystemSelectionChannels: not supported.");
@@ -1534,7 +1667,7 @@
public void getSystemSelectionChannels(Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, result,
mRILDefaultWorkSource);
@@ -1546,7 +1679,8 @@
try {
networkProxy.getSystemSelectionChannels(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSystemSelectionChannels", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "getSystemSelectionChannels", e);
}
} else {
if (RILJ_LOGV) riljLog("getSystemSelectionChannels: not supported.");
@@ -1562,7 +1696,7 @@
public void getModemStatus(Message result) {
RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
if (modemProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+ if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result,
mRILDefaultWorkSource);
@@ -1573,7 +1707,7 @@
try {
modemProxy.getModemStackStatus(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemStatus", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemStatus", e);
}
} else {
if (RILJ_LOGV) riljLog("getModemStatus: not supported.");
@@ -1588,7 +1722,8 @@
@Override
public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,
boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
- if (isEmergencyCall && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)
+ if (isEmergencyCall
+ && mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)
&& emergencyNumberInfo != null) {
emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode,
uusInfo, result);
@@ -1606,7 +1741,7 @@
try {
voiceProxy.dial(rr.mSerial, address, clirMode, uusInfo);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "dial", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "dial", e);
}
}
}
@@ -1615,7 +1750,7 @@
boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
if (voiceProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+ if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result,
mRILDefaultWorkSource);
@@ -1628,7 +1763,7 @@
voiceProxy.emergencyDial(rr.mSerial, RILUtils.convertNullToEmptyString(address),
emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "emergencyDial", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "emergencyDial", e);
}
} else {
riljLoge("emergencyDial is not supported with 1.4 below IRadio");
@@ -1647,13 +1782,13 @@
RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result, mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ " aid = " + aid);
}
try {
simProxy.getImsiForApp(rr.mSerial, RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getImsiForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getImsiForApp", e);
}
}
}
@@ -1672,7 +1807,7 @@
try {
voiceProxy.hangup(rr.mSerial, gsmIndex);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangup", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangup", e);
}
}
}
@@ -1692,7 +1827,7 @@
try {
voiceProxy.hangupWaitingOrBackground(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangupWaitingOrBackground", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangupWaitingOrBackground", e);
}
}
}
@@ -1713,7 +1848,7 @@
voiceProxy.hangupForegroundResumeBackground(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(
- VOICE_SERVICE, "hangupForegroundResumeBackground", e);
+ HAL_SERVICE_VOICE, "hangupForegroundResumeBackground", e);
}
}
}
@@ -1732,7 +1867,8 @@
try {
voiceProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "switchWaitingOrHoldingAndActive", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE,
+ "switchWaitingOrHoldingAndActive", e);
}
}
}
@@ -1750,7 +1886,7 @@
try {
voiceProxy.conference(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "conference", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "conference", e);
}
}
}
@@ -1768,7 +1904,7 @@
try {
voiceProxy.rejectCall(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "rejectCall", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "rejectCall", e);
}
}
}
@@ -1787,7 +1923,7 @@
try {
voiceProxy.getLastCallFailCause(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getLastCallFailCause", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getLastCallFailCause", e);
}
}
}
@@ -1806,7 +1942,7 @@
try {
networkProxy.getSignalStrength(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSignalStrength", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getSignalStrength", e);
}
}
}
@@ -1830,7 +1966,7 @@
try {
networkProxy.getVoiceRegistrationState(rr.mSerial, overrideHalVersion);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRegistrationState", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRegistrationState", e);
}
}
}
@@ -1854,7 +1990,7 @@
try {
networkProxy.getDataRegistrationState(rr.mSerial, overrideHalVersion);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getDataRegistrationState", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getDataRegistrationState", e);
}
}
}
@@ -1872,7 +2008,7 @@
try {
networkProxy.getOperator(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getOperator", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getOperator", e);
}
}
}
@@ -1895,7 +2031,7 @@
modemProxy.setRadioPower(rr.mSerial, on, forEmergencyCall,
preferredForEmergencyCall);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioPower", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioPower", e);
}
}
}
@@ -1914,7 +2050,7 @@
try {
voiceProxy.sendDtmf(rr.mSerial, c + "");
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendDtmf", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendDtmf", e);
}
}
}
@@ -1936,7 +2072,7 @@
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMS", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMS", e);
}
}
}
@@ -1977,7 +2113,7 @@
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMSExpectMore", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMSExpectMore", e);
}
}
}
@@ -2007,8 +2143,21 @@
dataProxy.setupDataCall(rr.mSerial, mPhoneId, accessNetworkType, dataProfile,
isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
trafficDescriptor, matchAllRuleAllowed);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "setupDataCall", e);
+ } catch (RemoteException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setupDataCall", e);
+ } catch (RuntimeException e) {
+ riljLoge("setupDataCall RuntimeException: " + e);
+ int error = RadioError.SYSTEM_ERR;
+ int responseType = RadioResponseType.SOLICITED;
+ processResponseInternal(HAL_SERVICE_DATA, rr.mSerial, error, responseType);
+ processResponseDoneInternal(rr, error, responseType, null);
+ }
+ } else {
+ riljLoge("setupDataCall: DataProxy is empty");
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+ result.sendToTarget();
}
}
}
@@ -2045,7 +2194,7 @@
RILUtils.convertNullToEmptyString(pin2),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "iccIoForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccIoForApp", e);
}
}
}
@@ -2067,7 +2216,7 @@
try {
voiceProxy.sendUssd(rr.mSerial, RILUtils.convertNullToEmptyString(ussd));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendUssd", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendUssd", e);
}
}
}
@@ -2086,7 +2235,7 @@
try {
voiceProxy.cancelPendingUssd(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "cancelPendingUssd", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "cancelPendingUssd", e);
}
}
}
@@ -2104,7 +2253,7 @@
try {
voiceProxy.getClir(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClir", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClir", e);
}
}
}
@@ -2123,7 +2272,7 @@
try {
voiceProxy.setClir(rr.mSerial, clirMode);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setClir", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setClir", e);
}
}
}
@@ -2144,7 +2293,7 @@
try {
voiceProxy.getCallForwardStatus(rr.mSerial, cfReason, serviceClass, number);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallForwardStatus", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallForwardStatus", e);
}
}
}
@@ -2167,7 +2316,7 @@
voiceProxy.setCallForward(
rr.mSerial, action, cfReason, serviceClass, number, timeSeconds);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallForward", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallForward", e);
}
}
}
@@ -2187,7 +2336,7 @@
try {
voiceProxy.getCallWaiting(rr.mSerial, serviceClass);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallWaiting", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallWaiting", e);
}
}
}
@@ -2207,7 +2356,7 @@
try {
voiceProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallWaiting", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallWaiting", e);
}
}
}
@@ -2228,7 +2377,7 @@
try {
messagingProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
"acknowledgeLastIncomingGsmSms", e);
}
}
@@ -2248,7 +2397,7 @@
voiceProxy.acceptCall(rr.mSerial);
mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "acceptCall", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "acceptCall", e);
}
}
}
@@ -2270,7 +2419,7 @@
dataProxy.deactivateDataCall(rr.mSerial, cid, reason);
mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "deactivateDataCall", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "deactivateDataCall", e);
}
}
}
@@ -2301,7 +2450,7 @@
RILUtils.convertNullToEmptyString(password),
serviceClass, RILUtils.convertNullToEmptyString(appId));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getFacilityLockForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getFacilityLockForApp", e);
}
}
}
@@ -2332,7 +2481,7 @@
RILUtils.convertNullToEmptyString(password), serviceClass,
RILUtils.convertNullToEmptyString(appId));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setFacilityLockForApp", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setFacilityLockForApp", e);
}
}
}
@@ -2357,7 +2506,7 @@
RILUtils.convertNullToEmptyString(oldPwd),
RILUtils.convertNullToEmptyString(newPwd));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "changeBarringPassword", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "changeBarringPassword", e);
}
}
}
@@ -2376,7 +2525,7 @@
try {
networkProxy.getNetworkSelectionMode(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getNetworkSelectionMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getNetworkSelectionMode", e);
}
}
}
@@ -2396,7 +2545,7 @@
networkProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(
- NETWORK_SERVICE, "setNetworkSelectionModeAutomatic", e);
+ HAL_SERVICE_NETWORK, "setNetworkSelectionModeAutomatic", e);
}
}
}
@@ -2417,7 +2566,8 @@
networkProxy.setNetworkSelectionModeManual(rr.mSerial,
RILUtils.convertNullToEmptyString(operatorNumeric), ran);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setNetworkSelectionModeManual", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "setNetworkSelectionModeManual", e);
}
}
}
@@ -2436,7 +2586,7 @@
try {
networkProxy.getAvailableNetworks(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAvailableNetworks", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getAvailableNetworks", e);
}
}
}
@@ -2451,7 +2601,7 @@
public void startNetworkScan(NetworkScanRequest networkScanRequest, Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN);
if (RILJ_LOGD) {
riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion);
@@ -2468,7 +2618,7 @@
networkProxy.startNetworkScan(rr.mSerial, networkScanRequest, overrideHalVersion,
result);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "startNetworkScan", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "startNetworkScan", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNetworkScan: REQUEST_NOT_SUPPORTED");
@@ -2484,7 +2634,7 @@
public void stopNetworkScan(Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
mRILDefaultWorkSource);
@@ -2495,7 +2645,7 @@
try {
networkProxy.stopNetworkScan(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "stopNetworkScan", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "stopNetworkScan", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNetworkScan: REQUEST_NOT_SUPPORTED");
@@ -2521,7 +2671,7 @@
try {
voiceProxy.startDtmf(rr.mSerial, c + "");
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "startDtmf", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "startDtmf", e);
}
}
}
@@ -2539,7 +2689,7 @@
try {
voiceProxy.stopDtmf(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "stopDtmf", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "stopDtmf", e);
}
}
}
@@ -2559,7 +2709,7 @@
try {
voiceProxy.separateConnection(rr.mSerial, gsmIndex);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "separateConnection", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "separateConnection", e);
}
}
}
@@ -2578,7 +2728,7 @@
try {
modemProxy.getBasebandVersion(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getBasebandVersion", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getBasebandVersion", e);
}
}
}
@@ -2597,7 +2747,7 @@
try {
voiceProxy.setMute(rr.mSerial, enableMute);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setMute", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setMute", e);
}
}
}
@@ -2615,7 +2765,7 @@
try {
voiceProxy.getMute(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getMute", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getMute", e);
}
}
}
@@ -2633,7 +2783,7 @@
try {
voiceProxy.getClip(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClip", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClip", e);
}
}
}
@@ -2661,7 +2811,7 @@
try {
dataProxy.getDataCallList(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "getDataCallList", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getDataCallList", e);
}
}
}
@@ -2692,7 +2842,8 @@
try {
networkProxy.setSuppServiceNotifications(rr.mSerial, enable);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSuppServiceNotifications", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "setSuppServiceNotifications", e);
}
}
}
@@ -2715,7 +2866,7 @@
RILUtils.convertNullToEmptyString(smsc),
RILUtils.convertNullToEmptyString(pdu));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToSim", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToSim", e);
}
}
}
@@ -2736,7 +2887,7 @@
try {
messagingProxy.deleteSmsOnSim(rr.mSerial, index);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnSim", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnSim", e);
}
}
}
@@ -2755,7 +2906,7 @@
try {
networkProxy.setBandMode(rr.mSerial, bandMode);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setBandMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setBandMode", e);
}
}
}
@@ -2774,7 +2925,7 @@
try {
networkProxy.getAvailableBandModes(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryAvailableBandMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "queryAvailableBandMode", e);
}
}
}
@@ -2794,7 +2945,7 @@
try {
simProxy.sendEnvelope(rr.mSerial, RILUtils.convertNullToEmptyString(contents));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelope", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelope", e);
}
}
}
@@ -2816,7 +2967,7 @@
simProxy.sendTerminalResponseToSim(rr.mSerial,
RILUtils.convertNullToEmptyString(contents));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "sendTerminalResponse", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendTerminalResponse", e);
}
}
}
@@ -2837,7 +2988,7 @@
simProxy.sendEnvelopeWithStatus(rr.mSerial,
RILUtils.convertNullToEmptyString(contents));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelopeWithStatus", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelopeWithStatus", e);
}
}
}
@@ -2856,7 +3007,7 @@
try {
voiceProxy.explicitCallTransfer(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "explicitCallTransfer", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "explicitCallTransfer", e);
}
}
}
@@ -2878,7 +3029,7 @@
try {
networkProxy.setPreferredNetworkTypeBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setPreferredNetworkType", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setPreferredNetworkType", e);
}
}
}
@@ -2897,7 +3048,7 @@
try {
networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getPreferredNetworkType", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getPreferredNetworkType", e);
}
}
}
@@ -2907,7 +3058,7 @@
@TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (!networkProxy.isEmpty()) {
- if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).less(RADIO_HAL_VERSION_1_6)) {
// For older HAL, redirects the call to setPreferredNetworkType.
setPreferredNetworkType(
RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result);
@@ -2924,7 +3075,8 @@
try {
networkProxy.setAllowedNetworkTypesBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setAllowedNetworkTypeBitmask", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "setAllowedNetworkTypeBitmask", e);
}
}
}
@@ -2943,7 +3095,8 @@
try {
networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAllowedNetworkTypeBitmask", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "getAllowedNetworkTypeBitmask", e);
}
}
}
@@ -2963,7 +3116,7 @@
try {
networkProxy.setLocationUpdates(rr.mSerial, enable);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setLocationUpdates", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setLocationUpdates", e);
}
}
}
@@ -2975,7 +3128,7 @@
public void isNrDualConnectivityEnabled(Message result, WorkSource workSource) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result,
getDefaultWorkSourceIfInvalid(workSource));
@@ -2986,7 +3139,8 @@
try {
networkProxy.isNrDualConnectivityEnabled(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "isNrDualConnectivityEnabled", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "isNrDualConnectivityEnabled", e);
}
} else {
if (RILJ_LOGD) {
@@ -3016,7 +3170,7 @@
WorkSource workSource) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result,
getDefaultWorkSourceIfInvalid(workSource));
@@ -3028,7 +3182,7 @@
try {
networkProxy.setNrDualConnectivityState(rr.mSerial, (byte) nrDualConnectivityState);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "enableNrDualConnectivity", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "enableNrDualConnectivity", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableNrDualConnectivity: REQUEST_NOT_SUPPORTED");
@@ -3054,7 +3208,7 @@
@Override
public void isVoNrEnabled(Message result, WorkSource workSource) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
if (!voiceProxy.isEmpty()) {
RILRequest rr = obtainRequest(RIL_REQUEST_IS_VONR_ENABLED , result,
@@ -3067,7 +3221,7 @@
try {
voiceProxy.isVoNrEnabled(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "isVoNrEnabled", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "isVoNrEnabled", e);
}
}
} else {
@@ -3087,7 +3241,7 @@
public void setVoNrEnabled(boolean enabled, Message result, WorkSource workSource) {
setVoNrEnabled(enabled);
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
if (!voiceProxy.isEmpty()) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_VONR, result,
@@ -3100,7 +3254,7 @@
try {
voiceProxy.setVoNrEnabled(rr.mSerial, enabled);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setVoNrEnabled", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setVoNrEnabled", e);
}
}
} else {
@@ -3132,7 +3286,7 @@
try {
simProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setCdmaSubscriptionSource", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setCdmaSubscriptionSource", e);
}
}
}
@@ -3151,7 +3305,8 @@
try {
networkProxy.getCdmaRoamingPreference(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryCdmaRoamingPreference", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "queryCdmaRoamingPreference", e);
}
}
}
@@ -3171,7 +3326,7 @@
try {
networkProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCdmaRoamingPreference", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCdmaRoamingPreference", e);
}
}
}
@@ -3190,7 +3345,7 @@
try {
voiceProxy.getTtyMode(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getTtyMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getTtyMode", e);
}
}
}
@@ -3209,7 +3364,7 @@
try {
voiceProxy.setTtyMode(rr.mSerial, ttyMode);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setTtyMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setTtyMode", e);
}
}
}
@@ -3229,7 +3384,7 @@
try {
voiceProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "setPreferredVoicePrivacy", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setPreferredVoicePrivacy", e);
}
}
}
@@ -3248,7 +3403,7 @@
try {
voiceProxy.getPreferredVoicePrivacy(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "getPreferredVoicePrivacy", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getPreferredVoicePrivacy", e);
}
}
}
@@ -3268,7 +3423,7 @@
voiceProxy.sendCdmaFeatureCode(rr.mSerial,
RILUtils.convertNullToEmptyString(featureCode));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendCdmaFeatureCode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendCdmaFeatureCode", e);
}
}
}
@@ -3289,7 +3444,7 @@
voiceProxy.sendBurstDtmf(rr.mSerial, RILUtils.convertNullToEmptyString(dtmfString),
on, off);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendBurstDtmf", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendBurstDtmf", e);
}
}
}
@@ -3309,13 +3464,13 @@
try {
messagingProxy.sendCdmaSmsExpectMore(rr.mSerial, pdu);
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mHalVersion.get(HAL_SERVICE_MESSAGING).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
SmsSession.Event.Format.SMS_FORMAT_3GPP2,
getOutgoingSmsMessageId(result));
}
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSMSExpectMore", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSMSExpectMore", e);
}
}
}
@@ -3337,7 +3492,7 @@
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSms", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSms", e);
}
}
}
@@ -3358,7 +3513,7 @@
try {
messagingProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, success, cause);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
"acknowledgeLastIncomingCdmaSms", e);
}
}
@@ -3379,7 +3534,7 @@
try {
messagingProxy.getGsmBroadcastConfig(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getGsmBroadcastConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getGsmBroadcastConfig", e);
}
}
}
@@ -3403,7 +3558,7 @@
try {
messagingProxy.setGsmBroadcastConfig(rr.mSerial, config);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setGsmBroadcastConfig", e);
}
}
}
@@ -3424,7 +3579,8 @@
try {
messagingProxy.setGsmBroadcastActivation(rr.mSerial, activate);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastActivation", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
+ "setGsmBroadcastActivation", e);
}
}
}
@@ -3444,7 +3600,7 @@
try {
messagingProxy.getCdmaBroadcastConfig(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getCdmaBroadcastConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getCdmaBroadcastConfig", e);
}
}
}
@@ -3468,7 +3624,7 @@
try {
messagingProxy.setCdmaBroadcastConfig(rr.mSerial, configs);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setCdmaBroadcastConfig", e);
}
}
}
@@ -3489,7 +3645,8 @@
try {
messagingProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastActivation", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
+ "setCdmaBroadcastActivation", e);
}
}
}
@@ -3508,7 +3665,7 @@
try {
simProxy.getCdmaSubscription(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscription", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscription", e);
}
}
}
@@ -3529,7 +3686,7 @@
try {
messagingProxy.writeSmsToRuim(rr.mSerial, status, pdu);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToRuim", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToRuim", e);
}
}
}
@@ -3550,7 +3707,7 @@
try {
messagingProxy.deleteSmsOnRuim(rr.mSerial, index);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnRuim", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnRuim", e);
}
}
}
@@ -3569,7 +3726,41 @@
try {
modemProxy.getDeviceIdentity(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getDeviceIdentity", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getDeviceIdentity", e);
+ }
+ }
+ }
+
+ @Override
+ public void getImei(Message result) {
+ RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
+ if (modemProxy.isEmpty()) {
+ if (RILJ_LOGD) {
+ Rlog.e(RILJ_LOG_TAG, "getImei: modemProxy is Empty");
+ }
+ return;
+ }
+ if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IMEI, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ modemProxy.getImei(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getImei", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.e(RILJ_LOG_TAG, "getImei: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
}
}
}
@@ -3588,7 +3779,7 @@
try {
voiceProxy.exitEmergencyCallbackMode(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(VOICE_SERVICE, "exitEmergencyCallbackMode", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "exitEmergencyCallbackMode", e);
}
}
}
@@ -3608,7 +3799,7 @@
try {
messagingProxy.getSmscAddress(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getSmscAddress", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getSmscAddress", e);
}
}
}
@@ -3630,7 +3821,7 @@
messagingProxy.setSmscAddress(rr.mSerial,
RILUtils.convertNullToEmptyString(address));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setSmscAddress", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setSmscAddress", e);
}
}
}
@@ -3651,7 +3842,7 @@
try {
messagingProxy.reportSmsMemoryStatus(rr.mSerial, available);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "reportSmsMemoryStatus", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "reportSmsMemoryStatus", e);
}
}
}
@@ -3670,7 +3861,7 @@
try {
simProxy.reportStkServiceIsRunning(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "reportStkServiceIsRunning", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "reportStkServiceIsRunning", e);
}
}
}
@@ -3689,7 +3880,7 @@
try {
simProxy.getCdmaSubscriptionSource(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscriptionSource", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscriptionSource", e);
}
}
}
@@ -3711,7 +3902,7 @@
messagingProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
RILUtils.convertNullToEmptyString(ackPdu));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
"acknowledgeIncomingGsmSmsWithPdu", e);
}
}
@@ -3731,7 +3922,7 @@
try {
networkProxy.getVoiceRadioTechnology(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRadioTechnology", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRadioTechnology", e);
}
}
}
@@ -3750,7 +3941,7 @@
try {
networkProxy.getCellInfoList(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getCellInfoList", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getCellInfoList", e);
}
}
}
@@ -3770,7 +3961,7 @@
try {
networkProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCellInfoListRate", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCellInfoListRate", e);
}
}
}
@@ -3790,7 +3981,7 @@
try {
dataProxy.setInitialAttachApn(rr.mSerial, dataProfile, isRoaming);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "setInitialAttachApn", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setInitialAttachApn", e);
}
}
}
@@ -3809,7 +4000,7 @@
try {
networkProxy.getImsRegistrationState(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getImsRegistrationState", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getImsRegistrationState", e);
}
}
}
@@ -3832,7 +4023,7 @@
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsGsmSms", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsGsmSms", e);
}
}
}
@@ -3854,7 +4045,7 @@
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsCdmaSms", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsCdmaSms", e);
}
}
}
@@ -3882,7 +4073,7 @@
simProxy.iccTransmitApduBasicChannel(
rr.mSerial, cla, instruction, p1, p2, p3, data);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduBasicChannel", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduBasicChannel", e);
}
}
}
@@ -3907,13 +4098,13 @@
simProxy.iccOpenLogicalChannel(rr.mSerial, RILUtils.convertNullToEmptyString(aid),
p2);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "iccOpenLogicalChannel", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccOpenLogicalChannel", e);
}
}
}
@Override
- public void iccCloseLogicalChannel(int channel, Message result) {
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (!simProxy.isEmpty()) {
RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result,
@@ -3921,20 +4112,19 @@
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
- + " channel = " + channel);
+ + " channel = " + channel + " isEs10 = " + isEs10);
}
-
try {
- simProxy.iccCloseLogicalChannel(rr.mSerial, channel);
+ simProxy.iccCloseLogicalChannel(rr.mSerial, channel, isEs10);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "iccCloseLogicalChannel", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccCloseLogicalChannel", e);
}
}
}
@Override
public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1, int p2,
- int p3, String data, Message result) {
+ int p3, String data, boolean isEs10Command, Message result) {
if (channel <= 0) {
throw new RuntimeException(
"Invalid channel in iccTransmitApduLogicalChannel: " + channel);
@@ -3951,6 +4141,7 @@
+ String.format(" channel = %d", channel)
+ String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
+ String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
+ + " isEs10Command = " + isEs10Command
+ " data = " + data);
} else {
riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
@@ -3959,9 +4150,9 @@
try {
simProxy.iccTransmitApduLogicalChannel(
- rr.mSerial, channel, cla, instruction, p1, p2, p3, data);
+ rr.mSerial, channel, cla, instruction, p1, p2, p3, data, isEs10Command);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduLogicalChannel", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduLogicalChannel", e);
}
}
}
@@ -3981,7 +4172,7 @@
try {
modemProxy.nvReadItem(rr.mSerial, itemID);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvReadItem", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvReadItem", e);
}
}
}
@@ -4002,7 +4193,7 @@
modemProxy.nvWriteItem(rr.mSerial, itemId,
RILUtils.convertNullToEmptyString(itemValue));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteItem", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteItem", e);
}
}
}
@@ -4023,7 +4214,7 @@
try {
modemProxy.nvWriteCdmaPrl(rr.mSerial, preferredRoamingList);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteCdmaPrl", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteCdmaPrl", e);
}
}
}
@@ -4043,7 +4234,7 @@
try {
modemProxy.nvResetConfig(rr.mSerial, resetType);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvResetConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvResetConfig", e);
}
}
}
@@ -4065,7 +4256,7 @@
try {
simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setUiccSubscription", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setUiccSubscription", e);
}
}
}
@@ -4080,7 +4271,7 @@
// EID should be supported as long as HAL >= 1.2.
// - in HAL 1.2 we have EID through ATR
// - in later HAL versions we also have EID through slot / card status.
- return mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2);
+ return mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2);
}
@Override
@@ -4097,7 +4288,7 @@
try {
dataProxy.setDataAllowed(rr.mSerial, allowed);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataAllowed", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataAllowed", e);
}
}
}
@@ -4117,7 +4308,7 @@
try {
modemProxy.getHardwareConfig(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getHardwareConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getHardwareConfig", e);
}
}
}
@@ -4140,7 +4331,7 @@
RILUtils.convertNullToEmptyString(data),
RILUtils.convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "requestIccSimAuthentication", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "requestIccSimAuthentication", e);
}
}
}
@@ -4163,7 +4354,7 @@
try {
dataProxy.setDataProfile(rr.mSerial, dps, isRoaming);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataProfile", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataProfile", e);
}
}
}
@@ -4181,7 +4372,7 @@
try {
modemProxy.requestShutdown(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "requestShutdown", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "requestShutdown", e);
}
}
}
@@ -4200,7 +4391,7 @@
try {
modemProxy.getRadioCapability(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getRadioCapability", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getRadioCapability", e);
}
}
}
@@ -4220,14 +4411,14 @@
try {
modemProxy.setRadioCapability(rr.mSerial, rc);
} catch (Exception e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioCapability", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioCapability", e);
}
}
}
@Override
public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+ if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
// We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
// Instead the LCE functionality is always-on and provides unsolicited indications.
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startLceService: REQUEST_NOT_SUPPORTED");
@@ -4251,14 +4442,14 @@
try {
radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(RADIO_SERVICE, "startLceService", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "startLceService", e);
}
}
}
@Override
public void stopLceService(Message result) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+ if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
// We have a 1.2 or later radio, so the LCE 1.0 LCE service control is unused.
// Instead the LCE functionality is always-on and provides unsolicited indications.
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopLceService: REQUEST_NOT_SUPPORTED");
@@ -4281,7 +4472,7 @@
try {
radioProxy.stopLceService(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(RADIO_SERVICE, "stopLceService", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "stopLceService", e);
}
}
}
@@ -4300,7 +4491,7 @@
long completionWindowMillis) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result,
getDefaultWorkSourceIfInvalid(workSource));
@@ -4315,7 +4506,7 @@
dataProxy.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction,
completionWindowMillis);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataThrottling", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataThrottling", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "setDataThrottling: REQUEST_NOT_SUPPORTED");
@@ -4340,7 +4531,7 @@
@Deprecated
@Override
public void pullLceData(Message result) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+ if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
// We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
// Instead the LCE functionality is always-on and provides unsolicited indications.
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "pullLceData: REQUEST_NOT_SUPPORTED");
@@ -4363,7 +4554,7 @@
try {
radioProxy.pullLceData(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(RADIO_SERVICE, "pullLceData", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "pullLceData", e);
}
}
}
@@ -4385,7 +4576,7 @@
mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT, rr.mSerial);
mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemActivityInfo", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemActivityInfo", e);
}
}
}
@@ -4408,7 +4599,7 @@
try {
simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules, result);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setAllowedCarriers", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setAllowedCarriers", e);
}
}
}
@@ -4427,7 +4618,7 @@
try {
simProxy.getAllowedCarriers(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getAllowedCarriers", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getAllowedCarriers", e);
}
}
}
@@ -4447,7 +4638,7 @@
try {
modemProxy.sendDeviceState(rr.mSerial, stateType, state);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(MODEM_SERVICE, "sendDeviceState", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "sendDeviceState", e);
}
}
}
@@ -4467,7 +4658,7 @@
try {
networkProxy.setIndicationFilter(rr.mSerial, filter);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setIndicationFilter", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setIndicationFilter", e);
}
}
}
@@ -4477,7 +4668,7 @@
@NonNull List<SignalThresholdInfo> signalThresholdInfos, @Nullable Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
result, mRILDefaultWorkSource);
@@ -4488,7 +4679,7 @@
try {
networkProxy.setSignalStrengthReportingCriteria(rr.mSerial, signalThresholdInfos);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE,
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
"setSignalStrengthReportingCriteria", e);
}
} else {
@@ -4502,7 +4693,7 @@
Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, result,
mRILDefaultWorkSource);
@@ -4516,7 +4707,7 @@
ran);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(
- NETWORK_SERVICE, "setLinkCapacityReportingCriteria", e);
+ HAL_SERVICE_NETWORK, "setLinkCapacityReportingCriteria", e);
}
} else {
riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less than 1.2");
@@ -4538,7 +4729,7 @@
try {
simProxy.setSimCardPower(rr.mSerial, state, result);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setSimCardPower", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setSimCardPower", e);
}
}
}
@@ -4549,7 +4740,7 @@
Objects.requireNonNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -4559,7 +4750,8 @@
try {
simProxy.setCarrierInfoForImsiEncryption(rr.mSerial, imsiEncryptionInfo);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "setCarrierInfoForImsiEncryption", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM,
+ "setCarrierInfoForImsiEncryption", e);
}
} else {
if (RILJ_LOGD) {
@@ -4579,7 +4771,7 @@
Objects.requireNonNull(packetData, "KeepaliveRequest cannot be null.");
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
RILRequest rr = obtainRequest(RIL_REQUEST_START_KEEPALIVE, result,
mRILDefaultWorkSource);
@@ -4590,7 +4782,7 @@
try {
dataProxy.startKeepalive(rr.mSerial, contextId, packetData, intervalMillis, result);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "startNattKeepalive", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startNattKeepalive", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNattKeepalive: REQUEST_NOT_SUPPORTED");
@@ -4606,7 +4798,7 @@
public void stopNattKeepalive(int sessionHandle, Message result) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
RILRequest rr = obtainRequest(RIL_REQUEST_STOP_KEEPALIVE, result,
mRILDefaultWorkSource);
@@ -4617,7 +4809,7 @@
try {
dataProxy.stopKeepalive(rr.mSerial, sessionHandle);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "stopNattKeepalive", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "stopNattKeepalive", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNattKeepalive: REQUEST_NOT_SUPPORTED");
@@ -4666,18 +4858,19 @@
public void enableUiccApplications(boolean enable, Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " " + enable);
}
try {
simProxy.enableUiccApplications(rr.mSerial, enable);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "enableUiccApplications", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "enableUiccApplications", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableUiccApplications: REQUEST_NOT_SUPPORTED");
@@ -4698,7 +4891,7 @@
public void areUiccApplicationsEnabled(Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT, result,
mRILDefaultWorkSource);
@@ -4709,7 +4902,7 @@
try {
simProxy.areUiccApplicationsEnabled(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "areUiccApplicationsEnabled", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "areUiccApplicationsEnabled", e);
}
} else {
if (RILJ_LOGD) {
@@ -4729,7 +4922,7 @@
@Override
public boolean canToggleUiccApplicationsEnablement() {
return !getRadioServiceProxy(RadioSimProxy.class, null).isEmpty()
- && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5);
+ && mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5);
}
@Override
@@ -4755,7 +4948,7 @@
voiceProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(
- VOICE_SERVICE, "handleStkCallSetupRequestFromSim", e);
+ HAL_SERVICE_VOICE, "handleStkCallSetupRequestFromSim", e);
}
}
}
@@ -4767,7 +4960,7 @@
public void getBarringInfo(Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_BARRING_INFO, result,
mRILDefaultWorkSource);
@@ -4778,7 +4971,7 @@
try {
networkProxy.getBarringInfo(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getBarringInfo", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getBarringInfo", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getBarringInfo: REQUEST_NOT_SUPPORTED");
@@ -4797,7 +4990,7 @@
public void allocatePduSessionId(Message result) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -4807,7 +5000,7 @@
try {
dataProxy.allocatePduSessionId(rr.mSerial);
} catch (RemoteException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "allocatePduSessionId", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "allocatePduSessionId", e);
}
} else {
AsyncResult.forMessage(result, null,
@@ -4823,7 +5016,7 @@
public void releasePduSessionId(Message result, int pduSessionId) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -4833,7 +5026,7 @@
try {
dataProxy.releasePduSessionId(rr.mSerial, pduSessionId);
} catch (RemoteException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "releasePduSessionId", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "releasePduSessionId", e);
}
} else {
AsyncResult.forMessage(result, null,
@@ -4849,7 +5042,7 @@
public void startHandover(Message result, int callId) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -4859,7 +5052,7 @@
try {
dataProxy.startHandover(rr.mSerial, callId);
} catch (RemoteException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "startHandover", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startHandover", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startHandover: REQUEST_NOT_SUPPORTED");
@@ -4878,7 +5071,7 @@
public void cancelHandover(Message result, int callId) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
@@ -4888,7 +5081,7 @@
try {
dataProxy.cancelHandover(rr.mSerial, callId);
} catch (RemoteException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "cancelHandover", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "cancelHandover", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "cancelHandover: REQUEST_NOT_SUPPORTED");
@@ -4905,7 +5098,7 @@
public void getSlicingConfig(Message result) {
RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
if (dataProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
mRILDefaultWorkSource);
@@ -4916,7 +5109,7 @@
try {
dataProxy.getSlicingConfig(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(DATA_SERVICE, "getSlicingConfig", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getSlicingConfig", e);
}
} else {
if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getSlicingConfig: REQUEST_NOT_SUPPORTED");
@@ -4930,7 +5123,7 @@
public void getSimPhonebookRecords(Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
mRILDefaultWorkSource);
@@ -4941,7 +5134,7 @@
try {
simProxy.getSimPhonebookRecords(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookRecords", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookRecords", e);
}
} else {
if (RILJ_LOGD) {
@@ -4959,7 +5152,7 @@
public void getSimPhonebookCapacity(Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
mRILDefaultWorkSource);
@@ -4970,7 +5163,7 @@
try {
simProxy.getSimPhonebookCapacity(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookCapacity", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookCapacity", e);
}
} else {
if (RILJ_LOGD) {
@@ -4988,7 +5181,7 @@
public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
if (simProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
mRILDefaultWorkSource);
@@ -5000,7 +5193,7 @@
try {
simProxy.updateSimPhonebookRecords(rr.mSerial, phonebookRecord);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(SIM_SERVICE, "updateSimPhonebookRecords", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "updateSimPhonebookRecords", e);
}
} else {
if (RILJ_LOGD) {
@@ -5025,7 +5218,7 @@
/* @TelephonyManager.UsageSetting */ int usageSetting) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_USAGE_SETTING, result,
mRILDefaultWorkSource);
@@ -5036,7 +5229,7 @@
try {
networkProxy.setUsageSetting(rr.mSerial, usageSetting);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setUsageSetting", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setUsageSetting", e);
}
} else {
if (RILJ_LOGD) {
@@ -5059,7 +5252,7 @@
public void getUsageSetting(Message result) {
RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
if (networkProxy.isEmpty()) return;
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_USAGE_SETTING, result,
mRILDefaultWorkSource);
@@ -5070,7 +5263,7 @@
try {
networkProxy.getUsageSetting(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getUsageSetting", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getUsageSetting", e);
}
} else {
if (RILJ_LOGD) {
@@ -5084,6 +5277,783 @@
}
}
+ @Override
+ public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ // Do not log function arg for privacy
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ imsProxy.setSrvccCallInfo(rr.mSerial,
+ RILUtils.convertToHalSrvccCall(srvccConnections));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "setSrvccCallInfo", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "setSrvccCallInfo: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void updateImsRegistrationInfo(
+ @RegistrationManager.ImsRegistrationState int state,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ int capabilities, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " state=" + state + ", radioTech=" + imsRadioTech
+ + ", suggested=" + suggestedAction + ", cap=" + capabilities);
+ }
+
+ android.hardware.radio.ims.ImsRegistration registrationInfo =
+ new android.hardware.radio.ims.ImsRegistration();
+ registrationInfo.regState = RILUtils.convertImsRegistrationState(state);
+ registrationInfo.accessNetworkType = RILUtils.convertImsRegistrationTech(imsRadioTech);
+ registrationInfo.suggestedAction = suggestedAction;
+ registrationInfo.capabilities = RILUtils.convertImsCapability(capabilities);
+
+ try {
+ imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsRegistrationInfo", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "updateImsRegistrationInfo: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void startImsTraffic(int token,
+ int trafficType, int accessNetworkType, int trafficDirection, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_START_IMS_TRAFFIC, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + "{" + token + ", " + trafficType + ", "
+ + accessNetworkType + ", " + trafficDirection + "}");
+ }
+
+ try {
+ imsProxy.startImsTraffic(rr.mSerial, token,
+ RILUtils.convertImsTrafficType(trafficType), accessNetworkType,
+ RILUtils.convertImsTrafficDirection(trafficDirection));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "startImsTraffic", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "startImsTraffic: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void stopImsTraffic(int token, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_STOP_IMS_TRAFFIC, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + "{" + token + "}");
+ }
+
+ try {
+ imsProxy.stopImsTraffic(rr.mSerial, token);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "stopImsTraffic", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "stopImsTraffic: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void triggerEpsFallback(int reason, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EPS_FALLBACK, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " reason=" + reason);
+ }
+
+ try {
+ imsProxy.triggerEpsFallback(rr.mSerial, reason);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "triggerEpsFallback", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "triggerEpsFallback: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond,
+ Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SEND_ANBR_QUERY, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ imsProxy.sendAnbrQuery(rr.mSerial, mediaType, direction, bitsPerSecond);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "sendAnbrQuery", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "sendAnbrQuery: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setEmergencyMode(int emcMode, Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_EMERGENCY_MODE, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " mode=" + EmergencyConstants.emergencyModeToString(emcMode));
+ }
+
+ try {
+ networkProxy.setEmergencyMode(rr.mSerial, emcMode);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setEmergencyMode", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "setEmergencyMode: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void triggerEmergencyNetworkScan(
+ @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+ @DomainSelectionService.EmergencyScanType int scanType, Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " networkType=" + RILUtils.accessNetworkTypesToString(accessNetwork)
+ + ", scanType=" + RILUtils.scanTypeToString(scanType));
+ }
+
+ try {
+ networkProxy.triggerEmergencyNetworkScan(rr.mSerial,
+ RILUtils.convertEmergencyNetworkScanTrigger(accessNetwork, scanType));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "triggerEmergencyNetworkScan", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "triggerEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void cancelEmergencyNetworkScan(boolean resetScan, Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " resetScan=" + resetScan);
+ }
+
+ try {
+ networkProxy.cancelEmergencyNetworkScan(rr.mSerial, resetScan);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+ "cancelEmergencyNetworkScan", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "cancelEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void exitEmergencyMode(Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_MODE, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ networkProxy.exitEmergencyMode(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "exitEmergencyMode", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "exitEmergencyMode: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Set if null ciphering / null integrity modes are permitted.
+ *
+ * @param result Callback message containing the success or failure status.
+ * @param enabled true if null ciphering / null integrity modes are permitted, false otherwise
+ */
+ @Override
+ public void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ networkProxy.setNullCipherAndIntegrityEnabled(rr.mSerial, enabled);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(
+ HAL_SERVICE_NETWORK, "setNullCipherAndIntegrityEnabled", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "setNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Get if null ciphering / null integrity are enabled / disabled.
+ *
+ * @param result Callback message containing the success or failure status.
+ */
+ @Override
+ public void isNullCipherAndIntegrityEnabled(Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ networkProxy.isNullCipherAndIntegrityEnabled(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(
+ HAL_SERVICE_NETWORK, "isNullCipherAndIntegrityEnabled", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "isNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) {
+ RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+ if (imsProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_CALL_STATUS, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " " + imsCallInfo);
+ }
+ try {
+ imsProxy.updateImsCallStatus(rr.mSerial, RILUtils.convertImsCallInfo(imsCallInfo));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsCallStatus", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "updateImsCallStatus: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setN1ModeEnabled(boolean enable, Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_N1_MODE_ENABLED, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ + " enable=" + enable);
+ }
+
+ try {
+ networkProxy.setN1ModeEnabled(rr.mSerial, enable);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setN1ModeEnabled", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "setN1ModeEnabled: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void isN1ModeEnabled(Message result) {
+ RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+ if (networkProxy.isEmpty()) return;
+ if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_IS_N1_MODE_ENABLED, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+ }
+
+ try {
+ networkProxy.isN1ModeEnabled(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "isN1ModeEnabled", e);
+ }
+ } else {
+ if (RILJ_LOGD) {
+ Rlog.d(RILJ_LOG_TAG, "isN1ModeEnabled: REQUEST_NOT_SUPPORTED");
+ }
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Get feature capabilities supported by satellite.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ @Override
+ public void getSatelliteCapabilities(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Turn satellite modem on/off.
+ *
+ * @param result Message that will be sent back to the requester
+ * @param on True for turning on.
+ * False for turning off.
+ */
+ @Override
+ public void setSatellitePower(Message result, boolean on) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get satellite modem state.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ @Override
+ public void getSatellitePowerState(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get satellite provision state.
+ *
+ * @param result Message that will be sent back to the requester
+ */
+ @Override
+ public void getSatelliteProvisionState(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Provision the subscription with a satellite provider. This is needed to register the
+ * subscription if the provider allows dynamic registration.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param imei IMEI of the SIM associated with the satellite modem.
+ * @param msisdn MSISDN of the SIM associated with the satellite modem.
+ * @param imsi IMSI of the SIM associated with the satellite modem.
+ * @param features List of features to be provisioned.
+ */
+ @Override
+ public void provisionSatelliteService(
+ Message result, String imei, String msisdn, String imsi, int[] features) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Add contacts that are allowed to be used for satellite communication. This is applicable for
+ * incoming messages as well.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param contacts List of allowed contacts to be added.
+ */
+ @Override
+ public void addAllowedSatelliteContacts(Message result, String[] contacts) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Remove contacts that are allowed to be used for satellite communication. This is applicable
+ * for incoming messages as well.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param contacts List of allowed contacts to be removed.
+ */
+ @Override
+ public void removeAllowedSatelliteContacts(Message result, String[] contacts) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Send text messages.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param messages List of messages in text format to be sent.
+ * @param destination The recipient of the message.
+ * @param latitude The current latitude of the device.
+ * @param longitude The current longitude of the device.
+ */
+ @Override
+ public void sendSatelliteMessages(Message result, String[] messages, String destination,
+ double latitude, double longitude) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get pending messages.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void getPendingSatelliteMessages(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get current satellite registration mode.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void getSatelliteMode(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Set the filter for what type of indication framework want to receive from modem.
+ *
+ * @param result Message that will be sent back to the requester.
+ * @param filterBitmask The filter bitmask identifying what type of indication framework want to
+ * receive from modem.
+ */
+ @Override
+ public void setSatelliteIndicationFilter(Message result, int filterBitmask) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Check whether satellite modem is supported by the device.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void isSatelliteSupported(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * User started pointing to the satellite. Modem should continue to update the ponting input
+ * as user moves device.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void startSendingSatellitePointingInfo(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Stop pointing to satellite indications.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void stopSendingSatellitePointingInfo(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get max text limit for messaging per message.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void getMaxCharactersPerSatelliteTextMessage(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get whether satellite communication is allowed for the current location
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * Get time for next visibility of satellite.
+ *
+ * @param result Message that will be sent back to the requester.
+ */
+ @Override
+ public void getTimeForNextSatelliteVisibility(Message result) {
+ // Satellite HAL APIs are not supported before Android V.
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
//***** Private Methods
/**
* This is a helper function to be called when an indication callback is called for any radio
@@ -5125,7 +6095,7 @@
*/
@VisibleForTesting
public RILRequest processResponse(RadioResponseInfo responseInfo) {
- return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error,
+ return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error,
responseInfo.type);
}
@@ -5139,7 +6109,7 @@
@VisibleForTesting
public RILRequest processResponse_1_6(
android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
- return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error,
+ return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error,
responseInfo.type);
}
@@ -5151,7 +6121,6 @@
* @param responseInfo RadioResponseInfo received in response callback
* @return RILRequest corresponding to the response
*/
- @VisibleForTesting
public RILRequest processResponse(int service,
android.hardware.radio.RadioResponseInfo responseInfo) {
return processResponseInternal(service, responseInfo.serial, responseInfo.error,
@@ -5186,6 +6155,7 @@
+ " ,error: " + error);
return null;
}
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial);
// Time logging for RIL command and storing it in TelephonyHistogram.
addToRilHistogram(rr);
@@ -5350,13 +6320,13 @@
RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null,
mRILDefaultWorkSource);
acquireWakeLock(rr, FOR_ACK_WAKELOCK);
- if (service == RADIO_SERVICE) {
+ if (service == HAL_SERVICE_RADIO) {
IRadio radioProxy = getRadioProxy(null);
if (radioProxy != null) {
try {
radioProxy.responseAcknowledgement();
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(RADIO_SERVICE, "sendAck", e);
+ handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "sendAck", e);
riljLoge("sendAck: " + e);
}
} else {
@@ -5501,7 +6471,7 @@
synchronized (mWakeLock) {
if (mWakeLockCount == 0 && !mWakeLock.isHeld()) return false;
Rlog.d(RILJ_LOG_TAG, "NOTE: mWakeLockCount is " + mWakeLockCount
- + "at time of clearing");
+ + " at time of clearing");
mWakeLockCount = 0;
mWakeLock.release();
mClientWakelockTracker.stopTrackingAll();
@@ -5674,6 +6644,22 @@
sb.append("[").append(hwcfg).append("] ");
}
s = sb.toString();
+ } else if (req == RIL_REQUEST_START_IMS_TRAFFIC
+ || req == RIL_UNSOL_CONNECTION_SETUP_FAILURE) {
+ sb = new StringBuilder("{");
+ Object[] info = (Object[]) ret;
+ int token = (Integer) info[0];
+ sb.append(token).append(", ");
+ if (info[1] != null) {
+ ConnectionFailureInfo failureInfo = (ConnectionFailureInfo) info[1];
+ sb.append(failureInfo.getReason()).append(", ");
+ sb.append(failureInfo.getCauseCode()).append(", ");
+ sb.append(failureInfo.getWaitTimeMillis());
+ } else {
+ sb.append("null");
+ }
+ sb.append("}");
+ s = sb.toString();
} else {
// Check if toString() was overridden. Java classes created from HIDL have a built-in
// toString() method, but AIDL classes only have it if the parcelable contains a
@@ -5732,43 +6718,43 @@
int response = RIL_UNSOL_CDMA_INFO_REC;
if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) {
if (mDisplayInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mDisplayInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) {
if (mSignalInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mSignalInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) {
if (mNumberInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mNumberInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) {
if (mRedirNumInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mRedirNumInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) {
if (mLineControlInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mLineControlInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) {
if (mT53ClirInfoRegistrants != null) {
- if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ if (isLogOrTrace()) unsljLogRet(response, infoRec.record);
mT53ClirInfoRegistrants.notifyRegistrants(
new AsyncResult(null, infoRec.record, null));
}
} else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) {
if (mT53AudCntrlInfoRegistrants != null) {
- if (RILJ_LOGD) {
+ if (isLogOrTrace()) {
unsljLogRet(response, infoRec.record);
}
mT53AudCntrlInfoRegistrants.notifyRegistrants(
@@ -5790,26 +6776,48 @@
Rlog.v(RILJ_LOG_TAG, msg + (" [PHONE" + mPhoneId + "]"));
}
+ boolean isLogOrTrace() {
+ return RIL.RILJ_LOGD || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
+ }
+
+ boolean isLogvOrTrace() {
+ return RIL.RILJ_LOGV || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
+ }
+
@UnsupportedAppUsage
void unsljLog(int response) {
- riljLog("[UNSL]< " + RILUtils.responseToString(response));
+ String logStr = RILUtils.responseToString(response);
+ if (RIL.RILJ_LOGD) {
+ riljLog("[UNSL]< " + logStr);
+ }
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
}
@UnsupportedAppUsage
void unsljLogMore(int response, String more) {
- riljLog("[UNSL]< " + RILUtils.responseToString(response) + " " + more);
+ String logStr = RILUtils.responseToString(response) + " " + more;
+ if (RIL.RILJ_LOGD) {
+ riljLog("[UNSL]< " + logStr);
+ }
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
}
@UnsupportedAppUsage
void unsljLogRet(int response, Object ret) {
- riljLog("[UNSL]< " + RILUtils.responseToString(response) + " "
- + retToString(response, ret));
+ String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret);
+ if (RIL.RILJ_LOGD) {
+ riljLog("[UNSL]< " + logStr);
+ }
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
}
@UnsupportedAppUsage
void unsljLogvRet(int response, Object ret) {
- riljLogv("[UNSL]< " + RILUtils.responseToString(response) + " "
- + retToString(response, ret));
+ String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret);
+ if (RIL.RILJ_LOGV) {
+ riljLogv("[UNSL]< " + logStr);
+ }
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
}
@Override
@@ -5829,6 +6837,13 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("RIL: " + this);
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_DATA));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_MESSAGING));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_MODEM));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_NETWORK));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_SIM));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_VOICE));
+ pw.println(" " + mServiceProxies.get(HAL_SERVICE_IMS));
pw.println(" mWakeLock=" + mWakeLock);
pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
synchronized (mRequestList) {
@@ -5904,31 +6919,60 @@
new CellSignalStrengthNr());
}
- /**
- * Get the HAL version.
- *
- * @return the current HalVersion
- */
- public HalVersion getHalVersion() {
- return mRadioVersion;
+ void notifyBarringInfoChanged(@NonNull BarringInfo barringInfo) {
+ mLastBarringInfo = barringInfo;
+ mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, barringInfo, null));
}
- private static String serviceToString(int service) {
+ /**
+ * Get the HAL version with a specific service.
+ *
+ * @param service the hal service id
+ * @return the current HalVersion
+ */
+ public HalVersion getHalVersion(int service) {
+ HalVersion halVersion = mHalVersion.get(service);
+ if (halVersion == null) {
+ if (isRadioServiceSupported(service)) {
+ halVersion = RADIO_HAL_VERSION_UNKNOWN;
+ } else {
+ halVersion = RADIO_HAL_VERSION_UNSUPPORTED;
+ }
+ }
+ return halVersion;
+ }
+
+ /**
+ * Get the HAL version corresponding to the interface version of a IRadioService module.
+ * @param interfaceVersion The interface version, from IRadioService#getInterfaceVersion().
+ * @return The corresponding HalVersion.
+ */
+ public static HalVersion getServiceHalVersion(int interfaceVersion) {
+ switch (interfaceVersion) {
+ case 1: return RADIO_HAL_VERSION_2_0;
+ case 2: return RADIO_HAL_VERSION_2_1;
+ default: return RADIO_HAL_VERSION_UNKNOWN;
+ }
+ }
+
+ private static String serviceToString(@HalService int service) {
switch (service) {
- case RADIO_SERVICE:
+ case HAL_SERVICE_RADIO:
return "RADIO";
- case DATA_SERVICE:
+ case HAL_SERVICE_DATA:
return "DATA";
- case MESSAGING_SERVICE:
+ case HAL_SERVICE_MESSAGING:
return "MESSAGING";
- case MODEM_SERVICE:
+ case HAL_SERVICE_MODEM:
return "MODEM";
- case NETWORK_SERVICE:
+ case HAL_SERVICE_NETWORK:
return "NETWORK";
- case SIM_SERVICE:
+ case HAL_SERVICE_SIM:
return "SIM";
- case VOICE_SERVICE:
+ case HAL_SERVICE_VOICE:
return "VOICE";
+ case HAL_SERVICE_IMS:
+ return "IMS";
default:
return "UNKNOWN:" + service;
}
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 61f1e82..9db186f 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -29,6 +29,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_BASEBAND_VERSION;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_HANDOVER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_USSD;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_BROADCAST_ACTIVATION;
@@ -58,6 +59,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DELETE_SMS_ON_SIM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IDENTITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IMEI;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF_START;
@@ -74,6 +76,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK2;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_MODE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXPLICIT_CALL_TRANSFER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ACTIVITY_INFO;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ALLOWED_CARRIERS;
@@ -112,7 +115,9 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_REGISTRATION_STATE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ISIM_AUTHENTICATION;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_N1_MODE_ENABLED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_VONR_ENABLED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_CALL_FAIL_CAUSE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE;
@@ -138,6 +143,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RESET_RADIO;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SCREEN_STATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_ANBR_QUERY;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
@@ -154,20 +160,24 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_PROFILE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_THROTTLING;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DC_RT_INFO_RATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_EMERGENCY_MODE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_FACILITY_LOCK;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOCATION_UPDATES;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_MUTE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_N1_MODE_ENABLED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SRVCC_CALL_INFO;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_TTY_MODE;
@@ -185,6 +195,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SMS_ACKNOWLEDGE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_HANDOVER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_IMS_TRAFFIC;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_KEEPALIVE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN;
@@ -194,12 +205,17 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SET_PROFILE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_IMS_TRAFFIC;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_KEEPALIVE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_NETWORK_SCAN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EPS_FALLBACK;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_CALL_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_REGISTRATION_STATE;
@@ -214,8 +230,10 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DC_RT_INFO_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NUMBER_LIST;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;
@@ -226,6 +244,7 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
@@ -253,6 +272,7 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIGNAL_STRENGTH;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_REFRESH;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_SMS_STORAGE_FULL;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SLICING_CONFIG_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SRVCC_STATE_NOTIFY;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CALL_SETUP;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CC_ALPHA_NOTIFY;
@@ -260,11 +280,13 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_PROACTIVE_COMMAND;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_END;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.InetAddresses;
import android.net.LinkAddress;
@@ -298,8 +320,11 @@
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.ClosedSubscriberGroupInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.EmergencyRegResult;
import android.telephony.LinkCapacityEstimate;
import android.telephony.ModemInfo;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhysicalChannelConfig;
@@ -326,6 +351,12 @@
import android.telephony.data.RouteSelectionDescriptor;
import android.telephony.data.TrafficDescriptor;
import android.telephony.data.UrspRule;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsDeregistrationReason;
+import android.telephony.satellite.SatelliteManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -339,6 +370,7 @@
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.data.KeepaliveStatus;
import com.android.internal.telephony.data.KeepaliveStatus.KeepaliveStatusCode;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
@@ -346,6 +378,7 @@
import com.android.internal.telephony.uicc.IccSlotPortMapping;
import com.android.internal.telephony.uicc.IccSlotStatus;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.PortUtils;
import com.android.internal.telephony.uicc.SimPhonebookRecord;
import com.android.telephony.Rlog;
@@ -1441,8 +1474,9 @@
if (ComprehensionTlvTag.TEXT_STRING.value() == ctlv.getTag()) {
byte[] target = Arrays.copyOfRange(ctlv.getRawValue(), from,
ctlv.getValueIndex() + ctlv.getLength());
- terminalResponse = terminalResponse.toLowerCase().replace(
- IccUtils.bytesToHexString(target).toLowerCase(), "********");
+ terminalResponse = terminalResponse.toLowerCase(Locale.ROOT).replace(
+ IccUtils.bytesToHexString(target).toLowerCase(Locale.ROOT),
+ "********");
}
// The text string tag and the length field should also be hidden.
from = ctlv.getValueIndex() + ctlv.getLength();
@@ -1660,12 +1694,10 @@
if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN) != 0) {
raf |= android.hardware.radio.RadioAccessFamily.IWLAN;
}
- if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE) != 0) {
+ if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE) != 0
+ || (networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA) != 0) {
raf |= android.hardware.radio.RadioAccessFamily.LTE;
}
- if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA) != 0) {
- raf |= android.hardware.radio.RadioAccessFamily.LTE_CA;
- }
if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0) {
raf |= android.hardware.radio.RadioAccessFamily.NR;
}
@@ -1801,10 +1833,12 @@
* @param p2 p2
* @param p3 p3
* @param data data
+ * @param radioHalVersion radio hal version
* @return The converted SimApdu
*/
public static android.hardware.radio.sim.SimApdu convertToHalSimApduAidl(int channel, int cla,
- int instruction, int p1, int p2, int p3, String data) {
+ int instruction, int p1, int p2, int p3, String data, boolean isEs10Command,
+ HalVersion radioHalVersion) {
android.hardware.radio.sim.SimApdu msg = new android.hardware.radio.sim.SimApdu();
msg.sessionId = channel;
msg.cla = cla;
@@ -1813,6 +1847,9 @@
msg.p2 = p2;
msg.p3 = p3;
msg.data = convertNullToEmptyString(data);
+ if (radioHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_2_1)) {
+ msg.isEs10 = isEs10Command;
+ }
return msg;
}
@@ -3256,7 +3293,8 @@
return new CellSignalStrengthNr(CellSignalStrengthNr.flip(ss.base.csiRsrp),
CellSignalStrengthNr.flip(ss.base.csiRsrq), ss.base.csiSinr,
ss.csiCqiTableIndex, ss.csiCqiReport, CellSignalStrengthNr.flip(ss.base.ssRsrp),
- CellSignalStrengthNr.flip(ss.base.ssRsrq), ss.base.ssSinr);
+ CellSignalStrengthNr.flip(ss.base.ssRsrq), ss.base.ssSinr,
+ CellInfo.UNAVAILABLE);
}
return null;
}
@@ -3271,7 +3309,7 @@
return new CellSignalStrengthNr(CellSignalStrengthNr.flip(ss.csiRsrp),
CellSignalStrengthNr.flip(ss.csiRsrq), ss.csiSinr, ss.csiCqiTableIndex,
primitiveArrayToArrayList(ss.csiCqiReport), CellSignalStrengthNr.flip(ss.ssRsrp),
- CellSignalStrengthNr.flip(ss.ssRsrq), ss.ssSinr);
+ CellSignalStrengthNr.flip(ss.ssRsrq), ss.ssSinr, ss.timingAdvance);
}
private static ClosedSubscriberGroupInfo convertHalClosedSubscriberGroupInfo(
@@ -3835,9 +3873,13 @@
convertHalQosBandwidth(eps.uplink), eps.qci);
case android.hardware.radio.data.Qos.nr:
android.hardware.radio.data.NrQos nr = qos.getNr();
+ int averagingWindowMs = nr.averagingWindowMillis;
+ if (averagingWindowMs
+ == android.hardware.radio.data.NrQos.AVERAGING_WINDOW_UNKNOWN) {
+ averagingWindowMs = nr.averagingWindowMs;
+ }
return new NrQos(convertHalQosBandwidth(nr.downlink),
- convertHalQosBandwidth(nr.uplink), nr.qfi, nr.fiveQi,
- nr.averagingWindowMs);
+ convertHalQosBandwidth(nr.uplink), nr.qfi, nr.fiveQi, averagingWindowMs);
default:
return null;
}
@@ -4331,6 +4373,7 @@
android.hardware.radio.sim.CardStatus cardStatus) {
IccCardStatus iccCardStatus = new IccCardStatus();
iccCardStatus.setCardState(cardStatus.cardState);
+ iccCardStatus.setMultipleEnabledProfilesMode(cardStatus.supportedMepMode);
iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
@@ -4358,7 +4401,9 @@
}
IccSlotPortMapping slotPortMapping = new IccSlotPortMapping();
slotPortMapping.mPhysicalSlotIndex = cardStatus.slotMap.physicalSlotId;
- slotPortMapping.mPortIndex = cardStatus.slotMap.portId;
+ slotPortMapping.mPortIndex = PortUtils.convertFromHalPortIndex(
+ cardStatus.slotMap.physicalSlotId, cardStatus.slotMap.portId,
+ iccCardStatus.mCardState, iccCardStatus.mSupportedMepMode);
iccCardStatus.mSlotPortMapping = slotPortMapping;
return iccCardStatus;
}
@@ -4474,6 +4519,7 @@
}
iccSlotStatus.atr = slotStatus.atr;
iccSlotStatus.eid = slotStatus.eid;
+ iccSlotStatus.setMultipleEnabledProfilesMode(slotStatus.supportedMepMode);
response.add(iccSlotStatus);
}
return response;
@@ -4541,7 +4587,8 @@
int logicalSlotIdx = mapping.getLogicalSlotIndex();
res[logicalSlotIdx] = new android.hardware.radio.config.SlotPortMapping();
res[logicalSlotIdx].physicalSlotId = mapping.getPhysicalSlotIndex();
- res[logicalSlotIdx].portId = mapping.getPortIndex();
+ res[logicalSlotIdx].portId = PortUtils.convertToHalPortIndex(
+ mapping.getPhysicalSlotIndex(), mapping.getPortIndex());
}
return res;
}
@@ -4566,12 +4613,14 @@
public static PhoneCapability convertHalPhoneCapability(int[] deviceNrCapabilities, Object o) {
int maxActiveVoiceCalls = 0;
int maxActiveData = 0;
+ int maxActiveInternetData = 0;
boolean validationBeforeSwitchSupported = false;
List<ModemInfo> logicalModemList = new ArrayList<>();
if (o instanceof android.hardware.radio.config.PhoneCapability) {
final android.hardware.radio.config.PhoneCapability phoneCapability =
(android.hardware.radio.config.PhoneCapability) o;
maxActiveData = phoneCapability.maxActiveData;
+ maxActiveInternetData = phoneCapability.maxActiveInternetData;
validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported;
for (int modemId : phoneCapability.logicalModemIds) {
logicalModemList.add(new ModemInfo(modemId));
@@ -4580,16 +4629,249 @@
final android.hardware.radio.config.V1_1.PhoneCapability phoneCapability =
(android.hardware.radio.config.V1_1.PhoneCapability) o;
maxActiveData = phoneCapability.maxActiveData;
+ maxActiveInternetData = phoneCapability.maxActiveInternetData;
validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported;
for (android.hardware.radio.config.V1_1.ModemInfo modemInfo :
phoneCapability.logicalModemList) {
logicalModemList.add(new ModemInfo(modemInfo.modemId));
}
}
+ // maxActiveInternetData defines how many logical modems can have internet PDN connections
+ // simultaneously. For L+L DSDS modem it’s 1, and for DSDA modem it’s 2.
+ maxActiveVoiceCalls = maxActiveInternetData;
return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList,
validationBeforeSwitchSupported, deviceNrCapabilities);
}
+ /**
+ * Convert network scan type
+ * @param scanType The network scan type
+ * @return The converted EmergencyScanType
+ */
+ public static int convertEmergencyScanType(int scanType) {
+ switch (scanType) {
+ case DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE:
+ return android.hardware.radio.network.EmergencyScanType.LIMITED_SERVICE;
+ case DomainSelectionService.SCAN_TYPE_FULL_SERVICE:
+ return android.hardware.radio.network.EmergencyScanType.FULL_SERVICE;
+ default:
+ return android.hardware.radio.network.EmergencyScanType.NO_PREFERENCE;
+ }
+ }
+
+ /**
+ * Convert to EmergencyNetworkScanTrigger
+ * @param accessNetwork The list of access network types
+ * @param scanType The network scan type
+ * @return The converted EmergencyNetworkScanTrigger
+ */
+ public static android.hardware.radio.network.EmergencyNetworkScanTrigger
+ convertEmergencyNetworkScanTrigger(@NonNull int[] accessNetwork, int scanType) {
+ int[] halAccessNetwork = new int[accessNetwork.length];
+ for (int i = 0; i < accessNetwork.length; i++) {
+ halAccessNetwork[i] = convertToHalAccessNetworkAidl(accessNetwork[i]);
+ }
+
+ android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest =
+ new android.hardware.radio.network.EmergencyNetworkScanTrigger();
+
+ scanRequest.accessNetwork = halAccessNetwork;
+ scanRequest.scanType = convertEmergencyScanType(scanType);
+ return scanRequest;
+ }
+
+ /**
+ * Convert EmergencyRegResult.aidl to EmergencyRegResult.
+ * @param halResult EmergencyRegResult.aidl in HAL.
+ * @return Converted EmergencyRegResult.
+ */
+ public static EmergencyRegResult convertHalEmergencyRegResult(
+ android.hardware.radio.network.EmergencyRegResult halResult) {
+ return new EmergencyRegResult(
+ halResult.accessNetwork,
+ convertHalRegState(halResult.regState),
+ halResult.emcDomain,
+ halResult.isVopsSupported,
+ halResult.isEmcBearerSupported,
+ halResult.nwProvidedEmc,
+ halResult.nwProvidedEmf,
+ halResult.mcc,
+ halResult.mnc,
+ getCountryCodeForMccMnc(halResult.mcc, halResult.mnc));
+ }
+
+ private static @NonNull String getCountryCodeForMccMnc(
+ @NonNull String mcc, @NonNull String mnc) {
+ if (TextUtils.isEmpty(mcc)) return "";
+ if (TextUtils.isEmpty(mnc)) mnc = "000";
+ String operatorNumeric = TextUtils.concat(mcc, mnc).toString();
+
+ MccTable.MccMnc mccMnc = MccTable.MccMnc.fromOperatorNumeric(operatorNumeric);
+ return MccTable.geoCountryCodeForMccMnc(mccMnc);
+ }
+
+ /**
+ * Convert RegResult.aidl to RegistrationState.
+ * @param halRegState RegResult in HAL.
+ * @return Converted RegistrationState.
+ */
+ public static @NetworkRegistrationInfo.RegistrationState int convertHalRegState(
+ int halRegState) {
+ switch (halRegState) {
+ case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+ case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ case android.hardware.radio.network.RegState.REG_HOME:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+ case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP:
+ case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP_EM:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
+ case android.hardware.radio.network.RegState.REG_DENIED:
+ case android.hardware.radio.network.RegState.REG_DENIED_EM:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_DENIED;
+ case android.hardware.radio.network.RegState.UNKNOWN:
+ case android.hardware.radio.network.RegState.UNKNOWN_EM:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
+ case android.hardware.radio.network.RegState.REG_ROAMING:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ default:
+ return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ }
+ }
+
+ /** Converts the array of network types to readable String array */
+ public static @NonNull String accessNetworkTypesToString(
+ @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetworkTypes) {
+ int length = accessNetworkTypes.length;
+ StringBuilder sb = new StringBuilder("{");
+ if (length > 0) {
+ sb.append(Arrays.stream(accessNetworkTypes)
+ .mapToObj(RILUtils::accessNetworkTypeToString)
+ .collect(Collectors.joining(",")));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static @NonNull String accessNetworkTypeToString(
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
+ switch (accessNetworkType) {
+ case AccessNetworkConstants.AccessNetworkType.UNKNOWN: return "UNKNOWN";
+ case AccessNetworkConstants.AccessNetworkType.GERAN: return "GERAN";
+ case AccessNetworkConstants.AccessNetworkType.UTRAN: return "UTRAN";
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN: return "EUTRAN";
+ case AccessNetworkConstants.AccessNetworkType.CDMA2000: return "CDMA2000";
+ case AccessNetworkConstants.AccessNetworkType.IWLAN: return "IWLAN";
+ case AccessNetworkConstants.AccessNetworkType.NGRAN: return "NGRAN";
+ default: return Integer.toString(accessNetworkType);
+ }
+ }
+
+ /** Converts scan type to readable String */
+ public static @NonNull String scanTypeToString(
+ @DomainSelectionService.EmergencyScanType int scanType) {
+ switch (scanType) {
+ case DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE:
+ return "LIMITED_SERVICE";
+ case DomainSelectionService.SCAN_TYPE_FULL_SERVICE:
+ return "FULL_SERVICE";
+ default:
+ return "NO_PREFERENCE";
+ }
+ }
+
+ /** Convert IMS deregistration reason */
+ public static @ImsDeregistrationReason int convertHalDeregistrationReason(int reason) {
+ switch (reason) {
+ case android.hardware.radio.ims.ImsDeregistrationReason.REASON_SIM_REMOVED:
+ return ImsRegistrationImplBase.REASON_SIM_REMOVED;
+ case android.hardware.radio.ims.ImsDeregistrationReason.REASON_SIM_REFRESH:
+ return ImsRegistrationImplBase.REASON_SIM_REFRESH;
+ case android.hardware.radio.ims.ImsDeregistrationReason
+ .REASON_ALLOWED_NETWORK_TYPES_CHANGED:
+ return ImsRegistrationImplBase.REASON_ALLOWED_NETWORK_TYPES_CHANGED;
+ default:
+ return ImsRegistrationImplBase.REASON_UNKNOWN;
+ }
+ }
+
+ /**
+ * Convert the IMS traffic type.
+ * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+ * @return The converted IMS traffic type.
+ */
+ public static int convertImsTrafficType(@MmTelFeature.ImsTrafficType int trafficType) {
+ switch (trafficType) {
+ case MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY:
+ return android.hardware.radio.ims.ImsTrafficType.EMERGENCY;
+ case MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY_SMS:
+ return android.hardware.radio.ims.ImsTrafficType.EMERGENCY_SMS;
+ case MmTelFeature.IMS_TRAFFIC_TYPE_VOICE:
+ return android.hardware.radio.ims.ImsTrafficType.VOICE;
+ case MmTelFeature.IMS_TRAFFIC_TYPE_VIDEO:
+ return android.hardware.radio.ims.ImsTrafficType.VIDEO;
+ case MmTelFeature.IMS_TRAFFIC_TYPE_SMS:
+ return android.hardware.radio.ims.ImsTrafficType.SMS;
+ case MmTelFeature.IMS_TRAFFIC_TYPE_REGISTRATION:
+ return android.hardware.radio.ims.ImsTrafficType.REGISTRATION;
+ }
+ return android.hardware.radio.ims.ImsTrafficType.UT_XCAP;
+ }
+
+ /**
+ * Convert the IMS traffic direction.
+ * @param trafficDirection Indicates the traffic direction.
+ * @return The converted IMS traffic direction.
+ */
+ public static int convertImsTrafficDirection(
+ @MmTelFeature.ImsTrafficDirection int trafficDirection) {
+ switch (trafficDirection) {
+ case MmTelFeature.IMS_TRAFFIC_DIRECTION_INCOMING:
+ return android.hardware.radio.ims.ImsCall.Direction.INCOMING;
+ default:
+ return android.hardware.radio.ims.ImsCall.Direction.OUTGOING;
+ }
+ }
+
+ /**
+ * Convert the IMS connection failure reason.
+ * @param halReason Specifies the reason that IMS connection failed.
+ * @return The converted IMS connection failure reason.
+ */
+ public static @ConnectionFailureInfo.FailureReason int convertHalConnectionFailureReason(
+ int halReason) {
+ switch (halReason) {
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_ACCESS_DENIED:
+ return ConnectionFailureInfo.REASON_ACCESS_DENIED;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_NAS_FAILURE:
+ return ConnectionFailureInfo.REASON_NAS_FAILURE;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_RACH_FAILURE:
+ return ConnectionFailureInfo.REASON_RACH_FAILURE;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_RLC_FAILURE:
+ return ConnectionFailureInfo.REASON_RLC_FAILURE;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_RRC_REJECT:
+ return ConnectionFailureInfo.REASON_RRC_REJECT;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_RRC_TIMEOUT:
+ return ConnectionFailureInfo.REASON_RRC_TIMEOUT;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_NO_SERVICE:
+ return ConnectionFailureInfo.REASON_NO_SERVICE;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_PDN_NOT_AVAILABLE:
+ return ConnectionFailureInfo.REASON_PDN_NOT_AVAILABLE;
+ case android.hardware.radio.ims.ConnectionFailureInfo
+ .ConnectionFailureReason.REASON_RF_BUSY:
+ return ConnectionFailureInfo.REASON_RF_BUSY;
+ }
+ return ConnectionFailureInfo.REASON_UNSPECIFIED;
+ }
+
/** Append the data to the end of an ArrayList */
public static void appendPrimitiveArrayToArrayList(byte[] src, ArrayList<Byte> dst) {
for (byte b : src) {
@@ -4983,6 +5265,9 @@
return "GET_SIM_PHONEBOOK_RECORDS";
case RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD:
return "UPDATE_SIM_PHONEBOOK_RECORD";
+ case RIL_REQUEST_DEVICE_IMEI:
+ return "DEVICE_IMEI";
+ /* The following requests are not defined in RIL.h */
case RIL_REQUEST_GET_SLOT_STATUS:
return "GET_SLOT_STATUS";
case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
@@ -5041,6 +5326,36 @@
return "SET_USAGE_SETTING";
case RIL_REQUEST_GET_USAGE_SETTING:
return "GET_USAGE_SETTING";
+ case RIL_REQUEST_SET_EMERGENCY_MODE:
+ return "SET_EMERGENCY_MODE";
+ case RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN:
+ return "TRIGGER_EMERGENCY_NETWORK_SCAN";
+ case RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN:
+ return "CANCEL_EMERGENCY_NETWORK_SCAN";
+ case RIL_REQUEST_EXIT_EMERGENCY_MODE:
+ return "EXIT_EMERGENCY_MODE";
+ case RIL_REQUEST_SET_SRVCC_CALL_INFO:
+ return "SET_SRVCC_CALL_INFO";
+ case RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO:
+ return "UPDATE_IMS_REGISTRATION_INFO";
+ case RIL_REQUEST_START_IMS_TRAFFIC:
+ return "START_IMS_TRAFFIC";
+ case RIL_REQUEST_STOP_IMS_TRAFFIC:
+ return "STOP_IMS_TRAFFIC";
+ case RIL_REQUEST_SEND_ANBR_QUERY:
+ return "SEND_ANBR_QUERY";
+ case RIL_REQUEST_TRIGGER_EPS_FALLBACK:
+ return "TRIGGER_EPS_FALLBACK";
+ case RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED:
+ return "SET_NULL_CIPHER_AND_INTEGRITY_ENABLED";
+ case RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED:
+ return "IS_NULL_CIPHER_AND_INTEGRITY_ENABLED";
+ case RIL_REQUEST_UPDATE_IMS_CALL_STATUS:
+ return "UPDATE_IMS_CALL_STATUS";
+ case RIL_REQUEST_SET_N1_MODE_ENABLED:
+ return "SET_N1_MODE_ENABLED";
+ case RIL_REQUEST_IS_N1_MODE_ENABLED:
+ return "IS_N1_MODE_ENABLED";
default:
return "<unknown request " + request + ">";
}
@@ -5161,6 +5476,9 @@
return "UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED";
case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED:
return "UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED";
+ case RIL_UNSOL_SLICING_CONFIG_CHANGED:
+ return "UNSOL_SLICING_CONFIG_CHANGED";
+ /* The follow unsols are not defined in RIL.h */
case RIL_UNSOL_ICC_SLOT_STATUS:
return "UNSOL_ICC_SLOT_STATUS";
case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
@@ -5173,8 +5491,16 @@
return "UNSOL_REGISTRATION_FAILED";
case RIL_UNSOL_BARRING_INFO_CHANGED:
return "UNSOL_BARRING_INFO_CHANGED";
+ case RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT:
+ return "UNSOL_EMERGENCY_NETWORK_SCAN_RESULT";
+ case RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION:
+ return "UNSOL_TRIGGER_IMS_DEREGISTRATION";
+ case RIL_UNSOL_CONNECTION_SETUP_FAILURE:
+ return "UNSOL_CONNECTION_SETUP_FAILURE";
+ case RIL_UNSOL_NOTIFY_ANBR:
+ return "UNSOL_NOTIFY_ANBR";
default:
- return "<unknown response>";
+ return "<unknown response " + response + ">";
}
}
@@ -5321,6 +5647,302 @@
return sb.toString();
}
+ /**
+ * Converts the list of call information for Single Radio Voice Call Continuity(SRVCC).
+ *
+ * @param srvccConnections The list of call information for SRVCC.
+ * @return The converted list of call information.
+ */
+ public static android.hardware.radio.ims.SrvccCall[] convertToHalSrvccCall(
+ SrvccConnection[] srvccConnections) {
+ if (srvccConnections == null) {
+ return new android.hardware.radio.ims.SrvccCall[0];
+ }
+
+ int length = srvccConnections.length;
+ android.hardware.radio.ims.SrvccCall[] srvccCalls =
+ new android.hardware.radio.ims.SrvccCall[length];
+
+ for (int i = 0; i < length; i++) {
+ srvccCalls[i] = new android.hardware.radio.ims.SrvccCall();
+ srvccCalls[i].index = i + 1;
+ srvccCalls[i].callType = convertSrvccCallType(srvccConnections[i].getType());
+ srvccCalls[i].callState = convertCallState(srvccConnections[i].getState());
+ srvccCalls[i].callSubstate =
+ convertSrvccCallSubState(srvccConnections[i].getSubState());
+ srvccCalls[i].ringbackToneType =
+ convertSrvccCallRingbackToneType(srvccConnections[i].getRingbackToneType());
+ srvccCalls[i].isMpty = srvccConnections[i].isMultiParty();
+ srvccCalls[i].isMT = srvccConnections[i].isIncoming();
+ srvccCalls[i].number = TextUtils.emptyIfNull(srvccConnections[i].getNumber());
+ srvccCalls[i].numPresentation =
+ convertPresentation(srvccConnections[i].getNumberPresentation());
+ srvccCalls[i].name = TextUtils.emptyIfNull(srvccConnections[i].getName());
+ srvccCalls[i].namePresentation =
+ convertPresentation(srvccConnections[i].getNamePresentation());
+ }
+
+ return srvccCalls;
+ }
+
+ /**
+ * Converts the call type.
+ *
+ * @param type The call type.
+ * @return The converted call type.
+ */
+ public static int convertSrvccCallType(int type) {
+ switch (type) {
+ case SrvccConnection.CALL_TYPE_NORMAL:
+ return android.hardware.radio.ims.SrvccCall.CallType.NORMAL;
+ case SrvccConnection.CALL_TYPE_EMERGENCY:
+ return android.hardware.radio.ims.SrvccCall.CallType.EMERGENCY;
+ default:
+ throw new RuntimeException("illegal call type " + type);
+ }
+ }
+
+ /**
+ * Converts the call state.
+ *
+ * @param state The call state.
+ * @return The converted call state.
+ */
+ public static int convertCallState(Call.State state) {
+ switch (state) {
+ case ACTIVE: return android.hardware.radio.voice.Call.STATE_ACTIVE;
+ case HOLDING: return android.hardware.radio.voice.Call.STATE_HOLDING;
+ case DIALING: return android.hardware.radio.voice.Call.STATE_DIALING;
+ case ALERTING: return android.hardware.radio.voice.Call.STATE_ALERTING;
+ case INCOMING: return android.hardware.radio.voice.Call.STATE_INCOMING;
+ case WAITING: return android.hardware.radio.voice.Call.STATE_WAITING;
+ default:
+ throw new RuntimeException("illegal state " + state);
+ }
+ }
+
+ /**
+ * Converts the substate of a call.
+ *
+ * @param state The substate of a call.
+ * @return The converted substate.
+ */
+ public static int convertSrvccCallSubState(int state) {
+ switch (state) {
+ case SrvccConnection.SUBSTATE_NONE:
+ return android.hardware.radio.ims.SrvccCall.CallSubState.NONE;
+ case SrvccConnection.SUBSTATE_PREALERTING:
+ return android.hardware.radio.ims.SrvccCall.CallSubState.PREALERTING;
+ default:
+ throw new RuntimeException("illegal substate " + state);
+ }
+ }
+
+ /**
+ * Converts the ringback tone type.
+ *
+ * @param type The ringback tone type.
+ * @return The converted ringback tone type.
+ */
+ public static int convertSrvccCallRingbackToneType(int type) {
+ switch (type) {
+ case SrvccConnection.TONE_NONE:
+ return android.hardware.radio.ims.SrvccCall.ToneType.NONE;
+ case SrvccConnection.TONE_LOCAL:
+ return android.hardware.radio.ims.SrvccCall.ToneType.LOCAL;
+ case SrvccConnection.TONE_NETWORK:
+ return android.hardware.radio.ims.SrvccCall.ToneType.NETWORK;
+ default:
+ throw new RuntimeException("illegal ringback tone type " + type);
+ }
+ }
+
+ /**
+ * Converts the number presentation type for caller id display.
+ *
+ * @param presentation The number presentation type.
+ * @return The converted presentation type.
+ */
+ public static int convertPresentation(int presentation) {
+ switch (presentation) {
+ case PhoneConstants.PRESENTATION_ALLOWED:
+ return android.hardware.radio.voice.Call.PRESENTATION_ALLOWED;
+ case PhoneConstants.PRESENTATION_RESTRICTED:
+ return android.hardware.radio.voice.Call.PRESENTATION_RESTRICTED;
+ case PhoneConstants.PRESENTATION_UNKNOWN:
+ return android.hardware.radio.voice.Call.PRESENTATION_UNKNOWN;
+ case PhoneConstants.PRESENTATION_PAYPHONE:
+ return android.hardware.radio.voice.Call.PRESENTATION_PAYPHONE;
+ default:
+ throw new RuntimeException("illegal presentation " + presentation);
+ }
+ }
+
+ /**
+ * Converts IMS registration state.
+ *
+ * @param state The IMS registration state.
+ * @return The converted HAL IMS registration state.
+ */
+ public static int convertImsRegistrationState(int state) {
+ switch (state) {
+ case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED:
+ return android.hardware.radio.ims.ImsRegistrationState.NOT_REGISTERED;
+ case RegistrationManager.REGISTRATION_STATE_REGISTERED:
+ return android.hardware.radio.ims.ImsRegistrationState.REGISTERED;
+ default:
+ throw new RuntimeException("illegal state " + state);
+ }
+ }
+
+ /**
+ * Converts IMS service radio technology.
+ *
+ * @param imsRadioTech The IMS service radio technology.
+ * @return The converted HAL access network type.
+ */
+
+ public static int convertImsRegistrationTech(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ switch (imsRadioTech) {
+ case ImsRegistrationImplBase.REGISTRATION_TECH_LTE:
+ return android.hardware.radio.AccessNetwork.EUTRAN;
+ case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN:
+ return android.hardware.radio.AccessNetwork.IWLAN;
+ case ImsRegistrationImplBase.REGISTRATION_TECH_NR:
+ return android.hardware.radio.AccessNetwork.NGRAN;
+ case ImsRegistrationImplBase.REGISTRATION_TECH_3G:
+ return android.hardware.radio.AccessNetwork.UTRAN;
+ default:
+ return android.hardware.radio.AccessNetwork.UNKNOWN;
+ }
+ }
+
+ /**
+ * Converts IMS capabilities.
+ *
+ * @param capabilities The IMS capabilities.
+ * @return The converted HAL IMS capabilities.
+ */
+ public static int convertImsCapability(int capabilities) {
+ int halCapabilities = android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_NONE;
+ if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE) > 0) {
+ halCapabilities |=
+ android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VOICE;
+ }
+ if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO) > 0) {
+ halCapabilities |=
+ android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VIDEO;
+ }
+ if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_SMS) > 0) {
+ halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_SMS;
+ }
+ if ((capabilities & CommandsInterface.IMS_RCS_CAPABILITIES) > 0) {
+ halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_RCS_CAPABILITIES;
+ }
+ return halCapabilities;
+ }
+
+ /** Converts the ImsCallInfo instances to HAL ImsCall instances. */
+ public static android.hardware.radio.ims.ImsCall[] convertImsCallInfo(
+ List<ImsCallInfo> imsCallInfos) {
+ if (imsCallInfos == null) {
+ return new android.hardware.radio.ims.ImsCall[0];
+ }
+
+ int length = 0;
+ for (int i = 0; i < imsCallInfos.size(); i++) {
+ if (imsCallInfos.get(i) != null) length++;
+ }
+ if (length == 0) {
+ return new android.hardware.radio.ims.ImsCall[0];
+ }
+
+ android.hardware.radio.ims.ImsCall[] halInfos =
+ new android.hardware.radio.ims.ImsCall[length];
+
+ int index = 0;
+ for (int i = 0; i < imsCallInfos.size(); i++) {
+ ImsCallInfo info = imsCallInfos.get(i);
+ if (info == null) continue;
+
+ halInfos[index] = new android.hardware.radio.ims.ImsCall();
+ halInfos[index].index = info.getIndex();
+ halInfos[index].callState = convertToHalImsCallState(info.getCallState());
+ halInfos[index].callType = info.isEmergencyCall()
+ ? android.hardware.radio.ims.ImsCall.CallType.EMERGENCY
+ : android.hardware.radio.ims.ImsCall.CallType.NORMAL;
+ halInfos[index].accessNetwork = convertToHalAccessNetworkAidl(info.getCallRadioTech());
+ halInfos[index].direction = info.isIncoming()
+ ? android.hardware.radio.ims.ImsCall.Direction.INCOMING
+ : android.hardware.radio.ims.ImsCall.Direction.OUTGOING;
+ halInfos[index].isHeldByRemote = info.isHeldByRemote();
+ index++;
+ }
+
+ return halInfos;
+ }
+
+ /**
+ * Convert satellite-related errors from CommandException.Error to
+ * SatelliteManager.SatelliteServiceResult.
+ * @param error The satellite error.
+ * @return The converted SatelliteServiceResult.
+ */
+ @SatelliteManager.SatelliteError
+ public static int convertToSatelliteError(
+ CommandException.Error error) {
+ switch (error) {
+ case INTERNAL_ERR:
+ //fallthrough to SYSTEM_ERR
+ case MODEM_ERR:
+ //fallthrough to SYSTEM_ERR
+ case SYSTEM_ERR:
+ return SatelliteManager.SATELLITE_MODEM_ERROR;
+ case INVALID_ARGUMENTS:
+ return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+ case INVALID_MODEM_STATE:
+ return SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+ case RADIO_NOT_AVAILABLE:
+ return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
+ case REQUEST_NOT_SUPPORTED:
+ return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED;
+ case NO_MEMORY:
+ //fallthrough to NO_RESOURCES
+ case NO_RESOURCES:
+ return SatelliteManager.SATELLITE_NO_RESOURCES;
+ case NETWORK_ERR:
+ return SatelliteManager.SATELLITE_NETWORK_ERROR;
+ case NO_NETWORK_FOUND:
+ return SatelliteManager.SATELLITE_NOT_REACHABLE;
+ case ABORTED:
+ return SatelliteManager.SATELLITE_REQUEST_ABORTED;
+ case ACCESS_BARRED:
+ return SatelliteManager.SATELLITE_ACCESS_BARRED;
+ default:
+ return SatelliteManager.SATELLITE_ERROR;
+ }
+ }
+
+ /**
+ * Converts the call state to HAL IMS call state.
+ *
+ * @param state The {@link Call.State}.
+ * @return The converted {@link android.hardware.radio.ims.ImsCall.CallState}.
+ */
+ private static int convertToHalImsCallState(Call.State state) {
+ switch (state) {
+ case ACTIVE: return android.hardware.radio.ims.ImsCall.CallState.ACTIVE;
+ case HOLDING: return android.hardware.radio.ims.ImsCall.CallState.HOLDING;
+ case DIALING: return android.hardware.radio.ims.ImsCall.CallState.DIALING;
+ case ALERTING: return android.hardware.radio.ims.ImsCall.CallState.ALERTING;
+ case INCOMING: return android.hardware.radio.ims.ImsCall.CallState.INCOMING;
+ case WAITING: return android.hardware.radio.ims.ImsCall.CallState.WAITING;
+ case DISCONNECTING: return android.hardware.radio.ims.ImsCall.CallState.DISCONNECTING;
+ default: return android.hardware.radio.ims.ImsCall.CallState.DISCONNECTED;
+ }
+ }
+
private static void logd(String log) {
Rlog.d("RILUtils", log);
}
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 9ac03b1..3e2be1d 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -36,6 +36,7 @@
import android.os.Registrant;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Trace;
import android.os.WorkSource;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotMapping;
@@ -44,7 +45,6 @@
import com.android.telephony.Rlog;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
@@ -342,6 +342,9 @@
private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
RILRequest rr = RILRequest.obtain(request, result, workSource);
+ Trace.asyncTraceForTrackBegin(
+ Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial);
+
synchronized (mRequestList) {
mRequestList.append(rr.mSerial, rr);
}
@@ -352,7 +355,10 @@
RILRequest rr;
synchronized (mRequestList) {
rr = mRequestList.get(serial);
+
if (rr != null) {
+ Trace.asyncTraceForTrackEnd(
+ Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial);
mRequestList.remove(serial);
}
}
@@ -636,4 +642,9 @@
private static void loge(String log) {
Rlog.e(TAG, log);
}
+
+ @Override
+ public String toString() {
+ return "RadioConfig[" + "mRadioConfigProxy=" + mRadioConfigProxy + ']';
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
index 851a8ef..76e6d5e 100644
--- a/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
+++ b/src/java/com/android/internal/telephony/RadioConfigIndicationAidl.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.os.AsyncResult;
+import android.os.Trace;
import com.android.internal.telephony.uicc.IccSlotStatus;
import com.android.telephony.Rlog;
@@ -43,7 +44,7 @@
public void simSlotsStatusChanged(
int type, android.hardware.radio.config.SimSlotStatus[] slotStatus) {
ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus);
- logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
+ logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
if (mRadioConfig.mSimSlotStatusRegistrant != null) {
mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
new AsyncResult(null, ret, null));
@@ -51,7 +52,8 @@
}
private static void logd(String log) {
- Rlog.d(TAG, log);
+ Rlog.d(TAG, "[UNSL]< " + log);
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", log);
}
@Override
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java b/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java
index 23a676b..3b5330b 100644
--- a/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java
+++ b/src/java/com/android/internal/telephony/RadioConfigIndicationHidl.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.os.AsyncResult;
+import android.os.Trace;
import com.android.internal.telephony.uicc.IccSlotStatus;
import com.android.telephony.Rlog;
@@ -42,7 +43,7 @@
public void simSlotsStatusChanged(int indicationType,
ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> slotStatus) {
ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus);
- logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
+ logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
if (mRadioConfig.mSimSlotStatusRegistrant != null) {
mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
new AsyncResult(null, ret, null));
@@ -55,7 +56,7 @@
public void simSlotsStatusChanged_1_2(int indicationType,
ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> slotStatus) {
ArrayList<IccSlotStatus> ret = RILUtils.convertHalSlotStatus(slotStatus);
- logd("[UNSL]< UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
+ logd("UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
if (mRadioConfig.mSimSlotStatusRegistrant != null) {
mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
new AsyncResult(null, ret, null));
@@ -63,6 +64,7 @@
}
private static void logd(String log) {
- Rlog.d(TAG, log);
+ Rlog.d(TAG, "[UNSL]< " + log);
+ Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", log);
}
}
diff --git a/src/java/com/android/internal/telephony/RadioConfigProxy.java b/src/java/com/android/internal/telephony/RadioConfigProxy.java
index a8601f1..edeb558 100644
--- a/src/java/com/android/internal/telephony/RadioConfigProxy.java
+++ b/src/java/com/android/internal/telephony/RadioConfigProxy.java
@@ -336,4 +336,11 @@
mRadioConfig.obtainMessage(RadioConfig.EVENT_AIDL_SERVICE_DEAD));
}
}
+
+ @Override
+ public String toString() {
+ return "RadioConfigProxy["
+ + "mRadioHalVersion=" + mRadioHalVersion
+ + ", mRadioConfigHalVersion=" + mRadioConfigHalVersion + ']';
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioDataProxy.java b/src/java/com/android/internal/telephony/RadioDataProxy.java
index cbc762a..9671077 100644
--- a/src/java/com/android/internal/telephony/RadioDataProxy.java
+++ b/src/java/com/android/internal/telephony/RadioDataProxy.java
@@ -45,12 +45,22 @@
* Set IRadioData as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param data IRadioData implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) {
- mHalVersion = halVersion;
+ public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) {
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(data.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mDataProxy = data;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
diff --git a/src/java/com/android/internal/telephony/RadioImsProxy.java b/src/java/com/android/internal/telephony/RadioImsProxy.java
new file mode 100644
index 0000000..cde2e06
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioImsProxy.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+/**
+ * A holder for IRadioIms.
+ * Use getAidl to get IRadioIms and call the AIDL implementations of the HAL APIs.
+ */
+public class RadioImsProxy extends RadioServiceProxy {
+ private static final String TAG = "RadioImsProxy";
+ private volatile android.hardware.radio.ims.IRadioIms mImsProxy = null;
+
+ /**
+ * Sets IRadioIms as the AIDL implementation for RadioServiceProxy.
+ * @param halVersion Radio HAL version.
+ * @param ims IRadioIms implementation.
+ *
+ * @return updated HAL version.
+ */
+ public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.ims.IRadioIms ims) {
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(ims.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
+ mImsProxy = ims;
+ mIsAidl = true;
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
+ }
+
+ /**
+ * Gets the AIDL implementation of RadioImsProxy.
+ * @return IRadioIms implementation.
+ */
+ public android.hardware.radio.ims.IRadioIms getAidl() {
+ return mImsProxy;
+ }
+
+ /**
+ * Resets RadioImsProxy.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ mImsProxy = null;
+ }
+
+ /**
+ * Checks whether a RadioIms implementation exists.
+ * @return true if there is neither a HIDL nor AIDL implementation.
+ */
+ @Override
+ public boolean isEmpty() {
+ return mRadioProxy == null && mImsProxy == null;
+ }
+
+ /**
+ * No implementation in IRadioIms.
+ * @throws RemoteException.
+ */
+ @Override
+ public void responseAcknowledgement() throws RemoteException {
+ /* Currently, IRadioIms doesn't support the following response types:
+ * - RadioIndicationType.UNSOLICITED_ACK_EXP
+ * - RadioResponseType.SOLICITED_ACK_EXP */
+ // no-op
+ }
+
+ /**
+ * Calls IRadioIms#setSrvccCallInfo.
+ * @param serial Serial number of request.
+ * @param srvccCalls The list of call information.
+ * @throws RemoteException.
+ */
+ public void setSrvccCallInfo(int serial,
+ android.hardware.radio.ims.SrvccCall[] srvccCalls) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.setSrvccCallInfo(serial, srvccCalls);
+ }
+ }
+
+ /**
+ * Calls IRadioIms#updateImsRegistrationInfo.
+ * @param serial Serial number of request.
+ * @param registrationInfo The registration state information.
+ * @throws RemoteException.
+ */
+ public void updateImsRegistrationInfo(int serial,
+ android.hardware.radio.ims.ImsRegistration registrationInfo) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.updateImsRegistrationInfo(serial, registrationInfo);
+ }
+ }
+
+ /**
+ * Calls IRadioIms#startImsTraffic.
+ * @param serial Serial number of request.
+ * @param token A nonce to identify the request.
+ * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+ * @param accessNetworkType The type of underlying radio access network used.
+ * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+ * mobile terminated use case eg. MO/MT call/SMS etc.
+ * @throws RemoteException.
+ */
+ public void startImsTraffic(int serial, int token, int trafficType, int accessNetworkType,
+ int trafficDirection) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.startImsTraffic(serial,
+ token, trafficType, accessNetworkType, trafficDirection);
+ }
+ }
+
+ /**
+ * Calls IRadioIms#stopImsTraffic.
+ * @param serial Serial number of request.
+ * @param token The token assigned by startImsTraffic.
+ * @throws RemoteException.
+ */
+ public void stopImsTraffic(int serial, int token)
+ throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.stopImsTraffic(serial, token);
+ }
+ }
+
+ /**
+ * Calls IRadioIms#triggerEpsFallback.
+ * @param serial Serial number of request.
+ * @param reason Specifies the reason for EPS fallback.
+ * @throws RemoteException.
+ */
+ public void triggerEpsFallback(int serial, int reason)
+ throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.triggerEpsFallback(serial, reason);
+ }
+ }
+
+ /**
+ * Calls IRadioIms#sendAnbrQuery.
+ * @param serial Serial number of request.
+ * @param mediaType Media type is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond The bit rate requested by the opponent UE.
+ * @throws RemoteException.
+ */
+ public void sendAnbrQuery(int serial, int mediaType, int direction, int bitsPerSecond)
+ throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.sendAnbrQuery(serial, mediaType, direction, bitsPerSecond);
+ }
+ }
+
+ /**
+ * Call IRadioIms#updateImsCallStatus
+ * @param serial Serial number of request.
+ * @param imsCalls The list of call status information.
+ * @throws RemoteException.
+ */
+ public void updateImsCallStatus(int serial,
+ android.hardware.radio.ims.ImsCall[] imsCalls) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mImsProxy.updateImsCallStatus(serial, imsCalls);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index c7244be..4f75412 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony;
-import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION;
@@ -136,21 +136,21 @@
* @param radioState android.hardware.radio.V1_0.RadioState
*/
public void radioStateChanged(int indicationType, int radioState) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int state = RILUtils.convertHalRadioState(radioState);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " +
- state);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogMore(
+ RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + state);
}
mRil.setRadioState(state, false /* forceNotifyRegistrants */);
}
public void callStateChanged(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
mRil.mCallStateRegistrants.notifyRegistrants();
}
@@ -160,18 +160,18 @@
* @param indicationType RadioIndicationType
*/
public void networkStateChanged(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
mRil.mNetworkStateRegistrants.notifyRegistrants();
}
public void newSms(int indicationType, ArrayList<Byte> pdu) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pduArray);
if (mRil.mGsmSmsRegistrant != null) {
@@ -181,10 +181,10 @@
}
public void newSmsStatusReport(int indicationType, ArrayList<Byte> pdu) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
if (mRil.mSmsStatusRegistrant != null) {
mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pduArray, null));
@@ -192,9 +192,9 @@
}
public void newSmsOnSim(int indicationType, int recordNumber) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
if (mRil.mSmsOnSimRegistrant != null) {
mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null));
@@ -202,9 +202,9 @@
}
public void onUssd(int indicationType, int ussdModeType, String msg) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
+ if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
// todo: Clean this up with a parcelable class for better self-documentation
String[] resp = new String[2];
@@ -216,9 +216,9 @@
}
public void nitzTimeReceived(int indicationType, String nitzTime, long receivedTime) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
// todo: Clean this up with a parcelable class for better self-documentation
Object[] result = new Object[2];
@@ -228,7 +228,7 @@
boolean ignoreNitz = TelephonyProperties.ignore_nitz().orElse(false);
if (ignoreNitz) {
- if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
+ if (mRil.isLogOrTrace()) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
} else {
if (mRil.mNITZTimeRegistrant != null) {
mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult (null, result, null));
@@ -241,13 +241,13 @@
public void currentSignalStrength(int indicationType,
android.hardware.radio.V1_0.SignalStrength signalStrength) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
SignalStrength ss = mRil.fixupSignalStrength10(ssInitial);
// Note this is set to "verbose" because it happens frequently
- if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+ if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
if (mRil.mSignalStrengthRegistrant != null) {
mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult (null, ss, null));
@@ -259,11 +259,11 @@
*/
public void currentLinkCapacityEstimate(int indicationType,
android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
if (mRil.mLceInfoRegistrants != null) {
mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -275,11 +275,11 @@
*/
public void currentLinkCapacityEstimate_1_6(int indicationType,
android.hardware.radio.V1_6.LinkCapacityEstimate lce) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
if (mRil.mLceInfoRegistrants != null) {
mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -291,11 +291,11 @@
*/
public void currentSignalStrength_1_2(int indicationType,
android.hardware.radio.V1_2.SignalStrength signalStrength) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
// Note this is set to "verbose" because it happens frequently
- if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+ if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
if (mRil.mSignalStrengthRegistrant != null) {
mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
@@ -308,11 +308,11 @@
public void currentSignalStrength_1_4(int indicationType,
android.hardware.radio.V1_4.SignalStrength signalStrength) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
- if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+ if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
if (mRil.mSignalStrengthRegistrant != null) {
mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
@@ -325,11 +325,11 @@
public void currentSignalStrength_1_6(int indicationType,
android.hardware.radio.V1_6.SignalStrength signalStrength) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
- if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+ if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
if (mRil.mSignalStrengthRegistrant != null) {
mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
@@ -341,7 +341,7 @@
*/
public void currentPhysicalChannelConfigs_1_4(int indicationType,
ArrayList<android.hardware.radio.V1_4.PhysicalChannelConfig> configs) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
physicalChannelConfigsIndication(configs);
}
@@ -350,7 +350,7 @@
*/
public void currentPhysicalChannelConfigs_1_6(int indicationType,
ArrayList<android.hardware.radio.V1_6.PhysicalChannelConfig> configs) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
physicalChannelConfigsIndication(configs);
}
@@ -359,7 +359,7 @@
*/
public void currentPhysicalChannelConfigs(int indicationType,
ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
physicalChannelConfigsIndication(configs);
}
@@ -368,7 +368,7 @@
*/
public void currentEmergencyNumberList(int indicationType,
ArrayList<android.hardware.radio.V1_4.EmergencyNumber> emergencyNumberList) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.size());
for (android.hardware.radio.V1_4.EmergencyNumber emergencyNumberHal
@@ -380,7 +380,7 @@
response.add(emergencyNumber);
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response);
// Cache emergency number list from last indication.
mRil.cacheEmergencyNumberListIndication(response);
@@ -422,7 +422,7 @@
}
public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
SuppServiceNotification notification = new SuppServiceNotification();
notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
@@ -431,7 +431,9 @@
notification.type = suppSvcNotification.type;
notification.number = suppSvcNotification.number;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+ }
if (mRil.mSsnRegistrant != null) {
mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult (null, notification, null));
@@ -439,9 +441,9 @@
}
public void stkSessionEnd(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
if (mRil.mCatSessionEndRegistrant != null) {
mRil.mCatSessionEndRegistrant.notifyRegistrant(new AsyncResult (null, null, null));
@@ -449,9 +451,9 @@
}
public void stkProactiveCommand(int indicationType, String cmd) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
if (mRil.mCatProCmdRegistrant != null) {
mRil.mCatProCmdRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null));
@@ -459,9 +461,9 @@
}
public void stkEventNotify(int indicationType, String cmd) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
if (mRil.mCatEventRegistrant != null) {
mRil.mCatEventRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null));
@@ -469,9 +471,9 @@
}
public void stkCallSetup(int indicationType, long timeout) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
if (mRil.mCatCallSetUpRegistrant != null) {
mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult (null, timeout, null));
@@ -479,9 +481,9 @@
}
public void simSmsStorageFull(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
if (mRil.mIccSmsFullRegistrant != null) {
mRil.mIccSmsFullRegistrant.notifyRegistrant();
@@ -489,20 +491,20 @@
}
public void simRefresh(int indicationType, SimRefreshResult refreshResult) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
IccRefreshResponse response = new IccRefreshResponse();
response.refreshResult = refreshResult.type;
response.efId = refreshResult.efId;
response.aid = refreshResult.aid;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response);
mRil.mIccRefreshRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
}
public void callRing(int indicationType, boolean isGsm, CdmaSignalInfoRecord record) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
char response[] = null;
@@ -517,7 +519,7 @@
mRil.writeMetricsCallRing(response);
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response);
if (mRil.mRingRegistrant != null) {
mRil.mRingRegistrant.notifyRegistrant(new AsyncResult (null, response, null));
@@ -525,17 +527,17 @@
}
public void simStatusChanged(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
mRil.mIccStatusChangedRegistrants.notifyRegistrants();
}
public void cdmaNewSms(int indicationType, CdmaSmsMessage msg) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
SmsMessage sms = new SmsMessage(RILUtils.convertHalCdmaSmsMessage(msg));
if (mRil.mCdmaSmsRegistrant != null) {
@@ -544,12 +546,12 @@
}
public void newBroadcastSms(int indicationType, ArrayList<Byte> data) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
byte[] response = RILUtils.arrayListToPrimitiveArray(data);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
- IccUtils.bytesToHexString(response));
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogvRet(
+ RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(response));
}
if (mRil.mGsmBroadcastSmsRegistrant != null) {
@@ -558,9 +560,9 @@
}
public void cdmaRuimSmsStorageFull(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
if (mRil.mIccSmsFullRegistrant != null) {
mRil.mIccSmsFullRegistrant.notifyRegistrant();
@@ -568,9 +570,9 @@
}
public void restrictedStateChanged(int indicationType, int state) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
+ if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
if (mRil.mRestrictedStateRegistrant != null) {
mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult (null, state, null));
@@ -578,9 +580,9 @@
}
public void enterEmergencyCallbackMode(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
if (mRil.mEmergencyCallbackModeRegistrant != null) {
mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
@@ -588,7 +590,7 @@
}
public void cdmaCallWaiting(int indicationType, CdmaCallWaiting callWaitingRecord) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
// todo: create a CdmaCallWaitingNotification constructor that takes in these fields to make
// sure no fields are missing
@@ -605,26 +607,28 @@
notification.numberType = callWaitingRecord.numberType;
notification.numberPlan = callWaitingRecord.numberPlan;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification);
mRil.mCallWaitingInfoRegistrants.notifyRegistrants(
new AsyncResult (null, notification, null));
}
public void cdmaOtaProvisionStatus(int indicationType, int status) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = status;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response);
+ }
mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
}
public void cdmaInfoRec(int indicationType,
android.hardware.radio.V1_0.CdmaInformationRecords records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int numberOfInfoRecs = records.infoRec.size();
for (int i = 0; i < numberOfInfoRecs; i++) {
@@ -712,63 +716,67 @@
+ CdmaInformationRecords.idToString(id) + " ");
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords);
+ }
mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords);
}
}
public void indicateRingbackTone(int indicationType, boolean start) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
+ if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null));
}
public void resendIncallMute(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
mRil.mResendIncallMuteRegistrants.notifyRegistrants();
}
public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = cdmaSource;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response);
+ }
mRil.mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
}
public void cdmaPrlChanged(int indicationType, int version) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = version;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
mRil.mCdmaPrlChangedRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
}
public void exitEmergencyCallbackMode(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants();
}
public void rilConnected(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
// Initial conditions
mRil.setRadioPower(false, null);
@@ -779,12 +787,14 @@
}
public void voiceRadioTechChanged(int indicationType, int rat) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = rat;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+ }
mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
@@ -793,49 +803,49 @@
/** Get unsolicited message for cellInfoList */
public void cellInfoList(int indicationType,
ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
responseCellInfoList(records);
}
/** Get unsolicited message for cellInfoList using HAL V1_2 */
public void cellInfoList_1_2(int indicationType,
ArrayList<android.hardware.radio.V1_2.CellInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
responseCellInfoList(records);
}
/** Get unsolicited message for cellInfoList using HAL V1_4 */
public void cellInfoList_1_4(int indicationType,
ArrayList<android.hardware.radio.V1_4.CellInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
responseCellInfoList(records);
}
/** Get unsolicited message for cellInfoList using HAL V1_5 */
public void cellInfoList_1_5(int indicationType,
ArrayList<android.hardware.radio.V1_5.CellInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
responseCellInfoList(records);
}
/** Get unsolicited message for cellInfoList using HAL V1_5 */
public void cellInfoList_1_6(int indicationType,
ArrayList<android.hardware.radio.V1_6.CellInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
responseCellInfoList(records);
}
private void responseCellInfoList(ArrayList<? extends Object> records) {
ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList((ArrayList<Object>) records);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
/** Get unsolicited message for uicc applications enablement changes. */
public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) {
+ if (mRil.isLogOrTrace()) {
mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled);
}
@@ -873,32 +883,34 @@
}
public void imsNetworkStateChanged(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants();
}
public void subscriptionStatusChanged(int indicationType, boolean activate) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = activate ? 1 : 0;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response);
+ }
mRil.mSubscriptionStatusRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
}
public void srvccStateNotify(int indicationType, int state) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int response[] = new int[1];
response[0] = state;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response);
mRil.writeMetricsSrvcc(state);
@@ -909,11 +921,11 @@
public void hardwareConfigChanged(
int indicationType,
ArrayList<android.hardware.radio.V1_0.HardwareConfig> configs) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
mRil.mHardwareConfigChangeRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
@@ -921,18 +933,18 @@
public void radioCapabilityIndication(int indicationType,
android.hardware.radio.V1_0.RadioCapability rc) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
RadioCapability response = RILUtils.convertHalRadioCapability(rc, mRil);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants(
new AsyncResult (null, response, null));
}
public void onSupplementaryServiceIndication(int indicationType, StkCcUnsolSsResult ss) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
int num;
SsData ssData = new SsData();
@@ -972,7 +984,7 @@
}
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData);
if (mRil.mSsRegistrant != null) {
mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null));
@@ -980,9 +992,9 @@
}
public void stkCallControlAlphaNotify(int indicationType, String alpha) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
if (mRil.mCatCcAlphaRegistrant != null) {
mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult (null, alpha, null));
@@ -990,11 +1002,11 @@
}
public void lceData(int indicationType, LceDataInfo lce) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
if (mRil.mLceInfoRegistrants != null) {
mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -1002,20 +1014,20 @@
}
public void pcoData(int indicationType, PcoDataInfo pco) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId,
RILUtils.arrayListToPrimitiveArray(pco.contents));
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
public void modemReset(int indicationType, String reason) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
mRil.writeMetricsModemRestartEvent(reason);
mRil.mModemResetRegistrants.notifyRegistrants(new AsyncResult(null, reason, null));
@@ -1026,9 +1038,11 @@
* @param indicationType RadioIndicationType
*/
public void carrierInfoForImsiEncryption(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
+ }
mRil.mCarrierInfoForImsiEncryptionRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
@@ -1041,11 +1055,12 @@
*/
public void keepaliveStatus(
int indicationType, android.hardware.radio.V1_1.KeepaliveStatus halStatus) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS,
- "handle=" + halStatus.sessionHandle + " code=" + halStatus.code);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(
+ RIL_UNSOL_KEEPALIVE_STATUS,
+ "handle=" + halStatus.sessionHandle + " code=" + halStatus.code);
}
KeepaliveStatus ks = new KeepaliveStatus(
@@ -1059,9 +1074,9 @@
* @param indicationType RadioIndicationType
*/
public void simPhonebookChanged(int indicationType) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) {
+ if (mRil.isLogOrTrace()) {
mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
}
@@ -1076,7 +1091,7 @@
*/
public void simPhonebookRecordsReceived(int indicationType, byte status,
ArrayList<PhonebookRecordInfo> records) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>();
@@ -1084,8 +1099,9 @@
simPhonebookRecords.add(RILUtils.convertHalPhonebookRecordInfo(record));
}
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(
+ RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
"status = " + status + " received " + records.size() + " records");
}
@@ -1108,7 +1124,7 @@
android.hardware.radio.V1_5.CellIdentity cellIdentity, String chosenPlmn,
@NetworkRegistrationInfo.Domain int domain,
int causeCode, int additionalCauseCode) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity);
if (ci == null
|| TextUtils.isEmpty(chosenPlmn)
@@ -1116,7 +1132,7 @@
|| (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0
|| causeCode < 0 || additionalCauseCode < 0
|| (causeCode == Integer.MAX_VALUE && additionalCauseCode == Integer.MAX_VALUE)) {
- reportAnomaly(
+ AnomalyReporter.reportAnomaly(
UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb4"),
"Invalid registrationFailed indication");
@@ -1139,10 +1155,10 @@
public void barringInfoChanged(int indicationType,
android.hardware.radio.V1_5.CellIdentity cellIdentity,
ArrayList<android.hardware.radio.V1_5.BarringInfo> barringInfos) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
if (cellIdentity == null || barringInfos == null) {
- reportAnomaly(
+ AnomalyReporter.reportAnomaly(
UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"),
"Invalid barringInfoChanged indication");
@@ -1208,22 +1224,29 @@
android.hardware.radio.V1_6.PhysicalChannelConfig config =
(android.hardware.radio.V1_6.PhysicalChannelConfig) obj;
PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
+ int band = PhysicalChannelConfig.BAND_UNKNOWN;
switch (config.band.getDiscriminator()) {
case Band.hidl_discriminator.geranBand:
- builder.setBand(config.band.geranBand());
+ band = config.band.geranBand();
break;
case Band.hidl_discriminator.utranBand:
- builder.setBand(config.band.utranBand());
+ band = config.band.utranBand();
break;
case Band.hidl_discriminator.eutranBand:
- builder.setBand(config.band.eutranBand());
+ band = config.band.eutranBand();
break;
case Band.hidl_discriminator.ngranBand:
- builder.setBand(config.band.ngranBand());
+ band = config.band.ngranBand();
break;
default:
mRil.riljLoge("Unsupported band " + config.band.getDiscriminator());
}
+ if (band == PhysicalChannelConfig.BAND_UNKNOWN) {
+ mRil.riljLoge("Unsupported unknown band.");
+ return;
+ } else {
+ builder.setBand(band);
+ }
response.add(builder.setCellConnectionStatus(
RILUtils.convertHalCellConnectionStatus(config.status))
.setDownlinkChannelNumber(config.downlinkChannelNumber)
@@ -1240,13 +1263,14 @@
}
}
} catch (IllegalArgumentException iae) {
- reportAnomaly(UUID.fromString("918f0970-9aa9-4bcd-a28e-e49a83fe77d5"),
- "RIL reported invalid PCC (HIDL)");
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("918f0970-9aa9-4bcd-a28e-e49a83fe77d5"),
+ "RIL reported invalid PCC (HIDL)");
mRil.riljLoge("Invalid PhysicalChannelConfig " + iae);
return;
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -1254,63 +1278,63 @@
private void responseNetworkScan(int indicationType,
android.hardware.radio.V1_1.NetworkScanResult result) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<CellInfo> cellInfos =
RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
private void responseNetworkScan_1_2(int indicationType,
android.hardware.radio.V1_2.NetworkScanResult result) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<CellInfo> cellInfos =
RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
private void responseNetworkScan_1_4(int indicationType,
android.hardware.radio.V1_4.NetworkScanResult result) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<CellInfo> cellInfos =
RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
private void responseNetworkScan_1_5(int indicationType,
android.hardware.radio.V1_5.NetworkScanResult result) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<CellInfo> cellInfos =
RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
private void responseNetworkScan_1_6(int indicationType,
android.hardware.radio.V1_6.NetworkScanResult result) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
ArrayList<CellInfo> cellInfos =
RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
private void responseDataCallListChanged(int indicationType, List<?> dcList) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList);
mRil.mDataCallListChangedRegistrants.notifyRegistrants(
@@ -1318,17 +1342,11 @@
}
private void responseApnUnthrottled(int indicationType, String apn) {
- mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn);
mRil.mApnUnthrottledRegistrants.notifyRegistrants(
new AsyncResult(null, apn, null));
}
-
- private void reportAnomaly(UUID uuid, String msg) {
- Phone phone = mRil.mPhoneId == null ? null : PhoneFactory.getPhone(mRil.mPhoneId);
- int carrierId = phone == null ? UNKNOWN_CARRIER_ID : phone.getCarrierId();
- AnomalyReporter.reportAnomaly(uuid, msg, carrierId);
- }
}
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
index 04c6b54..bab4d12 100644
--- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -28,6 +28,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Collections;
import java.util.Set;
@@ -164,6 +166,13 @@
}
}
+ /**
+ * Dump the fields of the instance
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mRadioConfig=" + mRadioConfig);
+ }
+
private static void log(final String s) {
Rlog.d(LOG_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/RadioMessagingProxy.java b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
index e68e957..69ccf36 100644
--- a/src/java/com/android/internal/telephony/RadioMessagingProxy.java
+++ b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
@@ -36,13 +36,23 @@
* Set IRadioMessaging as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param messaging IRadioMessaging implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion,
+ public HalVersion setAidl(HalVersion halVersion,
android.hardware.radio.messaging.IRadioMessaging messaging) {
- mHalVersion = halVersion;
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(messaging.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mMessagingProxy = messaging;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
diff --git a/src/java/com/android/internal/telephony/RadioModemProxy.java b/src/java/com/android/internal/telephony/RadioModemProxy.java
index 7aaf727..4178293 100644
--- a/src/java/com/android/internal/telephony/RadioModemProxy.java
+++ b/src/java/com/android/internal/telephony/RadioModemProxy.java
@@ -31,13 +31,23 @@
* Set IRadioModem as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param modem IRadioModem implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion,
+ public HalVersion setAidl(HalVersion halVersion,
android.hardware.radio.modem.IRadioModem modem) {
- mHalVersion = halVersion;
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(modem.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mModemProxy = modem;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
@@ -110,6 +120,19 @@
}
/**
+ * Call IRadioModem#getImei
+ *
+ * @param serial Serial number of request
+ * @throws RemoteException
+ */
+ public void getImei(int serial) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mModemProxy.getImei(serial);
+ }
+ }
+
+ /**
* Call IRadioModem#getHardwareConfig
* @param serial Serial number of request
* @throws RemoteException
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
index b881035..246c2e0 100644
--- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java
+++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -65,13 +65,23 @@
* Set IRadioNetwork as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param network IRadioNetwork implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion,
+ public HalVersion setAidl(HalVersion halVersion,
android.hardware.radio.network.IRadioNetwork network) {
- mHalVersion = halVersion;
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(network.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mNetworkProxy = network;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
@@ -828,4 +838,127 @@
}
// Only supported on AIDL.
}
+
+ /**
+ * Set the Emergency Mode
+ *
+ * @param serial Serial number of the request.
+ * @param emcModeType Defines the radio emergency mode type.
+ * @throws RemoteException
+ */
+ public void setEmergencyMode(int serial, int emcModeType) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.setEmergencyMode(serial, emcModeType);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Triggers an Emergency network scan.
+ *
+ * @param serial Serial number of the request.
+ * @param scanRequest Contains the preferred networks and type of service to be scanned.
+ * @throws RemoteException
+ */
+ public void triggerEmergencyNetworkScan(int serial,
+ android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest)
+ throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.triggerEmergencyNetworkScan(serial, scanRequest);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Cancels ongoing Emergency network scan
+ *
+ * @param serial Serial number of the request.
+ * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+ * If {@code true}, then the modem shall start the new scan from the beginning,
+ * otherwise the modem shall resume from the last search.
+ *
+ * @throws RemoteException
+ */
+ public void cancelEmergencyNetworkScan(int serial, boolean resetScan) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.cancelEmergencyNetworkScan(serial, resetScan);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Exits ongoing Emergency Mode
+ *
+ * @param serial Serial number of the request.
+ * @throws RemoteException
+ */
+ public void exitEmergencyMode(int serial) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.exitEmergencyMode(serial);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Set if null ciphering / null integrity is permitted.
+ *
+ * @param serial Serial number of the request.
+ * @param enabled true if null modes are allowed, false otherwise
+ * @throws RemoteException
+ */
+ public void setNullCipherAndIntegrityEnabled(int serial,
+ boolean enabled) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.setNullCipherAndIntegrityEnabled(serial, enabled);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Get if null ciphering / null integrity is permitted.
+ * @param serial Serial number of the request.
+ * @throws RemoteException
+ *
+ */
+ public void isNullCipherAndIntegrityEnabled(int serial) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.isNullCipherAndIntegrityEnabled(serial);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Checks whether N1 mode is enabled.
+ *
+ * @param serial Serial number of the request.
+ * @throws RemoteException
+ */
+ public void isN1ModeEnabled(int serial) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.isN1ModeEnabled(serial);
+ }
+ // Only supported on AIDL.
+ }
+
+ /**
+ * Enables or disables N1 mode.
+ *
+ * @param serial Serial number of request.
+ * @param enable Indicates whether to enable N1 mode or not.
+ * @throws RemoteException
+ */
+ public void setN1ModeEnabled(int serial, boolean enable) throws RemoteException {
+ if (isEmpty()) return;
+ if (isAidl()) {
+ mNetworkProxy.setN1ModeEnabled(serial, enable);
+ }
+ // Only supported on AIDL.
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 5265be5..0bc2958 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -2532,10 +2532,9 @@
ArrayList<NeighboringCellInfo> ret = new ArrayList<NeighboringCellInfo>();
NeighboringCellInfo cell;
- int[] subId = SubscriptionManager.getSubId(mRil.mPhoneId);
- int radioType =
- ((TelephonyManager) mRil.mContext.getSystemService(
- Context.TELEPHONY_SERVICE)).getDataNetworkType(subId[0]);
+ int radioType = ((TelephonyManager) mRil.mContext.getSystemService(
+ Context.TELEPHONY_SERVICE)).getDataNetworkType(
+ SubscriptionManager.getSubscriptionId(mRil.mPhoneId));
if (radioType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
for (int i = 0; i < cells.size(); i++) {
diff --git a/src/java/com/android/internal/telephony/RadioServiceProxy.java b/src/java/com/android/internal/telephony/RadioServiceProxy.java
index 8650dd0..4257327 100644
--- a/src/java/com/android/internal/telephony/RadioServiceProxy.java
+++ b/src/java/com/android/internal/telephony/RadioServiceProxy.java
@@ -78,4 +78,9 @@
if (isEmpty()) return;
if (!isAidl()) mRadioProxy.responseAcknowledgement();
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[mHalVersion=" + mHalVersion + ']';
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java
index c7e19c4..7c8ee7b 100644
--- a/src/java/com/android/internal/telephony/RadioSimProxy.java
+++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -41,12 +41,22 @@
* Set IRadioSim as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param sim IRadioSim implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) {
- mHalVersion = halVersion;
+ public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) {
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(sim.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mSimProxy = sim;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
@@ -262,14 +272,24 @@
}
/**
- * Call IRadioSim#iccCloseLogicalChannel
+ * Call IRadioSim#iccCloseLogicalChannelWithSessionInfo
* @param serial Serial number of request
* @param channelId Channel ID of the channel to be closed
+ * @param isEs10 Whether the logical channel is opened for performing ES10 operations.
* @throws RemoteException
*/
- public void iccCloseLogicalChannel(int serial, int channelId) throws RemoteException {
+ public void iccCloseLogicalChannel(int serial,
+ int channelId, boolean isEs10) throws RemoteException {
if (isEmpty()) return;
if (isAidl()) {
+ if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_2_1)) {
+ android.hardware.radio.sim.SessionInfo info =
+ new android.hardware.radio.sim.SessionInfo();
+ info.sessionId = channelId;
+ info.isEs10 = isEs10;
+ mSimProxy.iccCloseLogicalChannelWithSessionInfo(serial, info);
+ return;
+ }
mSimProxy.iccCloseLogicalChannel(serial, channelId);
} else {
mRadioProxy.iccCloseLogicalChannel(serial, channelId);
@@ -352,7 +372,8 @@
if (isEmpty()) return;
if (isAidl()) {
mSimProxy.iccTransmitApduBasicChannel(serial,
- RILUtils.convertToHalSimApduAidl(0, cla, instruction, p1, p2, p3, data));
+ RILUtils.convertToHalSimApduAidl(0, cla, instruction, p1, p2, p3, data,
+ false, mHalVersion));
} else {
mRadioProxy.iccTransmitApduBasicChannel(serial,
RILUtils.convertToHalSimApdu(0, cla, instruction, p1, p2, p3, data));
@@ -373,10 +394,29 @@
*/
public void iccTransmitApduLogicalChannel(int serial, int channel, int cla, int instruction,
int p1, int p2, int p3, String data) throws RemoteException {
+ iccTransmitApduLogicalChannel(serial, channel, cla, instruction, p1, p2, p3, data, false);
+ }
+
+ /**
+ * Call IRadioSim#iccTransmitApduLogicalChannel
+ * @param serial Serial number of request
+ * @param channel Channel ID of the channel to use for communication
+ * @param cla Class of the command
+ * @param instruction Instruction of the command
+ * @param p1 P1 value of the command
+ * @param p2 P2 value of the command
+ * @param p3 P3 value of the command
+ * @param data Data to be sent
+ * @param isEs10Command APDU is an isEs10 command or not
+ * @throws RemoteException
+ */
+ public void iccTransmitApduLogicalChannel(int serial, int channel, int cla, int instruction,
+ int p1, int p2, int p3, String data, boolean isEs10Command) throws RemoteException {
if (isEmpty()) return;
if (isAidl()) {
mSimProxy.iccTransmitApduLogicalChannel(serial,
- RILUtils.convertToHalSimApduAidl(channel, cla, instruction, p1, p2, p3, data));
+ RILUtils.convertToHalSimApduAidl(channel, cla, instruction, p1, p2, p3, data,
+ isEs10Command, mHalVersion));
} else {
mRadioProxy.iccTransmitApduLogicalChannel(serial,
RILUtils.convertToHalSimApdu(channel, cla, instruction, p1, p2, p3, data));
@@ -576,7 +616,7 @@
halImsiInfo.base.expirationTime = imsiEncryptionInfo.getExpirationTime().getTime();
}
for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
- halImsiInfo.base.carrierKey.add(new Byte(b));
+ halImsiInfo.base.carrierKey.add(Byte.valueOf(b));
}
halImsiInfo.keyType = (byte) imsiEncryptionInfo.getKeyType();
@@ -592,7 +632,7 @@
halImsiInfo.expirationTime = imsiEncryptionInfo.getExpirationTime().getTime();
}
for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
- halImsiInfo.carrierKey.add(new Byte(b));
+ halImsiInfo.carrierKey.add(Byte.valueOf(b));
}
((android.hardware.radio.V1_1.IRadio) mRadioProxy).setCarrierInfoForImsiEncryption(
diff --git a/src/java/com/android/internal/telephony/RadioVoiceProxy.java b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
index 6ac603b..7f46424 100644
--- a/src/java/com/android/internal/telephony/RadioVoiceProxy.java
+++ b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
@@ -35,12 +35,23 @@
* Set IRadioVoice as the AIDL implementation for RadioServiceProxy
* @param halVersion Radio HAL version
* @param voice IRadioVoice implementation
+ *
+ * @return updated HAL version
*/
- public void setAidl(HalVersion halVersion, android.hardware.radio.voice.IRadioVoice voice) {
- mHalVersion = halVersion;
+ public HalVersion setAidl(HalVersion halVersion,
+ android.hardware.radio.voice.IRadioVoice voice) {
+ HalVersion version = halVersion;
+ try {
+ version = RIL.getServiceHalVersion(voice.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "setAidl: " + e);
+ }
+ mHalVersion = version;
mVoiceProxy = voice;
mIsAidl = true;
- Rlog.d(TAG, "AIDL initialized");
+
+ Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+ return mHalVersion;
}
/**
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index 24a8ac5..aff62ae 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -16,13 +16,7 @@
package com.android.internal.telephony;
import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.os.PersistableBundle;
-import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
@@ -80,16 +74,11 @@
/** Constructor */
public RatRatcheter(Phone phone) {
mPhone = phone;
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- try {
- Context contextAsUser = phone.getContext().createPackageContextAsUser(
- phone.getContext().getPackageName(), 0, UserHandle.ALL);
- contextAsUser.registerReceiver(mConfigChangedReceiver,
- intentFilter, null /* broadcastPermission */, null);
- } catch (PackageManager.NameNotFoundException e) {
- Rlog.e(LOG_TAG, "Package name not found: " + e.getMessage());
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ if (ccm != null) {
+ ccm.registerCarrierConfigChangeListener(
+ mPhone.getContext().getMainExecutor(),
+ (slotIndex, subId, carrierId, specificCarrierId) -> resetRatFamilyMap());
}
resetRatFamilyMap();
}
@@ -152,15 +141,24 @@
synchronized (mRatFamilyMap) {
// Either the two technologies are the same or their families must be non-null
// and the same.
+ // To Fix Missing Null check
+ if (ss1.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN) == null
+ || ss2.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN) == null) {
+ return false;
+ }
+
int dataRat1 = ServiceState.networkTypeToRilRadioTechnology(
ss1.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.getAccessNetworkTechnology());
int dataRat2 = ServiceState.networkTypeToRilRadioTechnology(
ss2.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.getAccessNetworkTechnology());
+
// The api getAccessNetworkTechnology@NetworkRegistrationInfo always returns LTE though
// data rat is LTE CA. Because it uses mIsUsingCarrierAggregation to indicate whether
// it is LTE CA or not. However, we need its actual data rat to check if they are the
@@ -183,25 +181,16 @@
}
}
- private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
- resetRatFamilyMap();
- }
- }
- };
-
private void resetRatFamilyMap() {
synchronized(mRatFamilyMap) {
mRatFamilyMap.clear();
- final CarrierConfigManager configManager = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return;
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
- if (b == null) return;
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
+ CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES);
+ if (b == null || b.isEmpty()) return;
// Reads an array of strings, eg:
// ["GPRS, EDGE", "EVDO, EVDO_A, EVDO_B", "HSPA, HSDPA, HSUPA, HSPAP"]
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
deleted file mode 100644
index 83864e4..0000000
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/**
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.os.Build;
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.telephony.dataconnection.DataThrottler;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-import java.util.ArrayList;
-import java.util.Random;
-
-/**
- * Retry manager allows a simple way to declare a series of
- * retry timeouts. After creating a RetryManager the configure
- * method is used to define the sequence. A simple linear series
- * may be initialized using configure with three integer parameters
- * The other configure method allows a series to be declared using
- * a string.
- *<p>
- * The format of the configuration string is the apn type followed by a series of parameters
- * separated by a comma. There are two name value pair parameters plus a series
- * of delay times. The units of of these delay times is unspecified.
- * The name value pairs which may be specified are:
- *<ul>
- *<li>max_retries=<value>
- *<li>default_randomizationTime=<value>
- *</ul>
- *<p>
- * apn type specifies the APN type that the retry pattern will apply for. "others" is for all other
- * APN types not specified in the config.
- *
- * max_retries is the number of times that incrementRetryCount
- * maybe called before isRetryNeeded will return false. if value
- * is infinite then isRetryNeeded will always return true.
- *
- * default_randomizationTime will be used as the randomizationTime
- * for delay times which have no supplied randomizationTime. If
- * default_randomizationTime is not defined it defaults to 0.
- *<p>
- * The other parameters define The series of delay times and each
- * may have an optional randomization value separated from the
- * delay time by a colon.
- *<p>
- * Examples:
- * <ul>
- * <li>3 retries for mms with no randomization value which means its 0:
- * <ul><li><code>"mms:1000, 2000, 3000"</code></ul>
- *
- * <li>10 retries for default APN with a 500 default randomization value for each and
- * the 4..10 retries all using 3000 as the delay:
- * <ul><li><code>"default:max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul>
- *
- * <li>4 retries for supl APN with a 100 as the default randomization value for the first 2 values
- * and the other two having specified values of 500:
- * <ul><li><code>"supl:default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul>
- *
- * <li>Infinite number of retries for all other APNs with the first one at 1000, the second at 2000
- * all others will be at 3000.
- * <ul><li><code>"others:max_retries=infinite,1000,2000,3000</code></ul>
- * </ul>
- *
- * {@hide}
- */
-public class RetryManager {
- public static final String LOG_TAG = "RetryManager";
- public static final boolean DBG = true;
- public static final boolean VDBG = false; // STOPSHIP if true
-
- /**
- * The default retry configuration for APNs. See above for the syntax.
- */
- private static final String DEFAULT_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000";
-
- /**
- * The APN type used for all other APNs retry configuration.
- */
- private static final String OTHERS_APN_TYPE = "others";
-
- /**
- * The default value (in milliseconds) for delay between APN trying (mInterApnDelay)
- * within the same round
- */
- private static final long DEFAULT_INTER_APN_DELAY = 20000;
-
- /**
- * The default value (in milliseconds) for delay between APN trying (mFailFastInterApnDelay)
- * within the same round when we are in fail fast mode
- */
- private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000;
-
- /**
- * The default value (in milliseconds) for retrying APN after disconnect
- */
- private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
-
- /**
- * The value indicating retry should not occur.
- */
- public static final long NO_RETRY = Long.MAX_VALUE;
-
- /**
- * The value indicating network did not suggest any retry delay
- */
- public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_DURATION_UNDEFINED;
-
- /**
- * If the network suggests a retry delay in the data call setup response, we will retry
- * the current APN setting again. The maximum retry count is to prevent that network
- * keeps asking device to retry data setup forever and causes power consumption issue.
- */
- private static final int DEFAULT_MAX_SAME_APN_RETRY = 3;
-
- /**
- * The delay (in milliseconds) between APN trying within the same round
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private long mInterApnDelay;
-
- /**
- * The delay (in milliseconds) between APN trying within the same round when we are in
- * fail fast mode
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private long mFailFastInterApnDelay;
-
- /**
- * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports
- * data call lost)
- */
- private long mApnRetryAfterDisconnectDelay;
-
- /**
- * The counter for same APN retrying. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
- */
- private int mSameApnRetryCount = 0;
-
- /**
- * The maximum times that frameworks retries data setup with the same APN. This value could be
- * changed via carrier config. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
- */
- private int mMaxSameApnRetry = DEFAULT_MAX_SAME_APN_RETRY;
-
- /**
- * Retry record with times in milli-seconds
- */
- private static class RetryRec {
- long mDelayTime;
- long mRandomizationTime;
-
- RetryRec(long delayTime, long randomizationTime) {
- mDelayTime = delayTime;
- mRandomizationTime = randomizationTime;
- }
- }
-
- /**
- * The array of retry records
- */
- private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>();
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private Phone mPhone;
-
- private final DataThrottler mDataThrottler;
-
- /**
- * Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount
- */
- private boolean mRetryForever = false;
-
- /**
- * The maximum number of retries to attempt
- */
- private int mMaxRetryCount;
-
- /**
- * The current number of retries
- */
- private int mRetryCount = 0;
-
- /**
- * Random number generator. The random delay will be added into retry timer to avoid all devices
- * around retrying the APN at the same time.
- */
- private Random mRng = new Random();
-
- /**
- * Retry manager configuration string. See top of the detailed explanation.
- */
- private String mConfig;
-
- /**
- * The list to store APN setting candidates for data call setup. Most of the carriers only have
- * one APN, but few carriers have more than one.
- */
- private ArrayList<ApnSetting> mWaitingApns = new ArrayList<>();
-
- /**
- * Index pointing to the current trying APN from mWaitingApns
- */
- private int mCurrentApnIndex = -1;
-
- /**
- * Apn context type.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private String mApnType;
-
- private final @ApnType int apnType;
-
- /**
- * Retry manager constructor
- * @param phone Phone object
- * @param dataThrottler Data throttler
- * @param apnType APN type
- */
- public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler,
- @ApnType int apnType) {
- mPhone = phone;
- mDataThrottler = dataThrottler;
- this.apnType = apnType;
- }
-
- /**
- * Configure for using string which allow arbitrary
- * sequences of times. See class comments for the
- * string format.
- *
- * @return true if successful
- */
- @UnsupportedAppUsage
- private boolean configure(String configStr) {
- // Strip quotes if present.
- if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
- configStr = configStr.substring(1, configStr.length() - 1);
- }
-
- // Reset the retry manager since delay, max retry count, etc...will be reset.
- reset();
-
- if (DBG) log("configure: '" + configStr + "'");
- mConfig = configStr;
-
- if (!TextUtils.isEmpty(configStr)) {
- long defaultRandomization = 0;
-
- if (VDBG) log("configure: not empty");
-
- String strArray[] = configStr.split(",");
- for (int i = 0; i < strArray.length; i++) {
- if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
- Pair<Boolean, Integer> value;
- String splitStr[] = strArray[i].split("=", 2);
- splitStr[0] = splitStr[0].trim();
- if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
- if (splitStr.length > 1) {
- splitStr[1] = splitStr[1].trim();
- if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
- if (TextUtils.equals(splitStr[0], "default_randomization")) {
- value = parseNonNegativeInt(splitStr[0], splitStr[1]);
- if (!value.first) return false;
- defaultRandomization = value.second;
- } else if (TextUtils.equals(splitStr[0], "max_retries")) {
- if (TextUtils.equals("infinite", splitStr[1])) {
- mRetryForever = true;
- } else {
- value = parseNonNegativeInt(splitStr[0], splitStr[1]);
- if (!value.first) return false;
- mMaxRetryCount = value.second;
- }
- } else {
- Rlog.e(LOG_TAG, "Unrecognized configuration name value pair: "
- + strArray[i]);
- return false;
- }
- } else {
- /**
- * Assume a retry time with an optional randomization value
- * following a ":"
- */
- splitStr = strArray[i].split(":", 2);
- splitStr[0] = splitStr[0].trim();
- RetryRec rr = new RetryRec(0, 0);
- value = parseNonNegativeInt("delayTime", splitStr[0]);
- if (!value.first) return false;
- rr.mDelayTime = value.second;
-
- // Check if optional randomization value present
- if (splitStr.length > 1) {
- splitStr[1] = splitStr[1].trim();
- if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
- value = parseNonNegativeInt("randomizationTime", splitStr[1]);
- if (!value.first) return false;
- rr.mRandomizationTime = value.second;
- } else {
- rr.mRandomizationTime = defaultRandomization;
- }
- mRetryArray.add(rr);
- }
- }
- if (mRetryArray.size() > mMaxRetryCount) {
- mMaxRetryCount = mRetryArray.size();
- if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
- }
- } else {
- log("configure: cleared");
- }
-
- if (VDBG) log("configure: true");
- return true;
- }
-
- /**
- * Configure the retry manager
- */
- private void configureRetry() {
- String configString = null;
- String otherConfigString = null;
-
- try {
- if (TelephonyUtils.IS_DEBUGGABLE) {
- // Using system properties is easier for testing from command line.
- String config = SystemProperties.get("test.data_retry_config");
- if (!TextUtils.isEmpty(config)) {
- configure(config);
- return;
- }
- }
-
- CarrierConfigManager configManager = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
-
- mInterApnDelay = b.getLong(
- CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG,
- DEFAULT_INTER_APN_DELAY);
- mFailFastInterApnDelay = b.getLong(
- CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG,
- DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);
- mApnRetryAfterDisconnectDelay = b.getLong(
- CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
- DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
- mMaxSameApnRetry = b.getInt(
- CarrierConfigManager
- .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
- DEFAULT_MAX_SAME_APN_RETRY);
-
- // Load all retry patterns for all different APNs.
- String[] allConfigStrings = b.getStringArray(
- CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS);
- if (allConfigStrings != null) {
- for (String s : allConfigStrings) {
- if (!TextUtils.isEmpty(s)) {
- String splitStr[] = s.split(":", 2);
- if (splitStr.length == 2) {
- String apnTypeStr = splitStr[0].trim();
- // Check if this retry pattern is for the APN we want.
- if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) {
- // Extract the config string. Note that an empty string is valid
- // here, meaning no retry for the specified APN.
- configString = splitStr[1];
- break;
- } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) {
- // Extract the config string. Note that an empty string is valid
- // here, meaning no retry for all other APNs.
- otherConfigString = splitStr[1];
- }
- }
- }
- }
- }
-
- if (configString == null) {
- if (otherConfigString != null) {
- configString = otherConfigString;
- } else {
- // We should never reach here. If we reach here, it must be a configuration
- // error bug.
- log("Invalid APN retry configuration!. Use the default one now.");
- configString = DEFAULT_DATA_RETRY_CONFIG;
- }
- }
- } catch (NullPointerException ex) {
- // We should never reach here unless there is a bug
- log("Failed to read configuration! Use the hardcoded default value.");
-
- mInterApnDelay = DEFAULT_INTER_APN_DELAY;
- mFailFastInterApnDelay = DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING;
- configString = DEFAULT_DATA_RETRY_CONFIG;
- }
-
- if (VDBG) {
- log("mInterApnDelay = " + mInterApnDelay + ", mFailFastInterApnDelay = " +
- mFailFastInterApnDelay);
- }
-
- configure(configString);
- }
-
- /**
- * Return the timer that should be used to trigger the data reconnection
- */
- @UnsupportedAppUsage
- private long getRetryTimer() {
- int index;
- if (mRetryCount < mRetryArray.size()) {
- index = mRetryCount;
- } else {
- index = mRetryArray.size() - 1;
- }
-
- long retVal;
- if ((index >= 0) && (index < mRetryArray.size())) {
- retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
- } else {
- retVal = 0;
- }
-
- if (DBG) log("getRetryTimer: " + retVal);
- return retVal;
- }
-
- /**
- * Parse an integer validating the value is not negative.
- * @param name Name
- * @param stringValue Value
- * @return Pair.first == true if stringValue an integer >= 0
- */
- private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) {
- int value;
- Pair<Boolean, Integer> retVal;
- try {
- value = Integer.parseInt(stringValue);
- retVal = new Pair<>(validateNonNegativeInt(name, value), value);
- } catch (NumberFormatException e) {
- Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e);
- retVal = new Pair<>(false, 0);
- }
- if (VDBG) {
- log("parseNonNegativeInt: " + name + ", " + stringValue + ", "
- + retVal.first + ", " + retVal.second);
- }
- return retVal;
- }
-
- /**
- * Validate an integer is >= 0 and logs an error if not
- * @param name Name
- * @param value Value
- * @return Pair.first
- */
- private boolean validateNonNegativeInt(String name, long value) {
- boolean retVal;
- if (value < 0) {
- Rlog.e(LOG_TAG, name + " bad value: is < 0");
- retVal = false;
- } else {
- retVal = true;
- }
- if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
- return retVal;
- }
-
- /**
- * Return next random number for the index
- * @param index Retry index
- */
- private long nextRandomizationTime(int index) {
- long randomTime = mRetryArray.get(index).mRandomizationTime;
- if (randomTime == 0) {
- return 0;
- } else {
- return mRng.nextInt((int) randomTime);
- }
- }
-
- private long getNetworkSuggestedRetryDelay() {
- long retryElapseTime = mDataThrottler.getRetryTime(apnType);
- if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) {
- return retryElapseTime;
- }
-
- // The time from data throttler is system's elapsed time. We need to return the delta. If
- // less than 0, then return 0 (i.e. retry immediately).
- return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime());
- }
-
- /**
- * Get the next APN setting for data call setup.
- * @return APN setting to try. {@code null} if cannot find any APN,
- */
- public @Nullable ApnSetting getNextApnSetting() {
- if (mWaitingApns == null || mWaitingApns.size() == 0) {
- log("Waiting APN list is null or empty.");
- return null;
- }
-
- long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay();
- if (networkSuggestedRetryDelay == NO_RETRY) {
- log("Network suggested no retry.");
- return null;
- }
-
- // If the network had suggested a retry delay, we should retry the current APN again
- // (up to mMaxSameApnRetry times) instead of getting the next APN setting from
- // our own list. If the APN waiting list has been reset before a setup data responses
- // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry.
- if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY
- && mSameApnRetryCount < mMaxSameApnRetry) {
- mSameApnRetryCount++;
- return mWaitingApns.get(mCurrentApnIndex);
- }
-
- mSameApnRetryCount = 0;
-
- int index = mCurrentApnIndex;
- // Loop through the APN list to find out the index of next non-permanent failed APN.
- while (true) {
- if (++index == mWaitingApns.size()) index = 0;
-
- // Stop if we find the non-failed APN.
- if (!mWaitingApns.get(index).getPermanentFailed()) {
- break;
- }
-
- // If all APNs have permanently failed, bail out.
- if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) {
- return null;
- }
- }
-
- mCurrentApnIndex = index;
- return mWaitingApns.get(mCurrentApnIndex);
- }
-
- /**
- * Get the delay for trying the next waiting APN from the list.
- * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
- * delay.
- * @return delay in milliseconds
- */
- public long getDelayForNextApn(boolean failFastEnabled) {
-
- if (mWaitingApns == null || mWaitingApns.size() == 0) {
- log("Waiting APN list is null or empty.");
- return NO_RETRY;
- }
-
- long networkSuggestedDelay = getNetworkSuggestedRetryDelay();
- log("Network suggested delay=" + networkSuggestedDelay + "ms");
-
- if (networkSuggestedDelay == NO_RETRY) {
- log("Network suggested not retrying.");
- return NO_RETRY;
- }
-
- if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
- && mSameApnRetryCount < mMaxSameApnRetry) {
- // If the network explicitly suggests a retry delay, we should use it, even in fail fast
- // mode.
- log("Network suggested retry in " + networkSuggestedDelay + " ms.");
- return networkSuggestedDelay;
- }
-
- // In order to determine the delay to try next APN, we need to peek the next available APN.
- // Case 1 - If we will start the next round of APN trying,
- // we use the exponential-growth delay. (e.g. 5s, 10s, 30s...etc.)
- // Case 2 - If we are still within the same round of APN trying,
- // we use the fixed standard delay between APNs. (e.g. 20s)
-
- int index = mCurrentApnIndex;
- while (true) {
- if (++index >= mWaitingApns.size()) index = 0;
-
- // Stop if we find the non-failed APN.
- if (!mWaitingApns.get(index).getPermanentFailed()) {
- break;
- }
-
- // If all APNs have permanently failed, bail out.
- if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) {
- log("All APNs have permanently failed.");
- return NO_RETRY;
- }
- }
-
- long delay;
- if (index <= mCurrentApnIndex) {
- // Case 1, if the next APN is in the next round.
- if (!mRetryForever && mRetryCount + 1 > mMaxRetryCount) {
- log("Reached maximum retry count " + mMaxRetryCount + ".");
- return NO_RETRY;
- }
- delay = getRetryTimer();
- ++mRetryCount;
- } else {
- // Case 2, if the next APN is still in the same round.
- delay = mInterApnDelay;
- }
-
- if (failFastEnabled && delay > mFailFastInterApnDelay) {
- // If we enable fail fast mode, and the delay we got is longer than
- // fail-fast delay (mFailFastInterApnDelay), use the fail-fast delay.
- // If the delay we calculated is already shorter than fail-fast delay,
- // then ignore fail-fast delay.
- delay = mFailFastInterApnDelay;
- }
-
- return delay;
- }
-
- /**
- * Mark the APN setting permanently failed.
- * @param apn APN setting to be marked as permanently failed
- * */
- public void markApnPermanentFailed(ApnSetting apn) {
- if (apn != null) {
- apn.setPermanentFailed(true);
- }
- }
-
- /**
- * Reset the retry manager.
- */
- private void reset() {
- mMaxRetryCount = 0;
- mRetryCount = 0;
- mCurrentApnIndex = -1;
- mSameApnRetryCount = 0;
- mRetryArray.clear();
- }
-
- /**
- * Set waiting APNs for retrying in case needed.
- * @param waitingApns Waiting APN list
- */
- public void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
-
- if (waitingApns == null) {
- log("No waiting APNs provided");
- return;
- }
-
- mWaitingApns = waitingApns;
-
- // Since we replace the entire waiting APN list, we need to re-config this retry manager.
- configureRetry();
-
- for (ApnSetting apn : mWaitingApns) {
- apn.setPermanentFailed(false);
- }
-
- log("Setting " + mWaitingApns.size() + " waiting APNs.");
-
- if (VDBG) {
- for (int i = 0; i < mWaitingApns.size(); i++) {
- log(" [" + i + "]:" + mWaitingApns.get(i));
- }
- }
- }
-
- /**
- * Get the list of waiting APNs.
- * @return the list of waiting APNs
- */
- public @NonNull ArrayList<ApnSetting> getWaitingApns() {
- return mWaitingApns;
- }
-
- /**
- * Get the delay in milliseconds for APN retry after disconnect
- * @return The delay in milliseconds
- */
- public long getRetryAfterDisconnectDelay() {
- return mApnRetryAfterDisconnectDelay;
- }
-
- public String toString() {
- if (mConfig == null) return "";
- return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType)
- + " mRetryCount="
- + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex="
- + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount
- + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever="
- + mRetryForever + " mInterApnDelay=" + mInterApnDelay
- + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
- + " mConfig={" + mConfig + "}";
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void log(String s) {
- Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 5bc9d17..a78242a 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -27,16 +27,22 @@
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncResult;
@@ -60,6 +66,7 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.Html;
import android.text.Spanned;
@@ -80,6 +87,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
@@ -139,11 +150,30 @@
/** New status report received. */
protected static final int EVENT_NEW_SMS_STATUS_REPORT = 10;
+ /** Retry Sending RP-SMMA Notification */
+ protected static final int EVENT_RETRY_SMMA = 11;
// other
protected static final int EVENT_NEW_ICC_SMS = 14;
protected static final int EVENT_ICC_CHANGED = 15;
protected static final int EVENT_GET_IMS_SERVICE = 16;
+ /** Last TP - Message Reference value update to SIM */
+ private static final int EVENT_TPMR_SIM_UPDATE_RESPONSE = 17;
+
+ /** Handle SIM loaded */
+ private static final int EVENT_SIM_LOADED = 18;
+
+ /**
+ * When this change is enabled, more specific values of SMS sending error code
+ * {@link SmsManager#Result} will be returned to the SMS Apps.
+ *
+ * Refer to {@link SMSDispatcher#rilErrorToSmsManagerResult} fore more details of the new values
+ * of SMS sending error code that will be returned.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ static final long ADD_MORE_SMS_SENDING_ERROR_CODES = 250017070L;
+
@UnsupportedAppUsage
protected Phone mPhone;
@UnsupportedAppUsage
@@ -155,9 +185,18 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final TelephonyManager mTelephonyManager;
protected final LocalLog mLocalLog = new LocalLog(16);
+ protected final LocalLog mSmsOutgoingErrorCodes = new LocalLog(10);
/** Maximum number of times to retry sending a failed SMS. */
protected static final int MAX_SEND_RETRIES = 3;
+
+ /** Retransmitted Flag as specified in section 6.3.1.2 in TS 124011
+ * true: RP-SMMA Retried once and no more transmissions are permitted
+ * false: not retried at all and at least another transmission of the RP-SMMA message
+ * is currently permitted
+ */
+ protected boolean mRPSmmaRetried = false;
+
/** Delay before next send attempt on a failed SMS, in milliseconds. */
@VisibleForTesting
public static final int SEND_RETRY_DELAY = 2000;
@@ -190,6 +229,9 @@
@VisibleForTesting
public int mCarrierMessagingTimeout = 10 * 60 * 1000; //10 minutes
+ /** Used for storing last TP - Message Reference used*/
+ private int mMessageRef = -1;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected static int getNextConcatenatedRef() {
sConcatenatedRef += 1;
@@ -215,10 +257,33 @@
com.android.internal.R.bool.config_sms_capable);
mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone(
mPhone.getPhoneId(), mSmsCapable);
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
+ " mSmsSendDisabled=" + mSmsSendDisabled);
}
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ Rlog.d(TAG, "Received broadcast " + intent.getAction());
+ if (Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+ if (!intent.hasExtra(Intent.EXTRA_SIM_STATE)) {
+ Rlog.d(TAG, "Extra not found in intent.");
+ } else {
+ String simState = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
+ if (simState.equals(Intent.SIM_STATE_LOADED)) {
+ Rlog.d(TAG, "SIM_STATE_CHANGED : SIM_LOADED");
+ Message msg = obtainMessage(EVENT_SIM_LOADED);
+ msg.arg1 = getSubId();
+ sendMessage(msg);
+ }
+ }
+ }
+ }
+ };
+
/**
* Observe the secure setting for updated premium sms determination rules
*/
@@ -262,6 +327,26 @@
protected abstract String getFormat();
/**
+ * Gets the maximum number of times the SMS can be retried upon Failure,
+ * from the {@link android.telephony.CarrierConfigManager}
+ *
+ * @return the default maximum number of times SMS can be sent
+ */
+ protected int getMaxSmsRetryCount() {
+ return MAX_SEND_RETRIES;
+ }
+
+ /**
+ * Gets the Time delay before next send attempt on a failed SMS,
+ * from the {@link android.telephony.CarrierConfigManager}
+ *
+ * @return the Time in miiliseconds for delay before next send attempt on a failed SMS
+ */
+ protected int getSmsRetryDelayValue() {
+ return SEND_RETRY_DELAY;
+ }
+
+ /**
* Called when a status report is received. This should correspond to a previously successful
* SEND.
*
@@ -280,83 +365,181 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case EVENT_SEND_SMS_COMPLETE:
- // An outbound SMS has been successfully transferred, or failed.
- handleSendComplete((AsyncResult) msg.obj);
- break;
+ case EVENT_SEND_SMS_COMPLETE:
+ // An outbound SMS has been successfully transferred, or failed.
+ handleSendComplete((AsyncResult) msg.obj);
+ break;
- case EVENT_SEND_RETRY:
- Rlog.d(TAG, "SMS retry..");
- sendRetrySms((SmsTracker) msg.obj);
- break;
+ case EVENT_SEND_RETRY:
+ Rlog.d(TAG, "SMS retry..");
+ sendRetrySms((SmsTracker) msg.obj);
+ break;
- case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
- handleReachSentLimit((SmsTracker[]) (msg.obj));
- break;
+ case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
+ handleReachSentLimit((SmsTracker[]) (msg.obj));
+ break;
- case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
- handleConfirmShortCode(false, (SmsTracker[]) (msg.obj));
- break;
+ case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(false, (SmsTracker[]) (msg.obj));
+ break;
- case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
- handleConfirmShortCode(true, (SmsTracker[]) (msg.obj));
- break;
+ case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(true, (SmsTracker[]) (msg.obj));
+ break;
- case EVENT_SEND_CONFIRMED_SMS:
- {
- SmsTracker[] trackers = (SmsTracker[]) msg.obj;
- for (SmsTracker tracker : trackers) {
- sendSms(tracker);
- }
- mPendingTrackerCount--;
- break;
- }
-
- case EVENT_SENDING_NOT_ALLOWED:
- {
- SmsTracker[] trackers = (SmsTracker[]) msg.obj;
- Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
- + "sending SHORT_CODE_NEVER_ALLOWED error code.");
- handleSmsTrackersFailure(
- trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
- break;
- }
-
- case EVENT_STOP_SENDING:
- {
- SmsTracker[] trackers = (SmsTracker[]) msg.obj;
- int error;
- if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
- if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
- error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
- Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
- + "sending SHORT_CODE_NEVER_ALLOWED error code.");
- } else {
- error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
- Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
- + "sending SHORT_CODE_NOT_ALLOWED error code.");
+ case EVENT_SEND_CONFIRMED_SMS: {
+ SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+ for (SmsTracker tracker : trackers) {
+ sendSms(tracker);
}
- } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
- error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
- Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
- + "sending LIMIT_EXCEEDED error code.");
- } else {
- error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
- Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+ mPendingTrackerCount--;
+ break;
}
- handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
- mPendingTrackerCount--;
- break;
+ case EVENT_SENDING_NOT_ALLOWED: {
+ SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+ Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
+ + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+ handleSmsTrackersFailure(
+ trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
+ break;
+ }
+
+ case EVENT_STOP_SENDING: {
+ SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+ int error;
+ if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
+ if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
+ error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+ } else {
+ error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending SHORT_CODE_NOT_ALLOWED error code.");
+ }
+ } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
+ error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending LIMIT_EXCEEDED error code.");
+ } else {
+ error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
+ Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+ }
+
+ handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
+ mPendingTrackerCount--;
+ break;
+ }
+
+ case EVENT_NEW_SMS_STATUS_REPORT:
+ handleStatusReport(msg.obj);
+ break;
+ case EVENT_TPMR_SIM_UPDATE_RESPONSE:
+ handleMessageRefStatus(msg);
+ break;
+
+ case EVENT_SIM_LOADED:
+ /* sim TPMR value is given higher priority if both are non-negative number.
+ Use case:
+ if sim was used on another device and inserted in a new device,
+ that device will start sending the next TPMR after reading from the SIM.
+ */
+ mMessageRef = getTpmrValueFromSIM();
+ if (mMessageRef == -1) {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(msg.arg1);
+ if (subInfo != null) {
+ mMessageRef = subInfo.getLastUsedTPMessageReference();
+ }
+ }
+ break;
+
+ default:
+ Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
+ }
+ }
+
+ private void handleMessageRefStatus(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ Rlog.e(TAG, "Failed to update TP - Message reference value to SIM " + ar.exception);
+ } else {
+ Rlog.d(TAG, "TP - Message reference updated to SIM Successfully");
+ }
+ }
+
+ private void updateTPMessageReference() {
+ updateSIMLastTPMRValue(mMessageRef);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionManagerService.getInstance()
+ .setLastUsedTPMessageReference(getSubId(), mMessageRef);
+ } catch (SecurityException e) {
+ Rlog.e(TAG, "Security Exception caused on messageRef updation to DB " + e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void updateSIMLastTPMRValue(int messageRef) {
+ Message msg = obtainMessage(EVENT_TPMR_SIM_UPDATE_RESPONSE);
+ IccRecords iccRecords = getIccRecords();
+ if (iccRecords != null) {
+ iccRecords.setSmssTpmrValue(messageRef, msg);
+ }
+ }
+
+ private int getTpmrValueFromSIM() {
+ IccRecords iccRecords = getIccRecords();
+ if (iccRecords != null) {
+ return iccRecords.getSmssTpmrValue();
+ }
+ return -1;
+ }
+
+ private IccRecords getIccRecords() {
+ if (mPhone != null && mPhone.getIccRecords() != null) {
+ return mPhone.getIccRecords();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the next TP message Reference value incremented by 1 for every sms sent .
+ * once a max of 255 is reached TP message Reference is reset to 0.
+ *
+ * @return messageRef TP message Reference value
+ */
+ public int nextMessageRef() {
+ if (!isMessageRefIncrementViaTelephony()) {
+ return 0;
}
- case EVENT_NEW_SMS_STATUS_REPORT:
- handleStatusReport(msg.obj);
- break;
+ mMessageRef = (mMessageRef + 1) % 256;
+ updateTPMessageReference();
+ return mMessageRef;
+ }
- default:
- Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
+ /**
+ * As modem is using the last used TP-MR value present in SIM card, increment of
+ * messageRef(TP-MR) value should be prevented (config_stk_sms_send_support set to false)
+ * at telephony framework. In future, config_stk_sms_send_support flag will be enabled
+ * so that messageRef(TP-MR) increment will be done at framework side only.
+ *
+ * TODO:- Need to have new flag to control writing TP-MR value to SIM or shared prefrence.
+ */
+ public boolean isMessageRefIncrementViaTelephony() {
+ boolean isMessageRefIncrementEnabled = false;
+ try {
+ isMessageRefIncrementEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_stk_sms_send_support);
+ } catch (NotFoundException e) {
+ Rlog.e(TAG, "isMessageRefIncrementViaTelephony NotFoundException Exception");
}
+
+ Rlog.i(TAG, "bool.config_stk_sms_send_support= " + isMessageRefIncrementEnabled);
+ return isMessageRefIncrementEnabled;
}
/**
@@ -569,26 +752,50 @@
@Override
public void onSendSmsComplete(int result, int messageRef) {
Rlog.d(TAG, "onSendSmsComplete: result=" + result + " messageRef=" + messageRef);
- if (mCallbackCalled) {
- logWithLocalLog("onSendSmsComplete: unexpected call");
- AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
- "Unexpected onSendSmsComplete", mPhone.getCarrierId());
+ if (cleanupOnSendSmsComplete("onSendSmsComplete")) {
return;
}
- mCallbackCalled = true;
+
final long identity = Binder.clearCallingIdentity();
try {
- mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
processSendSmsResponse(mSmsSender.getSmsTracker(), result, messageRef);
- mSmsSender.removeTimeout();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ /**
+ * This method should be called only once.
+ */
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
- Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with result: " + result);
+ Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs="
+ + Arrays.toString(messageRefs));
+ if (cleanupOnSendSmsComplete("onSendMultipartSmsComplete")) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ processSendMultipartSmsResponse(mSmsSender.getSmsTrackers(), result, messageRefs);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private boolean cleanupOnSendSmsComplete(String callingFunction) {
+ if (mCallbackCalled) {
+ logWithLocalLog(callingFunction + ": unexpected call");
+ AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
+ "Unexpected " + callingFunction, mPhone.getCarrierId());
+ return true;
+ }
+
+ mCallbackCalled = true;
+ mSmsSender.removeTimeout();
+ mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
+
+ return false;
}
@Override
@@ -663,8 +870,7 @@
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- void sendSmsByCarrierApp(String carrierPackageName,
- MultipartSmsSenderCallback senderCallback) {
+ void sendSmsByCarrierApp(String carrierPackageName, SmsSenderCallback senderCallback) {
super.sendSmsByCarrierApp(carrierPackageName, senderCallback);
}
@@ -712,69 +918,6 @@
}
}
- /**
- * Callback for MultipartSmsSender from the carrier messaging service.
- * Once the result is ready, the carrier messaging service connection is disposed.
- */
- private final class MultipartSmsSenderCallback implements CarrierMessagingCallback {
- private final MultipartSmsSender mSmsSender;
- private boolean mCallbackCalled = false;
-
- MultipartSmsSenderCallback(MultipartSmsSender smsSender) {
- mSmsSender = smsSender;
- }
-
- @Override
- public void onSendSmsComplete(int result, int messageRef) {
- Rlog.e(TAG, "Unexpected onSendSmsComplete call with result: " + result);
- }
-
- /**
- * This method should be called only once.
- */
- @Override
- public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
- Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs="
- + Arrays.toString(messageRefs));
- if (mCallbackCalled) {
- logWithLocalLog("onSendMultipartSmsComplete: unexpected call");
- AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
- "Unexpected onSendMultipartSmsComplete", mPhone.getCarrierId());
- return;
- }
- mCallbackCalled = true;
- mSmsSender.removeTimeout();
- mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
-
- if (mSmsSender.mTrackers == null) {
- Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers.");
- return;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- processSendMultipartSmsResponse(mSmsSender.mTrackers, result, messageRefs);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void onReceiveSmsComplete(int result) {
- Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
- }
-
- @Override
- public void onSendMmsComplete(int result, byte[] sendConfPdu) {
- Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result);
- }
-
- @Override
- public void onDownloadMmsComplete(int result) {
- Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result);
- }
- }
-
private void processSendMultipartSmsResponse(
SmsTracker[] trackers, int result, int[] messageRefs) {
if (trackers == null) {
@@ -919,7 +1062,7 @@
// This is retry after failure over IMS but voice is not available.
// Set retry to max allowed, so no retry is sent and cause
// SmsManager.RESULT_ERROR_GENERIC_FAILURE to be returned to app.
- tracker.mRetryCount = MAX_SEND_RETRIES;
+ tracker.mRetryCount = getMaxSmsRetryCount();
Rlog.d(TAG, "handleSendComplete: Skipping retry: "
+ " isIms()=" + isIms()
@@ -942,7 +1085,7 @@
tracker.isFromDefaultSmsApplication(mContext),
tracker.getInterval());
} else if (error == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY
- && tracker.mRetryCount < MAX_SEND_RETRIES) {
+ && tracker.mRetryCount < getMaxSmsRetryCount()) {
// Retry after a delay if needed.
// TODO: According to TS 23.040, 9.2.3.6, we should resend
// with the same TP-MR as the failed message, and
@@ -954,7 +1097,7 @@
tracker.mRetryCount++;
int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
- sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
+ sendMessageDelayed(retryMsg, getSmsRetryDelayValue());
mPhone.getSmsStats().onOutgoingSms(
tracker.mImsRetry > 0 /* isOverIms */,
SmsConstants.FORMAT_3GPP2.equals(getFormat()),
@@ -981,8 +1124,31 @@
}
@SmsManager.Result
- private static int rilErrorToSmsManagerResult(CommandException.Error rilError,
+ private int rilErrorToSmsManagerResult(CommandException.Error rilError,
SmsTracker tracker) {
+ mSmsOutgoingErrorCodes.log("rilError: " + rilError
+ + ", MessageId: " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
+
+ ApplicationInfo appInfo = tracker.getAppInfo();
+ if (appInfo == null
+ || !CompatChanges.isChangeEnabled(ADD_MORE_SMS_SENDING_ERROR_CODES, appInfo.uid)) {
+ if (rilError == CommandException.Error.INVALID_RESPONSE
+ || rilError == CommandException.Error.SIM_PIN2
+ || rilError == CommandException.Error.SIM_PUK2
+ || rilError == CommandException.Error.SUBSCRIPTION_NOT_AVAILABLE
+ || rilError == CommandException.Error.SIM_ERR
+ || rilError == CommandException.Error.INVALID_SIM_STATE
+ || rilError == CommandException.Error.NO_SMS_TO_ACK
+ || rilError == CommandException.Error.SIM_BUSY
+ || rilError == CommandException.Error.SIM_FULL
+ || rilError == CommandException.Error.NO_SUBSCRIPTION
+ || rilError == CommandException.Error.NO_NETWORK_FOUND
+ || rilError == CommandException.Error.DEVICE_IN_USE
+ || rilError == CommandException.Error.ABORTED) {
+ return SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+ }
+ }
+
switch (rilError) {
case RADIO_NOT_AVAILABLE:
return SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE;
@@ -1032,6 +1198,34 @@
return SmsManager.RESULT_RIL_ACCESS_BARRED;
case BLOCKED_DUE_TO_CALL:
return SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL;
+ case INVALID_SMSC_ADDRESS:
+ return SmsManager.RESULT_INVALID_SMSC_ADDRESS;
+ case INVALID_RESPONSE:
+ return SmsManager.RESULT_RIL_INVALID_RESPONSE;
+ case SIM_PIN2:
+ return SmsManager.RESULT_RIL_SIM_PIN2;
+ case SIM_PUK2:
+ return SmsManager.RESULT_RIL_SIM_PUK2;
+ case SUBSCRIPTION_NOT_AVAILABLE:
+ return SmsManager.RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE;
+ case SIM_ERR:
+ return SmsManager.RESULT_RIL_SIM_ERROR;
+ case INVALID_SIM_STATE:
+ return SmsManager.RESULT_RIL_INVALID_SIM_STATE;
+ case NO_SMS_TO_ACK:
+ return SmsManager.RESULT_RIL_NO_SMS_TO_ACK;
+ case SIM_BUSY:
+ return SmsManager.RESULT_RIL_SIM_BUSY;
+ case SIM_FULL:
+ return SmsManager.RESULT_RIL_SIM_FULL;
+ case NO_SUBSCRIPTION:
+ return SmsManager.RESULT_RIL_NO_SUBSCRIPTION;
+ case NO_NETWORK_FOUND:
+ return SmsManager.RESULT_RIL_NO_NETWORK_FOUND;
+ case DEVICE_IN_USE:
+ return SmsManager.RESULT_RIL_DEVICE_IN_USE;
+ case ABORTED:
+ return SmsManager.RESULT_RIL_ABORTED;
default:
Rlog.d(TAG, "rilErrorToSmsManagerResult: " + rilError + " "
+ SmsController.formatCrossStackMessageId(tracker.mMessageId));
@@ -1133,14 +1327,15 @@
@UnsupportedAppUsage
protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+ int messageRef = nextMessageRef();
SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
- scAddr, destAddr, destPort, data, (deliveryIntent != null));
+ scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef);
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
SmsTracker tracker = getSmsTracker(callingPackage, map, sentIntent, deliveryIntent,
getFormat(), null /*messageUri*/, false /*expectMore*/,
null /*fullMessageText*/, false /*isText*/,
- true /*persistMessage*/, isForVvm, 0L /* messageId */);
+ true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef);
if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) {
sendSubmitPdu(tracker);
@@ -1252,18 +1447,131 @@
* Used for logging and diagnostics purposes. The id may be NULL.
*/
public void sendText(String destAddr, String scAddr, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
+ String callingPkg, boolean persistMessage, int priority,
+ boolean expectMore, int validityPeriod, boolean isForVvm,
+ long messageId) {
+ sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
+ persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * @param messageUri optional URI of the message if it is already stored in the system
+ * @param callingPkg the calling package name
+ * @param persistMessage whether to save the sent message into SMS DB for a
+ * non-default SMS app.
+ *
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ * @param messageId An id that uniquely identifies the message requested to be sent.
+ * Used for logging and diagnostics purposes. The id may be NULL.
+ * @param skipShortCodeCheck Skip check for short code type destination address.
+ */
+ public void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
String callingPkg, boolean persistMessage, int priority,
boolean expectMore, int validityPeriod, boolean isForVvm,
- long messageId) {
+ long messageId, boolean skipShortCodeCheck) {
Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId));
+ int messageRef = nextMessageRef();
SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
- scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod);
+ scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod,
+ messageRef);
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(callingPkg, map, sentIntent, deliveryIntent,
getFormat(), messageUri, expectMore, text, true /*isText*/,
- persistMessage, priority, validityPeriod, isForVvm, messageId);
+ persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef,
+ skipShortCodeCheck);
if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
sendSubmitPdu(tracker);
@@ -1319,6 +1627,14 @@
protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
int destPort, byte[] message, boolean statusReportRequested);
+ protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader,
+ int priority, int validityPeriod, int messageRef);
+
+
+ protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested, int messageRef);
+
/**
* Calculate the number of septets needed to encode the message. This function should only be
* called for individual segments of multipart message.
@@ -1494,12 +1810,13 @@
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
}
-
+ int messageRef = nextMessageRef();
trackers[i] =
getNewSubmitPduTracker(callingPkg, destAddr, scAddr, parts.get(i), smsHeader,
encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)),
unsentPartCount, anyPartFailed, messageUri,
- fullMessageText, priority, expectMore, validityPeriod, messageId);
+ fullMessageText, priority, expectMore, validityPeriod, messageId,
+ messageRef);
if (trackers[i] == null) {
triggerSentIntentForFailure(sentIntents);
return;
@@ -1509,12 +1826,10 @@
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package " + carrierPackage
- + " "
+ Rlog.d(TAG, "Found carrier package " + carrierPackage + " "
+ SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers)));
MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
- smsSender.sendSmsByCarrierApp(carrierPackage,
- new MultipartSmsSenderCallback(smsSender));
+ smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
} else {
Rlog.v(TAG, "No carrier package. "
+ SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers)));
@@ -1537,7 +1852,7 @@
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
String fullMessageText, int priority, boolean expectMore, int validityPeriod,
- long messageId) {
+ long messageId, int messageRef) {
if (isCdmaMo()) {
UserData uData = new UserData();
uData.payloadStr = message;
@@ -1567,7 +1882,7 @@
getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
(!lastPart || expectMore), fullMessageText, true /*isText*/,
true /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
- messageId);
+ messageId, messageRef, false);
} else {
Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
+ "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1578,15 +1893,14 @@
com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, deliveryIntent != null,
SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable,
- smsHeader.languageShiftTable, validityPeriod);
+ smsHeader.languageShiftTable, validityPeriod, messageRef);
if (pdu != null) {
- HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
- message, pdu);
+ HashMap map = getSmsTrackerMap(destinationAddress, scAddress, message, pdu);
return getSmsTracker(callingPackage, map, sentIntent,
deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/,
false /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
- messageId);
+ messageId, messageRef, false);
} else {
Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
+ "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1744,7 +2058,8 @@
*/
boolean checkDestination(SmsTracker[] trackers) {
if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION)
- == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm) {
+ == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm
+ || trackers[0].mSkipShortCodeDestAddrCheck) {
return true; // app is pre-approved to send to short codes
} else {
int rule = mPremiumSmsRule.get();
@@ -1785,6 +2100,12 @@
trackers[0].mDestAddress, networkCountryIso));
}
+ if (smsCategory != SmsManager.SMS_CATEGORY_NOT_SHORT_CODE) {
+ int xmlVersion = mSmsDispatchersController.getUsageMonitor()
+ .getShortCodeXmlFileVersion();
+ mPhone.getSmsStats().onOutgoingShortCodeSms(smsCategory, xmlVersion);
+ }
+
if (smsCategory == SmsManager.SMS_CATEGORY_NOT_SHORT_CODE
|| smsCategory == SmsManager.SMS_CATEGORY_FREE_SHORT_CODE
|| smsCategory == SmsManager.SMS_CATEGORY_STANDARD_SHORT_CODE) {
@@ -2082,6 +2403,7 @@
private Boolean mIsFromDefaultSmsApplication;
private int mCarrierId;
+ private boolean mSkipShortCodeDestAddrCheck;
// SMS anomaly uuid -- unexpected error from RIL
private final UUID mAnomalyUnexpectedErrorFromRilUUID =
UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886");
@@ -2091,7 +2413,8 @@
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId,
boolean isText, boolean persistMessage, int userId, int priority,
- int validityPeriod, boolean isForVvm, long messageId, int carrierId) {
+ int validityPeriod, boolean isForVvm, long messageId, int carrierId,
+ int messageRef, boolean skipShortCodeDestAddrCheck) {
mData = data;
mSentIntent = sentIntent;
mDeliveryIntent = deliveryIntent;
@@ -2102,7 +2425,7 @@
mExpectMore = expectMore;
mImsRetry = 0;
mUsesImsServiceForIms = false;
- mMessageRef = 0;
+ mMessageRef = messageRef;
mUnsentPartCount = unsentPartCount;
mAnyPartFailed = anyPartFailed;
mMessageUri = messageUri;
@@ -2117,6 +2440,7 @@
mIsForVvm = isForVvm;
mMessageId = messageId;
mCarrierId = carrierId;
+ mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck;
}
public HashMap<String, Object> getData() {
@@ -2131,12 +2455,21 @@
return mAppInfo != null ? mAppInfo.packageName : null;
}
+ /**
+ * Get the calling Application Info
+ * @return Application Info
+ */
+ public ApplicationInfo getAppInfo() {
+ return mAppInfo == null ? null : mAppInfo.applicationInfo;
+ }
+
/** Return if the SMS was originated from the default SMS application. */
public boolean isFromDefaultSmsApplication(Context context) {
if (mIsFromDefaultSmsApplication == null) {
+ UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(context, mSubId);
// Perform a lazy initialization, due to the cost of the operation.
- mIsFromDefaultSmsApplication =
- SmsApplication.isDefaultSmsApplication(context, getAppPackageName());
+ mIsFromDefaultSmsApplication = SmsApplication.isDefaultSmsApplicationAsUser(context,
+ getAppPackageName(), userHandle);
}
return mIsFromDefaultSmsApplication;
}
@@ -2384,7 +2717,7 @@
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText,
boolean persistMessage, int priority, int validityPeriod, boolean isForVvm,
- long messageId) {
+ long messageId, int messageRef, boolean skipShortCodeCheck) {
// Get package info via packagemanager
UserHandle callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid());
final int userId = callingUser.getIdentifier();
@@ -2401,28 +2734,30 @@
return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore,
fullMessageText, getSubId(), isText, persistMessage, userId, priority,
- validityPeriod, isForVvm, messageId, mPhone.getCarrierId());
+ validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef,
+ skipShortCodeCheck);
}
protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
- boolean isForVvm, long messageId) {
+ boolean isForVvm, long messageId, int messageRef) {
return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
expectMore, fullMessageText, isText, persistMessage,
SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
- messageId);
+ messageId, messageRef, false);
}
protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
- int priority, int validityPeriod, boolean isForVvm, long messageId) {
+ int priority, int validityPeriod, boolean isForVvm, long messageId, int messageRef,
+ boolean skipShortCodeCheck) {
return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
expectMore, fullMessageText, isText, persistMessage, priority, validityPeriod,
- isForVvm, messageId);
+ isForVvm, messageId, messageRef, skipShortCodeCheck);
}
protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
@@ -2591,7 +2926,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int getSubId() {
- return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.getPhoneId());
+ return SubscriptionManager.getSubscriptionId(mPhone.getPhoneId());
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2641,10 +2976,17 @@
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println(TAG);
pw.increaseIndent();
+
pw.println("mLocalLog:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
pw.decreaseIndent();
+
+ pw.println("mSmsOutgoingErrorCodes:");
+ pw.increaseIndent();
+ mSmsOutgoingErrorCodes.dump(fd, pw, args);
+ pw.decreaseIndent();
+
pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
old mode 100755
new mode 100644
index 751d59a..50eea7f
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -37,7 +37,6 @@
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.hardware.radio.V1_0.CellInfoType;
-import android.net.NetworkCapabilities;
import android.os.AsyncResult;
import android.os.BaseBundle;
import android.os.Build;
@@ -72,7 +71,6 @@
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
@@ -93,11 +91,14 @@
import com.android.internal.telephony.cdnr.CarrierDisplayNameData;
import com.android.internal.telephony.cdnr.CarrierDisplayNameResolver;
import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
import com.android.internal.telephony.data.DataNetwork;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
-import com.android.internal.telephony.dataconnection.DataConnection;
+import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccRecords;
@@ -146,7 +147,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private UiccController mUiccController = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private UiccCardApplication mUiccApplcation = null;
+ private UiccCardApplication mUiccApplication = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccRecords mIccRecords = null;
@@ -182,13 +183,12 @@
private final Set<Integer> mRadioPowerOffReasons = new HashSet();
- // TODO - this should not be public, right now used externally GsmConnetion.
+ // TODO - this should not be public, right now used externally GsmConnection.
public RestrictedState mRestrictedState;
/**
- * A unique identifier to track requests associated with a poll
- * and ignore stale responses. The value is a count-down of
- * expected responses in this pollingContext.
+ * A unique identifier to track requests associated with a poll and ignore stale responses.
+ * The value is a count-down of expected responses in this pollingContext.
*/
@VisibleForTesting
public int[] mPollingContext;
@@ -217,13 +217,12 @@
private RegistrantList mNrStateChangedRegistrants = new RegistrantList();
private RegistrantList mNrFrequencyChangedRegistrants = new RegistrantList();
private RegistrantList mCssIndicatorChangedRegistrants = new RegistrantList();
- private final RegistrantList mBandwidthChangedRegistrants = new RegistrantList();
private final RegistrantList mAirplaneModeChangedRegistrants = new RegistrantList();
private final RegistrantList mAreaCodeChangedRegistrants = new RegistrantList();
- /* Radio power off pending flag and tag counter */
- private boolean mPendingRadioPowerOffAfterDataOff = false;
- private int mPendingRadioPowerOffAfterDataOffTag = 0;
+ /* Radio power off pending flag */
+ // @GuardedBy("this")
+ private volatile boolean mPendingRadioPowerOffAfterDataOff = false;
/** Waiting period before recheck gprs and voice registration. */
public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
@@ -280,12 +279,10 @@
protected static final int EVENT_RADIO_POWER_OFF_DONE = 54;
protected static final int EVENT_PHYSICAL_CHANNEL_CONFIG = 55;
protected static final int EVENT_CELL_LOCATION_RESPONSE = 56;
- protected static final int EVENT_CARRIER_CONFIG_CHANGED = 57;
private static final int EVENT_POLL_STATE_REQUEST = 58;
// Timeout event used when delaying radio power off to wait for IMS deregistration to happen.
private static final int EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT = 62;
protected static final int EVENT_RESET_LAST_KNOWN_CELL_IDENTITY = 63;
- private static final int EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED = 64;
// Telecom has un/registered a PhoneAccount that provides OTT voice calling capability, e.g.
// wi-fi calling.
protected static final int EVENT_TELECOM_VOICE_SERVICE_STATE_OVERRIDE_CHANGED = 65;
@@ -312,7 +309,7 @@
// Show PLMN only and only if this bit is set.
public static final int CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN = 1 << 1;
- private List<Message> mPendingCellInfoRequests = new LinkedList<Message>();
+ private List<Message> mPendingCellInfoRequests = new LinkedList<>();
// @GuardedBy("mPendingCellInfoRequests")
private boolean mIsPendingCellInfoRequest = false;
@@ -323,14 +320,10 @@
private CarrierDisplayNameResolver mCdnr;
private boolean mImsRegistrationOnOff = false;
- /** Radio is disabled by carrier. Radio power will not be override if this field is set */
- private boolean mRadioDisabledByCarrier = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mDeviceShuttingDown = false;
/** Keep track of SPN display rules, so we only broadcast intent if something changes. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private boolean mSpnUpdatePending = false;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCurSpn = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCurDataSpn = null;
@@ -349,13 +342,11 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SubscriptionManager mSubscriptionManager;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private SubscriptionController mSubscriptionController;
+ private SubscriptionManagerService mSubscriptionManagerService;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener =
new SstSubscriptionsChangedListener();
-
private final RatRatcheter mRatRatcheter;
private final LocaleTracker mLocaleTracker;
@@ -368,6 +359,7 @@
private final LocalLog mCdnrLogs = new LocalLog(64);
private Pattern mOperatorNameStringPattern;
+ private PersistableBundle mCarrierConfig;
private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
@@ -387,7 +379,6 @@
// If not, then the subId has changed, so we need to remember the old subId,
// even if the new subId is invalid (likely).
mPrevSubId = mSubId;
- mSubId = curSubId;
// Update voicemail count and notify message waiting changed regardless of
// whether the new subId is valid. This is an exception to the general logic
@@ -396,72 +387,64 @@
// which seems desirable.
mPhone.updateVoiceMail();
- if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ if (!SubscriptionManager.isValidSubscriptionId(curSubId)) {
if (SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
// just went from valid to invalid subId, so notify phone state listeners
// with final broadcast
mPhone.notifyServiceStateChangedForSubId(mOutOfServiceSS,
ServiceStateTracker.this.mPrevSubId);
}
- // If the new subscription ID isn't valid, then we don't need to do all the
- // UI updating, so we're done.
- return;
+ } else {
+ Context context = mPhone.getContext();
+
+ mPhone.notifyPhoneStateChanged();
+
+ if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
+ // just went from invalid to valid subId, so notify with current service
+ // state in case our service state was never broadcasted (we don't notify
+ // service states when the subId is invalid)
+ mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+ }
+
+ boolean restoreSelection = !context.getResources().getBoolean(
+ com.android.internal.R.bool.skip_restoring_network_selection);
+ mPhone.sendSubscriptionSettings(restoreSelection);
+
+ setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());
+
+ // Remove old network selection sharedPreferences since SP key names are now
+ // changed to include subId. This will be done only once when upgrading from an
+ // older build that did not include subId in the names.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ context);
+ String oldNetworkSelection = sp.getString(
+ Phone.NETWORK_SELECTION_KEY, "");
+ String oldNetworkSelectionName = sp.getString(
+ Phone.NETWORK_SELECTION_NAME_KEY, "");
+ String oldNetworkSelectionShort = sp.getString(
+ Phone.NETWORK_SELECTION_SHORT_KEY, "");
+ if (!TextUtils.isEmpty(oldNetworkSelection)
+ || !TextUtils.isEmpty(oldNetworkSelectionName)
+ || !TextUtils.isEmpty(oldNetworkSelectionShort)) {
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(Phone.NETWORK_SELECTION_KEY + curSubId,
+ oldNetworkSelection);
+ editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + curSubId,
+ oldNetworkSelectionName);
+ editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + curSubId,
+ oldNetworkSelectionShort);
+ editor.remove(Phone.NETWORK_SELECTION_KEY);
+ editor.remove(Phone.NETWORK_SELECTION_NAME_KEY);
+ editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY);
+ editor.commit();
+ }
+
+ // Once sub id becomes valid, we need to update the service provider name
+ // displayed on the UI again. The old SPN update intents sent to
+ // MobileSignalController earlier were actually ignored due to invalid sub id.
+ updateSpnDisplay();
}
-
- Context context = mPhone.getContext();
-
- mPhone.notifyPhoneStateChanged();
-
- if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
- // just went from invalid to valid subId, so notify with current service
- // state in case our service state was never broadcasted (we don't notify
- // service states when the subId is invalid)
- mPhone.notifyServiceStateChanged(mPhone.getServiceState());
- }
-
- boolean restoreSelection = !context.getResources().getBoolean(
- com.android.internal.R.bool.skip_restoring_network_selection);
- mPhone.sendSubscriptionSettings(restoreSelection);
-
- setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());
-
- if (mSpnUpdatePending) {
- mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), mCurShowPlmn,
- mCurPlmn, mCurShowSpn, mCurSpn);
- mSpnUpdatePending = false;
- }
-
- // Remove old network selection sharedPreferences since SP key names are now
- // changed to include subId. This will be done only once when upgrading from an
- // older build that did not include subId in the names.
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
- context);
- String oldNetworkSelection = sp.getString(
- Phone.NETWORK_SELECTION_KEY, "");
- String oldNetworkSelectionName = sp.getString(
- Phone.NETWORK_SELECTION_NAME_KEY, "");
- String oldNetworkSelectionShort = sp.getString(
- Phone.NETWORK_SELECTION_SHORT_KEY, "");
- if (!TextUtils.isEmpty(oldNetworkSelection)
- || !TextUtils.isEmpty(oldNetworkSelectionName)
- || !TextUtils.isEmpty(oldNetworkSelectionShort)) {
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(Phone.NETWORK_SELECTION_KEY + mSubId,
- oldNetworkSelection);
- editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + mSubId,
- oldNetworkSelectionName);
- editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + mSubId,
- oldNetworkSelectionShort);
- editor.remove(Phone.NETWORK_SELECTION_KEY);
- editor.remove(Phone.NETWORK_SELECTION_NAME_KEY);
- editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY);
- editor.commit();
- }
-
- // Once sub id becomes valid, we need to update the service provider name
- // displayed on the UI again. The old SPN update intents sent to
- // MobileSignalController earlier were actually ignored due to invalid sub id.
- updateSpnDisplay();
+ mSubId = curSubId;
}
};
@@ -560,15 +543,13 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- int phoneId = intent.getExtras().getInt(CarrierConfigManager.EXTRA_SLOT_INDEX);
- // Ignore the carrier config changed if the phoneId is not matched.
- if (phoneId == mPhone.getPhoneId()) {
- sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
- }
- } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) {
+ if (action.equals(Intent.ACTION_LOCALE_CHANGED)) {
+ log("ACTION_LOCALE_CHANGED");
// Update emergency string or operator name, polling service state.
pollState();
+ // Depends on modem, ServiceState is not necessarily updated, so make sure updating
+ // SPN.
+ updateSpnDisplay();
} else if (action.equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
String lastKnownNetworkCountry = intent.getStringExtra(
TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY);
@@ -579,6 +560,10 @@
}
};
+ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ onCarrierConfigurationChanged(slotIndex);
+
//CDMA
// Min values used to by getOtasp()
public static final String UNACTIVATED_MIN2_VALUE = "000000";
@@ -620,11 +605,17 @@
private int mLastKnownAreaCode = CellInfo.UNAVAILABLE;
/**
- * Indicating if there is any data network existing. This is used in airplane mode turning on
- * scenario, where service state tracker should wait all data disconnected before powering
- * down the modem.
+ * Data network controller callback for all data disconnected. This is used when turning on
+ * airplane mode, where service state tracker should wait for all data disconnected on all
+ * subscriptions before powering down the modem.
*/
- private boolean mAnyDataExisting = false;
+ private DataNetworkControllerCallback mDataDisconnectedCallback;
+
+ /**
+ * AccessNetworksManagerCallback is used for preferred on the IWLAN when preferred transport
+ * type changed in AccessNetworksManager.
+ */
+ private AccessNetworksManagerCallback mAccessNetworksManagerCallback = null;
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
mNitzState = TelephonyComponentFactory.getInstance()
@@ -655,15 +646,20 @@
mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);
mCi.registerForPhysicalChannelConfiguration(this, EVENT_PHYSICAL_CHANNEL_CONFIG, null);
- mSubscriptionController = SubscriptionController.getInstance();
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
mSubscriptionManager = SubscriptionManager.from(phone.getContext());
mSubscriptionManager.addOnSubscriptionsChangedListener(
new android.os.HandlerExecutor(this), mOnSubscriptionsChangedListener);
mRestrictedState = new RestrictedState();
+ mCarrierConfig = getCarrierConfig();
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ // Callback which directly handle config change should be executed in handler thread
+ ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
+
mAccessNetworksManager = mPhone.getAccessNetworksManager();
mOutOfServiceSS = new ServiceState();
- mOutOfServiceSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false);
+ mOutOfServiceSS.setOutOfService(false);
for (int transportType : mAccessNetworksManager.getAvailableTransports()) {
mRegStateManagers.append(transportType, new NetworkRegistrationManager(
@@ -686,12 +682,11 @@
Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0);
if (!mDesiredPowerState) {
- mRadioPowerOffReasons.add(Phone.RADIO_POWER_REASON_USER);
+ mRadioPowerOffReasons.add(TelephonyManager.RADIO_POWER_REASON_USER);
}
mRadioPowerLog.log("init : airplane mode = " + airplaneMode + " enableCellularOnBoot = " +
enableCellularOnBoot);
-
mPhone.getCarrierActionAgent().registerForCarrierAction(CARRIER_ACTION_SET_RADIO_ENABLED,
this, EVENT_RADIO_POWER_FROM_CARRIER, null, false);
@@ -699,7 +694,6 @@
Context context = mPhone.getContext();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
context.registerReceiver(mIntentReceiver, filter);
@@ -721,8 +715,31 @@
registerForImsCapabilityChanged(mCSST,
CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
- if (mPhone.isUsingNewDataStack()) {
- sendEmptyMessage(EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED);
+ mDataDisconnectedCallback = new DataNetworkControllerCallback(this::post) {
+ @Override
+ public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) {
+ log("onAnyDataNetworkExistingChanged: anyDataExisting=" + anyDataExisting);
+ if (!anyDataExisting) {
+ sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED);
+ }
+ }
+ };
+
+ mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(this::post) {
+ @Override
+ public void onPreferredTransportChanged(int networkCapability) {
+ // Check if preferred on IWLAN was changed in ServiceState.
+ boolean isIwlanPreferred = mAccessNetworksManager.isAnyApnOnIwlan();
+ if (mSS.isIwlanPreferred() != isIwlanPreferred) {
+ log("onPreferredTransportChanged: IwlanPreferred is changed to "
+ + isIwlanPreferred);
+ mSS.setIwlanPreferred(isIwlanPreferred);
+ mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+ }
+ }
+ };
+ if (mAccessNetworksManagerCallback != null) {
+ mAccessNetworksManager.registerCallback(mAccessNetworksManagerCallback);
}
}
@@ -759,9 +776,9 @@
}
mSS = new ServiceState();
- mSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false);
+ mSS.setOutOfService(false);
mNewSS = new ServiceState();
- mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false);
+ mNewSS.setOutOfService(false);
mLastCellInfoReqTime = 0;
mLastCellInfoList = null;
mStartedGprsRegCheck = false;
@@ -854,17 +871,28 @@
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CARRIER_ACTION_SET_RADIO_ENABLED);
mPhone.getContext().unregisterReceiver(mIntentReceiver);
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ if (ccm != null && mCarrierConfigChangeListener != null) {
+ ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+ }
if (mCSST != null) {
mCSST.dispose();
mCSST = null;
}
+ if (mAccessNetworksManagerCallback != null) {
+ mAccessNetworksManager.unregisterCallback(mAccessNetworksManagerCallback);
+ mAccessNetworksManagerCallback = null;
+ }
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean getDesiredPowerState() {
return mDesiredPowerState;
}
- public boolean getPowerStateFromCarrier() { return !mRadioDisabledByCarrier; }
+
+ public boolean getPowerStateFromCarrier() {
+ return !mRadioPowerOffReasons.contains(TelephonyManager.RADIO_POWER_REASON_CARRIER);
+ }
public List<PhysicalChannelConfig> getPhysicalChannelConfigList() {
return mLastPhysicalChannelConfigList;
@@ -898,7 +926,7 @@
if (nrs != null) {
int rat = ServiceState.networkTypeToRilRadioTechnology(
nrs.getAccessNetworkTechnology());
- int drs = regCodeToServiceState(nrs.getRegistrationState());
+ int drs = regCodeToServiceState(nrs.getNetworkRegistrationState());
return new Pair<>(drs, rat);
}
return null;
@@ -1054,7 +1082,7 @@
* @return the current reasons for which the radio is off.
*/
public Set<Integer> getRadioPowerOffReasons() {
- return mRadioPowerOffReasons;
+ return Set.copyOf(mRadioPowerOffReasons);
}
/**
@@ -1080,14 +1108,14 @@
public void setRadioPower(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
- Phone.RADIO_POWER_REASON_USER);
+ TelephonyManager.RADIO_POWER_REASON_USER);
}
/**
* Turn on or off radio power with option to specify whether it's for emergency call and specify
* a reason for setting the power state.
- * More details check {@link PhoneInternalInterface#setRadioPower(
- * boolean, boolean, boolean, boolean, int)}.
+ * More details check {@link
+ * PhoneInternalInterface#setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}.
*/
public void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) {
@@ -1110,7 +1138,7 @@
if (power && !mRadioPowerOffReasons.isEmpty()) {
log("setRadioPowerForReason " + "power: " + power + " forEmergencyCall= "
+ forEmergencyCall + " isSelectedPhoneForEmergencyCall: "
- + isSelectedPhoneForEmergencyCall + " forceApply " + forceApply + "reason:"
+ + isSelectedPhoneForEmergencyCall + " forceApply " + forceApply + " reason: "
+ reason + " will not power on the radio as it is powered off for the "
+ "following reasons: " + mRadioPowerOffReasons + ".");
return;
@@ -1121,23 +1149,6 @@
}
/**
- * Radio power set from carrier action. if set to false means carrier desire to turn radio off
- * and radio wont be re-enabled unless carrier explicitly turn it back on.
- * @param enable indicate if radio power is enabled or disabled from carrier action.
- */
- public void setRadioPowerFromCarrier(boolean enable) {
- boolean disableByCarrier = !enable;
- if (mRadioDisabledByCarrier == disableByCarrier) {
- log("setRadioPowerFromCarrier mRadioDisabledByCarrier is already "
- + disableByCarrier + " Do nothing.");
- return;
- }
-
- mRadioDisabledByCarrier = disableByCarrier;
- setPowerStateToDesired();
- }
-
- /**
* These two flags manage the behavior of the cell lock -- the
* lock should be held if either flag is true. The intention is
* to allow temporary acquisition of the lock to get a single
@@ -1189,22 +1200,9 @@
switch (msg.what) {
case EVENT_SET_RADIO_POWER_OFF:
synchronized(this) {
- if (mPhone.isUsingNewDataStack()) {
- mPendingRadioPowerOffAfterDataOff = false;
- log("Wait for all data networks torn down timed out. Power off now.");
- hangupAndPowerOff();
- return;
- }
- if (mPendingRadioPowerOffAfterDataOff &&
- (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
- if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOffTag += 1;
- mPendingRadioPowerOffAfterDataOff = false;
- } else {
- log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
- "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
- }
+ mPendingRadioPowerOffAfterDataOff = false;
+ log("Wait for all data networks torn down timed out. Power off now.");
+ hangupAndPowerOff();
}
break;
@@ -1223,8 +1221,8 @@
mCdnr.updateEfFromUsim(null /* Usim */);
}
onUpdateIccAvailability();
- if (mUiccApplcation == null
- || mUiccApplcation.getState() != AppState.APPSTATE_READY) {
+ if (mUiccApplication == null
+ || mUiccApplication.getState() != AppState.APPSTATE_READY) {
mIsSimReady = false;
updateSpnDisplay();
}
@@ -1471,45 +1469,28 @@
}
break;
- case EVENT_REGISTER_DATA_NETWORK_EXISTING_CHANGED: {
- mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
- new DataNetworkControllerCallback(this::post) {
- @Override
- public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) {
- if (mAnyDataExisting != anyDataExisting) {
- mAnyDataExisting = anyDataExisting;
- log("onAnyDataNetworkExistingChanged: anyDataExisting="
- + anyDataExisting);
- if (!mAnyDataExisting) {
- sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED);
- }
- }
- }
- });
- break;
- }
case EVENT_ALL_DATA_DISCONNECTED:
- if (mPhone.isUsingNewDataStack()) {
- log("EVENT_ALL_DATA_DISCONNECTED");
- if (mPendingRadioPowerOffAfterDataOff) {
+ log("EVENT_ALL_DATA_DISCONNECTED");
+ synchronized (this) {
+ if (!mPendingRadioPowerOffAfterDataOff) return;
+ boolean areAllDataDisconnectedOnAllPhones = true;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone.getDataNetworkController().areAllDataDisconnected()) {
+ phone.getDataNetworkController()
+ .unregisterDataNetworkControllerCallback(
+ mDataDisconnectedCallback);
+ } else {
+ log("Still waiting for all data disconnected on phone: "
+ + phone.getSubId());
+ areAllDataDisconnectedOnAllPhones = false;
+ }
+ }
+ if (areAllDataDisconnectedOnAllPhones) {
mPendingRadioPowerOffAfterDataOff = false;
removeMessages(EVENT_SET_RADIO_POWER_OFF);
- if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
+ if (DBG) log("Data disconnected for all phones, turn radio off now.");
hangupAndPowerOff();
}
- return;
- }
- int dds = SubscriptionManager.getDefaultDataSubscriptionId();
- ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
- synchronized(this) {
- if (mPendingRadioPowerOffAfterDataOff) {
- if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOffTag += 1;
- mPendingRadioPowerOffAfterDataOff = false;
- } else {
- log("EVENT_ALL_DATA_DISCONNECTED is stale");
- }
}
break;
@@ -1666,7 +1647,8 @@
if (ar.exception == null) {
boolean enable = (boolean) ar.result;
if (DBG) log("EVENT_RADIO_POWER_FROM_CARRIER: " + enable);
- setRadioPowerFromCarrier(enable);
+ setRadioPowerForReason(enable, false, false, false,
+ TelephonyManager.RADIO_POWER_REASON_CARRIER);
}
break;
@@ -1675,8 +1657,8 @@
if (ar.exception == null) {
List<PhysicalChannelConfig> list = (List<PhysicalChannelConfig>) ar.result;
if (VDBG) {
- log("EVENT_PHYSICAL_CHANNEL_CONFIG: size=" + list.size() + " list="
- + list);
+ log("EVENT_PHYSICAL_CHANNEL_CONFIG: list=" + list
+ + (list == null ? "" : ", list.size()=" + list.size()));
}
mLastPhysicalChannelConfigList = list;
boolean hasChanged = false;
@@ -1695,9 +1677,14 @@
// Notify NR frequency, NR connection status or bandwidths changed.
if (hasChanged) {
mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+ mServiceStateChangedRegistrants.notifyRegistrants();
TelephonyMetrics.getInstance().writeServiceStateChanged(
mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ ImsPhone imsPhone = (ImsPhone) mPhone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.getImsStats().onServiceStateChanged(mSS);
+ }
mServiceStateStats.onServiceStateChanged(mSS);
}
}
@@ -1718,10 +1705,6 @@
rspRspMsg.sendToTarget();
break;
- case EVENT_CARRIER_CONFIG_CHANGED:
- onCarrierConfigChanged();
- break;
-
case EVENT_POLL_STATE_REQUEST:
pollStateInternal(false);
break;
@@ -2106,16 +2089,20 @@
if (physicalChannelConfigs != null) {
for (PhysicalChannelConfig config : physicalChannelConfigs) {
if (isNrPhysicalChannelConfig(config) && isInternetPhysicalChannelConfig(config)) {
- // Update the NR frequency range if there is an internet data connection
+ // Update the NR frequency range if there is an active internet data connection
// associated with this NR physical channel channel config.
- newFrequencyRange = ServiceState.getBetterNRFrequencyRange(
- newFrequencyRange, config.getFrequencyRange());
- break;
+ // If there are multiple valid configs, use the highest frequency range value.
+ newFrequencyRange = Math.max(newFrequencyRange, config.getFrequencyRange());
}
}
}
boolean hasChanged = newFrequencyRange != ss.getNrFrequencyRange();
+ if (hasChanged) {
+ log(String.format("NR frequency range changed from %s to %s.",
+ ServiceState.frequencyRangeToString(ss.getNrFrequencyRange()),
+ ServiceState.frequencyRangeToString(newFrequencyRange)));
+ }
ss.setNrFrequencyRange(newFrequencyRange);
return hasChanged;
}
@@ -2146,6 +2133,11 @@
}
boolean hasChanged = newNrState != oldNrState;
+ if (hasChanged) {
+ log(String.format("NR state changed from %s to %s.",
+ NetworkRegistrationInfo.nrStateToString(oldNrState),
+ NetworkRegistrationInfo.nrStateToString(newNrState)));
+ }
regInfo.setNrState(newNrState);
ss.addNetworkRegistrationInfo(regInfo);
return hasChanged;
@@ -2157,18 +2149,8 @@
private boolean isInternetPhysicalChannelConfig(PhysicalChannelConfig config) {
for (int cid : config.getContextIds()) {
- if (mPhone.isUsingNewDataStack()) {
- if (mPhone.getDataNetworkController().isInternetNetwork(cid)) {
- return true;
- }
- } else {
- DataConnection dc = mPhone.getDcTracker(
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .getDataConnectionByContextId(cid);
- if (dc != null && dc.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- return true;
- }
+ if (mPhone.getDataNetworkController().isInternetNetwork(cid)) {
+ return true;
}
}
return false;
@@ -2195,14 +2177,14 @@
if (wlanPsRegState != null
&& wlanPsRegState.getAccessNetworkTechnology()
== TelephonyManager.NETWORK_TYPE_IWLAN
- && wlanPsRegState.getRegistrationState()
+ && wlanPsRegState.getNetworkRegistrationState()
== NetworkRegistrationInfo.REGISTRATION_STATE_HOME
&& isIwlanPreferred) {
serviceState.setDataRegState(ServiceState.STATE_IN_SERVICE);
} else if (wwanPsRegState != null) {
// If the device is not camped on IWLAN, then we use cellular PS registration state
// to compute reg state and rat.
- int regState = wwanPsRegState.getRegistrationState();
+ int regState = wwanPsRegState.getNetworkRegistrationState();
serviceState.setDataRegState(regCodeToServiceState(regState));
}
if (DBG) {
@@ -2218,10 +2200,8 @@
VoiceSpecificRegistrationInfo voiceSpecificStates =
networkRegState.getVoiceSpecificInfo();
- int registrationState = networkRegState.getRegistrationState();
+ int registrationState = networkRegState.getNetworkRegistrationState();
int cssIndicator = voiceSpecificStates.cssSupported ? 1 : 0;
- int newVoiceRat = ServiceState.networkTypeToRilRadioTechnology(
- networkRegState.getAccessNetworkTechnology());
mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
mNewSS.setCssIndicator(cssIndicator);
mNewSS.addNetworkRegistrationInfo(networkRegState);
@@ -2254,7 +2234,7 @@
&& !isRoamIndForHomeSystem(roamingIndicator);
mNewSS.setVoiceRoaming(cdmaRoaming);
mRoamingIndicator = roamingIndicator;
- mIsInPrl = (systemIsInPrl == 0) ? false : true;
+ mIsInPrl = systemIsInPrl != 0;
mDefaultRoamingIndicator = defaultRoamingIndicator;
int systemId = 0;
@@ -2300,7 +2280,7 @@
mNewSS.addNetworkRegistrationInfo(networkRegState);
DataSpecificRegistrationInfo dataSpecificStates =
networkRegState.getDataSpecificInfo();
- int registrationState = networkRegState.getRegistrationState();
+ int registrationState = networkRegState.getNetworkRegistrationState();
int serviceState = regCodeToServiceState(registrationState);
int newDataRat = ServiceState.networkTypeToRilRadioTechnology(
networkRegState.getAccessNetworkTechnology());
@@ -2631,8 +2611,7 @@
*/
private boolean isRoamIndForHomeSystem(int roamInd) {
// retrieve the carrier-specified list of ERIs for home system
- final PersistableBundle config = getCarrierConfig();
- int[] homeRoamIndicators = config.getIntArray(CarrierConfigManager
+ int[] homeRoamIndicators = mCarrierConfig.getIntArray(CarrierConfigManager
.KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY);
log("isRoamIndForHomeSystem: homeRoamIndicators=" + Arrays.toString(homeRoamIndicators));
@@ -2661,8 +2640,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void updateRoamingState() {
- PersistableBundle bundle = getCarrierConfig();
-
if (mPhone.isPhoneTypeGsm()) {
/**
* Since the roaming state of gsm service (from +CREG) and
@@ -2687,14 +2664,14 @@
roaming = false;
}
- if (alwaysOnHomeNetwork(bundle)) {
+ if (alwaysOnHomeNetwork(mCarrierConfig)) {
log("updateRoamingState: carrier config override always on home network");
roaming = false;
- } else if (isNonRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())) {
+ } else if (isNonRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())) {
log("updateRoamingState: carrier config override set non roaming:"
+ mNewSS.getOperatorNumeric());
roaming = false;
- } else if (isRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())) {
+ } else if (isRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())) {
log("updateRoamingState: carrier config override set roaming:"
+ mNewSS.getOperatorNumeric());
roaming = true;
@@ -2704,16 +2681,16 @@
} else {
String systemId = Integer.toString(mNewSS.getCdmaSystemId());
- if (alwaysOnHomeNetwork(bundle)) {
+ if (alwaysOnHomeNetwork(mCarrierConfig)) {
log("updateRoamingState: carrier config override always on home network");
setRoamingOff();
- } else if (isNonRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())
- || isNonRoamingInCdmaNetwork(bundle, systemId)) {
+ } else if (isNonRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())
+ || isNonRoamingInCdmaNetwork(mCarrierConfig, systemId)) {
log("updateRoamingState: carrier config override set non-roaming:"
+ mNewSS.getOperatorNumeric() + ", " + systemId);
setRoamingOff();
- } else if (isRoamingInGsmNetwork(bundle, mNewSS.getOperatorNumeric())
- || isRoamingInCdmaNetwork(bundle, systemId)) {
+ } else if (isRoamingInGsmNetwork(mCarrierConfig, mNewSS.getOperatorNumeric())
+ || isRoamingInCdmaNetwork(mCarrierConfig, systemId)) {
log("updateRoamingState: carrier config override set roaming:"
+ mNewSS.getOperatorNumeric() + ", " + systemId);
setRoamingOn();
@@ -2744,24 +2721,21 @@
if (!mPhone.isPhoneTypeGsm() && !mSS.getRoaming()) {
boolean hasBrandOverride = mUiccController.getUiccPort(getPhoneId()) != null
&& mUiccController.getUiccPort(getPhoneId()).getOperatorBrandOverride() != null;
- if (!hasBrandOverride) {
- PersistableBundle config = getCarrierConfig();
- if (config.getBoolean(
+ if (!hasBrandOverride && mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL)) {
- String operator = config.getString(
- CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING);
- log("updateOperatorNameFromCarrierConfig: changing from "
- + mSS.getOperatorAlpha() + " to " + operator);
- // override long and short operator name, keeping numeric the same
- mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric());
- }
+ String operator = mCarrierConfig.getString(
+ CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING);
+ log("updateOperatorNameFromCarrierConfig: changing from "
+ + mSS.getOperatorAlpha() + " to " + operator);
+ // override long and short operator name, keeping numeric the same
+ mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric());
}
}
}
private void notifySpnDisplayUpdate(CarrierDisplayNameData data) {
int subId = mPhone.getSubId();
- // Update ACTION_SERVICE_PROVIDERS_UPDATED IFF any value changes
+ // Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changes
if (mSubId != subId
|| data.shouldShowPlmn() != mCurShowPlmn
|| data.shouldShowSpn() != mCurShowSpn
@@ -2791,13 +2765,12 @@
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- if (!mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(),
- data.shouldShowPlmn(), data.getPlmn(), data.shouldShowSpn(), data.getSpn())) {
- mSpnUpdatePending = true;
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull(
+ getCarrierName(data.shouldShowPlmn(), data.getPlmn(),
+ data.shouldShowSpn(), data.getSpn())));
}
}
-
- mSubId = subId;
mCurShowSpn = data.shouldShowSpn();
mCurShowPlmn = data.shouldShowPlmn();
mCurSpn = data.getSpn();
@@ -2805,6 +2778,26 @@
mCurPlmn = data.getPlmn();
}
+ @NonNull
+ private String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) {
+ String carrierName = "";
+ if (showPlmn) {
+ carrierName = plmn;
+ if (showSpn) {
+ // Need to show both plmn and spn if both are not same.
+ if (!Objects.equals(spn, plmn)) {
+ String separator = mPhone.getContext().getString(
+ com.android.internal.R.string.kg_text_message_separator).toString();
+ carrierName = new StringBuilder().append(carrierName).append(separator)
+ .append(spn).toString();
+ }
+ }
+ } else if (showSpn) {
+ carrierName = spn;
+ }
+ return carrierName;
+ }
+
private void updateSpnDisplayCdnr() {
log("updateSpnDisplayCdnr+");
CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
@@ -2815,8 +2808,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public void updateSpnDisplay() {
- PersistableBundle config = getCarrierConfig();
- if (config.getBoolean(CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) {
+ if (mCarrierConfig.getBoolean(
+ CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) {
updateSpnDisplayCdnr();
} else {
updateSpnDisplayLegacy();
@@ -2829,14 +2822,15 @@
String spn = null;
String dataSpn = null;
boolean showSpn = false;
- String plmn = null;
- boolean showPlmn = false;
+ String plmn;
+ boolean showPlmn;
String wfcVoiceSpnFormat = null;
String wfcDataSpnFormat = null;
String wfcFlightSpnFormat = null;
int combinedRegState = getCombinedRegState(mSS);
if (mPhone.getImsPhone() != null && mPhone.getImsPhone().isWifiCallingEnabled()
+ && mPhone.isImsRegistered()
&& (combinedRegState == ServiceState.STATE_IN_SERVICE
&& mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN)) {
// In Wi-Fi Calling mode (connected to WiFi and WFC enabled),
@@ -2847,20 +2841,17 @@
//
// 2) Show PLMN + Wi-Fi Calling if there is no valid SPN in case 1
- int voiceIdx = 0;
- int dataIdx = 0;
- int flightModeIdx = -1;
- boolean useRootLocale = false;
+ int voiceIdx;
+ int dataIdx;
+ int flightModeIdx;
+ boolean useRootLocale;
- PersistableBundle bundle = getCarrierConfig();
-
- voiceIdx = bundle.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT);
- dataIdx = bundle.getInt(
- CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT);
- flightModeIdx = bundle.getInt(
+ voiceIdx = mCarrierConfig.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT);
+ dataIdx = mCarrierConfig.getInt(CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT);
+ flightModeIdx = mCarrierConfig.getInt(
CarrierConfigManager.KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT);
useRootLocale =
- bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+ mCarrierConfig.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
String[] wfcSpnFormats = SubscriptionManager.getResourcesForSubId(mPhone.getContext(),
mPhone.getSubId(), useRootLocale)
@@ -2891,21 +2882,19 @@
&& (mPhone.getImsPhone() != null)
&& (mPhone.getImsPhone().getImsRegistrationTech()
== ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
- // In Cros SIM Calling mode show SPN or PLMN + Cross SIM Calling
+ // In Cross SIM Calling mode show SPN or PLMN + Cross SIM Calling
//
// 1) Show SPN + Cross SIM Calling If SIM has SPN and SPN display condition
// is satisfied or SPN override is enabled for this carrier
//
// 2) Show PLMN + Cross SIM Calling if there is no valid SPN in case 1
- PersistableBundle bundle = getCarrierConfig();
int crossSimSpnFormatIdx =
- bundle.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
+ mCarrierConfig.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
boolean useRootLocale =
- bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+ mCarrierConfig.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId(
- mPhone.getContext(),
- mPhone.getSubId(), useRootLocale)
+ mPhone.getContext(), mPhone.getSubId(), useRootLocale)
.getStringArray(R.array.crossSimSpnFormats);
if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
@@ -2935,7 +2924,6 @@
// EXTRA_SHOW_PLMN = true
// EXTRA_PLMN = null
- IccRecords iccRecords = mIccRecords;
int rule = getCarrierNameDisplayBitmask(mSS);
boolean noService = false;
if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE
@@ -2951,8 +2939,7 @@
} else {
// No service at all
plmn = Resources.getSystem()
- .getText(
- com.android.internal.R.string.lockscreen_carrier_default)
+ .getText(com.android.internal.R.string.lockscreen_carrier_default)
.toString();
noService = true;
}
@@ -2995,8 +2982,7 @@
} else if (!TextUtils.isEmpty(plmn)) {
// Show PLMN + Cross-SIM Calling if there is no valid SPN in the above case
String originalPlmn = plmn.trim();
- PersistableBundle config = getCarrierConfig();
- if (mIccRecords != null && config.getBoolean(
+ if (mIccRecords != null && mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) {
originalPlmn = mIccRecords.getPnnHomeName();
}
@@ -3021,8 +3007,7 @@
// Show PLMN + Wi-Fi Calling if there is no valid SPN in the above case
String originalPlmn = plmn.trim();
- PersistableBundle config = getCarrierConfig();
- if (mIccRecords != null && config.getBoolean(
+ if (mIccRecords != null && mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) {
originalPlmn = mIccRecords.getPnnHomeName();
}
@@ -3105,12 +3090,12 @@
protected void setPowerStateToDesired(boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
if (DBG) {
- String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown +
- ", mDesiredPowerState=" + mDesiredPowerState +
- ", getRadioState=" + mCi.getRadioState() +
- ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier +
- ", IMS reg state=" + mImsRegistrationOnOff +
- ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
+ String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown
+ + ", mDesiredPowerState=" + mDesiredPowerState
+ + ", getRadioState=" + mCi.getRadioState()
+ + ", mRadioPowerOffReasons=" + mRadioPowerOffReasons
+ + ", IMS reg state=" + mImsRegistrationOnOff
+ + ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
log(tmpLog);
mRadioPowerLog.log(tmpLog);
}
@@ -3122,22 +3107,13 @@
}
// If we want it on and it's off, turn it on
- if (mDesiredPowerState && !mRadioDisabledByCarrier
+ if (mDesiredPowerState && mRadioPowerOffReasons.isEmpty()
&& (forceApply || mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF)) {
mCi.setRadioPower(true, forEmergencyCall, isSelectedPhoneForEmergencyCall, null);
- } else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState()
+ } else if ((!mDesiredPowerState || !mRadioPowerOffReasons.isEmpty()) && mCi.getRadioState()
== TelephonyManager.RADIO_POWER_ON) {
- // If it's on and available and we want it off gracefully
- if (!mPhone.isUsingNewDataStack() && mImsRegistrationOnOff
- && getRadioPowerOffDelayTimeoutForImsRegistration() > 0) {
- if (DBG) log("setPowerStateToDesired: delaying power off until IMS dereg.");
- startDelayRadioOffWaitingForImsDeregTimeout();
- // Return early here as we do not want to hit the cancel timeout code below.
- return;
- } else {
- if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()");
- powerOffRadioSafely();
- }
+ if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()");
+ powerOffRadioSafely();
} else if (mDeviceShuttingDown
&& (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE)) {
// !mDesiredPowerState condition above will happen first if the radio is on, so we will
@@ -3151,7 +3127,6 @@
/**
* Cancel the EVENT_POWER_OFF_RADIO_DELAYED event if it is currently pending to be completed.
- * @return true if there was a pending timeout message in the queue, false otherwise.
*/
private void cancelDelayRadioOffWaitingForImsDeregTimeout() {
if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) {
@@ -3160,21 +3135,6 @@
}
}
- /**
- * Start a timer to turn off the radio if IMS does not move to deregistered after the
- * radio power off event occurred. If this event already exists in the message queue, then
- * ignore the new request and use the existing one.
- */
- private void startDelayRadioOffWaitingForImsDeregTimeout() {
- if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) {
- if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: timer exists, ignoring");
- return;
- }
- if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: starting timer");
- sendEmptyMessageDelayed(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT,
- getRadioPowerOffDelayTimeoutForImsRegistration());
- }
-
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
return;
@@ -3182,7 +3142,7 @@
UiccCardApplication newUiccApplication = getUiccCardApplication();
- if (mUiccApplcation != newUiccApplication) {
+ if (mUiccApplication != newUiccApplication) {
// Remove the EF records that come from UICC
if (mIccRecords instanceof SIMRecords) {
@@ -3191,26 +3151,26 @@
mCdnr.updateEfFromRuim(null /* ruim */);
}
- if (mUiccApplcation != null) {
+ if (mUiccApplication != null) {
log("Removing stale icc objects.");
- mUiccApplcation.unregisterForReady(this);
+ mUiccApplication.unregisterForReady(this);
if (mIccRecords != null) {
mIccRecords.unregisterForRecordsLoaded(this);
}
mIccRecords = null;
- mUiccApplcation = null;
+ mUiccApplication = null;
}
if (newUiccApplication != null) {
log("New card found");
- mUiccApplcation = newUiccApplication;
- mIccRecords = mUiccApplcation.getIccRecords();
+ mUiccApplication = newUiccApplication;
+ mIccRecords = mUiccApplication.getIccRecords();
if (mPhone.isPhoneTypeGsm()) {
- mUiccApplcation.registerForReady(this, EVENT_SIM_READY, null);
+ mUiccApplication.registerForReady(this, EVENT_SIM_READY, null);
if (mIccRecords != null) {
mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
}
} else if (mIsSubscriptionFromRuim) {
- mUiccApplcation.registerForReady(this, EVENT_RUIM_READY, null);
+ mUiccApplication.registerForReady(this, EVENT_RUIM_READY, null);
if (mIccRecords != null) {
mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
}
@@ -3297,7 +3257,6 @@
+ " mImsRegistrationOnOff=" + mImsRegistrationOnOff
+ "}");
-
if (mImsRegistrationOnOff && !registered) {
// moving to deregistered, only send this event if we need to re-evaluate
if (getRadioPowerOffDelayTimeoutForImsRegistration() > 0) {
@@ -3310,6 +3269,9 @@
}
}
mImsRegistrationOnOff = registered;
+
+ // It's possible ServiceState changes did not trigger SPN display update; we update it here.
+ updateSpnDisplay();
}
public void onImsCapabilityChanged() {
@@ -3346,7 +3308,7 @@
nri = mNewSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false);
+ mNewSS.setOutOfService(false);
// Add the IWLAN registration info back to service state.
if (nri != null) {
mNewSS.addNetworkRegistrationInfo(nri);
@@ -3363,7 +3325,7 @@
nri = mNewSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), true);
+ mNewSS.setOutOfService(true);
// Add the IWLAN registration info back to service state.
if (nri != null) {
mNewSS.addNetworkRegistrationInfo(nri);
@@ -3455,20 +3417,21 @@
useDataRegStateForDataOnlyDevices();
processIwlanRegistrationInfo();
- if (TelephonyUtils.IS_DEBUGGABLE && mPhone.mTelephonyTester != null) {
- mPhone.mTelephonyTester.overrideServiceState(mNewSS);
+ updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
+ updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
+
+ if (TelephonyUtils.IS_DEBUGGABLE && mPhone.getTelephonyTester() != null) {
+ mPhone.getTelephonyTester().overrideServiceState(mNewSS);
}
NetworkRegistrationInfo networkRegState = mNewSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
- updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
if (DBG) {
- log("Poll ServiceState done: "
- + " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]"
- + " oldMaxDataCalls=" + mMaxDataCalls
+ log("Poll ServiceState done: oldSS=" + mSS);
+ log("Poll ServiceState done: newSS=" + mNewSS);
+ log("Poll ServiceState done: oldMaxDataCalls=" + mMaxDataCalls
+ " mNewMaxDataCalls=" + mNewMaxDataCalls
+ " oldReasonDataDenied=" + mReasonDataDenied
+ " mNewReasonDataDenied=" + mNewReasonDataDenied);
@@ -3489,14 +3452,10 @@
mSS.getState() == ServiceState.STATE_POWER_OFF
&& mNewSS.getState() != ServiceState.STATE_POWER_OFF;
- SparseBooleanArray hasDataAttached = new SparseBooleanArray(
- mAccessNetworksManager.getAvailableTransports().length);
- SparseBooleanArray hasDataDetached = new SparseBooleanArray(
- mAccessNetworksManager.getAvailableTransports().length);
- SparseBooleanArray hasRilDataRadioTechnologyChanged = new SparseBooleanArray(
- mAccessNetworksManager.getAvailableTransports().length);
- SparseBooleanArray hasDataRegStateChanged = new SparseBooleanArray(
- mAccessNetworksManager.getAvailableTransports().length);
+ SparseBooleanArray hasDataAttached = new SparseBooleanArray();
+ SparseBooleanArray hasDataDetached = new SparseBooleanArray();
+ SparseBooleanArray hasRilDataRadioTechnologyChanged = new SparseBooleanArray();
+ SparseBooleanArray hasDataRegStateChanged = new SparseBooleanArray();
boolean anyDataRegChanged = false;
boolean anyDataRatChanged = false;
boolean hasAlphaRawChanged =
@@ -3510,11 +3469,6 @@
NetworkRegistrationInfo newNrs = mNewSS.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, transport);
- // If the previously it was not in service, and now it's in service, trigger the
- // attached event. Also if airplane mode was just turned on, and data is already in
- // service, we need to trigger the attached event again so that DcTracker can setup
- // data on all connectable APNs again (because we've already torn down all data
- // connections just before airplane mode turned on)
boolean changed = (oldNrs == null || !oldNrs.isInService() || hasAirplaneModeOnChanged)
&& (newNrs != null && newNrs.isInService());
hasDataAttached.put(transport, changed);
@@ -3539,9 +3493,9 @@
anyDataRatChanged = true;
}
- int oldRegState = oldNrs != null ? oldNrs.getRegistrationState()
+ int oldRegState = oldNrs != null ? oldNrs.getNetworkRegistrationState()
: NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
- int newRegState = newNrs != null ? newNrs.getRegistrationState()
+ int newRegState = newNrs != null ? newNrs.getNetworkRegistrationState()
: NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
hasDataRegStateChanged.put(transport, oldRegState != newRegState);
if (oldRegState != newRegState) {
@@ -3598,8 +3552,6 @@
boolean hasCssIndicatorChanged = (mSS.getCssIndicator() != mNewSS.getCssIndicator());
- boolean hasBandwidthChanged = mSS.getCellBandwidths() != mNewSS.getCellBandwidths();
-
boolean has4gHandoff = false;
boolean hasMultiApnSupport = false;
boolean hasLostMultiApnSupport = false;
@@ -3643,7 +3595,6 @@
+ " hasCssIndicatorChanged = " + hasCssIndicatorChanged
+ " hasNrFrequencyRangeChanged = " + hasNrFrequencyRangeChanged
+ " hasNrStateChanged = " + hasNrStateChanged
- + " hasBandwidthChanged = " + hasBandwidthChanged
+ " hasAirplaneModeOnlChanged = " + hasAirplaneModeOnChanged);
}
@@ -3690,7 +3641,7 @@
ServiceState oldMergedSS = new ServiceState(mPhone.getServiceState());
mSS = new ServiceState(mNewSS);
- mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), false);
+ mNewSS.setOutOfService(false);
mCellIdentity = primaryCellIdentity;
if (mSS.getState() == ServiceState.STATE_IN_SERVICE && primaryCellIdentity != null) {
@@ -3736,10 +3687,6 @@
mCssIndicatorChangedRegistrants.notifyRegistrants();
}
- if (hasBandwidthChanged) {
- mBandwidthChangedRegistrants.notifyRegistrants();
- }
-
if (hasRejectCauseChanged) {
setNotification(CS_REJECT_CAUSE_ENABLED);
}
@@ -3775,8 +3722,10 @@
// incomplete.
// CellIdentity can return a null MCC and MNC in CDMA
String localeOperator = operatorNumeric;
- if (isInvalidOperatorNumeric(operatorNumeric)
- || mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) {
+ if (mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) {
+ localeOperator = null;
+ }
+ if (isInvalidOperatorNumeric(localeOperator)) {
for (CellIdentity cid : prioritizedCids) {
if (!TextUtils.isEmpty(cid.getPlmn())) {
localeOperator = cid.getPlmn();
@@ -3817,6 +3766,10 @@
TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ ImsPhone imsPhone = (ImsPhone) mPhone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.getImsStats().onServiceStateChanged(mSS);
+ }
mServiceStateStats.onServiceStateChanged(mSS);
}
@@ -3982,8 +3935,9 @@
}
}
- if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY &&
- mIccRecords != null && getCombinedRegState(mSS) == ServiceState.STATE_IN_SERVICE
+ if (mUiccApplication != null && mUiccApplication.getState() == AppState.APPSTATE_READY
+ && mIccRecords != null
+ && getCombinedRegState(mSS) == ServiceState.STATE_IN_SERVICE
&& !ServiceState.isPsOnlyTech(mSS.getRilVoiceRadioTechnology())) {
// SIM is found on the device. If ERI roaming is OFF, and SID/NID matches
// one configured in SIM, use operator name from CSIM record. Note that ERI, SID,
@@ -4014,10 +3968,9 @@
}
String carrierName = mIccRecords != null ? mIccRecords.getServiceProviderName() : "";
- PersistableBundle config = getCarrierConfig();
- if (config.getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL)
+ if (mCarrierConfig.getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL)
|| TextUtils.isEmpty(carrierName)) {
- return config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ return mCarrierConfig.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
}
return carrierName;
@@ -4036,7 +3989,6 @@
*/
@CarrierNameDisplayBitmask
public int getCarrierNameDisplayBitmask(ServiceState ss) {
- PersistableBundle config = getCarrierConfig();
if (!TextUtils.isEmpty(getOperatorBrandOverride())) {
// If the operator has been overridden, all PLMNs will be considered HOME PLMNs, only
// show SPN.
@@ -4046,7 +3998,7 @@
// This is a hack from IccRecords#getServiceProviderName().
return CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN;
} else {
- boolean useRoamingFromServiceState = config.getBoolean(
+ boolean useRoamingFromServiceState = mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL);
int carrierDisplayNameConditionFromSim =
mIccRecords == null ? 0 : mIccRecords.getCarrierNameDisplayCondition();
@@ -4253,7 +4205,7 @@
}
/**
- * Do not set roaming state in case of oprators considered non-roaming.
+ * Do not set roaming state in case of operators considered non-roaming.
*
* Can use mcc or mcc+mnc as item of
* {@link CarrierConfigManager#KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
@@ -4266,8 +4218,7 @@
private boolean isOperatorConsideredNonRoaming(ServiceState s) {
String operatorNumeric = s.getOperatorNumeric();
- PersistableBundle config = getCarrierConfig();
- String[] numericArray = config.getStringArray(
+ String[] numericArray = mCarrierConfig.getStringArray(
CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY);
if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
@@ -4284,8 +4235,7 @@
private boolean isOperatorConsideredRoaming(ServiceState s) {
String operatorNumeric = s.getOperatorNumeric();
- PersistableBundle config = getCarrierConfig();
- String[] numericArray = config.getStringArray(
+ String[] numericArray = mCarrierConfig.getStringArray(
CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY);
if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
return false;
@@ -4318,12 +4268,12 @@
((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) ||
((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
//ignore the normal call and data restricted state before SIM READY
- if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY) {
+ if (mUiccApplication != null
+ && mUiccApplication.getState() == AppState.APPSTATE_READY) {
newRs.setCsNormalRestricted(
((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) ||
((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
- newRs.setPsRestricted(
- (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0);
+ newRs.setPsRestricted((state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL) != 0);
}
if (DBG) log("onRestrictedStateChanged: new rs "+ newRs);
@@ -4537,12 +4487,9 @@
}
Context context = mPhone.getContext();
- SubscriptionInfo info = mSubscriptionController
- .getActiveSubscriptionInfo(mPhone.getSubId(), context.getOpPackageName(),
- context.getAttributionTag());
-
- //if subscription is part of a group and non-primary, suppress all notifications
- if (info == null || (info.isOpportunistic() && info.getGroupUuid() != null)) {
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(mPhone.getSubId());
+ if (subInfo == null || !subInfo.isVisible()) {
log("cannot setNotification on invisible subid mSubId=" + mSubId);
return;
}
@@ -4555,10 +4502,8 @@
return;
}
- boolean autoCancelCsRejectNotification = false;
-
- PersistableBundle bundle = getCarrierConfig();
- boolean disableVoiceBarringNotification = bundle.getBoolean(
+ boolean autoCancelCsRejectNotification;
+ boolean disableVoiceBarringNotification = mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
if (disableVoiceBarringNotification && (notifyType == CS_ENABLED
|| notifyType == CS_NORMAL_ENABLED
@@ -4566,7 +4511,7 @@
if (DBG) log("Voice/emergency call barred notification disabled");
return;
}
- autoCancelCsRejectNotification = bundle.getBoolean(
+ autoCancelCsRejectNotification = mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
CharSequence details = "";
@@ -4576,7 +4521,7 @@
final boolean multipleSubscriptions = (((TelephonyManager) mPhone.getContext()
.getSystemService(Context.TELEPHONY_SERVICE)).getPhoneCount() > 1);
- final int simNumber = mSubscriptionController.getSlotIndex(mSubId) + 1;
+ int simNumber = SubscriptionManager.getSlotIndex(mSubId) + 1;
switch (notifyType) {
case PS_ENABLED:
@@ -4987,9 +4932,10 @@
*
* @param h handler to notify
* @param what what code of message when delivered
+ * @param userobj the user obj that will be passed back when notify
*/
- public void registerForServiceStateChanged(Handler h, int what) {
- mServiceStateChangedRegistrants.addUnique(h, what, null);
+ public void registerForServiceStateChanged(Handler h, int what, Object userobj) {
+ mServiceStateChangedRegistrants.addUnique(h, what, userobj);
}
/**
@@ -5009,79 +4955,33 @@
public void powerOffRadioSafely() {
synchronized (this) {
if (!mPendingRadioPowerOffAfterDataOff) {
- if (mPhone.isUsingNewDataStack()) {
- if (mAnyDataExisting) {
- log("powerOffRadioSafely: Tear down all data networks.");
- mPhone.getDataNetworkController().tearDownAllDataNetworks(
- DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON);
- sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF,
- POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT);
- } else {
- log("powerOffRadioSafely: No data is connected.");
- sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED);
- }
- mPendingRadioPowerOffAfterDataOff = true;
- return;
+ // hang up all active voice calls first
+ if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
+ mPhone.mCT.mRingingCall.hangupIfAlive();
+ mPhone.mCT.mBackgroundCall.hangupIfAlive();
+ mPhone.mCT.mForegroundCall.hangupIfAlive();
}
- int dds = SubscriptionManager.getDefaultDataSubscriptionId();
- // To minimize race conditions we call cleanUpAllConnections on
- // both if else paths instead of before this isDisconnected test.
- if (mPhone.areAllDataDisconnected()
- && (dds == mPhone.getSubId()
- || (dds != mPhone.getSubId()
- && ProxyController.getInstance().areAllDataDisconnected(dds)))) {
- // To minimize race conditions we do this after isDisconnected
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- if (mPhone.getDcTracker(transport) != null) {
- mPhone.getDcTracker(transport).cleanUpAllConnections(
- Phone.REASON_RADIO_TURNED_OFF);
- }
- }
- if (DBG) {
- log("powerOffRadioSafely: Data disconnected, turn off radio now.");
- }
- hangupAndPowerOff();
- } else {
- // hang up all active voice calls first
- if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
- mPhone.mCT.mRingingCall.hangupIfAlive();
- mPhone.mCT.mBackgroundCall.hangupIfAlive();
- mPhone.mCT.mForegroundCall.hangupIfAlive();
- }
- for (int transport : mAccessNetworksManager.getAvailableTransports()) {
- if (mPhone.getDcTracker(transport) != null) {
- mPhone.getDcTracker(transport).cleanUpAllConnections(
- Phone.REASON_RADIO_TURNED_OFF);
- }
- }
- if (dds != mPhone.getSubId()
- && !ProxyController.getInstance().areAllDataDisconnected(dds)) {
- if (DBG) {
- log(String.format("powerOffRadioSafely: Data is active on DDS (%d)."
- + " Wait for all data disconnect", dds));
- }
- // Data is not disconnected on DDS. Wait for the data disconnect complete
- // before sending the RADIO_POWER off.
- ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
- EVENT_ALL_DATA_DISCONNECTED);
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (!phone.getDataNetworkController().areAllDataDisconnected()) {
+ log("powerOffRadioSafely: Data is active on phone " + phone.getSubId()
+ + ". Wait for all data disconnect.");
mPendingRadioPowerOffAfterDataOff = true;
+ phone.getDataNetworkController().registerDataNetworkControllerCallback(
+ mDataDisconnectedCallback);
}
- Message msg = Message.obtain(this);
- msg.what = EVENT_SET_RADIO_POWER_OFF;
- msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
- if (sendMessageDelayed(msg, 30000)) {
- if (DBG) {
- log("powerOffRadioSafely: Wait up to 30s for data to isconnect, then"
- + " turn off radio.");
- }
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- log("powerOffRadioSafely: Cannot send delayed Msg, turn off radio right"
- + " away.");
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOff = false;
- }
+ }
+
+ // Tear down outside of the disconnected check to prevent race conditions.
+ mPhone.getDataNetworkController().tearDownAllDataNetworks(
+ DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON);
+
+ if (mPendingRadioPowerOffAfterDataOff) {
+ sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF,
+ POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT);
+ } else {
+ log("powerOffRadioSafely: No data is connected, turn off radio now.");
+ hangupAndPowerOff();
}
}
}
@@ -5097,7 +4997,6 @@
if (mPendingRadioPowerOffAfterDataOff) {
if (DBG) log("Process pending request to turn radio off.");
hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOffTag += 1;
mPendingRadioPowerOffAfterDataOff = false;
return true;
}
@@ -5105,9 +5004,11 @@
}
}
- private void onCarrierConfigChanged() {
- PersistableBundle config = getCarrierConfig();
- log("CarrierConfigChange " + config);
+ private void onCarrierConfigurationChanged(int slotIndex) {
+ if (slotIndex != mPhone.getPhoneId()) return;
+
+ mCarrierConfig = getCarrierConfig();
+ log("CarrierConfigChange " + mCarrierConfig);
// Load the ERI based on carrier config. Carrier might have their specific ERI.
if (mEriManager != null) {
@@ -5115,8 +5016,8 @@
mCdnr.updateEfForEri(getOperatorNameFromEri());
}
- updateOperatorNamePattern(config);
- mCdnr.updateEfFromCarrierConfig(config);
+ updateOperatorNamePattern(mCarrierConfig);
+ mCdnr.updateEfFromCarrierConfig(mCarrierConfig);
mPhone.notifyCallForwardingIndicator();
// Sometimes the network registration information comes before carrier config is ready.
@@ -5174,8 +5075,8 @@
// Determine if the Icc card exists
private boolean iccCardExists() {
boolean iccCardExist = false;
- if (mUiccApplcation != null) {
- iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
+ if (mUiccApplication != null) {
+ iccCardExist = mUiccApplication.getState() != AppState.APPSTATE_UNKNOWN;
}
return iccCardExist;
}
@@ -5326,18 +5227,15 @@
pw.println(" mNewSS=" + mNewSS);
pw.println(" mVoiceCapable=" + mVoiceCapable);
pw.println(" mRestrictedState=" + mRestrictedState);
- pw.println(" mPollingContext=" + mPollingContext + " - " +
- (mPollingContext != null ? mPollingContext[0] : ""));
+ pw.println(" mPollingContext=" + Arrays.toString(mPollingContext));
pw.println(" mDesiredPowerState=" + mDesiredPowerState);
pw.println(" mRestrictedState=" + mRestrictedState);
pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
- pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
pw.println(" mCellIdentity=" + Rlog.pii(VDBG, mCellIdentity));
pw.println(" mLastCellInfoReqTime=" + mLastCellInfoReqTime);
dumpCellInfoList(pw);
pw.flush();
pw.println(" mAllowedNetworkTypes=" + mAllowedNetworkTypes);
- pw.println(" mAnyDataExisting=" + mAnyDataExisting);
pw.println(" mMaxDataCalls=" + mMaxDataCalls);
pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
pw.println(" mReasonDataDenied=" + mReasonDataDenied);
@@ -5366,8 +5264,8 @@
pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator);
pw.println(" mRegistrationState=" + mRegistrationState);
pw.println(" mMdn=" + mMdn);
- pw.println(" mHomeSystemId=" + mHomeSystemId);
- pw.println(" mHomeNetworkId=" + mHomeNetworkId);
+ pw.println(" mHomeSystemId=" + Arrays.toString(mHomeSystemId));
+ pw.println(" mHomeNetworkId=" + Arrays.toString(mHomeNetworkId));
pw.println(" mMin=" + mMin);
pw.println(" mPrlVersion=" + mPrlVersion);
pw.println(" mIsMinInfoReady=" + mIsMinInfoReady);
@@ -5381,9 +5279,8 @@
pw.println(" mImsRegistrationOnOff=" + mImsRegistrationOnOff);
pw.println(" pending radio off event="
+ hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT));
- pw.println(" mRadioDisabledByCarrier" + mRadioDisabledByCarrier);
+ pw.println(" mRadioPowerOffReasons=" + mRadioPowerOffReasons);
pw.println(" mDeviceShuttingDown=" + mDeviceShuttingDown);
- pw.println(" mSpnUpdatePending=" + mSpnUpdatePending);
pw.println(" mCellInfoMinIntervalMs=" + mCellInfoMinIntervalMs);
pw.println(" mEriManager=" + mEriManager);
@@ -5605,14 +5502,8 @@
return mPhone.getPhoneId();
}
- /* Reset Service state when IWLAN is enabled as polling in airplane mode
- * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF
- */
-
-
/**
- * This method adds IWLAN registration info for legacy mode devices camped on IWLAN. It also
- * makes some adjustments when the device camps on IWLAN in airplane mode.
+ * This method makes some adjustments when the device camps on IWLAN in airplane mode.
*/
private void processIwlanRegistrationInfo() {
if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
@@ -5626,7 +5517,7 @@
}
// operator info should be kept in SS
String operator = mNewSS.getOperatorAlphaLong();
- mNewSS.setOutOfService(mAccessNetworksManager.isInLegacyMode(), true);
+ mNewSS.setOutOfService(true);
if (resetIwlanRatVal) {
mNewSS.setDataRegState(ServiceState.STATE_IN_SERVICE);
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
@@ -5636,17 +5527,6 @@
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
.build();
mNewSS.addNetworkRegistrationInfo(nri);
- if (mAccessNetworksManager.isInLegacyMode()) {
- // If in legacy mode, simulate the behavior that IWLAN registration info
- // is reported through WWAN transport.
- nri = new NetworkRegistrationInfo.Builder()
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- mNewSS.addNetworkRegistrationInfo(nri);
- }
mNewSS.setOperatorAlphaLong(operator);
// Since it's in airplane mode, cellular must be out of service. The only possible
// transport for data to go through is the IWLAN transport. Setting this to true
@@ -5656,31 +5536,6 @@
}
return;
}
-
- // If the device operates in legacy mode and camps on IWLAN, modem reports IWLAN as a RAT
- // through WWAN registration info. To be consistent with the behavior with AP-assisted mode,
- // we manually make a WLAN registration info for clients to consume. In this scenario,
- // both WWAN and WLAN registration info are the IWLAN registration info and that's the
- // unfortunate limitation we have when the device operates in legacy mode. In AP-assisted
- // mode, the WWAN registration will correctly report the actual cellular registration info
- // when the device camps on IWLAN.
- if (mAccessNetworksManager.isInLegacyMode()) {
- NetworkRegistrationInfo wwanNri = mNewSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (wwanNri != null && wwanNri.getAccessNetworkTechnology()
- == TelephonyManager.NETWORK_TYPE_IWLAN) {
- NetworkRegistrationInfo wlanNri = new NetworkRegistrationInfo.Builder()
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .setRegistrationState(wwanNri.getRegistrationState())
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
- .setRejectCause(wwanNri.getRejectCause())
- .setEmergencyOnly(wwanNri.isEmergencyEnabled())
- .setAvailableServices(wwanNri.getAvailableServices())
- .build();
- mNewSS.addNetworkRegistrationInfo(wlanNri);
- }
- }
}
/**
@@ -5938,25 +5793,6 @@
}
/**
- * Registers for cell bandwidth changed.
- * @param h handler to notify
- * @param what what code of message when delivered
- * @param obj placed in Message.obj
- */
- public void registerForBandwidthChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mBandwidthChangedRegistrants.add(r);
- }
-
- /**
- * Unregisters for cell bandwidth changed.
- * @param h handler to notify
- */
- public void unregisterForBandwidthChanged(Handler h) {
- mBandwidthChangedRegistrants.remove(h);
- }
-
- /**
* Get the NR data connection context ids.
*
* @return data connection context ids.
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index fc378a2..59f7333 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -16,12 +16,11 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
@@ -46,6 +45,7 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -56,6 +56,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
@@ -96,7 +97,6 @@
private static final int EVENT_POLL_SIGNAL_STRENGTH = 7;
private static final int EVENT_SIGNAL_STRENGTH_UPDATE = 8;
private static final int EVENT_POLL_SIGNAL_STRENGTH_DONE = 9;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 10;
@NonNull
private final Phone mPhone;
@@ -141,20 +141,6 @@
@NonNull
private final LocalLog mLocalLog = new LocalLog(64);
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
- int phoneId = intent.getExtras().getInt(CarrierConfigManager.EXTRA_SLOT_INDEX);
- // Ignore the carrier config changed if the phoneId is not matched.
- if (phoneId == mPhone.getPhoneId()) {
- sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
- }
- }
- }
- };
-
public SignalStrengthController(@NonNull Phone phone) {
mPhone = phone;
mCi = mPhone.mCi;
@@ -164,10 +150,12 @@
mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
setSignalStrengthDefaultValues();
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
mCarrierConfig = getCarrierConfig();
- IntentFilter filter = new IntentFilter();
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
+ // Callback which directly handle config change should be executed on handler thread
+ ccm.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ onCarrierConfigurationChanged(slotIndex));
}
@Override
@@ -227,6 +215,11 @@
while (it.hasNext()) {
SignalRequestRecord srr = it.next();
if (srr.mRequest.getLiveToken().equals(record.mRequest.getLiveToken())) {
+ try {
+ srr.mRequest.getLiveToken().unlinkToDeath(srr, 0);
+ } catch (NoSuchElementException ignored) {
+ // Either never linked or has already unlinked, ignore anyway
+ }
it.remove();
}
}
@@ -275,11 +268,6 @@
break;
}
- case EVENT_CARRIER_CONFIG_CHANGED: {
- onCarrierConfigChanged();
- break;
- }
-
default:
log("Unhandled message with number: " + msg.what);
break;
@@ -351,7 +339,7 @@
|| (curTime - mSignalStrengthUpdatedTime > SIGNAL_STRENGTH_REFRESH_THRESHOLD_IN_MS);
if (!isStale) return false;
- List<SubscriptionInfo> subInfoList = SubscriptionController.getInstance()
+ List<SubscriptionInfo> subInfoList = SubscriptionManagerService.getInstance()
.getActiveSubscriptionInfoList(mPhone.getContext().getOpPackageName(),
mPhone.getContext().getAttributionTag());
@@ -417,7 +405,7 @@
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0));
}
- if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+ if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
int[] lteRsrqThresholds = mCarrierConfig.getIntArray(
CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
if (lteRsrqThresholds != null) {
@@ -474,6 +462,18 @@
AccessNetworkConstants.AccessNetworkType.NGRAN,
(nrMeasurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0));
}
+
+ int[] wcdmaEcnoThresholds = mCarrierConfig.getIntArray(
+ CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY);
+ if (wcdmaEcnoThresholds != null) {
+ signalThresholdInfos.add(
+ createSignalThresholdsInfo(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+ wcdmaEcnoThresholds,
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ false));
+ }
+
}
consolidatedAndSetReportingCriteria(signalThresholdInfos);
@@ -507,7 +507,7 @@
AccessNetworkConstants.AccessNetworkType.CDMA2000,
true));
- if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+ if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
signalThresholdInfos.add(
createSignalThresholdsInfo(
SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
@@ -540,6 +540,12 @@
AccessNetworkThresholds.NGRAN_SSSINR,
AccessNetworkConstants.AccessNetworkType.NGRAN,
false));
+ signalThresholdInfos.add(
+ createSignalThresholdsInfo(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+ AccessNetworkThresholds.UTRAN_ECNO,
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ false));
}
consolidatedAndSetReportingCriteria(signalThresholdInfos);
@@ -552,12 +558,13 @@
for (SignalThresholdInfo signalThresholdInfo : signalThresholdInfos) {
final int ran = signalThresholdInfo.getRadioAccessNetworkType();
final int measurementType = signalThresholdInfo.getSignalMeasurementType();
- final boolean isEnabledForSystem = signalThresholdInfo.isEnabled();
+ final boolean isEnabledForSystem =
+ signalThresholdInfo.isEnabled() && shouldHonorSystemThresholds();
int[] consolidatedThresholds =
getConsolidatedSignalThresholds(
ran,
measurementType,
- isEnabledForSystem && shouldHonorSystemThresholds()
+ isEnabledForSystem
? signalThresholdInfo.getThresholds()
: new int[]{},
ALIGNMENT_HYSTERESIS_DB);
@@ -567,12 +574,14 @@
measurementType,
mPhone.getSubId(),
mPhone.isDeviceIdle());
+ int hysteresisDb = getMinimumHysteresisDb(isEnabledForAppRequest, ran, measurementType,
+ consolidatedThresholds);
consolidatedSignalThresholdInfos.add(
new SignalThresholdInfo.Builder()
.setRadioAccessNetworkType(ran)
.setSignalMeasurementType(measurementType)
.setHysteresisMs(REPORTING_HYSTERESIS_MILLIS)
- .setHysteresisDb(REPORTING_HYSTERESIS_DB)
+ .setHysteresisDb(hysteresisDb)
.setThresholds(consolidatedThresholds, true /*isSystem*/)
.setIsEnabled(isEnabledForSystem || isEnabledForAppRequest)
.build());
@@ -583,6 +592,126 @@
+ consolidatedSignalThresholdInfos);
}
+ /**
+ * Return the minimum hysteresis dB from all available sources:
+ * - system default
+ * - value set by client through API
+ * - threshold delta
+ */
+ @VisibleForTesting
+ public int getMinimumHysteresisDb(boolean isEnabledForAppRequest, int ran, int measurementType,
+ final int[] consolidatedThresholdList) {
+
+ int currHysteresisDb = getHysteresisDbFromCarrierConfig(ran, measurementType);
+
+ if (isEnabledForAppRequest) {
+ // Get minimum hysteresisDb at api
+ int apiHysteresisDb =
+ getHysteresisDbFromSignalThresholdInfoRequests(ran, measurementType);
+
+ // Choose minimum of hysteresisDb between api Vs current system/cc value set
+ currHysteresisDb = Math.min(currHysteresisDb, apiHysteresisDb);
+
+ // Hal Req: choose hysteresis db value to be smaller of smallest of threshold delta
+ currHysteresisDb = computeHysteresisDbOnSmallestThresholdDelta(
+ currHysteresisDb, consolidatedThresholdList);
+ }
+ return currHysteresisDb;
+ }
+
+ /**
+ * Get the hysteresis db value from Signal Requests
+ * Note: Based on the current use case, there does not exist multile App signal threshold info
+ * requests with hysteresis db value, so this logic picks the latest hysteresis db value set.
+ *
+ * TODO(b/262655157): Support Multiple App Hysteresis DB value customisation
+ */
+ private int getHysteresisDbFromSignalThresholdInfoRequests(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalThresholdInfo.SignalMeasurementType int measurement) {
+ int apiHysteresisDb = REPORTING_HYSTERESIS_DB;
+ for (SignalRequestRecord record : mSignalRequestRecords) {
+ for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) {
+ if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)) {
+ if (info.getHysteresisDb() >= 0) {
+ apiHysteresisDb = info.getHysteresisDb();
+ }
+ }
+ }
+ }
+ return apiHysteresisDb;
+ }
+
+ private int getHysteresisDbFromCarrierConfig(int ran, int measurement) {
+ int configHysteresisDb = REPORTING_HYSTERESIS_DB;
+ String configKey = null;
+
+ switch (ran) {
+ case AccessNetworkConstants.AccessNetworkType.GERAN:
+ if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) {
+ configKey = CarrierConfigManager.KEY_GERAN_RSSI_HYSTERESIS_DB_INT;
+ }
+ break;
+ case AccessNetworkConstants.AccessNetworkType.UTRAN:
+ if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP) {
+ configKey = CarrierConfigManager.KEY_UTRAN_RSCP_HYSTERESIS_DB_INT;
+ } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO) {
+ configKey = CarrierConfigManager.KEY_UTRAN_ECNO_HYSTERESIS_DB_INT;
+ }
+ break;
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+ if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP) {
+ configKey = CarrierConfigManager.KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT;
+ } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ) {
+ configKey = CarrierConfigManager.KEY_EUTRAN_RSRQ_HYSTERESIS_DB_INT;
+ } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR) {
+ configKey = CarrierConfigManager.KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT;
+ }
+ break;
+ case AccessNetworkConstants.AccessNetworkType.NGRAN:
+ if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP) {
+ configKey = CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT;
+ } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ) {
+ configKey = CarrierConfigManager.KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT;
+ } else if (measurement == SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR) {
+ configKey = CarrierConfigManager.KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT;
+ }
+ break;
+ default:
+ localLog("No matching configuration");
+ }
+ if (configKey != null) {
+ configHysteresisDb = mCarrierConfig.getInt(configKey, REPORTING_HYSTERESIS_DB);
+ }
+ return configHysteresisDb >= SignalThresholdInfo.HYSTERESIS_DB_MINIMUM
+ ? configHysteresisDb : REPORTING_HYSTERESIS_DB;
+ }
+
+ /**
+ * This method computes the hysteresis db value between smaller of the smallest Threshold Delta
+ * and system / cc / api hysteresis db value determined.
+ *
+ * @param currMinHysteresisDb smaller value between system / cc / api hysteresis db value
+ * @param signalThresholdInfoArray consolidated threshold info with App request consolidated.
+ * @return current minimum hysteresis db value computed between above params.
+ *
+ */
+ private int computeHysteresisDbOnSmallestThresholdDelta(
+ int currMinHysteresisDb, final int[] signalThresholdInfoArray) {
+ int index = 0;
+ if (signalThresholdInfoArray.length > 1) {
+ while (index != signalThresholdInfoArray.length - 1) {
+ if (signalThresholdInfoArray[index + 1] - signalThresholdInfoArray[index]
+ < currMinHysteresisDb) {
+ currMinHysteresisDb =
+ signalThresholdInfoArray[index + 1] - signalThresholdInfoArray[index];
+ }
+ index++;
+ }
+ }
+ return currMinHysteresisDb;
+ }
+
void setSignalStrengthDefaultValues() {
mSignalStrength = new SignalStrength();
mSignalStrengthUpdatedTime = System.currentTimeMillis();
@@ -731,7 +860,11 @@
&& srr.mRequest.isSystemThresholdReportingRequestedWhileIdle());
}
- void onDeviceIdleStateChanged(boolean isDeviceIdle) {
+ /**
+ * Get notified when device idle state changed
+ */
+ @VisibleForTesting
+ public void onDeviceIdleStateChanged(boolean isDeviceIdle) {
sendMessage(obtainMessage(EVENT_ON_DEVICE_IDLE_STATE_CHANGED, isDeviceIdle));
localLog("onDeviceIdleStateChanged isDeviceIdle=" + isDeviceIdle);
@@ -968,7 +1101,9 @@
return earfcnPairList;
}
- private void onCarrierConfigChanged() {
+ private void onCarrierConfigurationChanged(int slotIndex) {
+ if (slotIndex != mPhone.getPhoneId()) return;
+
mCarrierConfig = getCarrierConfig();
log("Carrier Config changed.");
@@ -1097,6 +1232,16 @@
15, /* SIGNAL_STRENGTH_GOOD */
30 /* SIGNAL_STRENGTH_GREAT */
};
+
+ /**
+ * List of dBm thresholds for UTRAN {@link AccessNetworkConstants.AccessNetworkType} ECNO
+ */
+ public static final int[] UTRAN_ECNO = new int[]{
+ -24, /* SIGNAL_STRENGTH_POOR */
+ -14, /* SIGNAL_STRENGTH_MODERATE */
+ -6, /* SIGNAL_STRENGTH_GOOD */
+ 1 /* SIGNAL_STRENGTH_GREAT */
+ };
}
private static void log(String msg) {
diff --git a/src/java/com/android/internal/telephony/SimIndication.java b/src/java/com/android/internal/telephony/SimIndication.java
index f3ece16..d74a9b1 100644
--- a/src/java/com/android/internal/telephony/SimIndication.java
+++ b/src/java/com/android/internal/telephony/SimIndication.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED;
@@ -53,9 +55,11 @@
* @param indicationType Type of radio indication
*/
public void carrierInfoForImsiEncryption(int indicationType) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
+ }
mRil.mCarrierInfoForImsiEncryptionRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
@@ -67,10 +71,12 @@
* @param cdmaSource New CdmaSubscriptionSource
*/
public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
int[] response = new int[]{cdmaSource};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response);
+ }
mRil.mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -81,9 +87,9 @@
* @param indicationType Type of radio indication
*/
public void simPhonebookChanged(int indicationType) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) {
+ if (mRil.isLogOrTrace()) {
mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
}
@@ -98,7 +104,7 @@
*/
public void simPhonebookRecordsReceived(int indicationType, byte status,
android.hardware.radio.sim.PhonebookRecordInfo[] records) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>();
@@ -106,8 +112,9 @@
simPhonebookRecords.add(RILUtils.convertHalPhonebookRecordInfo(record));
}
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(
+ RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
"status = " + status + " received " + records.length + " records");
}
@@ -122,14 +129,14 @@
*/
public void simRefresh(int indicationType,
android.hardware.radio.sim.SimRefreshResult refreshResult) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
IccRefreshResponse response = new IccRefreshResponse();
response.refreshResult = refreshResult.type;
response.efId = refreshResult.efId;
response.aid = refreshResult.aid;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response);
mRil.mIccRefreshRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -139,9 +146,9 @@
* @param indicationType Type of radio indication
*/
public void simStatusChanged(int indicationType) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
mRil.mIccStatusChangedRegistrants.notifyRegistrants();
}
@@ -154,9 +161,9 @@
* Refer to TS 102.223 section 9.4 for command types
*/
public void stkEventNotify(int indicationType, String cmd) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
if (mRil.mCatEventRegistrant != null) {
mRil.mCatEventRegistrant.notifyRegistrant(new AsyncResult(null, cmd, null));
@@ -170,9 +177,9 @@
* Refer to TS 102.223 section 9.4 for command types
*/
public void stkProactiveCommand(int indicationType, String cmd) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
if (mRil.mCatProCmdRegistrant != null) {
mRil.mCatProCmdRegistrant.notifyRegistrant(new AsyncResult(null, cmd, null));
@@ -184,9 +191,9 @@
* @param indicationType Type of radio indication
*/
public void stkSessionEnd(int indicationType) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
if (mRil.mCatSessionEndRegistrant != null) {
mRil.mCatSessionEndRegistrant.notifyRegistrant(new AsyncResult(null, null, null));
@@ -199,11 +206,13 @@
* @param activate false for subscription deactivated, true for subscription activated
*/
public void subscriptionStatusChanged(int indicationType, boolean activate) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
int[] response = new int[]{activate ? 1 : 0};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response);
+ }
mRil.mSubscriptionStatusRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
@@ -215,9 +224,9 @@
* @param enabled Whether uiccApplications are enabled or disabled
*/
public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
- mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_SIM, indicationType);
- if (RIL.RILJ_LOGD) {
+ if (mRil.isLogOrTrace()) {
mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled);
}
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index b0099fb..1e1dbe5 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -16,8 +16,11 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.sim.CarrierRestrictions;
import android.hardware.radio.sim.IRadioSimResponse;
import android.telephony.CarrierRestrictionRules;
import android.telephony.TelephonyManager;
@@ -41,7 +44,7 @@
private void responseIccIo(RadioResponseInfo responseInfo,
android.hardware.radio.sim.IccIoResult result) {
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
IccIoResult ret = new IccIoResult(result.sw1, result.sw2, result.simResponse);
@@ -68,7 +71,7 @@
*/
public void areUiccApplicationsEnabledResponse(RadioResponseInfo responseInfo,
boolean enabled) {
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
@@ -83,7 +86,7 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void changeIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
@@ -91,14 +94,14 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void changeIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error.
*/
public void enableUiccApplicationsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -109,7 +112,7 @@
public void getAllowedCarriersResponse(RadioResponseInfo responseInfo,
android.hardware.radio.sim.CarrierRestrictions carrierRestrictions,
int multiSimPolicy) {
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr == null) {
return;
}
@@ -125,7 +128,6 @@
if (!carrierRestrictions.allowedCarriersPrioritized) {
carrierRestrictionDefault = CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED;
}
-
ret = CarrierRestrictionRules.newBuilder()
.setAllowedCarriers(RILUtils.convertHalCarrierList(
carrierRestrictions.allowedCarriers))
@@ -133,6 +135,7 @@
carrierRestrictions.excludedCarriers))
.setDefaultCarrierRestriction(carrierRestrictionDefault)
.setMultiSimPolicy(policy)
+ .setCarrierRestrictionStatus(carrierRestrictions.status)
.build();
if (responseInfo.error == RadioError.NONE) {
@@ -154,7 +157,7 @@
public void getCdmaSubscriptionResponse(RadioResponseInfo responseInfo, String mdn,
String hSid, String hNid, String min, String prl) {
RadioResponse.responseStrings(
- RIL.SIM_SERVICE, mRil, responseInfo, mdn, hSid, hNid, min, prl);
+ HAL_SERVICE_SIM, mRil, responseInfo, mdn, hSid, hNid, min, prl);
}
/**
@@ -162,7 +165,7 @@
* @param source CDMA subscription source
*/
public void getCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo, int source) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, source);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, source);
}
/**
@@ -171,7 +174,7 @@
* specified barring facility is active. "0" means "disabled for all"
*/
public void getFacilityLockForAppResponse(RadioResponseInfo responseInfo, int response) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, response);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, response);
}
/**
@@ -180,7 +183,7 @@
*/
public void getIccCardStatusResponse(RadioResponseInfo responseInfo,
android.hardware.radio.sim.CardStatus cardStatus) {
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
IccCardStatus iccCardStatus = RILUtils.convertHalCardStatus(cardStatus);
@@ -197,7 +200,7 @@
* @param imsi String containing the IMSI
*/
public void getImsiForAppResponse(RadioResponseInfo responseInfo, String imsi) {
- RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, imsi);
+ RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, imsi);
}
/**
@@ -207,7 +210,7 @@
public void getSimPhonebookCapacityResponse(RadioResponseInfo responseInfo,
android.hardware.radio.sim.PhonebookCapacity pbCapacity) {
AdnCapacity capacity = RILUtils.convertHalPhonebookCapacity(pbCapacity);
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
RadioResponse.sendMessageResponse(rr.mResult, capacity);
@@ -220,14 +223,21 @@
* @param responseInfo Response info struct containing response type, serial no. and error.
*/
public void getSimPhonebookRecordsResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void iccCloseLogicalChannelResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void iccCloseLogicalChannelWithSessionInfoResponse(RadioResponseInfo responseInfo) {
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -252,7 +262,7 @@
for (int i = 0; i < selectResponse.length; i++) {
arr.add((int) selectResponse[i]);
}
- RadioResponse.responseIntArrayList(RIL.SIM_SERVICE, mRil, responseInfo, arr);
+ RadioResponse.responseIntArrayList(HAL_SERVICE_SIM, mRil, responseInfo, arr);
}
/**
@@ -277,7 +287,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void reportStkServiceIsRunningResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -286,7 +296,7 @@
*/
public void requestIccSimAuthenticationResponse(RadioResponseInfo responseInfo,
android.hardware.radio.sim.IccIoResult iccIo) {
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
IccIoResult ret = new IccIoResult(iccIo.sw1, iccIo.sw2,
@@ -315,7 +325,7 @@
* string starting with first byte of response
*/
public void sendEnvelopeResponse(RadioResponseInfo responseInfo, String commandResponse) {
- RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, commandResponse);
+ RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, commandResponse);
}
/**
@@ -331,7 +341,7 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendTerminalResponseToSimResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -339,7 +349,7 @@
*/
public void setAllowedCarriersResponse(RadioResponseInfo responseInfo) {
int ret = TelephonyManager.SET_CARRIER_RESTRICTION_ERROR;
- RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
if (rr != null) {
mRil.riljLog("setAllowedCarriersResponse - error = " + responseInfo.error);
@@ -355,14 +365,14 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCarrierInfoForImsiEncryptionResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -370,21 +380,21 @@
* @param retry 0 is the number of retries remaining, or -1 if unknown
*/
public void setFacilityLockForAppResponse(RadioResponseInfo responseInfo, int retry) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, retry);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, retry);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setSimCardPowerResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setUiccSubscriptionResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
}
/**
@@ -392,7 +402,7 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
@@ -400,7 +410,7 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
@@ -408,7 +418,7 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyIccPuk2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
@@ -416,7 +426,7 @@
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyIccPukForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
}
/**
@@ -428,7 +438,7 @@
public void supplySimDepersonalizationResponse(RadioResponseInfo responseInfo, int persoType,
int remainingRetries) {
RadioResponse.responseInts(
- RIL.SIM_SERVICE, mRil, responseInfo, persoType, remainingRetries);
+ HAL_SERVICE_SIM, mRil, responseInfo, persoType, remainingRetries);
}
/**
@@ -437,7 +447,7 @@
*/
public void updateSimPhonebookRecordsResponse(RadioResponseInfo responseInfo,
int updatedRecordIndex) {
- RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, updatedRecordIndex);
+ RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, updatedRecordIndex);
}
@Override
diff --git a/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
index 149e425..8fcc567 100644
--- a/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
+++ b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
@@ -106,7 +106,11 @@
* @return A string describing the anomaly event
*/
public @NonNull String getFrequencyString() {
- return String.format("%d times within %d ms.", mNumOccurrences, mWindowSizeMillis);
+ if (mWindowSizeMillis >= 1000L) {
+ return mNumOccurrences + " times within " + mWindowSizeMillis / 1000L + " seconds";
+ } else {
+ return mNumOccurrences + " times within " + mWindowSizeMillis + "ms";
+ }
}
@Override
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 7c52a42..ecd6276 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static java.util.Map.entry;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -32,6 +34,7 @@
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;
import java.util.HashMap;
@@ -72,19 +75,18 @@
/** Mapping from DB COLUMN to PDU_PENDING_MESSAGE_PROJECTION index */
static final Map<Integer, Integer> PDU_PENDING_MESSAGE_PROJECTION_INDEX_MAPPING =
- new HashMap<Integer, Integer>() {{
- put(InboundSmsHandler.PDU_COLUMN, 0);
- put(InboundSmsHandler.SEQUENCE_COLUMN, 1);
- put(InboundSmsHandler.DESTINATION_PORT_COLUMN, 2);
- put(InboundSmsHandler.DATE_COLUMN, 3);
- put(InboundSmsHandler.REFERENCE_NUMBER_COLUMN, 4);
- put(InboundSmsHandler.COUNT_COLUMN, 5);
- put(InboundSmsHandler.ADDRESS_COLUMN, 6);
- put(InboundSmsHandler.ID_COLUMN, 7);
- put(InboundSmsHandler.MESSAGE_BODY_COLUMN, 8);
- put(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN, 9);
- put(InboundSmsHandler.SUBID_COLUMN, 10);
- }};
+ Map.ofEntries(
+ entry(InboundSmsHandler.PDU_COLUMN, 0),
+ entry(InboundSmsHandler.SEQUENCE_COLUMN, 1),
+ entry(InboundSmsHandler.DESTINATION_PORT_COLUMN, 2),
+ entry(InboundSmsHandler.DATE_COLUMN, 3),
+ entry(InboundSmsHandler.REFERENCE_NUMBER_COLUMN, 4),
+ entry(InboundSmsHandler.COUNT_COLUMN, 5),
+ entry(InboundSmsHandler.ADDRESS_COLUMN, 6),
+ entry(InboundSmsHandler.ID_COLUMN, 7),
+ entry(InboundSmsHandler.MESSAGE_BODY_COLUMN, 8),
+ entry(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN, 9),
+ entry(InboundSmsHandler.SUBID_COLUMN, 10));
private static SmsBroadcastUndelivered instance;
@@ -258,10 +260,8 @@
* Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
*/
private static void broadcastSms(InboundSmsTracker tracker) {
- InboundSmsHandler handler;
int subId = tracker.getSubId();
- // TODO consider other subs in this subId's group as well
- int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ int phoneId = SubscriptionManagerService.getInstance().getPhoneId(subId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
Rlog.e(TAG, "broadcastSms: ignoring message; no phone found for subId " + subId);
return;
@@ -272,7 +272,7 @@
+ " phoneId " + phoneId);
return;
}
- handler = phone.getInboundSmsHandler(tracker.is3gpp2());
+ InboundSmsHandler handler = phone.getInboundSmsHandler(tracker.is3gpp2());
if (handler != null) {
handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
} else {
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 9f79161..97161f8 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -18,6 +18,8 @@
package com.android.internal.telephony;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static com.android.internal.telephony.util.TelephonyUtils.checkDumpPermission;
import android.annotation.Nullable;
@@ -26,6 +28,7 @@
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.BaseBundle;
import android.os.Binder;
@@ -40,6 +43,9 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -47,6 +53,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Locale;
/**
* Implements the ISmsImplBase interface used in the SmsManager API.
@@ -56,7 +63,8 @@
private final Context mContext;
- protected SmsController(Context context) {
+ @VisibleForTesting
+ public SmsController(Context context) {
mContext = context;
ServiceRegisterer smsServiceRegisterer = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
@@ -151,6 +159,23 @@
if (callingPackage == null) {
callingPackage = getCallingPackage();
}
+ Rlog.d(LOG_TAG, "sendDataForSubscriber caller=" + callingPackage);
+
+ // Check if user is associated with the subscription
+ if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), destAddr)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
+ // Perform FDN check
+ if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+ return;
+ }
+
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort,
@@ -186,14 +211,74 @@
String callingAttributionTag, String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, long messageId) {
+ sendTextForSubscriber(subId, callingPackage, callingAttributionTag, destAddr, scAddr,
+ text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
+ false, false);
+
+ }
+
+ /**
+ * @param subId Subscription Id
+ * @param callingAttributionTag the attribution tag of the caller
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success, or relevant errors
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param skipFdnCheck if set to true, FDN check must be skipped .This is set in case of STK sms
+ *
+ * @hide
+ */
+ public void sendTextForSubscriber(int subId, String callingPackage,
+ String callingAttributionTag, String destAddr, String scAddr, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipFdnCheck,
+ boolean skipShortCodeCheck) {
if (callingPackage == null) {
callingPackage = getCallingPackage();
}
+ Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage);
+
+ if (skipFdnCheck || skipShortCodeCheck) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MODIFY_PHONE_STATE permission.");
+ }
+ }
if (!getSmsPermissions(subId).checkCallingCanSendText(persistMessageForNonDefaultSmsApp,
callingPackage, callingAttributionTag, "Sending SMS message")) {
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
return;
}
+
+ // Check if user is associated with the subscription
+ boolean crossUserFullGranted = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED;
+ Rlog.d(LOG_TAG, "sendTextForSubscriber: caller has INTERACT_ACROSS_USERS_FULL? "
+ + crossUserFullGranted);
+ if (!crossUserFullGranted
+ && !TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), destAddr)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
+ // Perform FDN check
+ if (!skipFdnCheck && isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+ return;
+ }
+
long token = Binder.clearCallingIdentity();
SubscriptionInfo info;
try {
@@ -201,11 +286,12 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
if (isBluetoothSubscription(info)) {
sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
} else {
sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
- persistMessageForNonDefaultSmsApp, messageId);
+ persistMessageForNonDefaultSmsApp, messageId, skipShortCodeCheck);
}
}
@@ -222,16 +308,17 @@
private void sendIccText(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
- boolean persistMessageForNonDefaultSmsApp, long messageId) {
+ boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
Rlog.d(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr"
- + " Subscription: " + subId + " id: " + messageId);
+ + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
- deliveryIntent, persistMessageForNonDefaultSmsApp, messageId);
+ deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
+ skipShortCodeCheck);
} else {
Rlog.e(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr is null for"
- + " Subscription: " + subId + " id: " + messageId);
+ + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
@@ -259,6 +346,23 @@
if (callingPackage == null) {
callingPackage = getCallingPackage();
}
+ Rlog.d(LOG_TAG, "sendTextForSubscriberWithOptions caller=" + callingPackage);
+
+ // Check if user is associated with the subscription
+ if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), destAddr)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
+ // Perform FDN check
+ if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+ return;
+ }
+
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr,
@@ -281,6 +385,23 @@
if (getCallingPackage() != null) {
callingPackage = getCallingPackage();
}
+ Rlog.d(LOG_TAG, "sendMultipartTextForSubscriber caller=" + callingPackage);
+
+ // Check if user is associated with the subscription
+ if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), destAddr)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
+ // Perform FDN check
+ if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+ return;
+ }
+
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr,
@@ -288,7 +409,7 @@
messageId);
} else {
Rlog.e(LOG_TAG, "sendMultipartTextForSubscriber iccSmsIntMgr is null for"
- + " Subscription: " + subId + " id: " + messageId);
+ + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
@@ -301,6 +422,23 @@
if (callingPackage == null) {
callingPackage = getCallingPackage();
}
+ Rlog.d(LOG_TAG, "sendMultipartTextForSubscriberWithOptions caller=" + callingPackage);
+
+ // Check if user is associated with the subscription
+ if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), destAddr)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
+ // Perform FDN check
+ if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+ return;
+ }
+
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag,
@@ -409,7 +547,7 @@
// Don't show the SMS SIM Pick activity if it is not foreground.
boolean isCallingProcessForeground = am != null
&& am.getUidImportance(Binder.getCallingUid())
- == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
if (!isCallingProcessForeground) {
Rlog.d(LOG_TAG, "isSmsSimPickActivityNeeded: calling process not foreground. "
+ "Suppressing activity.");
@@ -481,14 +619,15 @@
@Override
public int getPreferredSmsSubscription() {
// If there is a default, choose that one.
- int defaultSubId = SubscriptionController.getInstance().getDefaultSmsSubId();
+ int defaultSubId = SubscriptionManagerService.getInstance().getDefaultSmsSubId();
+
if (SubscriptionManager.isValidSubscriptionId(defaultSubId)) {
return defaultSubId;
}
// No default, if there is only one sub active, choose that as the "preferred" sub id.
long token = Binder.clearCallingIdentity();
try {
- int[] activeSubs = SubscriptionController.getInstance()
+ int[] activeSubs = SubscriptionManagerService.getInstance()
.getActiveSubIdList(true /*visibleOnly*/);
if (activeSubs.length == 1) {
return activeSubs[0];
@@ -519,6 +658,8 @@
throw new SecurityException("sendStoredText: Package " + callingPkg
+ "does not belong to " + Binder.getCallingUid());
}
+ Rlog.d(LOG_TAG, "sendStoredText caller=" + callingPkg);
+
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendStoredText(callingPkg, callingAttributionTag, messageUri, scAddress,
sentIntent, deliveryIntent);
@@ -537,6 +678,8 @@
throw new SecurityException("sendStoredMultipartText: Package " + callingPkg
+ " does not belong to " + Binder.getCallingUid());
}
+ Rlog.d(LOG_TAG, "sendStoredMultipartText caller=" + callingPkg);
+
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingAttributionTag, messageUri,
scAddress, sentIntents, deliveryIntents);
@@ -690,6 +833,58 @@
}
@Override
+ public void setStorageMonitorMemoryStatusOverride(int subId, boolean isStorageAvailable) {
+ Phone phone = getPhone(subId);
+ Context context;
+ if (phone != null) {
+ context = phone.getContext();
+ } else {
+ Rlog.e(LOG_TAG, "Phone Object is Null");
+ return;
+ }
+ // If it doesn't have modify phone state permission
+ // a SecurityException will be thrown.
+ if (context.checkPermission(android.Manifest
+ .permission.MODIFY_PHONE_STATE, Binder.getCallingPid(),
+ Binder.getCallingUid()) != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "setStorageMonitorMemoryStatusOverride needs MODIFY_PHONE_STATE");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ phone.mSmsStorageMonitor.sendMemoryStatusOverride(isStorageAvailable);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void clearStorageMonitorMemoryStatusOverride(int subId) {
+ Phone phone = getPhone(subId);
+ Context context;
+ if (phone != null) {
+ context = phone.getContext();
+ } else {
+ Rlog.e(LOG_TAG, "Phone Object is Null");
+ return;
+ }
+ // If it doesn't have modify phone state permission
+ // a SecurityException will be thrown.
+ if (context.checkPermission(android.Manifest
+ .permission.MODIFY_PHONE_STATE, Binder.getCallingPid(),
+ Binder.getCallingUid()) != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "clearStorageMonitorMemoryStatusOverride needs MODIFY_PHONE_STATE");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ phone.mSmsStorageMonitor.clearMemoryStatusOverride();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public int checkSmsShortCodeDestination(int subId, String callingPackage,
String callingFeatureId, String destAddress, String countryIso) {
if (callingPackage == null) {
@@ -714,6 +909,24 @@
public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
String callingAttributionTag, int subId, String number, int port, String text,
PendingIntent sentIntent) {
+ Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber caller=" + callingPackage);
+
+ // Do not send non-emergency SMS in ECBM as it forces device to exit ECBM.
+ if(getPhone(subId).isInEcm()) {
+ Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber: Do not send non-emergency "
+ + "SMS in ECBM as it forces device to exit ECBM.");
+ return;
+ }
+
+ // Check if user is associated with the subscription
+ if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
+ Binder.getCallingUserHandle(), number)) {
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
+ Binder.getCallingUid(), callingPackage);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED);
+ return;
+ }
+
if (port == 0) {
sendTextForSubscriberWithSelfPermissionsInternal(subId, callingPackage,
callingAttributionTag, number, null, text, sentIntent, null, false,
@@ -856,4 +1069,49 @@
public static String formatCrossStackMessageId(long id) {
return "{x-message-id:" + id + "}";
}
-}
+
+ /**
+ * The following function checks if destination address or smsc is blocked due to FDN.
+ * @param subId subscription ID
+ * @param destAddr destination address of the message
+ * @return true if either destAddr or smscAddr is blocked due to FDN.
+ */
+ @VisibleForTesting
+ public boolean isNumberBlockedByFDN(int subId, String destAddr, String callingPackage) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ if (!FdnUtils.isFdnEnabled(phoneId)) {
+ return false;
+ }
+
+ // Skip FDN check for emergency numbers
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (tm.isEmergencyNumber(destAddr)) {
+ return false;
+ }
+
+ // Check if destAddr is present in FDN list
+ String defaultCountryIso = tm.getSimCountryIso().toUpperCase(Locale.ENGLISH);
+ if (FdnUtils.isNumberBlockedByFDN(phoneId, destAddr, defaultCountryIso)) {
+ return true;
+ }
+
+ // Get SMSC address for this subscription
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ String smscAddr;
+ if (iccSmsIntMgr != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ smscAddr = iccSmsIntMgr.getSmscAddressFromIccEf(callingPackage);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ Rlog.e(LOG_TAG, "getSmscAddressFromIccEfForSubscriber iccSmsIntMgr is null"
+ + " for Subscription: " + subId);
+ return true;
+ }
+
+ // Check if smscAddr is present in FDN list
+ return FdnUtils.isNumberBlockedByFDN(phoneId, smscAddr, defaultCountryIso);
+ }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 53556ac..d2dfcac 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -20,6 +20,8 @@
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
@@ -29,19 +31,30 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.UserManager;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.DomainSelectionService;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.domainselection.EmergencySmsDomainSelectionConnection;
+import com.android.internal.telephony.domainselection.SmsDomainSelectionConnection;
import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
import com.android.internal.telephony.gsm.GsmSMSDispatcher;
import com.android.telephony.Rlog;
@@ -50,6 +63,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
/**
*
@@ -115,6 +130,195 @@
new HashMap<>();
/**
+ * Testing interface for injecting mock DomainSelectionConnection and a flag to indicate
+ * whether the domain selection is supported.
+ */
+ @VisibleForTesting
+ public interface DomainSelectionResolverProxy {
+ /**
+ * Returns a {@link DomainSelectionConnection} created using the specified
+ * context and callback.
+ *
+ * @param phone The {@link Phone} instance.
+ * @param selectorType The domain selector type to identify the domain selection connection.
+ * A {@link DomainSelectionService#SELECTOR_TYPE_SMS} is used for SMS.
+ * @param isEmergency A flag to indicate whether this connection is
+ * for an emergency SMS or not.
+ */
+ @Nullable DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+ @DomainSelectionService.SelectorType int selectorType, boolean isEmergency);
+
+ /**
+ * Checks if the device supports the domain selection service to route the call / SMS /
+ * supplementary services to the appropriate domain.
+ *
+ * @return {@code true} if the domain selection is supported on the device,
+ * {@code false} otherwise.
+ */
+ boolean isDomainSelectionSupported();
+ }
+
+ private DomainSelectionResolverProxy mDomainSelectionResolverProxy =
+ new DomainSelectionResolverProxy() {
+ @Override
+ @Nullable
+ public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+ @DomainSelectionService.SelectorType int selectorType,
+ boolean isEmergency) {
+ try {
+ return DomainSelectionResolver.getInstance().getDomainSelectionConnection(
+ phone, selectorType, isEmergency);
+ } catch (IllegalStateException e) {
+ // In general, DomainSelectionResolver is always initialized by TeleService,
+ // but if it's not initialized (like in unit tests),
+ // it returns null to perform the legacy behavior in this case.
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isDomainSelectionSupported() {
+ return DomainSelectionResolver.getInstance().isDomainSelectionSupported();
+ }
+ };
+
+ /** Stores the sending SMS information for a pending request. */
+ private class PendingRequest {
+ public static final int TYPE_DATA = 1;
+ public static final int TYPE_TEXT = 2;
+ public static final int TYPE_MULTIPART_TEXT = 3;
+ public static final int TYPE_RETRY_SMS = 4;
+
+ public final int type;
+ public final SMSDispatcher.SmsTracker tracker;
+ public final String callingPackage;
+ public final String destAddr;
+ public final String scAddr;
+ public final ArrayList<PendingIntent> sentIntents;
+ public final ArrayList<PendingIntent> deliveryIntents;
+ public final boolean isForVvm;
+ // sendData specific
+ public final byte[] data;
+ public final int destPort;
+ // sendText / sendMultipartText specific
+ public final ArrayList<String> texts;
+ public final Uri messageUri;
+ public final boolean persistMessage;
+ public final int priority;
+ public final boolean expectMore;
+ public final int validityPeriod;
+ public final long messageId;
+ public final boolean skipShortCodeCheck;
+
+ PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage,
+ String destAddr, String scAddr, ArrayList<PendingIntent> sentIntents,
+ ArrayList<PendingIntent> deliveryIntents, boolean isForVvm, byte[] data,
+ int destPort, ArrayList<String> texts, Uri messageUri, boolean persistMessage,
+ int priority, boolean expectMore, int validityPeriod, long messageId,
+ boolean skipShortCodeCheck) {
+ this.type = type;
+ this.tracker = tracker;
+ this.callingPackage = callingPackage;
+ this.destAddr = destAddr;
+ this.scAddr = scAddr;
+ this.sentIntents = sentIntents;
+ this.deliveryIntents = deliveryIntents;
+ this.isForVvm = isForVvm;
+
+ this.data = data;
+ this.destPort = destPort;
+
+ this.texts = texts;
+ this.messageUri = messageUri;
+ this.persistMessage = persistMessage;
+ this.priority = priority;
+ this.expectMore = expectMore;
+ this.validityPeriod = validityPeriod;
+ this.messageId = messageId;
+ this.skipShortCodeCheck = skipShortCodeCheck;
+ }
+ }
+
+ /**
+ * Manages the {@link DomainSelectionConnection} instance and its related information.
+ */
+ @VisibleForTesting
+ protected class DomainSelectionConnectionHolder
+ implements DomainSelectionConnection.DomainSelectionConnectionCallback {
+ private final boolean mEmergency;
+ // Manages the pending request while selecting a proper domain.
+ private final List<PendingRequest> mPendingRequests = new ArrayList<>();
+ // Manages the domain selection connections: MO SMS or emergency SMS.
+ private DomainSelectionConnection mConnection;
+
+ DomainSelectionConnectionHolder(boolean emergency) {
+ mEmergency = emergency;
+ }
+
+ /**
+ * Returns a {@link DomainSelectionConnection} instance.
+ */
+ public DomainSelectionConnection getConnection() {
+ return mConnection;
+ }
+
+ /**
+ * Returns a list of {@link PendingRequest} that was added
+ * while the domain selection is performed.
+ */
+ public List<PendingRequest> getPendingRequests() {
+ return mPendingRequests;
+ }
+
+ /**
+ * Checks whether or not the domain selection is requested.
+ * If there is no pending request, the domain selection request is needed to
+ * select a proper domain for MO SMS.
+ */
+ public boolean isDomainSelectionRequested() {
+ return !mPendingRequests.isEmpty();
+ }
+
+ /**
+ * Checks whether or not this holder is for an emergency SMS.
+ */
+ public boolean isEmergency() {
+ return mEmergency;
+ }
+
+ /**
+ * Clears all pending requests.
+ */
+ public void clearAllRequests() {
+ mPendingRequests.clear();
+ }
+
+ /**
+ * Add a new pending request.
+ */
+ public void addRequest(@NonNull PendingRequest request) {
+ mPendingRequests.add(request);
+ }
+
+ /**
+ * Sets a {@link DomainSelectionConnection} instance.
+ */
+ public void setConnection(DomainSelectionConnection connection) {
+ mConnection = connection;
+ }
+
+
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ notifyDomainSelectionTerminated(this);
+ }
+ }
+
+ /** Manages the domain selection connections: MO SMS or emergency SMS. */
+ private DomainSelectionConnectionHolder mDscHolder;
+ private DomainSelectionConnectionHolder mEmergencyDscHolder;
+
+ /**
* Puts a delivery pending tracker to the map based on the format.
*
* @param tracker the tracker awaiting a delivery status report.
@@ -129,6 +333,14 @@
public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
SmsUsageMonitor usageMonitor) {
+ this(phone, storageMonitor, usageMonitor, phone.getLooper());
+ }
+
+ @VisibleForTesting
+ public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
+ SmsUsageMonitor usageMonitor, Looper looper) {
+ super(looper);
+
Rlog.d(TAG, "SmsDispatchersController created");
mContext = phone.getContext();
@@ -141,9 +353,9 @@
mImsSmsDispatcher = new ImsSmsDispatcher(phone, this, ImsManager::getConnector);
mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
- storageMonitor, phone);
+ storageMonitor, phone, looper);
mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
- storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
+ storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper);
mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
SmsBroadcastUndelivered.initialize(phone.getContext(),
mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
@@ -188,6 +400,9 @@
mCdmaDispatcher.dispose();
mGsmInboundSmsHandler.dispose();
mCdmaInboundSmsHandler.dispose();
+ // Cancels the domain selection request if it's still in progress.
+ finishDomainSelection(mDscHolder);
+ finishDomainSelection(mEmergencyDscHolder);
}
/**
@@ -242,6 +457,21 @@
}
}
+ private String getSmscAddressFromUSIMWithPhoneIdentity(String callingPkg) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ IccSmsInterfaceManager iccSmsIntMgr = mPhone.getIccSmsInterfaceManager();
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.getSmscAddressFromIccEf(callingPkg);
+ } else {
+ Rlog.d(TAG, "getSmscAddressFromIccEf iccSmsIntMgr is null");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return null;
+ }
+
private void reevaluateTimerStatus() {
long currentTime = System.currentTimeMillis();
@@ -390,7 +620,12 @@
// SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
android.telephony.SmsMessage msg =
android.telephony.SmsMessage.createFromPdu(pdu, format);
- injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms);
+ injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms, 0 /* unused */);
+ }
+
+ @VisibleForTesting
+ public void setImsSmsDispatcher(ImsSmsDispatcher imsSmsDispatcher) {
+ mImsSmsDispatcher = imsSmsDispatcher;
}
/**
@@ -405,7 +640,7 @@
*/
@VisibleForTesting
public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
- boolean ignoreClass, boolean isOverIms) {
+ boolean ignoreClass, boolean isOverIms, int token) {
Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
try {
if (msg == null) {
@@ -427,7 +662,7 @@
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mGsmInboundSmsHandler");
mGsmInboundSmsHandler.sendMessage(
- InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar);
+ InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, token, ar);
} else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mCdmaInboundSmsHandler");
@@ -445,20 +680,60 @@
}
/**
+ * sets ImsManager object.
+ *
+ * @param imsManager holds a valid object or a null for setting
+ */
+ public boolean setImsManager(ImsManager imsManager) {
+ if (mGsmInboundSmsHandler != null) {
+ mGsmInboundSmsHandler.setImsManager(imsManager);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Retry the message along to the radio.
*
* @param tracker holds the SMS message to send
*/
public void sendRetrySms(SMSDispatcher.SmsTracker tracker) {
- String oldFormat = tracker.mFormat;
boolean retryUsingImsService = false;
- if (!tracker.mUsesImsServiceForIms && mImsSmsDispatcher.isAvailable()) {
- // If this tracker has not been handled by ImsSmsDispatcher yet and IMS Service is
- // available now, retry this failed tracker using IMS Service.
- retryUsingImsService = true;
+ if (!tracker.mUsesImsServiceForIms) {
+ if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) {
+ DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false);
+
+ // If the DomainSelectionConnection is not available,
+ // fallback to the legacy implementation.
+ if (holder != null && holder.getConnection() != null) {
+ sendSmsUsingDomainSelection(holder,
+ new PendingRequest(PendingRequest.TYPE_RETRY_SMS, tracker,
+ null, null, null, null, null, false, null, 0, null, null, false,
+ 0, false, 0, 0L, false),
+ "sendRetrySms");
+ return;
+ }
+ }
+
+ if (mImsSmsDispatcher.isAvailable()) {
+ // If this tracker has not been handled by ImsSmsDispatcher yet and IMS Service is
+ // available now, retry this failed tracker using IMS Service.
+ retryUsingImsService = true;
+ }
}
+ sendRetrySms(tracker, retryUsingImsService);
+ }
+
+ /**
+ * Retry the message along to the radio.
+ *
+ * @param tracker holds the SMS message to send
+ * @param retryUsingImsService a flag to indicate whether the retry SMS can use the ImsService
+ */
+ public void sendRetrySms(SMSDispatcher.SmsTracker tracker, boolean retryUsingImsService) {
+ String oldFormat = tracker.mFormat;
// If retryUsingImsService is true, newFormat will be IMS SMS format. Otherwise, newFormat
// will be based on voice technology.
String newFormat =
@@ -503,7 +778,8 @@
scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
} else {
pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
- scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+ scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null,
+ 0, 0, 0, -1, tracker.mMessageRef);
}
} else if (map.containsKey("data")) {
byte[] data = (byte[]) map.get("data");
@@ -518,7 +794,7 @@
} else {
pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
scAddr, destAddr, destPort.intValue(), data,
- (tracker.mDeliveryIntent != null));
+ (tracker.mDeliveryIntent != null), tracker.mMessageRef);
}
}
@@ -544,6 +820,23 @@
}
/**
+ * Memory Available Event
+ * @param result callback message
+ */
+ public void reportSmsMemoryStatus(Message result) {
+ Rlog.d(TAG, "reportSmsMemoryStatus: ");
+ try {
+ mImsSmsDispatcher.onMemoryAvailable();
+ AsyncResult.forMessage(result, null, null);
+ result.sendToTarget();
+ } catch (Exception e) {
+ Rlog.e(TAG, "reportSmsMemoryStatus Failed ", e);
+ AsyncResult.forMessage(result, null, e);
+ result.sendToTarget();
+ }
+ }
+
+ /**
* SMS over IMS is supported if IMS is registered and SMS is supported on IMS.
*
* @return true if SMS over IMS is supported via an IMS Service or mIms is true for the older
@@ -589,6 +882,347 @@
return (mCdmaDispatcher.getFormat().equals(format));
}
+ /** Sets a proxy interface for accessing the methods of {@link DomainSelectionResolver}. */
+ @VisibleForTesting
+ public void setDomainSelectionResolverProxy(@NonNull DomainSelectionResolverProxy proxy) {
+ mDomainSelectionResolverProxy = proxy;
+ }
+
+ /**
+ * Determines whether or not to use CDMA format for MO SMS when the domain selection uses.
+ * If the domain is {@link NetworkRegistrationInfo#DOMAIN_PS}, then format is based on
+ * IMS SMS format, otherwise format is based on current phone type.
+ *
+ * @return {@code true} if CDMA format should be used for MO SMS, {@code false} otherwise.
+ */
+ private boolean isCdmaMo(@NetworkRegistrationInfo.Domain int domain) {
+ if (domain != NetworkRegistrationInfo.DOMAIN_PS) {
+ // IMS is not registered, use voice technology to determine SMS format.
+ return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
+ }
+ // IMS is registered with SMS support
+ return isCdmaFormat(mImsSmsDispatcher.getFormat());
+ }
+
+ /**
+ * Returns a {@link DomainSelectionConnectionHolder} according to the flag specified.
+ *
+ * @param emergency The flag to indicate that the domain selection is for an emergency SMS.
+ * @return A {@link DomainSelectionConnectionHolder} instance or null.
+ */
+ @VisibleForTesting
+ @Nullable
+ protected DomainSelectionConnectionHolder getDomainSelectionConnectionHolder(
+ boolean emergency) {
+ return emergency ? mEmergencyDscHolder : mDscHolder;
+ }
+
+ /**
+ * Returns a {@link DomainSelectionConnectionHolder} if the domain selection supports,
+ * return null otherwise.
+ *
+ * @param emergency The flag to indicate that the domain selection is for an emergency SMS.
+ * @return A {@link DomainSelectionConnectionHolder} that grabs the
+ * {@link DomainSelectionConnection} and its related information to use the domain
+ * selection architecture.
+ */
+ private DomainSelectionConnectionHolder getDomainSelectionConnection(boolean emergency) {
+ DomainSelectionConnectionHolder holder = getDomainSelectionConnectionHolder(emergency);
+ DomainSelectionConnection connection = (holder != null) ? holder.getConnection() : null;
+ boolean created = false;
+
+ if (connection == null) {
+ connection = mDomainSelectionResolverProxy.getDomainSelectionConnection(
+ mPhone, DomainSelectionService.SELECTOR_TYPE_SMS, emergency);
+
+ if (connection == null) {
+ // Domain selection architecture is not supported.
+ // Use the legacy architecture.
+ return null;
+ }
+
+ created = true;
+ }
+
+ if (holder == null) {
+ holder = new DomainSelectionConnectionHolder(emergency);
+
+ if (emergency) {
+ mEmergencyDscHolder = holder;
+ } else {
+ mDscHolder = holder;
+ }
+ }
+
+ holder.setConnection(connection);
+
+ return holder;
+ }
+
+ /**
+ * Requests the domain selection for MO SMS.
+ *
+ * @param holder The {@link DomainSelectionConnectionHolder} that contains the
+ * {@link DomainSelectionConnection} and its related information.
+ */
+ private void requestDomainSelection(@NonNull DomainSelectionConnectionHolder holder) {
+ DomainSelectionService.SelectionAttributes attr =
+ new DomainSelectionService.SelectionAttributes.Builder(mPhone.getPhoneId(),
+ mPhone.getSubId(), DomainSelectionService.SELECTOR_TYPE_SMS)
+ .setEmergency(holder.isEmergency())
+ .build();
+
+ if (holder.isEmergency()) {
+ EmergencySmsDomainSelectionConnection emergencyConnection =
+ (EmergencySmsDomainSelectionConnection) holder.getConnection();
+ CompletableFuture<Integer> future =
+ emergencyConnection.requestDomainSelection(attr, holder);
+ future.thenAcceptAsync((domain) -> {
+ if (VDBG) {
+ logd("requestDomainSelection(emergency): domain="
+ + DomainSelectionService.getDomainName(domain));
+ }
+ sendAllPendingRequests(holder, domain);
+ finishDomainSelection(holder);
+ }, this::post);
+ } else {
+ SmsDomainSelectionConnection connection =
+ (SmsDomainSelectionConnection) holder.getConnection();
+ CompletableFuture<Integer> future = connection.requestDomainSelection(attr, holder);
+ future.thenAcceptAsync((domain) -> {
+ if (VDBG) {
+ logd("requestDomainSelection: domain="
+ + DomainSelectionService.getDomainName(domain));
+ }
+ sendAllPendingRequests(holder, domain);
+ finishDomainSelection(holder);
+ }, this::post);
+ }
+ }
+
+ /**
+ * Sends a SMS after selecting the domain via the domain selection service.
+ *
+ * @param holder The {@link DomainSelectionConnectionHolder} that contains the
+ * {@link DomainSelectionConnection} and its related information.
+ * @param request The {@link PendingRequest} that stores the SMS request
+ * (data, text, multipart text) to be sent.
+ * @param logTag The log tag to display which method called this method.
+ */
+ private void sendSmsUsingDomainSelection(@NonNull DomainSelectionConnectionHolder holder,
+ @NonNull PendingRequest request, @NonNull String logTag) {
+ boolean isDomainSelectionRequested = holder.isDomainSelectionRequested();
+ // The domain selection is in progress so waits for the result of
+ // the domain selection by adding this request to the pending list.
+ holder.addRequest(request);
+
+ if (!isDomainSelectionRequested) {
+ if (VDBG) {
+ logd("requestDomainSelection: " + logTag);
+ }
+ requestDomainSelection(holder);
+ }
+ }
+
+ /**
+ * Finishes the domain selection for MO SMS.
+ *
+ * @param holder The {@link DomainSelectionConnectionHolder} object that is being finished.
+ */
+ private void finishDomainSelection(DomainSelectionConnectionHolder holder) {
+ DomainSelectionConnection connection = (holder != null) ? holder.getConnection() : null;
+
+ if (connection != null) {
+ // After this method is called, the domain selection service will clean up
+ // its resources and finish the procedure that are related to the current domain
+ // selection request.
+ connection.finishSelection();
+ }
+
+ if (holder != null) {
+ final List<PendingRequest> pendingRequests = holder.getPendingRequests();
+
+ logd("finishDomainSelection: pendingRequests=" + pendingRequests.size());
+
+ for (PendingRequest r : pendingRequests) {
+ triggerSentIntentForFailure(r.sentIntents);
+ }
+
+ holder.clearAllRequests();
+ holder.setConnection(null);
+ }
+ }
+
+ /**
+ * Notifies the application that MO SMS is not sent by the error of domain selection.
+ *
+ * @param holder The {@link DomainSelectionConnectionHolder} object that is being terminated.
+ */
+ private void notifyDomainSelectionTerminated(@NonNull DomainSelectionConnectionHolder holder) {
+ final List<PendingRequest> pendingRequests = holder.getPendingRequests();
+
+ logd("notifyDomainSelectionTerminated: pendingRequests=" + pendingRequests.size());
+
+ for (PendingRequest r : pendingRequests) {
+ triggerSentIntentForFailure(r.sentIntents);
+ }
+
+ holder.setConnection(null);
+ holder.clearAllRequests();
+ }
+
+ /**
+ * Sends all pending requests for MO SMS.
+ *
+ * @param holder The {@link DomainSelectionConnectionHolder} object that all the pending
+ * requests are handled.
+ * @param domain The domain where the SMS is being sent, which can be one of the following:
+ * - {@link NetworkRegistrationInfo#DOMAIN_PS}
+ * - {@link NetworkRegistrationInfo#DOMAIN_CS}
+ */
+ private void sendAllPendingRequests(@NonNull DomainSelectionConnectionHolder holder,
+ @NetworkRegistrationInfo.Domain int domain) {
+ final List<PendingRequest> pendingRequests = holder.getPendingRequests();
+
+ if (VDBG) {
+ logd("sendAllPendingRequests: domain=" + DomainSelectionService.getDomainName(domain)
+ + ", size=" + pendingRequests.size());
+ }
+
+ for (PendingRequest r : pendingRequests) {
+ switch (r.type) {
+ case PendingRequest.TYPE_DATA:
+ sendData(domain, r);
+ break;
+ case PendingRequest.TYPE_TEXT:
+ sendText(domain, r);
+ break;
+ case PendingRequest.TYPE_MULTIPART_TEXT:
+ sendMultipartText(domain, r);
+ break;
+ case PendingRequest.TYPE_RETRY_SMS:
+ sendRetrySms(r.tracker, (domain == NetworkRegistrationInfo.DOMAIN_PS));
+ break;
+ default:
+ // Not reachable.
+ break;
+ }
+ }
+
+ holder.clearAllRequests();
+ }
+
+ /**
+ * Sends a data based SMS to a specific application port.
+ *
+ * @param domain The domain where the SMS is being sent, which can be one of the following:
+ * - {@link NetworkRegistrationInfo#DOMAIN_PS}
+ * - {@link NetworkRegistrationInfo#DOMAIN_CS}
+ * @param request The pending request for MO SMS.
+ */
+ private void sendData(@NetworkRegistrationInfo.Domain int domain,
+ @NonNull PendingRequest request) {
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ mImsSmsDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
+ request.destPort, request.data, request.sentIntents.get(0),
+ request.deliveryIntents.get(0), request.isForVvm);
+ } else if (isCdmaMo(domain)) {
+ mCdmaDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
+ request.destPort, request.data, request.sentIntents.get(0),
+ request.deliveryIntents.get(0), request.isForVvm);
+ } else {
+ mGsmDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
+ request.destPort, request.data, request.sentIntents.get(0),
+ request.deliveryIntents.get(0), request.isForVvm);
+ }
+ }
+
+ /**
+ * Sends a text based SMS.
+ *
+ * @param domain The domain where the SMS is being sent, which can be one of the following:
+ * - {@link NetworkRegistrationInfo#DOMAIN_PS}
+ * - {@link NetworkRegistrationInfo#DOMAIN_CS}
+ * @param request The pending request for MO SMS.
+ */
+ private void sendText(@NetworkRegistrationInfo.Domain int domain,
+ @NonNull PendingRequest request) {
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ mImsSmsDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+ request.sentIntents.get(0), request.deliveryIntents.get(0),
+ request.messageUri, request.callingPackage, request.persistMessage,
+ request.priority, false /*request.expectMore*/, request.validityPeriod,
+ request.isForVvm, request.messageId, request.skipShortCodeCheck);
+ } else {
+ if (isCdmaMo(domain)) {
+ mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+ request.sentIntents.get(0), request.deliveryIntents.get(0),
+ request.messageUri, request.callingPackage, request.persistMessage,
+ request.priority, request.expectMore, request.validityPeriod,
+ request.isForVvm, request.messageId, request.skipShortCodeCheck);
+ } else {
+ mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+ request.sentIntents.get(0), request.deliveryIntents.get(0),
+ request.messageUri, request.callingPackage, request.persistMessage,
+ request.priority, request.expectMore, request.validityPeriod,
+ request.isForVvm, request.messageId, request.skipShortCodeCheck);
+ }
+ }
+ }
+
+ /**
+ * Sends a multi-part text based SMS.
+ *
+ * @param domain The domain where the SMS is being sent, which can be one of the following:
+ * - {@link NetworkRegistrationInfo#DOMAIN_PS}
+ * - {@link NetworkRegistrationInfo#DOMAIN_CS}
+ * @param request The pending request for MO SMS.
+ */
+ private void sendMultipartText(@NetworkRegistrationInfo.Domain int domain,
+ @NonNull PendingRequest request) {
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ mImsSmsDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+ request.sentIntents, request.deliveryIntents, request.messageUri,
+ request.callingPackage, request.persistMessage, request.priority,
+ false /*request.expectMore*/, request.validityPeriod, request.messageId);
+ } else {
+ if (isCdmaMo(domain)) {
+ mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+ request.sentIntents, request.deliveryIntents, request.messageUri,
+ request.callingPackage, request.persistMessage, request.priority,
+ request.expectMore, request.validityPeriod, request.messageId);
+ } else {
+ mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+ request.sentIntents, request.deliveryIntents, request.messageUri,
+ request.callingPackage, request.persistMessage, request.priority,
+ request.expectMore, request.validityPeriod, request.messageId);
+ }
+ }
+ }
+
+ private void triggerSentIntentForFailure(@NonNull PendingIntent sentIntent) {
+ try {
+ sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (CanceledException e) {
+ logd("Intent has been canceled!");
+ }
+ }
+
+ private void triggerSentIntentForFailure(@NonNull List<PendingIntent> sentIntents) {
+ for (PendingIntent sentIntent : sentIntents) {
+ triggerSentIntentForFailure(sentIntent);
+ }
+ }
+
+ /**
+ * Creates an ArrayList object from any object.
+ */
+ private static <T> ArrayList<T> asArrayList(T object) {
+ ArrayList<T> list = new ArrayList<>();
+ list.add(object);
+ return list;
+ }
+
/**
* Send a data based SMS to a specific application port.
*
@@ -670,6 +1304,26 @@
*/
protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+ if (TextUtils.isEmpty(scAddr)) {
+ scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPackage);
+ }
+
+ if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) {
+ DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false);
+
+ // If the DomainSelectionConnection is not available,
+ // fallback to the legacy implementation.
+ if (holder != null && holder.getConnection() != null) {
+ sendSmsUsingDomainSelection(holder,
+ new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage,
+ destAddr, scAddr, asArrayList(sentIntent),
+ asArrayList(deliveryIntent), isForVvm, data, destPort, null, null,
+ false, 0, false, 0, 0L, false),
+ "sendData");
+ return;
+ }
+ }
+
if (mImsSmsDispatcher.isAvailable()) {
mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
deliveryIntent, isForVvm);
@@ -783,19 +1437,148 @@
PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
long messageId) {
+ sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
+ persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * @param messageUri optional URI of the message if it is already stored in the system
+ * @param callingPkg the calling package name
+ * @param persistMessage whether to save the sent message into SMS DB for a
+ * non-default SMS app.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ * @param skipShortCodeCheck Skip check for short code type destination address.
+ */
+ public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
+ int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
+ long messageId, boolean skipShortCodeCheck) {
+ if (TextUtils.isEmpty(scAddr)) {
+ scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg);
+ }
+
+ if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ boolean isEmergency = tm.isEmergencyNumber(destAddr);
+ DomainSelectionConnectionHolder holder = getDomainSelectionConnection(isEmergency);
+
+ // If the DomainSelectionConnection is not available,
+ // fallback to the legacy implementation.
+ if (holder != null && holder.getConnection() != null) {
+ sendSmsUsingDomainSelection(holder,
+ new PendingRequest(PendingRequest.TYPE_TEXT, null, callingPkg,
+ destAddr, scAddr, asArrayList(sentIntent),
+ asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text),
+ messageUri, persistMessage, priority, expectMore, validityPeriod,
+ messageId, skipShortCodeCheck),
+ "sendText");
+ return;
+ }
+ }
+
if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
- validityPeriod, isForVvm, messageId);
+ validityPeriod, isForVvm, messageId, skipShortCodeCheck);
} else {
if (isCdmaMo()) {
mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, expectMore,
- validityPeriod, isForVvm, messageId);
+ validityPeriod, isForVvm, messageId, skipShortCodeCheck);
} else {
mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, priority, expectMore,
- validityPeriod, isForVvm, messageId);
+ validityPeriod, isForVvm, messageId, skipShortCodeCheck);
}
}
}
@@ -909,6 +1692,26 @@
ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
long messageId) {
+ if (TextUtils.isEmpty(scAddr)) {
+ scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg);
+ }
+
+ if (mDomainSelectionResolverProxy.isDomainSelectionSupported()) {
+ DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false);
+
+ // If the DomainSelectionConnection is not available,
+ // fallback to the legacy implementation.
+ if (holder != null && holder.getConnection() != null) {
+ sendSmsUsingDomainSelection(holder,
+ new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null,
+ callingPkg, destAddr, scAddr, sentIntents, deliveryIntents, false,
+ null, 0, parts, messageUri, persistMessage, priority, expectMore,
+ validityPeriod, messageId, false),
+ "sendMultipartText");
+ return;
+ }
+ }
+
if (mImsSmsDispatcher.isAvailable()) {
mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
deliveryIntents, messageUri, callingPkg, persistMessage, priority,
@@ -1065,4 +1868,8 @@
private void logd(String msg) {
Rlog.d(TAG, msg);
}
+
+ private void logi(String s) {
+ Rlog.i(TAG + " [" + mPhone.getPhoneId() + "]", s);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsPermissions.java b/src/java/com/android/internal/telephony/SmsPermissions.java
index 44751ac..529eea0 100644
--- a/src/java/com/android/internal/telephony/SmsPermissions.java
+++ b/src/java/com/android/internal/telephony/SmsPermissions.java
@@ -23,9 +23,11 @@
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
+import android.os.UserHandle;
import android.service.carrier.CarrierMessagingService;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
/**
@@ -132,10 +134,19 @@
*/
public boolean checkCallingOrSelfCanGetSmscAddress(String callingPackage, String message) {
// Allow it to the default SMS app always.
- if (!isCallerDefaultSmsPackage(callingPackage)) {
- TelephonyPermissions
- .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
- mContext, mPhone.getSubId(), message);
+ boolean isDefaultSmsPackage;
+ int callerUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (!isDefaultSmsPackage) {
+ Rlog.d(LOG_TAG, "Caller is not a default SMS application");
+ TelephonyPermissions.
+ enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mContext, mPhone.getSubId(), message);
}
return true;
}
@@ -151,33 +162,49 @@
*/
public boolean checkCallingOrSelfCanSetSmscAddress(String callingPackage, String message) {
// Allow it to the default SMS app always.
- if (!isCallerDefaultSmsPackage(callingPackage)) {
+ boolean isDefaultSmsPackage;
+ int callerUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ isDefaultSmsPackage = isCallerDefaultSmsPackage(callingPackage, callerUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (!isDefaultSmsPackage) {
+ Rlog.d(LOG_TAG, "Caller is not a default SMS application");
// Allow it with MODIFY_PHONE_STATE or Carrier Privileges
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
- mContext, mPhone.getSubId(), message);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext,
+ mPhone.getSubId(), message);
}
return true;
}
/** Check if a package is default SMS app. */
@VisibleForTesting
- public boolean isCallerDefaultSmsPackage(String packageName) {
- if (packageNameMatchesCallingUid(packageName)) {
- return SmsApplication.isDefaultSmsApplication(mContext, packageName);
+ public boolean isCallerDefaultSmsPackage(String packageName, int callerUid) {
+ if (packageNameMatchesCallingUid(packageName, callerUid)) {
+ UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext,
+ mPhone.getSubId());
+ return SmsApplication.isDefaultSmsApplicationAsUser(mContext, packageName, userHandle);
}
return false;
}
+ @VisibleForTesting
+ public boolean packageNameMatchesCallingUid(String packageName) {
+ return packageNameMatchesCallingUid(packageName, Binder.getCallingUid());
+ }
+
/**
* Check if the passed in packageName belongs to the calling uid.
* @param packageName name of the package to check
* @return true if package belongs to calling uid, false otherwise
*/
@VisibleForTesting
- public boolean packageNameMatchesCallingUid(String packageName) {
+ public boolean packageNameMatchesCallingUid(String packageName, int callerUid) {
try {
((AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE))
- .checkPackage(Binder.getCallingUid(), packageName);
+ .checkPackage(callerUid, packageName);
// If checkPackage doesn't throw an exception then we are the given package
return true;
} catch (SecurityException e) {
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
old mode 100755
new mode 100644
index b890a4d..2736f7a
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -18,17 +18,22 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.provider.Telephony.Sms.Intents;
import android.telephony.SubscriptionManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
/**
@@ -39,16 +44,28 @@
* dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
*/
public class SmsStorageMonitor extends Handler {
- private static final String TAG = "SmsStorageMonitor";
+ private static final String TAG = "SmsStorageMonitor1";
+
+ /** Maximum number of times to retry memory status reporting */
+ private static final int MAX_RETRIES = 1;
+
+ /** Delay before next attempt on a failed memory status reporting, in milliseconds. */
+ private static final int RETRY_DELAY = 5000; // 5 seconds
/** SIM/RUIM storage is full */
private static final int EVENT_ICC_FULL = 1;
+ /** Report memory status */
+ private static final int EVENT_REPORT_MEMORY_STATUS = 2;
+
/** Memory status reporting is acknowledged by RIL */
- private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2;
+ private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 3;
+
+ /** Retry memory status reporting */
+ private static final int EVENT_RETRY_MEMORY_STATUS_REPORTING = 4;
/** Radio is ON */
- private static final int EVENT_RADIO_ON = 3;
+ private static final int EVENT_RADIO_ON = 5;
/** Context from phone object passed to constructor. */
private final Context mContext;
@@ -56,14 +73,23 @@
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
- private boolean mReportMemoryStatusPending;
+ private int mMaxRetryCount = MAX_RETRIES;
+ private int mRetryDelay = RETRY_DELAY;
+ private int mRetryCount = 0;
+ private boolean mIsWaitingResponse = false;
+ private boolean mNeedNewReporting = false;
+ private boolean mIsMemoryStatusReportingFailed = false;
/** it is use to put in to extra value for SIM_FULL_ACTION and SMS_REJECTED_ACTION */
Phone mPhone;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- final CommandsInterface mCi; // accessed from inner class
- boolean mStorageAvailable = true; // accessed from inner class
+ final CommandsInterface mCi;
+ boolean mStorageAvailable = true;
+
+ boolean mInitialStorageAvailableStatus = true;
+
+ private boolean mMemoryStatusOverrideFlag = false;
/**
* Hold the wake lock for 5 seconds, which should be enough time for
@@ -93,6 +119,41 @@
mContext.registerReceiver(mResultReceiver, filter);
}
+ /**
+ * Overriding of the Memory Status by the TestApi and send the event to Handler to test
+ * the RP-SMMA feature
+ * @param isStorageAvailable boolean value specifies the MemoryStatus to be
+ * sent to Handler
+ */
+ public void sendMemoryStatusOverride(boolean isStorageAvailable) {
+ if (!mMemoryStatusOverrideFlag) {
+ mInitialStorageAvailableStatus = mStorageAvailable;
+ mMemoryStatusOverrideFlag = true;
+ }
+ mStorageAvailable = isStorageAvailable;
+ if (isStorageAvailable) {
+ sendMessage(obtainMessage(EVENT_REPORT_MEMORY_STATUS));
+ }
+ }
+
+ /**
+ * reset Memory Status change made by {@link #sendMemoryStatusOverride}
+ */
+ public void clearMemoryStatusOverride() {
+ mStorageAvailable = mInitialStorageAvailableStatus;
+ mMemoryStatusOverrideFlag = false;
+ }
+
+ @VisibleForTesting
+ public void setMaxRetries(int maxCount) {
+ mMaxRetryCount = maxCount;
+ }
+
+ @VisibleForTesting
+ public void setRetryDelayInMillis(int delay) {
+ mRetryDelay = delay;
+ }
+
public void dispose() {
mCi.unSetOnIccSmsFull(this);
mCi.unregisterForOn(this);
@@ -105,35 +166,97 @@
*/
@Override
public void handleMessage(Message msg) {
- AsyncResult ar;
+ boolean isAvailable = mStorageAvailable;
switch (msg.what) {
case EVENT_ICC_FULL:
handleIccFull();
break;
- case EVENT_REPORT_MEMORY_STATUS_DONE:
- ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- mReportMemoryStatusPending = true;
- Rlog.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
- + mStorageAvailable);
+ case EVENT_REPORT_MEMORY_STATUS:
+ if (mIsWaitingResponse) {
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS - deferred");
+ // Previous reporting is on-going now. New reporting will be issued in
+ // EVENT_REPORT_MEMORY_STATUS_DONE.
+ mNeedNewReporting = true;
} else {
- mReportMemoryStatusPending = false;
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS - report sms memory status"
+ + (isAvailable ? "(not full)" : "(full)"));
+ // Clear a delayed EVENT_RETRY_MEMORY_STATUS_REPORTING
+ // and mIsMemoryStatusReportingFailed.
+ removeMessages(EVENT_RETRY_MEMORY_STATUS_REPORTING);
+ mIsMemoryStatusReportingFailed = false;
+ mRetryCount = 0;
+ sendMemoryStatusReport(isAvailable);
}
break;
+ case EVENT_REPORT_MEMORY_STATUS_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ mIsWaitingResponse = false;
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - "
+ + (ar.exception == null ? "succeeded" : "failed"));
+ if (mNeedNewReporting) {
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - report again now"
+ + (isAvailable ? "(not full)" : "(full)"));
+ // New reportings have been requested, report last memory status here.
+ mNeedNewReporting = false;
+ mRetryCount = 0;
+ sendMemoryStatusReport(isAvailable);
+ } else {
+ if (ar.exception != null) {
+ if (mRetryCount++ < mMaxRetryCount) {
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - retry in "
+ + mRetryDelay + "ms");
+ sendMessageDelayed(
+ obtainMessage(EVENT_RETRY_MEMORY_STATUS_REPORTING),
+ mRetryDelay);
+ } else {
+ Rlog.v(TAG, "EVENT_REPORT_MEMORY_STATUS_DONE - "
+ + "no retry anymore(pended)");
+ mRetryCount = 0;
+ mIsMemoryStatusReportingFailed = true;
+ }
+ } else {
+ mRetryCount = 0;
+ mIsMemoryStatusReportingFailed = false;
+ }
+ }
+ break;
+
+ case EVENT_RETRY_MEMORY_STATUS_REPORTING:
+ Rlog.v(TAG, "EVENT_RETRY_MEMORY_STATUS_REPORTING - retry"
+ + (isAvailable ? "(not full)" : "(full)"));
+ sendMemoryStatusReport(isAvailable);
+ break;
+
case EVENT_RADIO_ON:
- if (mReportMemoryStatusPending) {
- Rlog.v(TAG, "Sending pending memory status report : mStorageAvailable = "
- + mStorageAvailable);
- mCi.reportSmsMemoryStatus(mStorageAvailable,
- obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ if (mIsMemoryStatusReportingFailed) {
+ Rlog.v(TAG, "EVENT_RADIO_ON - report failed sms memory status"
+ + (isAvailable ? "(not full)" : "(full)"));
+ sendMemoryStatusReport(isAvailable);
}
break;
}
}
+ private void sendMemoryStatusReport(boolean isAvailable) {
+ mIsWaitingResponse = true;
+ Resources r = mContext.getResources();
+ if (r.getBoolean(com.android.internal.R.bool.config_smma_notification_supported_over_ims)) {
+ IccSmsInterfaceManager smsIfcMngr = mPhone.getIccSmsInterfaceManager();
+ if (smsIfcMngr != null) {
+ Rlog.d(TAG, "sendMemoryStatusReport: smsIfcMngr is available");
+ if (smsIfcMngr.mDispatchersController.isIms() && isAvailable) {
+ smsIfcMngr.mDispatchersController.reportSmsMemoryStatus(
+ obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ return;
+ }
+ }
+ }
+ mCi.reportSmsMemoryStatus(isAvailable, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ }
+
private void createWakelock() {
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor");
@@ -145,9 +268,14 @@
* that SIM storage for SMS messages is full.
*/
private void handleIccFull() {
+ UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext,
+ mPhone.getSubId());
+ ComponentName componentName = SmsApplication.getDefaultSimFullApplicationAsUser(mContext,
+ false, userHandle);
+
// broadcast SIM_FULL intent
Intent intent = new Intent(Intents.SIM_FULL_ACTION);
- intent.setComponent(SmsApplication.getDefaultSimFullApplication(mContext, false));
+ intent.setComponent(componentName);
mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
@@ -161,12 +289,12 @@
private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
- mStorageAvailable = false;
- mCi.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
- } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
- mStorageAvailable = true;
- mCi.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ final String action = intent.getAction();
+ if (Intent.ACTION_DEVICE_STORAGE_FULL.equals(action)
+ || Intent.ACTION_DEVICE_STORAGE_NOT_FULL.equals(action)) {
+ mStorageAvailable =
+ Intent.ACTION_DEVICE_STORAGE_FULL.equals(action) ? false : true;
+ sendMessage(obtainMessage(EVENT_REPORT_MEMORY_STATUS));
}
}
};
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 8bcdc07..8e4ac60 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -43,6 +43,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -73,6 +74,8 @@
private static final String SHORT_CODE_PATH = "/data/misc/sms/codes";
+ private static final String SHORT_CODE_VERSION_PATH = "/data/misc/sms/metadata/version";
+
/** Default checking period for SMS sent without user permission. */
private static final int DEFAULT_SMS_CHECK_PERIOD = 60000; // 1 minute
@@ -128,6 +131,8 @@
/** Last modified time for pattern file */
private long mPatternFileLastModified = 0;
+ private int mPatternFileVersion = -1;
+
private RoleManager mRoleManager;
/** Directory for per-app SMS permission XML file. */
@@ -415,9 +420,11 @@
if (mPatternFile.exists()) {
if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from file");
mCurrentPatternMatcher = getPatternMatcherFromFile(countryIso);
+ mPatternFileVersion = getPatternFileVersionFromFile();
} else {
if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from resource");
mCurrentPatternMatcher = getPatternMatcherFromResource(countryIso);
+ mPatternFileVersion = -1;
}
mCurrentCountry = countryIso;
}
@@ -655,6 +662,37 @@
return false;
}
+ private int getPatternFileVersionFromFile() {
+ File versionFile = new File(SHORT_CODE_VERSION_PATH);
+ if (versionFile.exists()) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(versionFile));
+ String version = reader.readLine();
+ if (version != null) {
+ return Integer.parseInt(version);
+ }
+ } catch (IOException e) {
+ Rlog.e(TAG, "File reader exception reading short code "
+ + "pattern file version", e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ Rlog.e(TAG, "File reader exception closing short code "
+ + "pattern file version reader", e);
+ }
+ }
+ }
+ return -1;
+ }
+
+ public int getShortCodeXmlFileVersion() {
+ return mPatternFileVersion;
+ }
+
private static void log(String msg) {
Rlog.d(TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/SrvccConnection.java b/src/java/com/android/internal/telephony/SrvccConnection.java
new file mode 100644
index 0000000..f25a15c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SrvccConnection.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING;
+
+import android.net.Uri;
+import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.text.TextUtils;
+
+import com.android.ims.internal.ConferenceParticipant;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.telephony.Rlog;
+
+/**
+ * Connection information for SRVCC
+ */
+public class SrvccConnection {
+ private static final String TAG = "SrvccConnection";
+
+ public static final int CALL_TYPE_NORMAL = 0;
+ public static final int CALL_TYPE_EMERGENCY = 1;
+
+ public static final int SUBSTATE_NONE = 0;
+ /** Pre-alerting state. Applicable for MT calls only */
+ public static final int SUBSTATE_PREALERTING = 1;
+
+ public static final int TONE_NONE = 0;
+ public static final int TONE_LOCAL = 1;
+ public static final int TONE_NETWORK = 2;
+
+ /** Values are CALL_TYPE_ */
+ private int mType = CALL_TYPE_NORMAL;
+
+ /** Values are Call.State */
+ private Call.State mState;
+
+ /** Values are SUBSTATE_ */
+ private int mSubstate = SUBSTATE_NONE;
+
+ /** Values are TONE_ */
+ private int mRingbackToneType = TONE_NONE;
+
+ /** true if it is a multi-party call */
+ private boolean mIsMpty = false;
+
+ /** true if it is a mobile terminated call */
+ private boolean mIsMT;
+
+ /** Remote party nummber */
+ private String mNumber;
+
+ /** Values are PhoneConstants.PRESENTATION_ */
+ private int mNumPresentation;
+
+ /** Remote party name */
+ private String mName;
+
+ /** Values are PhoneConstants.PRESENTATION_ */
+ private int mNamePresentation;
+
+ public SrvccConnection(ImsCallProfile profile,
+ ImsPhoneConnection c, @PreciseCallStates int preciseCallState) {
+ mState = toCallState(preciseCallState);
+ if (mState == Call.State.ALERTING) {
+ mRingbackToneType = isLocalTone(profile) ? TONE_LOCAL : TONE_NETWORK;
+ }
+ if (preciseCallState == PRECISE_CALL_STATE_INCOMING_SETUP) {
+ mSubstate = SUBSTATE_PREALERTING;
+ }
+
+ if (c == null) {
+ initialize(profile);
+ } else {
+ initialize(c);
+ }
+ }
+
+ public SrvccConnection(ConferenceParticipant cp, @PreciseCallStates int preciseCallState) {
+ Rlog.d(TAG, "initialize with ConferenceParticipant");
+ mState = toCallState(preciseCallState);
+ mIsMT = cp.getCallDirection() == android.telecom.Call.Details.DIRECTION_INCOMING;
+ mNumber = getParticipantAddress(cp.getHandle());
+ mNumPresentation = cp.getParticipantPresentation();
+ if (mNumPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+ mNumber = "";
+ }
+ mName = cp.getDisplayName();
+ if (!TextUtils.isEmpty(mName)) {
+ mNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
+ } else {
+ mNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
+ }
+ mIsMpty = true;
+ }
+
+ private static String getParticipantAddress(Uri address) {
+ if (address == null) {
+ return null;
+ }
+
+ String number = address.getSchemeSpecificPart();
+ if (TextUtils.isEmpty(number)) {
+ return null;
+ }
+
+ String[] numberParts = number.split("[@;:]");
+ if (numberParts.length == 0) return null;
+
+ return numberParts[0];
+ }
+
+ // MT call in alerting or prealerting state
+ private void initialize(ImsCallProfile profile) {
+ Rlog.d(TAG, "initialize with ImsCallProfile");
+ mIsMT = true;
+ mNumber = profile.getCallExtra(ImsCallProfile.EXTRA_OI);
+ mName = profile.getCallExtra(ImsCallProfile.EXTRA_CNA);
+ mNumPresentation = ImsCallProfile.OIRToPresentation(
+ profile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
+ mNamePresentation = ImsCallProfile.OIRToPresentation(
+ profile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
+ }
+
+ private void initialize(ImsPhoneConnection c) {
+ Rlog.d(TAG, "initialize with ImsPhoneConnection");
+ if (c.isEmergencyCall()) {
+ mType = CALL_TYPE_EMERGENCY;
+ }
+ mIsMT = c.isIncoming();
+ mNumber = c.getAddress();
+ mNumPresentation = c.getNumberPresentation();
+ mName = c.getCnapName();
+ mNamePresentation = c.getCnapNamePresentation();
+ }
+
+ private boolean isLocalTone(ImsCallProfile profile) {
+ if (profile == null) return false;
+
+ ImsStreamMediaProfile mediaProfile = profile.getMediaProfile();
+ if (mediaProfile == null) return false;
+
+ boolean shouldPlayRingback =
+ (mediaProfile.getAudioDirection() == ImsStreamMediaProfile.DIRECTION_INACTIVE)
+ ? true : false;
+ return shouldPlayRingback;
+ }
+
+ private static Call.State toCallState(int preciseCallState) {
+ switch (preciseCallState) {
+ case PRECISE_CALL_STATE_ACTIVE: return Call.State.ACTIVE;
+ case PRECISE_CALL_STATE_HOLDING: return Call.State.HOLDING;
+ case PRECISE_CALL_STATE_DIALING: return Call.State.DIALING;
+ case PRECISE_CALL_STATE_ALERTING: return Call.State.ALERTING;
+ case PRECISE_CALL_STATE_INCOMING: return Call.State.INCOMING;
+ case PRECISE_CALL_STATE_WAITING: return Call.State.WAITING;
+ case PRECISE_CALL_STATE_INCOMING_SETUP: return Call.State.INCOMING;
+ default:
+ }
+ return Call.State.DISCONNECTED;
+ }
+
+ /** Returns the type of the call */
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns the state */
+ public Call.State getState() {
+ return mState;
+ }
+
+ /** Updates the state */
+ public void setState(Call.State state) {
+ mState = state;
+ }
+
+ /** Returns the sub state */
+ public int getSubState() {
+ return mSubstate;
+ }
+
+ /** Returns the ringback tone type */
+ public int getRingbackToneType() {
+ return mRingbackToneType;
+ }
+
+ /** true if it is a multi-party call */
+ public boolean isMultiParty() {
+ return mIsMpty;
+ }
+
+ /** true if it is a mobile terminated call */
+ public boolean isIncoming() {
+ return mIsMT;
+ }
+
+ /** Returns the remote party nummber */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /** Returns the number presentation */
+ public int getNumberPresentation() {
+ return mNumPresentation;
+ }
+
+ /** Returns the remote party name */
+ public String getName() {
+ return mName;
+ }
+
+ /** Returns the name presentation */
+ public int getNamePresentation() {
+ return mNamePresentation;
+ }
+
+ /**
+ * Build a human representation of a connection instance, suitable for debugging.
+ * Don't log personal stuff unless in debug mode.
+ * @return a string representing the internal state of this connection.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" type:").append(getType());
+ sb.append(", state:").append(getState());
+ sb.append(", subState:").append(getSubState());
+ sb.append(", toneType:").append(getRingbackToneType());
+ sb.append(", mpty:").append(isMultiParty());
+ sb.append(", incoming:").append(isIncoming());
+ sb.append(", numberPresentation:").append(getNumberPresentation());
+ sb.append(", number:").append(Rlog.pii(TAG, getNumber()));
+ sb.append(", namePresentation:").append(getNamePresentation());
+ sb.append(", name:").append(Rlog.pii(TAG, getName()));
+ return sb.toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
deleted file mode 100644
index 7e762d7..0000000
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ /dev/null
@@ -1,4820 +0,0 @@
-/*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.telephony;
-
-import static android.Manifest.permission.READ_PHONE_NUMBERS;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.telephony.TelephonyManager.MULTISIM_ALLOWED;
-import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION;
-import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.os.RegistrantList;
-import android.os.RemoteException;
-import android.os.TelephonyServiceManager.ServiceRegisterer;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Telephony.SimInfo;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telephony.AnomalyReporter;
-import android.telephony.CarrierConfigManager;
-import android.telephony.RadioAccessFamily;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.SimDisplayNameSource;
-import android.telephony.SubscriptionManager.UsageSetting;
-import android.telephony.TelephonyFrameworkInitializer;
-import android.telephony.TelephonyManager;
-import android.telephony.TelephonyRegistryManager;
-import android.telephony.UiccAccessRule;
-import android.telephony.UiccPortInfo;
-import android.telephony.UiccSlotInfo;
-import android.telephony.UiccSlotMapping;
-import android.telephony.euicc.EuiccManager;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.LocalLog;
-import android.util.Log;
-
-import com.android.ims.ImsManager;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.data.DataEnabledOverride;
-import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccCard;
-import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccProfile;
-import com.android.internal.telephony.uicc.UiccSlot;
-import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
-
-/**
- * Implementation of the ISub interface.
- *
- * Any setters which take subId, slotIndex or phoneId as a parameter will throw an exception if the
- * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
- *
- * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
- * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()).
- *
- * Finally, any getters which perform the mapping between subscriptions, slots and phones will
- * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
- * will fail and return the appropriate error value. Ie calling
- * getSlotIndex(INVALID_SUBSCRIPTION_ID) will return INVALID_SIM_SLOT_INDEX and calling
- * getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null.
- *
- */
-public class SubscriptionController extends ISub.Stub {
- private static final String LOG_TAG = "SubscriptionController";
- private static final boolean DBG = false;
- private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
- private static final boolean DBG_CACHE = false;
- private static final int DEPRECATED_SETTING = -1;
- private static final ParcelUuid INVALID_GROUP_UUID =
- ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING);
- private final LocalLog mLocalLog = new LocalLog(128);
- private static final int SUB_ID_FOUND = 1;
- private static final int NO_ENTRY_FOR_SLOT_INDEX = -1;
- private static final int SUB_ID_NOT_IN_SLOT = -2;
-
- // Lock that both mCacheActiveSubInfoList and mCacheOpportunisticSubInfoList use.
- private Object mSubInfoListLock = new Object();
-
- /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
- private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();
-
- /* Similar to mCacheActiveSubInfoList but only caching opportunistic subscriptions. */
- private List<SubscriptionInfo> mCacheOpportunisticSubInfoList = new ArrayList<>();
- private AtomicBoolean mOpptSubInfoListChangedDirtyBit = new AtomicBoolean();
-
- private static final Comparator<SubscriptionInfo> SUBSCRIPTION_INFO_COMPARATOR =
- (arg0, arg1) -> {
- // Primary sort key on SimSlotIndex
- int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
- if (flag == 0) {
- // Secondary sort on SubscriptionId
- return arg0.getSubscriptionId() - arg1.getSubscriptionId();
- }
- return flag;
- };
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected final Object mLock = new Object();
-
- /** The singleton instance. */
- protected static SubscriptionController sInstance = null;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected Context mContext;
- protected TelephonyManager mTelephonyManager;
- protected UiccController mUiccController;
-
- private AppOpsManager mAppOps;
-
- // Allows test mocks to avoid SELinux failures on invalidate calls.
- private static boolean sCachingEnabled = true;
-
- // Each slot can have multiple subs.
- private static class WatchedSlotIndexToSubIds {
- private Map<Integer, ArrayList<Integer>> mSlotIndexToSubIds = new ConcurrentHashMap<>();
-
- WatchedSlotIndexToSubIds() {
- }
-
- public void clear() {
- mSlotIndexToSubIds.clear();
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
-
- public Set<Entry<Integer, ArrayList<Integer>>> entrySet() {
- return mSlotIndexToSubIds.entrySet();
- }
-
- // Force all updates to data structure through wrapper.
- public ArrayList<Integer> getCopy(int slotIndex) {
- ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
- if (subIdList == null) {
- return null;
- }
-
- return new ArrayList<Integer>(subIdList);
- }
-
- public void put(int slotIndex, ArrayList<Integer> value) {
- mSlotIndexToSubIds.put(slotIndex, value);
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
-
- public void remove(int slotIndex) {
- mSlotIndexToSubIds.remove(slotIndex);
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
-
- public int size() {
- return mSlotIndexToSubIds.size();
- }
-
- @VisibleForTesting
- public Map<Integer, ArrayList<Integer>> getMap() {
- return mSlotIndexToSubIds;
- }
-
- public int removeFromSubIdList(int slotIndex, int subId) {
- ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
- if (subIdList == null) {
- return NO_ENTRY_FOR_SLOT_INDEX;
- } else {
- if (subIdList.contains(subId)) {
- subIdList.remove(new Integer(subId));
- if (subIdList.isEmpty()) {
- mSlotIndexToSubIds.remove(slotIndex);
- }
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- return SUB_ID_FOUND;
- } else {
- return SUB_ID_NOT_IN_SLOT;
- }
- }
- }
-
- public void addToSubIdList(int slotIndex, Integer value) {
- ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
- if (subIdList == null) {
- subIdList = new ArrayList<Integer>();
- subIdList.add(value);
- mSlotIndexToSubIds.put(slotIndex, subIdList);
- } else {
- subIdList.add(value);
- }
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
-
- public void clearSubIdList(int slotIndex) {
- ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
- if (subIdList != null) {
- subIdList.clear();
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
- }
- }
-
- public static class WatchedInt {
- private int mValue;
-
- public WatchedInt(int initialValue) {
- mValue = initialValue;
- }
-
- public int get() {
- return mValue;
- }
-
- public void set(int newValue) {
- mValue = newValue;
- }
- }
-
- private static WatchedSlotIndexToSubIds sSlotIndexToSubIds = new WatchedSlotIndexToSubIds();
-
- protected static WatchedInt sDefaultFallbackSubId =
- new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- @Override
- public void set(int newValue) {
- super.set(newValue);
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- }
- };
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private int[] colorArr;
- private long mLastISubServiceRegTime;
- private RegistrantList mUiccAppsEnableChangeRegList = new RegistrantList();
-
- // The properties that should be shared and synced across grouped subscriptions.
- private static final Set<String> GROUP_SHARING_PROPERTIES = new HashSet<>(Arrays.asList(
- SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
- SubscriptionManager.VT_IMS_ENABLED,
- SubscriptionManager.WFC_IMS_ENABLED,
- SubscriptionManager.WFC_IMS_MODE,
- SubscriptionManager.WFC_IMS_ROAMING_MODE,
- SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
- SubscriptionManager.DATA_ROAMING,
- SubscriptionManager.DISPLAY_NAME,
- SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES,
- SubscriptionManager.UICC_APPLICATIONS_ENABLED,
- SubscriptionManager.IMS_RCS_UCE_ENABLED,
- SubscriptionManager.CROSS_SIM_CALLING_ENABLED,
- SubscriptionManager.NR_ADVANCED_CALLING_ENABLED
- ));
-
- public static SubscriptionController init(Context c) {
- synchronized (SubscriptionController.class) {
- if (sInstance == null) {
- sInstance = new SubscriptionController(c);
- } else {
- Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- return sInstance;
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static SubscriptionController getInstance() {
- if (sInstance == null) {
- Log.wtf(LOG_TAG, "getInstance null");
- }
-
- return sInstance;
- }
-
- protected SubscriptionController(Context c) {
- internalInit(c);
- migrateImsSettings();
- }
-
- protected void internalInit(Context c) {
- mContext = c;
- mTelephonyManager = TelephonyManager.from(mContext);
-
- try {
- mUiccController = UiccController.getInstance();
- } catch(RuntimeException ex) {
- throw new RuntimeException(
- "UiccController has to be initialised before SubscriptionController init");
- }
-
- mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
-
- ServiceRegisterer subscriptionServiceRegisterer = TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer();
- if (subscriptionServiceRegisterer.get() == null) {
- subscriptionServiceRegisterer.register(this);
- mLastISubServiceRegTime = System.currentTimeMillis();
- }
-
- // clear SLOT_INDEX for all subs
- clearSlotIndexForSubInfoRecords();
-
- // Cache Setting values
- cacheSettingValues();
-
- // Initial invalidate activates caching.
- invalidateDefaultSubIdCaches();
- invalidateDefaultDataSubIdCaches();
- invalidateDefaultSmsSubIdCaches();
- invalidateActiveDataSubIdCaches();
- invalidateSlotIndexCaches();
-
- mContext.getContentResolver().registerContentObserver(
- SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri.equals(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI)) {
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
-
- SubscriptionManager subManager = SubscriptionManager.from(mContext);
- for (SubscriptionInfo subInfo : getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag())) {
- if (SubscriptionController.getInstance()
- .isActiveSubId(subInfo.getSubscriptionId())) {
- ImsManager imsManager = ImsManager.getInstance(mContext,
- subInfo.getSimSlotIndex());
- imsManager.updateImsServiceConfig();
- }
- }
- }
- }
- });
-
- if (DBG) logdl("[SubscriptionController] init by Context");
- }
-
- /**
- * Should only be triggered once.
- */
- public void notifySubInfoReady() {
- // broadcast default subId.
- sendDefaultChangedBroadcast(SubscriptionManager.getDefaultSubscriptionId());
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private boolean isSubInfoReady() {
- return SubscriptionInfoUpdater.isSubInfoInitialized();
- }
-
- /**
- * This function marks SIM_SLOT_INDEX as INVALID for all subscriptions in the database. This
- * should be done as part of initialization.
- *
- * TODO: SIM_SLOT_INDEX is based on current state and should not even be persisted in the
- * database.
- */
- private void clearSlotIndexForSubInfoRecords() {
- if (mContext == null) {
- logel("[clearSlotIndexForSubInfoRecords] TelephonyManager or mContext is null");
- return;
- }
-
- // Update all subscriptions in simInfo db with invalid slot index
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, null, null);
- }
-
- /**
- * Cache the Settings values by reading these values from Setting from disk to prevent disk I/O
- * access during the API calling. This is based on an assumption that the Settings system will
- * itself cache this value after the first read and thus only the first read after boot will
- * access the disk.
- */
- private void cacheSettingValues() {
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected void enforceModifyPhoneState(String message) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE, message);
- }
-
- private void enforceReadPrivilegedPhoneState(String message) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
- }
-
- /**
- * Returns whether the {@code callingPackage} has access to subscriber identifiers on the
- * specified {@code subId} using the provided {@code message} in any resulting
- * SecurityException.
- */
- private boolean hasSubscriberIdentifierAccess(int subId, String callingPackage,
- String callingFeatureId, String message, boolean reportFailure) {
- try {
- return TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mContext, subId,
- callingPackage, callingFeatureId, message, reportFailure);
- } catch (SecurityException e) {
- // A SecurityException indicates that the calling package is targeting at least the
- // minimum level that enforces identifier access restrictions and the new access
- // requirements are not met.
- return false;
- }
- }
-
- /**
- * Returns whether the {@code callingPackage} has access to the phone number on the specified
- * {@code subId} using the provided {@code message} in any resulting SecurityException.
- */
- private boolean hasPhoneNumberAccess(int subId, String callingPackage, String callingFeatureId,
- String message) {
- try {
- return TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(mContext, subId,
- callingPackage, callingFeatureId, message);
- } catch (SecurityException e) {
- return false;
- }
- }
-
- /**
- * Broadcast when SubscriptionInfo has changed
- * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener
- */
- private void broadcastSimInfoContentChanged() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
- mContext.sendBroadcast(intent);
- intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
- mContext.sendBroadcast(intent);
- }
-
- /**
- * Notify the changed of subscription info.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void notifySubscriptionInfoChanged() {
- TelephonyRegistryManager trm =
- (TelephonyRegistryManager)
- mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
- if (DBG) logd("notifySubscriptionInfoChanged:");
- trm.notifySubscriptionInfoChanged();
-
- // FIXME: Remove if listener technique accepted.
- broadcastSimInfoContentChanged();
-
- MultiSimSettingController.getInstance().notifySubscriptionInfoChanged();
- TelephonyMetrics metrics = TelephonyMetrics.getInstance();
- List<SubscriptionInfo> subInfos;
- synchronized (mSubInfoListLock) {
- subInfos = new ArrayList<>(mCacheActiveSubInfoList);
- }
-
- if (mOpptSubInfoListChangedDirtyBit.getAndSet(false)) {
- notifyOpportunisticSubscriptionInfoChanged();
- }
- metrics.updateActiveSubscriptionInfoList(subInfos);
- }
-
- /**
- * New SubInfoRecord instance and fill in detail info
- * @param cursor
- * @return the query result of desired SubInfoRecord
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private SubscriptionInfo getSubInfoRecord(Cursor cursor) {
- int id = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
- String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.ICC_ID));
- int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.SIM_SLOT_INDEX));
- String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.DISPLAY_NAME));
- String carrierName = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.CARRIER_NAME));
- int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.NAME_SOURCE));
- int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.HUE));
- String number = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.NUMBER));
- int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.DATA_ROAMING));
- String mcc = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MCC_STRING));
- String mnc = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MNC_STRING));
- String ehplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.EHPLMNS));
- String hplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.HPLMNS));
- String[] ehplmns = ehplmnsRaw == null ? null : ehplmnsRaw.split(",");
- String[] hplmns = hplmnsRaw == null ? null : hplmnsRaw.split(",");
-
- // cardId is the private ICCID/EID string, also known as the card string
- String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.CARD_ID));
- String countryIso = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.ISO_COUNTRY_CODE));
- // publicCardId is the publicly exposed int card ID
- int publicCardId = mUiccController.convertToPublicCardId(cardId);
- boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.IS_EMBEDDED)) == 1;
- int carrierId = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.CARRIER_ID));
- UiccAccessRule[] accessRules;
- if (isEmbedded) {
- accessRules = UiccAccessRule.decodeRules(cursor.getBlob(
- cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES)));
- } else {
- accessRules = null;
- }
- UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRules(cursor.getBlob(
- cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS)));
- boolean isOpportunistic = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.IS_OPPORTUNISTIC)) == 1;
- String groupUUID = cursor.getString(cursor.getColumnIndexOrThrow(
- SubscriptionManager.GROUP_UUID));
- int profileClass = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.PROFILE_CLASS));
- int portIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.PORT_INDEX));
- int subType = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.SUBSCRIPTION_TYPE));
- String groupOwner = getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER,
- /*defaultVal*/ null);
- boolean areUiccApplicationsEnabled = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.UICC_APPLICATIONS_ENABLED)) == 1;
-
- int usageSetting = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.USAGE_SETTING));
-
- if (VDBG) {
- String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
- String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId);
- logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:"
- + simSlotIndex + " carrierid:" + carrierId + " displayName:" + displayName
- + " nameSource:" + nameSource + " iconTint:" + iconTint
- + " dataRoaming:" + dataRoaming + " mcc:" + mcc + " mnc:" + mnc
- + " countIso:" + countryIso + " isEmbedded:"
- + isEmbedded + " accessRules:" + Arrays.toString(accessRules)
- + " carrierConfigAccessRules: " + Arrays.toString(carrierConfigAccessRules)
- + " cardId:" + cardIdToPrint + " portIndex:" + portIndex
- + " publicCardId:" + publicCardId
- + " isOpportunistic:" + isOpportunistic + " groupUUID:" + groupUUID
- + " profileClass:" + profileClass + " subscriptionType: " + subType
- + " carrierConfigAccessRules:" + carrierConfigAccessRules
- + " areUiccApplicationsEnabled: " + areUiccApplicationsEnabled
- + " usageSetting: " + usageSetting);
- }
-
- // If line1number has been set to a different number, use it instead.
- String line1Number = mTelephonyManager.getLine1Number(id);
- if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
- number = line1Number;
- }
- // FIXME(b/210771052): constructing a complete SubscriptionInfo requires a port index,
- // but the port index isn't available here. Should it actually be part of SubscriptionInfo?
- SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
- carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
- mcc, mnc, countryIso, isEmbedded, accessRules, cardId, publicCardId,
- isOpportunistic, groupUUID, /* isGroupDisabled= */ false , carrierId, profileClass,
- subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
- portIndex, usageSetting);
- info.setAssociatedPlmns(ehplmns, hplmns);
- return info;
- }
-
- private String getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal) {
- // Return defaultVal if the column doesn't exist.
- int columnIndex = cursor.getColumnIndex(column);
- return (columnIndex == -1) ? defaultVal : cursor.getString(columnIndex);
- }
-
- /**
- * Get a subscription that matches IccId.
- * @return null if there isn't a match, or subscription info if there is one.
- */
- public SubscriptionInfo getSubInfoForIccId(String iccId) {
- List<SubscriptionInfo> info = getSubInfo(
- SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null);
- if (info == null || info.size() == 0) return null;
- // Should be at most one subscription with the iccid.
- return info.get(0);
- }
-
- /**
- * Query SubInfoRecord(s) from subinfo database
- * @param selection A filter declaring which rows to return
- * @param queryKey query key content
- * @return Array list of queried result from database
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) {
- if (VDBG) logd("selection:" + selection + ", querykey: " + queryKey);
- String[] selectionArgs = null;
- if (queryKey != null) {
- selectionArgs = new String[] {queryKey.toString()};
- }
- ArrayList<SubscriptionInfo> subList = null;
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, selection, selectionArgs, null);
- try {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- SubscriptionInfo subInfo = getSubInfoRecord(cursor);
- if (subInfo != null) {
- if (subList == null) {
- subList = new ArrayList<SubscriptionInfo>();
- }
- subList.add(subInfo);
- }
- }
- } else {
- if (DBG) logd("Query fail");
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return subList;
- }
-
- /**
- * Find unused color to be set for new SubInfoRecord
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return RGB integer value of color
- */
- private int getUnusedColor(String callingPackage, String callingFeatureId) {
- List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage,
- callingFeatureId);
- colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
- int colorIdx = 0;
-
- if (availableSubInfos != null) {
- for (int i = 0; i < colorArr.length; i++) {
- int j;
- for (j = 0; j < availableSubInfos.size(); j++) {
- if (colorArr[i] == availableSubInfos.get(j).getIconTint()) {
- break;
- }
- }
- if (j == availableSubInfos.size()) {
- return colorArr[i];
- }
- }
- colorIdx = availableSubInfos.size() % colorArr.length;
- }
- return colorArr[colorIdx];
- }
-
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
- return getActiveSubscriptionInfo(subId, callingPackage, null);
- }
-
- /**
- * Get the active SubscriptionInfo with the subId key
- * @param subId The unique SubscriptionInfo key in database
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return SubscriptionInfo, maybe null if its not active
- */
- @Override
- public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage,
- String callingFeatureId) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
- callingFeatureId, "getActiveSubscriptionInfo")) {
- return null;
- }
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- List<SubscriptionInfo> subList;
- try {
- subList = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (si.getSubscriptionId() == subId) {
- if (VDBG) {
- logd("[getActiveSubscriptionInfo]+ subId=" + subId + " subInfo=" + si);
- }
- return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId,
- "getActiveSubscriptionInfo");
- }
- }
- }
- if (DBG) {
- logd("[getActiveSubscriptionInfo]- subId=" + subId
- + " subList=" + subList + " subInfo=null");
- }
-
- return null;
- }
-
- /**
- * Get a single subscription info record for a given subscription.
- *
- * @param subId the subId to query.
- *
- * @hide
- */
- public SubscriptionInfo getSubscriptionInfo(int subId) {
- synchronized (mSubInfoListLock) {
- // check cache for active subscriptions first, before querying db
- for (SubscriptionInfo subInfo : mCacheActiveSubInfoList) {
- if (subInfo.getSubscriptionId() == subId) {
- return subInfo;
- }
- }
-
- // check cache for opportunistic subscriptions too, before querying db
- for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) {
- if (subInfo.getSubscriptionId() == subId) {
- return subInfo;
- }
- }
- }
-
- List<SubscriptionInfo> subInfoList = getSubInfo(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
- if (subInfoList == null || subInfoList.isEmpty()) return null;
- return subInfoList.get(0);
- }
-
- /**
- * Get the active SubscriptionInfo associated with the iccId
- * @param iccId the IccId of SIM card
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return SubscriptionInfo, maybe null if its not active
- */
- @Override
- public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage,
- String callingFeatureId) {
- enforceReadPrivilegedPhoneState("getActiveSubscriptionInfoForIccId");
- return getActiveSubscriptionInfoForIccIdInternal(iccId);
- }
-
- /**
- * Get the active SubscriptionInfo associated with the given iccId. The caller *must* perform
- * permission checks when using this method.
- */
- private SubscriptionInfo getActiveSubscriptionInfoForIccIdInternal(String iccId) {
- if (iccId == null) {
- return null;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (iccId.equals(si.getIccId())) {
- if (DBG)
- logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
- return si;
- }
- }
- }
- if (DBG) {
- logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
- + " subList=" + subList + " subInfo=null");
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- return null;
- }
-
- /**
- * Get the active SubscriptionInfo associated with the slotIndex.
- * This API does not return details on Remote-SIM subscriptions.
- * @param slotIndex the slot which the subscription is inserted
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return SubscriptionInfo, null for Remote-SIMs or non-active slotIndex.
- */
- @Override
- public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
- String callingPackage, String callingFeatureId) {
- Phone phone = PhoneFactory.getPhone(slotIndex);
- if (phone == null) {
- if (DBG) {
- loge("[getActiveSubscriptionInfoForSimSlotIndex] no phone, slotIndex=" + slotIndex);
- }
- return null;
- }
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, phone.getSubId(), callingPackage, callingFeatureId,
- "getActiveSubscriptionInfoForSimSlotIndex")) {
- return null;
- }
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- List<SubscriptionInfo> subList;
- try {
- subList = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (si.getSimSlotIndex() == slotIndex) {
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex="
- + slotIndex + " subId=" + si);
- } else {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex="
- + slotIndex + " subId=" + conditionallyRemoveIdentifiers(si, false,
- false));
- }
- return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId,
- "getActiveSubscriptionInfoForSimSlotIndex");
- }
- }
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex
- + " subId=null");
- }
- } else {
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
- }
- }
-
-
- return null;
- }
-
- /**
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return List of all SubscriptionInfo records in database,
- * include those that were inserted before, maybe empty but not null.
- * @hide
- */
- @Override
- public List<SubscriptionInfo> getAllSubInfoList(String callingPackage,
- String callingFeatureId) {
- return getAllSubInfoList(callingPackage, callingFeatureId, false);
- }
-
- /**
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @param skipConditionallyRemoveIdentifier if set, skip removing identifier conditionally
- * @return List of all SubscriptionInfo records in database,
- * include those that were inserted before, maybe empty but not null.
- * @hide
- */
- public List<SubscriptionInfo> getAllSubInfoList(String callingPackage,
- String callingFeatureId, boolean skipConditionallyRemoveIdentifier) {
- if (VDBG) logd("[getAllSubInfoList]+");
-
- // This API isn't public, so no need to provide a valid subscription ID - we're not worried
- // about carrier-privileged callers not having access.
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getAllSubInfoList")) {
- return null;
- }
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- List<SubscriptionInfo> subList;
- try {
- subList = getSubInfo(null, null);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (subList != null && !skipConditionallyRemoveIdentifier) {
- if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
- subList = subList.stream().map(
- subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
- callingPackage, callingFeatureId, "getAllSubInfoList"))
- .collect(Collectors.toList());
- } else {
- if (VDBG) logd("[getAllSubInfoList]- no info return");
- }
- return subList;
- }
-
- private List<SubscriptionInfo> makeCacheListCopyWithLock(List<SubscriptionInfo> cacheSubList) {
- synchronized (mSubInfoListLock) {
- return new ArrayList<>(cacheSubList);
- }
- }
-
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
- return getSubscriptionInfoListFromCacheHelper(callingPackage, null,
- makeCacheListCopyWithLock(mCacheActiveSubInfoList));
- }
-
- /**
- * Get the SubInfoRecord(s) of the currently active SIM(s) - which include both local
- * and remote SIMs.
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return Array list of currently inserted SubInfoRecord(s)
- */
- @Override
- public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage,
- String callingFeatureId) {
- return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
- makeCacheListCopyWithLock(mCacheActiveSubInfoList));
- }
-
- /**
- * Refresh the cache of SubInfoRecord(s) of the currently available SIM(s) - including
- * local & remote SIMs.
- */
- @VisibleForTesting // For mockito to mock this method
- public void refreshCachedActiveSubscriptionInfoList() {
- boolean opptSubListChanged;
-
- List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
- SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
- + SubscriptionManager.SUBSCRIPTION_TYPE + "="
- + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM,
- null);
-
- synchronized (mSubInfoListLock) {
- if (activeSubscriptionInfoList != null) {
- // Log when active sub info changes.
- if (mCacheActiveSubInfoList.size() != activeSubscriptionInfoList.size()
- || !mCacheActiveSubInfoList.containsAll(activeSubscriptionInfoList)) {
- logdl("Active subscription info list changed. " + activeSubscriptionInfoList);
- }
-
- mCacheActiveSubInfoList.clear();
- activeSubscriptionInfoList.sort(SUBSCRIPTION_INFO_COMPARATOR);
- mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
- } else {
- logd("activeSubscriptionInfoList is null.");
- mCacheActiveSubInfoList.clear();
- }
- if (DBG_CACHE) {
- if (!mCacheActiveSubInfoList.isEmpty()) {
- for (SubscriptionInfo si : mCacheActiveSubInfoList) {
- logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info="
- + si);
- }
- } else {
- logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
- }
- }
- }
-
- // Refresh cached opportunistic sub list and detect whether it's changed.
- refreshCachedOpportunisticSubscriptionInfoList();
- }
-
- @Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int getActiveSubInfoCount(String callingPackage) {
- return getActiveSubInfoCount(callingPackage, null);
- }
-
- /**
- * Get the SUB count of active SUB(s)
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package.
- * @return active SIM count
- */
- @Override
- public int getActiveSubInfoCount(String callingPackage, String callingFeatureId) {
- // Let getActiveSubscriptionInfoList perform permission checks / filtering.
- List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage,
- callingFeatureId);
- if (records == null) {
- if (VDBG) logd("[getActiveSubInfoCount] records null");
- return 0;
- }
- if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
- return records.size();
- }
-
- /**
- * Get the SUB count of all SUB(s) in SubscriptoinInfo database
- * @param callingPackage The package making the IPC.
- * @param callingFeatureId The feature in the package
- * @return all SIM count in database, include what was inserted before
- */
- @Override
- public int getAllSubInfoCount(String callingPackage, String callingFeatureId) {
- if (DBG) logd("[getAllSubInfoCount]+");
-
- // This API isn't public, so no need to provide a valid subscription ID - we're not worried
- // about carrier-privileged callers not having access.
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getAllSubInfoCount")) {
- return 0;
- }
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, null, null, null);
- try {
- if (cursor != null) {
- int count = cursor.getCount();
- if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
- return count;
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
-
- return 0;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * @return the maximum number of local subscriptions this device will support at any one time.
- */
- @Override
- public int getActiveSubInfoCountMax() {
- // FIXME: This valid now but change to use TelephonyDevController in the future
- return mTelephonyManager.getSimCount();
- }
-
- @Override
- public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage,
- String callingFeatureId) {
- try {
- enforceReadPrivilegedPhoneState("getAvailableSubscriptionInfoList");
- } catch (SecurityException e) {
- try {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE, null);
- // If caller doesn't have READ_PRIVILEGED_PHONE_STATE permission but only
- // has READ_PHONE_STATE permission, log this event.
- EventLog.writeEvent(0x534e4554, "185235454", Binder.getCallingUid());
- } catch (SecurityException ex) {
- // Ignore
- }
- throw new SecurityException("Need READ_PRIVILEGED_PHONE_STATE to call "
- + " getAvailableSubscriptionInfoList");
- }
-
- // Now that all security checks pass, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- String selection = SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
- + SubscriptionManager.SUBSCRIPTION_TYPE + "="
- + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
-
- EuiccManager euiccManager =
- (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
- if (euiccManager.isEnabled()) {
- selection += " OR " + SubscriptionManager.IS_EMBEDDED + "=1";
- }
-
- // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if
- // they are in inactive slot or programmatically disabled, they are still considered
- // available. In this case we get their iccid from slot info and include their
- // subscriptionInfos.
- List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
-
- if (!iccIds.isEmpty()) {
- selection += " OR (" + getSelectionForIccIdList(iccIds.toArray(new String[0]))
- + ")";
- }
-
- List<SubscriptionInfo> subList = getSubInfo(selection, null /* queryKey */);
-
- if (subList != null) {
- subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
-
- if (VDBG) logdl("[getAvailableSubInfoList]- " + subList.size() + " infos return");
- } else {
- if (DBG) logdl("[getAvailableSubInfoList]- no info return");
- }
-
- return subList;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private List<String> getIccIdsOfInsertedPhysicalSims() {
- List<String> ret = new ArrayList<>();
- UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots();
- if (uiccSlots == null) return ret;
-
- for (UiccSlot uiccSlot : uiccSlots) {
- if (uiccSlot != null && uiccSlot.getCardState() != null
- && uiccSlot.getCardState().isCardPresent()
- && !uiccSlot.isEuicc()) {
- // Non euicc slots will have single port, so use default port index.
- String iccId = uiccSlot.getIccId(TelephonyManager.DEFAULT_PORT_INDEX);
- if (!TextUtils.isEmpty(iccId)) {
- ret.add(IccUtils.stripTrailingFs(iccId));
- }
- }
- }
-
- return ret;
- }
-
- @Override
- public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(String callingPackage) {
- EuiccManager euiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
- if (!euiccManager.isEnabled()) {
- if (DBG) {
- logdl("[getAccessibleSubInfoList] Embedded subscriptions are disabled");
- }
- return null;
- }
-
- // Verify that the given package belongs to the calling UID.
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-
- // Perform the operation as ourselves. If the caller cannot read phone state, they may still
- // have carrier privileges per the subscription metadata, so we always need to make the
- // query and then filter the results.
- final long identity = Binder.clearCallingIdentity();
- List<SubscriptionInfo> subList;
- try {
- subList = getSubInfo(SubscriptionManager.IS_EMBEDDED + "=1", null);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- if (subList == null) {
- if (DBG) logdl("[getAccessibleSubInfoList] No info returned");
- return null;
- }
-
- // Filter the list to only include subscriptions which the (restored) caller can manage.
- List<SubscriptionInfo> filteredList = subList.stream()
- .filter(subscriptionInfo ->
- subscriptionInfo.canManageSubscription(mContext, callingPackage))
- .sorted(SUBSCRIPTION_INFO_COMPARATOR)
- .collect(Collectors.toList());
- if (VDBG) {
- logdl("[getAccessibleSubInfoList] " + filteredList.size() + " infos returned");
- }
- return filteredList;
- }
-
- /**
- * Return the list of subscriptions in the database which are either:
- * <ul>
- * <li>Embedded (but see note about {@code includeNonRemovableSubscriptions}, or
- * <li>In the given list of current embedded ICCIDs (which may not yet be in the database, or
- * which may not currently be marked as embedded).
- * </ul>
- *
- * <p>NOTE: This is not accessible to external processes, so it does not need a permission
- * check. It is only intended for use by {@link SubscriptionInfoUpdater}.
- *
- * @param embeddedIccids all ICCIDs of available embedded subscriptions. This is used to surface
- * entries for profiles which had been previously deleted.
- * @param isEuiccRemovable whether the current ICCID is removable. Non-removable subscriptions
- * will only be returned if the current ICCID is not removable; otherwise, they are left
- * alone (not returned here unless in the embeddedIccids list) under the assumption that
- * they will still be accessible when the eUICC containing them is activated.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public List<SubscriptionInfo> getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
- String[] embeddedIccids, boolean isEuiccRemovable) {
- StringBuilder whereClause = new StringBuilder();
- whereClause.append("(").append(SubscriptionManager.IS_EMBEDDED).append("=1");
- if (isEuiccRemovable) {
- // Current eUICC is removable, so don't return non-removable subscriptions (which would
- // be deleted), as these are expected to still be present on a different, non-removable
- // eUICC.
- whereClause.append(" AND ").append(SubscriptionManager.IS_REMOVABLE).append("=1");
- }
- // Else, return both removable and non-removable subscriptions. This is expected to delete
- // all removable subscriptions, which is desired as they may not be accessible.
-
- whereClause.append(") OR ").append(SubscriptionManager.ICC_ID).append(" IN (");
- // ICCIDs are validated to contain only numbers when passed in, and come from a trusted
- // app, so no need to escape.
- for (int i = 0; i < embeddedIccids.length; i++) {
- if (i > 0) {
- whereClause.append(",");
- }
- whereClause.append("'").append(embeddedIccids[i]).append("'");
- }
- whereClause.append(")");
-
- List<SubscriptionInfo> list = getSubInfo(whereClause.toString(), null);
- if (list == null) {
- return Collections.emptyList();
- }
- return list;
- }
-
- @Override
- public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
- "requestEmbeddedSubscriptionInfoListRefresh");
- long token = Binder.clearCallingIdentity();
- try {
- PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, null /* callback */);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Asynchronously refresh the embedded subscription info list for the embedded card has the
- * given card id {@code cardId}.
- *
- * @param callback Optional callback to execute after the refresh completes. Must terminate
- * quickly as it will be called from SubscriptionInfoUpdater's handler thread.
- */
- // No permission check needed as this is not exposed via AIDL.
- public void requestEmbeddedSubscriptionInfoListRefresh(
- int cardId, @Nullable Runnable callback) {
- PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback);
- }
-
- /**
- * Asynchronously refresh the embedded subscription info list for the embedded card has the
- * default card id return by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
- *
- * @param callback Optional callback to execute after the refresh completes. Must terminate
- * quickly as it will be called from SubscriptionInfoUpdater's handler thread.
- */
- // No permission check needed as this is not exposed via AIDL.
- public void requestEmbeddedSubscriptionInfoListRefresh(@Nullable Runnable callback) {
- PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(
- mTelephonyManager.getCardIdForDefaultEuicc(), callback);
- }
-
- /**
- * Add a new SubInfoRecord to subinfo database if needed
- * @param iccId the IccId of the SIM card
- * @param slotIndex the slot which the SIM is inserted
- * @return 0 if success, < 0 on error.
- */
- @Override
- public int addSubInfoRecord(String iccId, int slotIndex) {
- return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
- }
-
- /**
- * Add a new subscription info record, if needed.
- * @param uniqueId This is the unique identifier for the subscription within the specific
- * subscription type.
- * @param displayName human-readable name of the device the subscription corresponds to.
- * @param slotIndex value for {@link SubscriptionManager#SIM_SLOT_INDEX}
- * @param subscriptionType the type of subscription to be added.
- * @return 0 if success, < 0 on error.
- */
- @Override
- public int addSubInfo(String uniqueId, String displayName, int slotIndex,
- int subscriptionType) {
- if (DBG) {
- String iccIdStr = uniqueId;
- if (!isSubscriptionForRemoteSim(subscriptionType)) {
- iccIdStr = SubscriptionInfo.givePrintableIccid(uniqueId);
- }
- logdl("[addSubInfoRecord]+ iccid: " + iccIdStr
- + ", slotIndex: " + slotIndex
- + ", subscriptionType: " + subscriptionType);
- }
-
- enforceModifyPhoneState("addSubInfo");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- if (uniqueId == null) {
- if (DBG) logdl("[addSubInfo]- null iccId");
- return -1;
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- String selection = SubscriptionManager.ICC_ID + "=?";
- String[] args;
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- PackageManager packageManager = mContext.getPackageManager();
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- logel("[addSubInfo] Remote SIM can only be added when FEATURE_AUTOMOTIVE"
- + " is supported");
- return -1;
- }
- selection += " AND " + SubscriptionManager.SUBSCRIPTION_TYPE + "=?";
- args = new String[]{uniqueId, Integer.toString(subscriptionType)};
- } else {
- selection += " OR " + SubscriptionManager.ICC_ID + "=?";
- args = new String[]{uniqueId, IccUtils.getDecimalSubstring(uniqueId)};
- }
- Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
- new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
- SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE,
- SubscriptionManager.ICC_ID, SubscriptionManager.CARD_ID,
- SubscriptionManager.PORT_INDEX},
- selection, args, null);
-
- boolean setDisplayName = false;
- try {
- boolean recordsDoNotExist = (cursor == null || !cursor.moveToFirst());
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- if (recordsDoNotExist) {
- // create a Subscription record
- slotIndex = SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB;
- Uri uri = insertEmptySubInfoRecord(uniqueId, displayName,
- slotIndex, subscriptionType);
- if (DBG) logd("[addSubInfoRecord] New record created: " + uri);
- } else {
- if (DBG) logdl("[addSubInfoRecord] Record already exists");
- }
- } else { // Handle Local SIM devices
- if (recordsDoNotExist) {
- setDisplayName = true;
- Uri uri = insertEmptySubInfoRecord(uniqueId, slotIndex);
- if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
- } else { // there are matching records in the database for the given ICC_ID
- int subId = cursor.getInt(0);
- int oldSimInfoId = cursor.getInt(1);
- int nameSource = cursor.getInt(2);
- String oldIccId = cursor.getString(3);
- String oldCardId = cursor.getString(4);
- int oldPortIndex = cursor.getInt(5);
- ContentValues value = new ContentValues();
-
- if (slotIndex != oldSimInfoId) {
- value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
- }
-
- if (oldIccId != null && oldIccId.length() < uniqueId.length()
- && (oldIccId.equals(IccUtils.getDecimalSubstring(uniqueId)))) {
- value.put(SubscriptionManager.ICC_ID, uniqueId);
- }
-
- UiccCard card = mUiccController.getUiccCardForPhone(slotIndex);
- if (card != null) {
- String cardId = card.getCardId();
- if (cardId != null && cardId != oldCardId) {
- value.put(SubscriptionManager.CARD_ID, cardId);
- }
- }
-
- //update portIndex for pSim
- UiccSlot slot = mUiccController.getUiccSlotForPhone(slotIndex);
- if (slot != null && !slot.isEuicc()) {
- int portIndex = slot.getPortIndexFromIccId(uniqueId);
- if (portIndex != oldPortIndex) {
- value.put(SubscriptionManager.PORT_INDEX, portIndex);
- }
- }
-
- if (value.size() > 0) {
- resolver.update(SubscriptionManager.getUriForSubscriptionId(subId),
- value, null, null);
- }
-
- if (DBG) logdl("[addSubInfoRecord] Record already exists");
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- selection = SubscriptionManager.SIM_SLOT_INDEX + "=?";
- args = new String[] {String.valueOf(slotIndex)};
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- selection = SubscriptionManager.ICC_ID + "=? AND "
- + SubscriptionManager.SUBSCRIPTION_TYPE + "=?";
- args = new String[]{uniqueId, Integer.toString(subscriptionType)};
- }
- cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
- selection, args, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- do {
- int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
- // If sSlotIndexToSubIds already has the same subId for a slotIndex/phoneId,
- // do not add it.
- if (addToSubIdList(slotIndex, subId, subscriptionType)) {
- // TODO While two subs active, if user deactivats first
- // one, need to update the default subId with second one.
-
- // FIXME: Currently we assume phoneId == slotIndex which in the future
- // may not be true, for instance with multiple subs per slot.
- // But is true at the moment.
- int subIdCountMax = getActiveSubInfoCountMax();
- int defaultSubId = getDefaultSubId();
- if (DBG) {
- logdl("[addSubInfoRecord]"
- + " sSlotIndexToSubIds.size=" + sSlotIndexToSubIds.size()
- + " slotIndex=" + slotIndex + " subId=" + subId
- + " defaultSubId=" + defaultSubId
- + " simCount=" + subIdCountMax);
- }
-
- // Set the default sub if not set or if single sim device
- if (!isSubscriptionForRemoteSim(subscriptionType)) {
- if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
- || subIdCountMax == 1) {
- logdl("setting default fallback subid to " + subId);
- setDefaultFallbackSubId(subId, subscriptionType);
- }
- // If single sim device, set this subscription as the default for
- // everything
- if (subIdCountMax == 1) {
- if (DBG) {
- logdl("[addSubInfoRecord] one sim set defaults to subId="
- + subId);
- }
- setDefaultDataSubId(subId);
- setDefaultSmsSubId(subId);
- setDefaultVoiceSubId(subId);
- }
- } else {
- updateDefaultSubIdsIfNeeded(subId, subscriptionType);
- }
- } else {
- if (DBG) {
- logdl("[addSubInfoRecord] current SubId is already known, "
- + "IGNORE");
- }
- }
- if (DBG) {
- logdl("[addSubInfoRecord] hashmap(" + slotIndex + "," + subId + ")");
- }
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- // Refresh the Cache of Active Subscription Info List. This should be done after
- // updating sSlotIndexToSubIds which is done through addToSubIdList() above.
- refreshCachedActiveSubscriptionInfoList();
-
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- notifySubscriptionInfoChanged();
- } else { // Handle Local SIM devices
- // Set Display name after sub id is set above so as to get valid simCarrierName
- int subId = getSubIdUsingPhoneId(slotIndex);
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- if (DBG) {
- logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId);
- }
- return -1;
- }
- if (setDisplayName) {
- String simCarrierName = mTelephonyManager.getSimOperatorName(subId);
- String nameToSet;
-
- if (!TextUtils.isEmpty(simCarrierName)) {
- nameToSet = simCarrierName;
- } else {
- nameToSet = "CARD " + Integer.toString(slotIndex + 1);
- }
-
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value,
- null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
- }
-
- if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubIds.size());
- }
-
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return 0;
- }
-
- private void updateDefaultSubIdsIfNeeded(int newDefault, int subscriptionType) {
- if (DBG) {
- logdl("[updateDefaultSubIdsIfNeeded] newDefault=" + newDefault
- + ", subscriptionType=" + subscriptionType);
- }
- // Set the default ot new value only if the current default is invalid.
- if (!isActiveSubscriptionId(getDefaultSubId())) {
- // current default is not valid anylonger. set a new default
- if (DBG) {
- logdl("[updateDefaultSubIdsIfNeeded] set sDefaultFallbackSubId=" + newDefault);
- }
- setDefaultFallbackSubId(newDefault, subscriptionType);
- }
-
- int value = getDefaultSmsSubId();
- if (!isActiveSubscriptionId(value)) {
- // current default is not valid. set it to the given newDefault value
- setDefaultSmsSubId(newDefault);
- }
- value = getDefaultDataSubId();
- if (!isActiveSubscriptionId(value)) {
- setDefaultDataSubId(newDefault);
- }
- value = getDefaultVoiceSubId();
- if (!isActiveSubscriptionId(value)) {
- setDefaultVoiceSubId(newDefault);
- }
- }
-
- /**
- * This method returns true if the given subId is among the list of currently active
- * subscriptions.
- */
- private boolean isActiveSubscriptionId(int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
- ArrayList<Integer> subIdList = getActiveSubIdArrayList();
- if (subIdList.isEmpty()) return false;
- return subIdList.contains(new Integer(subId));
- }
-
- /*
- * Delete subscription info record for the given device.
- * @param uniqueId This is the unique identifier for the subscription within the specific
- * subscription type.
- * @param subscriptionType the type of subscription to be removed
- * @return 0 if success, < 0 on error.
- */
- @Override
- public int removeSubInfo(String uniqueId, int subscriptionType) {
- enforceModifyPhoneState("removeSubInfo");
- if (DBG) {
- logd("[removeSubInfo] uniqueId: " + uniqueId
- + ", subscriptionType: " + subscriptionType);
- }
-
- // validate the given info - does it exist in the active subscription list
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- int slotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- synchronized (mSubInfoListLock) {
- for (SubscriptionInfo info : mCacheActiveSubInfoList) {
- if ((info.getSubscriptionType() == subscriptionType)
- && info.getIccId().equalsIgnoreCase(uniqueId)) {
- subId = info.getSubscriptionId();
- slotIndex = info.getSimSlotIndex();
- break;
- }
- }
- }
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- if (DBG) {
- logd("Invalid subscription details: subscriptionType = " + subscriptionType
- + ", uniqueId = " + uniqueId);
- }
- return -1;
- }
-
- if (DBG) logd("removing the subid : " + subId);
-
- // Now that all security checks passes, perform the operation as ourselves.
- int result = 0;
- final long identity = Binder.clearCallingIdentity();
- try {
- ContentResolver resolver = mContext.getContentResolver();
- result = resolver.delete(SubscriptionManager.CONTENT_URI,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=? AND "
- + SubscriptionManager.SUBSCRIPTION_TYPE + "=?",
- new String[]{Integer.toString(subId), Integer.toString(subscriptionType)});
- if (result != 1) {
- if (DBG) {
- logd("found NO subscription to remove with subscriptionType = "
- + subscriptionType + ", uniqueId = " + uniqueId);
- }
- return -1;
- }
- refreshCachedActiveSubscriptionInfoList();
- result = sSlotIndexToSubIds.removeFromSubIdList(slotIndex, subId);
- if (result == NO_ENTRY_FOR_SLOT_INDEX) {
- loge("sSlotIndexToSubIds has no entry for slotIndex = " + slotIndex);
- } else if (result == SUB_ID_NOT_IN_SLOT) {
- loge("sSlotIndexToSubIds has no subid: " + subId + ", in index: " + slotIndex);
- }
-
- // Since a subscription is removed, if this one is set as default for any setting,
- // set some other subid as the default.
- int newDefault = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- SubscriptionInfo info = null;
- final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (!records.isEmpty()) {
- // yes, we have more subscriptions. pick the first one.
- // FIXME do we need a policy to figure out which one is to be next default
- info = records.get(0);
- }
- updateDefaultSubIdsIfNeeded(info.getSubscriptionId(), info.getSubscriptionType());
-
- notifySubscriptionInfoChanged();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return result;
- }
-
- /**
- * Clear an subscriptionInfo to subinfo database if needed by updating slot index to invalid.
- * @param slotIndex the slot which the SIM is removed
- */
- public void clearSubInfoRecord(int slotIndex) {
- if (DBG) logdl("[clearSubInfoRecord]+ iccId:" + " slotIndex:" + slotIndex);
-
- // update simInfo db with invalid slot index
- ContentResolver resolver = mContext.getContentResolver();
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- String where = "(" + SubscriptionManager.SIM_SLOT_INDEX + "=" + slotIndex + ")";
- resolver.update(SubscriptionManager.CONTENT_URI, value, where, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- sSlotIndexToSubIds.remove(slotIndex);
- }
-
- /**
- * Insert an empty SubInfo record into the database.
- *
- * <p>NOTE: This is not accessible to external processes, so it does not need a permission
- * check. It is only intended for use by {@link SubscriptionInfoUpdater}. If there is a
- * subscription record exist with the same ICCID, no new empty record will be created.
- *
- * @return the URL of the newly created row. Return <code>null</code> if no new empty record is
- * created.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- @Nullable
- public Uri insertEmptySubInfoRecord(String iccId, int slotIndex) {
- if (getSubInfoForIccId(iccId) != null) {
- loge("insertEmptySubInfoRecord: Found existing record by ICCID. Do not create a "
- + "new empty entry.");
- return null;
- }
- return insertEmptySubInfoRecord(iccId, null, slotIndex,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
- }
-
- Uri insertEmptySubInfoRecord(String uniqueId, String displayName, int slotIndex,
- int subscriptionType) {
- ContentResolver resolver = mContext.getContentResolver();
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.ICC_ID, uniqueId);
- int color = getUnusedColor(mContext.getOpPackageName(), mContext.getAttributionTag());
- // default SIM color differs between slots
- value.put(SubscriptionManager.HUE, color);
- value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
- value.put(SubscriptionManager.CARRIER_NAME, "");
- value.put(SubscriptionManager.CARD_ID, uniqueId);
- value.put(SubscriptionManager.SUBSCRIPTION_TYPE, subscriptionType);
- if (!TextUtils.isEmpty(displayName)) {
- value.put(SubscriptionManager.DISPLAY_NAME, displayName);
- }
- if (!isSubscriptionForRemoteSim(subscriptionType)) {
- UiccCard card = mUiccController.getUiccCardForPhone(slotIndex);
- if (card != null) {
- String cardId = card.getCardId();
- if (cardId != null) {
- value.put(SubscriptionManager.CARD_ID, cardId);
- }
- }
- UiccSlot slot = mUiccController.getUiccSlotForPhone(slotIndex);
- if (slot != null) {
- value.put(SubscriptionManager.PORT_INDEX, slot.getPortIndexFromIccId(uniqueId));
- }
- }
- value.put(SubscriptionManager.ALLOWED_NETWORK_TYPES,
- "user=" + RadioAccessFamily.getRafFromNetworkType(
- RILConstants.PREFERRED_NETWORK_MODE));
-
- value.put(SubscriptionManager.USAGE_SETTING,
- SubscriptionManager.USAGE_SETTING_UNKNOWN);
-
- Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- return uri;
- }
-
- /**
- * Generate and set carrier text based on input parameters
- * @param showPlmn flag to indicate if plmn should be included in carrier text
- * @param plmn plmn to be included in carrier text
- * @param showSpn flag to indicate if spn should be included in carrier text
- * @param spn spn to be included in carrier text
- * @return true if carrier text is set, false otherwise
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
- String spn) {
- synchronized (mLock) {
- int subId = getSubIdUsingPhoneId(slotIndex);
- if (mContext.getPackageManager().resolveContentProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
- !SubscriptionManager.isValidSubscriptionId(subId)) {
- // No place to store this info. Notify registrants of the change anyway as they
- // might retrieve the SPN/PLMN text from the SST sticky broadcast.
- // TODO: This can be removed once SubscriptionController is not running on devices
- // that don't need it, such as TVs.
- if (DBG) logd("[setPlmnSpn] No valid subscription to store info");
- notifySubscriptionInfoChanged();
- return false;
- }
- String carrierText = "";
- if (showPlmn) {
- carrierText = plmn;
- if (showSpn) {
- // Need to show both plmn and spn if both are not same.
- if(!Objects.equals(spn, plmn)) {
- String separator = mContext.getString(
- com.android.internal.R.string.kg_text_message_separator).toString();
- carrierText = new StringBuilder().append(carrierText).append(separator)
- .append(spn).toString();
- }
- }
- } else if (showSpn) {
- carrierText = spn;
- }
- setCarrierText(carrierText, subId);
- return true;
- }
- }
-
- /**
- * Set carrier text by simInfo index
- * @param text new carrier text
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- private int setCarrierText(String text, int subId) {
- if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
-
- enforceModifyPhoneState("setCarrierText");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- boolean update = true;
- int result = 0;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- update = !TextUtils.equals(text, subInfo.getCarrierName());
- }
- if (update) {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.CARRIER_NAME, text);
-
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setCarrierText]: no value update");
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set SIM color tint by simInfo index
- * @param tint the tint color of the SIM
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- @Override
- public int setIconTint(int tint, int subId) {
- if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
-
- enforceModifyPhoneState("setIconTint");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.HUE, tint);
- if (DBG) logd("[setIconTint]- tint:" + tint + " set");
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * This is only for internal use and the returned priority is arbitrary. The idea is to give a
- * higher value to name source that has higher priority to override other name sources.
- * @param nameSource Source of display name
- * @return int representing the priority. Higher value means higher priority.
- */
- public static int getNameSourcePriority(@SimDisplayNameSource int nameSource) {
- int index = Arrays.asList(
- SubscriptionManager.NAME_SOURCE_CARRIER_ID,
- SubscriptionManager.NAME_SOURCE_SIM_PNN,
- SubscriptionManager.NAME_SOURCE_SIM_SPN,
- SubscriptionManager.NAME_SOURCE_CARRIER,
- SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority.
- ).indexOf(nameSource);
- return (index < 0) ? 0 : index;
- }
-
- /**
- * Validate whether the NAME_SOURCE_SIM_PNN, NAME_SOURCE_SIM_SPN and
- * NAME_SOURCE_CARRIER exist or not.
- */
- @VisibleForTesting
- public boolean isExistingNameSourceStillValid(SubscriptionInfo subInfo) {
-
- int subId = subInfo.getSubscriptionId();
- int phoneId = getPhoneId(subInfo.getSubscriptionId());
-
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone == null) {
- return true;
- }
-
- String spn;
-
- switch (subInfo.getNameSource()) {
- case SubscriptionManager.NAME_SOURCE_SIM_PNN:
- String pnn = phone.getPlmn();
- return !TextUtils.isEmpty(pnn);
- case SubscriptionManager.NAME_SOURCE_SIM_SPN:
- spn = getServiceProviderName(phoneId);
- return !TextUtils.isEmpty(spn);
- case SubscriptionManager.NAME_SOURCE_CARRIER:
- // Can not validate eSIM since it should not override with a lower priority source
- // if the name is actually coming from eSIM and not from carrier config.
- if (subInfo.isEmbedded()) {
- return true;
- }
- CarrierConfigManager configLoader =
- mContext.getSystemService(CarrierConfigManager.class);
- PersistableBundle config =
- configLoader.getConfigForSubId(subId);
- if (config == null) {
- return true;
- }
- boolean isCarrierNameOverride = config.getBoolean(
- CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
- String carrierName = config.getString(
- CarrierConfigManager.KEY_CARRIER_NAME_STRING);
- spn = getServiceProviderName(phoneId);
- return isCarrierNameOverride
- || (TextUtils.isEmpty(spn) && !TextUtils.isEmpty(carrierName));
- case SubscriptionManager.NAME_SOURCE_CARRIER_ID:
- case SubscriptionManager.NAME_SOURCE_USER_INPUT:
- return true;
- }
- return false;
- }
-
- @VisibleForTesting
- public String getServiceProviderName(int phoneId) {
- UiccProfile profile = mUiccController.getUiccProfileForPhone(phoneId);
- if (profile == null) {
- return null;
- }
- return profile.getServiceProviderName();
- }
-
- /**
- * Set display name by simInfo index with name source
- * @param displayName the display name of SIM card
- * @param subId the unique SubInfoRecord index in database
- * @param nameSource SIM display name source
- * @return the number of records updated
- */
- @Override
- public int setDisplayNameUsingSrc(String displayName, int subId,
- @SimDisplayNameSource int nameSource) {
- if (DBG) {
- logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId
- + " nameSource:" + nameSource);
- }
-
- enforceModifyPhoneState("setDisplayNameUsingSrc");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- List<SubscriptionInfo> allSubInfo = getSubInfo(null, null);
- // if there is no sub in the db, return 0 since subId does not exist in db
- if (allSubInfo == null || allSubInfo.isEmpty()) return 0;
- for (SubscriptionInfo subInfo : allSubInfo) {
- int subInfoNameSource = subInfo.getNameSource();
- boolean isHigherPriority = (getNameSourcePriority(subInfoNameSource)
- > getNameSourcePriority(nameSource));
- boolean isEqualPriorityAndName = (getNameSourcePriority(subInfoNameSource)
- == getNameSourcePriority(nameSource))
- && (TextUtils.equals(displayName, subInfo.getDisplayName()));
- if (subInfo.getSubscriptionId() == subId
- && isExistingNameSourceStillValid(subInfo)
- && (isHigherPriority || isEqualPriorityAndName)) {
- logd("Name source " + subInfoNameSource + "'s priority "
- + getNameSourcePriority(subInfoNameSource) + " is greater than "
- + "name source " + nameSource + "'s priority "
- + getNameSourcePriority(nameSource) + ", return now.");
- return 0;
- }
- }
- String nameToSet;
- if (TextUtils.isEmpty(displayName) || displayName.trim().length() == 0) {
- nameToSet = mTelephonyManager.getSimOperatorName(subId);
- if (TextUtils.isEmpty(nameToSet)) {
- if (nameSource == SubscriptionManager.NAME_SOURCE_USER_INPUT
- && SubscriptionManager.isValidSlotIndex(getSlotIndex(subId))) {
- nameToSet = "CARD " + (getSlotIndex(subId) + 1);
- } else {
- nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
- }
- }
- } else {
- nameToSet = displayName;
- }
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- if (nameSource >= SubscriptionManager.NAME_SOURCE_CARRIER_ID) {
- if (DBG) logd("Set nameSource=" + nameSource);
- value.put(SubscriptionManager.NAME_SOURCE, nameSource);
- }
- if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
-
- // Update the nickname on the eUICC chip if it's an embedded subscription.
- SubscriptionInfo sub = getSubscriptionInfo(subId);
- if (sub != null && sub.isEmbedded()) {
- // Ignore the result.
- int cardId = sub.getCardId();
- if (DBG) logd("Updating embedded sub nickname on cardId: " + cardId);
- EuiccManager euiccManager = ((EuiccManager)
- mContext.getSystemService(Context.EUICC_SERVICE)).createForCardId(cardId);
- euiccManager.updateSubscriptionNickname(subId, displayName,
- // This PendingIntent simply fulfills the requirement to pass in a callback;
- // we don't care about the result (hence 0 requestCode and no action
- // specified on the intent).
- PendingIntent.getService(
- mContext, 0 /* requestCode */, new Intent(),
- PendingIntent.FLAG_IMMUTABLE /* flags */));
- }
-
- int result = updateDatabase(value, subId, true);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set phone number by subId
- * @param number the phone number of the SIM
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- @Override
- public int setDisplayNumber(String number, int subId) {
- if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
-
- enforceModifyPhoneState("setDisplayNumber");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- int result = 0;
- int phoneId = getPhoneId(subId);
-
- if (number == null || phoneId < 0 ||
- phoneId >= mTelephonyManager.getPhoneCount()) {
- if (DBG) logd("[setDisplayNumber]- fail");
- return -1;
- }
- boolean update = true;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- update = !TextUtils.equals(subInfo.getNumber(), number);
- }
- if (update) {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.NUMBER, number);
-
- // This function had a call to update number on the SIM (Phone.setLine1Number()) but
- // that was removed as there doesn't seem to be a reason for that. If it is added
- // back, watch out for deadlocks.
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- if (DBG) logd("[setDisplayNumber]- update result :" + result);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setDisplayNumber]: no value update");
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set the EHPLMNs and HPLMNs associated with the subscription.
- */
- public void setAssociatedPlmns(String[] ehplmns, String[] hplmns, int subId) {
- if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId);
-
- validateSubId(subId);
- int phoneId = getPhoneId(subId);
-
- if (phoneId < 0 || phoneId >= mTelephonyManager.getPhoneCount()) {
- if (DBG) logd("[setAssociatedPlmns]- fail");
- return;
- }
-
- // remove trailing empty strings which will also get stripped from
- // SubscriptionInfo.getEhplmns() and SubscriptionInfo.getHplmns()
- String formattedEhplmns = ehplmns == null ? "" :
- Arrays.stream(ehplmns).filter(s -> s != null && !s.isEmpty())
- .collect(Collectors.joining(","));
- String formattedHplmns = hplmns == null ? "" :
- Arrays.stream(hplmns).filter(s -> s != null && !s.isEmpty())
- .collect(Collectors.joining(","));
- boolean noChange = false;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- noChange = (ehplmns == null && subInfo.getEhplmns().isEmpty())
- || String.join(",", subInfo.getEhplmns()).equals(formattedEhplmns);
- noChange = noChange && (hplmns == null && subInfo.getHplmns().isEmpty())
- || String.join(",", subInfo.getHplmns()).equals(formattedHplmns);
- }
- if (!noChange) {
- ContentValues value = new ContentValues(2);
- value.put(SubscriptionManager.EHPLMNS, formattedEhplmns);
- value.put(SubscriptionManager.HPLMNS, formattedHplmns);
-
- int count = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- if (DBG) logd("[setAssociatedPlmns]- update result :" + count);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId + "no value update");
- }
- }
-
- /**
- * Set data roaming by simInfo index
- * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- @Override
- public int setDataRoaming(int roaming, int subId) {
- if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
-
- enforceModifyPhoneState("setDataRoaming");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- if (roaming < 0) {
- if (DBG) logd("[setDataRoaming]- fail");
- return -1;
- }
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.DATA_ROAMING, roaming);
- if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
-
- int result = updateDatabase(value, subId, true);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set device to device status sharing preference
- * @param sharing the sharing preference to set
- * @param subId
- * @return the number of records updated
- */
- @Override
- public int setDeviceToDeviceStatusSharing(int sharing, int subId) {
- if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " subId:" + subId);
-
- enforceModifyPhoneState("setDeviceToDeviceStatusSharing");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- if (sharing < 0) {
- if (DBG) logd("[setDeviceToDeviceStatusSharing]- fail");
- return -1;
- }
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.D2D_STATUS_SHARING, sharing);
- if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " set");
-
- int result = updateDatabase(value, subId, true);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set contacts that allow device to device status sharing.
- * @param contacts contacts to set
- * @param subscriptionId
- * @return the number of records updated
- */
- @Override
- public int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId) {
- if (DBG) {
- logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
- + " subId:" + subscriptionId);
- }
-
- enforceModifyPhoneState("setDeviceToDeviceStatusSharingContacts");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subscriptionId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS, contacts);
- if (DBG) {
- logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
- + " set");
- }
-
- int result = updateDatabase(value, subscriptionId, true);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- public void syncGroupedSetting(int refSubId) {
- logd("syncGroupedSetting");
- try (Cursor cursor = mContext.getContentResolver().query(
- SubscriptionManager.CONTENT_URI, null,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
- new String[] {String.valueOf(refSubId)}, null)) {
- if (cursor == null || !cursor.moveToFirst()) {
- logd("[syncGroupedSetting] failed. Can't find refSubId " + refSubId);
- return;
- }
-
- ContentValues values = new ContentValues(GROUP_SHARING_PROPERTIES.size());
- for (String propKey : GROUP_SHARING_PROPERTIES) {
- copyDataFromCursorToContentValue(propKey, cursor, values);
- }
- updateDatabase(values, refSubId, true);
- }
- }
-
- private void copyDataFromCursorToContentValue(String propKey, Cursor cursor,
- ContentValues values) {
- int columnIndex = cursor.getColumnIndex(propKey);
- if (columnIndex == -1) {
- logd("[copyDataFromCursorToContentValue] can't find column " + propKey);
- return;
- }
-
- switch (propKey) {
- case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
- case SubscriptionManager.VT_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
- case SubscriptionManager.DATA_ROAMING:
- case SubscriptionManager.IMS_RCS_UCE_ENABLED:
- case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
- case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED:
- values.put(propKey, cursor.getInt(columnIndex));
- break;
- case SubscriptionManager.DISPLAY_NAME:
- case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
- values.put(propKey, cursor.getString(columnIndex));
- break;
- default:
- loge("[copyDataFromCursorToContentValue] invalid propKey " + propKey);
- }
- }
-
- // TODO: replace all updates with this helper method.
- private int updateDatabase(ContentValues value, int subId, boolean updateEntireGroup) {
- List<SubscriptionInfo> infoList = getSubscriptionsInGroup(getGroupUuid(subId),
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (!updateEntireGroup || infoList == null || infoList.size() == 0) {
- // Only update specified subscriptions.
- return mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- } else {
- // Update all subscriptions in the same group.
- int[] subIdList = new int[infoList.size()];
- for (int i = 0; i < infoList.size(); i++) {
- subIdList[i] = infoList.get(i).getSubscriptionId();
- }
- return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, getSelectionForSubIdList(subIdList), null);
- }
- }
-
- /**
- * Set carrier id by subId
- * @param carrierId the subscription carrier id.
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- *
- * @see TelephonyManager#getSimCarrierId()
- */
- public int setCarrierId(int carrierId, int subId) {
- if (DBG) logd("[setCarrierId]+ carrierId:" + carrierId + " subId:" + subId);
-
- enforceModifyPhoneState("setCarrierId");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- validateSubId(subId);
- int result = 0;
- boolean update = true;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- update = subInfo.getCarrierId() != carrierId;
- }
- if (update) {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.CARRIER_ID, carrierId);
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setCarrierId]: no value update");
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set MCC/MNC by subscription ID
- * @param mccMnc MCC/MNC associated with the subscription
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- public int setMccMnc(String mccMnc, int subId) {
- String mccString = mccMnc.substring(0, 3);
- String mncString = mccMnc.substring(3);
- int mcc = 0;
- int mnc = 0;
- try {
- mcc = Integer.parseInt(mccString);
- mnc = Integer.parseInt(mncString);
- } catch (NumberFormatException e) {
- loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
- }
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- // check if there are any update
- boolean update = true;
- if (subInfo != null) {
- update = (subInfo.getMcc() != mcc) || (subInfo.getMnc() != mnc)
- || !mccString.equals(subInfo.getMccString())
- || !mncString.equals(subInfo.getMncString());
- }
- int result = 0;
- if (update) {
- ContentValues value = new ContentValues(4);
- value.put(SubscriptionManager.MCC, mcc);
- value.put(SubscriptionManager.MNC, mnc);
- value.put(SubscriptionManager.MCC_STRING, mccString);
- value.put(SubscriptionManager.MNC_STRING, mncString);
-
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setMccMnc] - no values update");
- }
- return result;
- }
-
- /**
- * Scrub given IMSI on production builds.
- */
- private String scrubImsi(String imsi) {
- if (Build.IS_ENG) {
- return imsi;
- } else if (imsi != null) {
- return imsi.substring(0, Math.min(6, imsi.length())) + "...";
- } else {
- return "null";
- }
- }
-
- /**
- * Set IMSI by subscription ID
- * @param imsi IMSI (International Mobile Subscriber Identity)
- * @return the number of records updated
- */
- public int setImsi(String imsi, int subId) {
- if (DBG) logd("[setImsi]+ imsi:" + scrubImsi(imsi) + " subId:" + subId);
- boolean update = true;
- int result = 0;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- update = !TextUtils.equals(getImsiPrivileged(subId),imsi);
- }
-
- if (update) {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.IMSI, imsi);
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setImsi]: no value update");
- }
- return result;
- }
-
- /**
- * Set uicc applications being enabled or disabled.
- * @param enabled whether uicc applications are enabled or disabled.
- * @return the number of records updated
- */
- public int setUiccApplicationsEnabled(boolean enabled, int subId) {
- if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId);
-
- enforceModifyPhoneState("setUiccApplicationsEnabled");
-
- long identity = Binder.clearCallingIdentity();
- try {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifyUiccAppsEnableChanged();
- notifySubscriptionInfoChanged();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Register to change of uicc applications enablement changes.
- * @param notifyNow whether to notify target upon registration.
- */
- public void registerForUiccAppsEnabled(Handler handler, int what, Object object,
- boolean notifyNow) {
- mUiccAppsEnableChangeRegList.addUnique(handler, what, object);
- if (notifyNow) {
- handler.obtainMessage(what, object).sendToTarget();
- }
- }
-
- /**
- * Unregister to change of uicc applications enablement changes.
- */
- public void unregisterForUiccAppsEnabled(Handler handler) {
- mUiccAppsEnableChangeRegList.remove(handler);
- }
-
- private void notifyUiccAppsEnableChanged() {
- mUiccAppsEnableChangeRegList.notifyRegistrants();
- }
-
- /**
- * Get IMSI by subscription ID
- * For active subIds, this will always return the corresponding imsi
- * For inactive subIds, once they are activated once, even if they are deactivated at the time
- * of calling this function, the corresponding imsi will be returned
- * When calling this method, the permission check should have already been done to allow
- * only privileged read
- *
- * @return imsi
- */
- public String getImsiPrivileged(int subId) {
- try (Cursor cursor = mContext.getContentResolver().query(
- SubscriptionManager.CONTENT_URI, null,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
- new String[] {String.valueOf(subId)}, null)) {
- String imsi = null;
- if (cursor != null) {
- if (cursor.moveToNext()) {
- imsi = getOptionalStringFromCursor(cursor, SubscriptionManager.IMSI,
- /*defaultVal*/ null);
- }
- } else {
- logd("getImsiPrivileged: failed to retrieve imsi.");
- }
-
- return imsi;
- }
- }
-
- /**
- * Set ISO country code by subscription ID
- * @param iso iso country code associated with the subscription
- * @param subId the unique SubInfoRecord index in database
- * @return the number of records updated
- */
- public int setCountryIso(String iso, int subId) {
- if (DBG) logd("[setCountryIso]+ iso:" + iso + " subId:" + subId);
- boolean update = true;
- int result = 0;
- SubscriptionInfo subInfo = getSubscriptionInfo(subId);
- if (subInfo != null) {
- update = !TextUtils.equals(subInfo.getCountryIso(), iso);
- }
- if (update) {
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso);
-
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
- } else {
- if (DBG) logd("[setCountryIso]: no value update");
- }
- return result;
- }
-
- @Override
- public int getSlotIndex(int subId) {
- if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId);
-
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- subId = getDefaultSubId();
- }
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- if (DBG) logd("[getSlotIndex]- subId invalid");
- return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- }
-
- int size = sSlotIndexToSubIds.size();
-
- if (size == 0) {
- if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead");
- return SubscriptionManager.SIM_NOT_INSERTED;
- }
-
- for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
- int sim = entry.getKey();
- ArrayList<Integer> subs = entry.getValue();
-
- if (subs != null && subs.contains(subId)) {
- if (VDBG) logv("[getSlotIndex]- return = " + sim);
- return sim;
- }
- }
-
- if (DBG) logd("[getSlotIndex]- return fail");
- return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- }
-
- /**
- * Return the subId for specified slot Id.
- * @deprecated
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- @Deprecated
- public int[] getSubId(int slotIndex) {
- if (VDBG) printStackTrace("[getSubId]+ slotIndex=" + slotIndex);
-
- // Map default slotIndex to the current default subId.
- // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous
- // as a slot maybe used for multiple different type of "connections"
- // such as: voice, data and sms. But we're doing the best we can and using
- // getDefaultSubId which makes a best guess.
- if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
- slotIndex = getSlotIndex(getDefaultSubId());
- if (VDBG) logd("[getSubId] map default slotIndex=" + slotIndex);
- }
-
- // Check that we have a valid slotIndex or the slotIndex is for a remote SIM (remote SIM
- // uses special slot index that may be invalid otherwise)
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)
- && slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) {
- if (DBG) logd("[getSubId]- invalid slotIndex=" + slotIndex);
- return null;
- }
-
- // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate.
- int size = sSlotIndexToSubIds.size();
- if (size == 0) {
- if (VDBG) {
- logd("[getSubId]- sSlotIndexToSubIds.size == 0, return null slotIndex="
- + slotIndex);
- }
- return null;
- }
-
- // Convert ArrayList to array
- ArrayList<Integer> subIds = sSlotIndexToSubIds.getCopy(slotIndex);
- if (subIds != null && subIds.size() > 0) {
- int[] subIdArr = new int[subIds.size()];
- for (int i = 0; i < subIds.size(); i++) {
- subIdArr[i] = subIds.get(i);
- }
- if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
- return subIdArr;
- } else {
- if (DBG) logd("[getSubId]- numSubIds == 0, return null slotIndex=" + slotIndex);
- return null;
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public int getPhoneId(int subId) {
- if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
- int phoneId;
-
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- subId = getDefaultSubId();
- if (DBG) logd("[getPhoneId] asked for default subId=" + subId);
- }
-
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- if (VDBG) {
- logdl("[getPhoneId]- invalid subId return="
- + SubscriptionManager.INVALID_PHONE_INDEX);
- }
- return SubscriptionManager.INVALID_PHONE_INDEX;
- }
-
- int size = sSlotIndexToSubIds.size();
- if (size == 0) {
- phoneId = mDefaultPhoneId;
- if (VDBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
- return phoneId;
- }
-
- // FIXME: Assumes phoneId == slotIndex
- for (Entry<Integer, ArrayList<Integer>> entry: sSlotIndexToSubIds.entrySet()) {
- int sim = entry.getKey();
- ArrayList<Integer> subs = entry.getValue();
-
- if (subs != null && subs.contains(subId)) {
- if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
- return sim;
- }
- }
-
- phoneId = mDefaultPhoneId;
- if (VDBG) {
- logd("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
- }
- return phoneId;
-
- }
-
- /**
- * @return the number of records cleared
- */
- @Override
- public int clearSubInfo() {
- enforceModifyPhoneState("clearSubInfo");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- int size = sSlotIndexToSubIds.size();
-
- if (size == 0) {
- if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
- return 0;
- }
-
- sSlotIndexToSubIds.clear();
- if (DBG) logdl("[clearSubInfo]- clear size=" + size);
- return size;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void logvl(String msg) {
- logv(msg);
- mLocalLog.log(msg);
- }
-
- private void logv(String msg) {
- Rlog.v(LOG_TAG, msg);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected void logdl(String msg) {
- logd(msg);
- mLocalLog.log(msg);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void logd(String msg) {
- Rlog.d(LOG_TAG, msg);
- }
-
- private void logel(String msg) {
- loge(msg);
- mLocalLog.log(msg);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void loge(String msg) {
- Rlog.e(LOG_TAG, msg);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public int getDefaultSubId() {
- int subId;
- boolean isVoiceCapable = mTelephonyManager.isVoiceCapable();
- if (isVoiceCapable) {
- subId = getDefaultVoiceSubId();
- if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId);
- } else {
- subId = getDefaultDataSubId();
- if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
- }
- if (!isActiveSubId(subId)) {
- subId = sDefaultFallbackSubId.get();
- if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
- }
- if (VDBG) logv("[getDefaultSubId]- value = " + subId);
- return subId;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public void setDefaultSmsSubId(int subId) {
- enforceModifyPhoneState("setDefaultSmsSubId");
-
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
- }
- if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId);
- setGlobalSetting(Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
- broadcastDefaultSmsSubIdChanged(subId);
- }
-
- private void broadcastDefaultSmsSubIdChanged(int subId) {
- // Broadcast an Intent for default sms sub change
- if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
- Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- SubscriptionManager.putSubscriptionIdExtra(intent, subId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public int getDefaultSmsSubId() {
- int subId = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
- return subId;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public void setDefaultVoiceSubId(int subId) {
- enforceModifyPhoneState("setDefaultVoiceSubId");
-
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
- }
-
- logdl("[setDefaultVoiceSubId] subId=" + subId);
-
- int previousDefaultSub = getDefaultSubId();
-
- setGlobalSetting(Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
- broadcastDefaultVoiceSubIdChanged(subId);
-
- PhoneAccountHandle newHandle =
- subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
- ? null : mTelephonyManager.getPhoneAccountHandleForSubscriptionId(
- subId);
-
- TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
- PhoneAccountHandle currentHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
- logd("[setDefaultVoiceSubId] current phoneAccountHandle=" + currentHandle);
-
- if (!Objects.equals(currentHandle, newHandle)) {
- telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle);
- logd("[setDefaultVoiceSubId] change to phoneAccountHandle=" + newHandle);
- } else {
- logd("[setDefaultVoiceSubId] default phoneAccountHandle not changed.");
- }
-
- if (previousDefaultSub != getDefaultSubId()) {
- sendDefaultChangedBroadcast(getDefaultSubId());
- logd(String.format("[setDefaultVoiceSubId] change to subId=%d", getDefaultSubId()));
- } else {
- logd(String.format("[setDefaultVoiceSubId] default subId not changed. subId=%d",
- previousDefaultSub));
- }
- }
-
- /**
- * Broadcast intent of ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public void broadcastDefaultVoiceSubIdChanged(int subId) {
- // Broadcast an Intent for default voice sub change
- if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
- Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- SubscriptionManager.putSubscriptionIdExtra(intent, subId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public int getDefaultVoiceSubId() {
- int subId = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
- return subId;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public int getDefaultDataSubId() {
- int subId = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (VDBG) logd("[getDefaultDataSubId] subId=" + subId);
- return subId;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Override
- public void setDefaultDataSubId(int subId) {
- enforceModifyPhoneState("setDefaultDataSubId");
-
- final long identity = Binder.clearCallingIdentity();
- try {
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
- }
-
- ProxyController proxyController = ProxyController.getInstance();
- int len = TelephonyManager.from(mContext).getActiveModemCount();
- logdl("[setDefaultDataSubId] num phones=" + len + ", subId=" + subId);
-
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- // Only re-map modems if the new default data sub is valid
- RadioAccessFamily[] rafs = new RadioAccessFamily[len];
- boolean atLeastOneMatch = false;
- for (int phoneId = 0; phoneId < len; phoneId++) {
- Phone phone = PhoneFactory.getPhone(phoneId);
- int raf;
- int id = phone.getSubId();
- if (id == subId) {
- // TODO Handle the general case of N modems and M subscriptions.
- raf = proxyController.getMaxRafSupported();
- atLeastOneMatch = true;
- } else {
- // TODO Handle the general case of N modems and M subscriptions.
- raf = proxyController.getMinRafSupported();
- }
- logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF="
- + raf);
- rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
- }
- if (atLeastOneMatch) {
- proxyController.setRadioCapability(rafs);
- } else {
- if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating.");
- }
- }
-
- int previousDefaultSub = getDefaultSubId();
- setGlobalSetting(Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
- MultiSimSettingController.getInstance().notifyDefaultDataSubChanged();
- broadcastDefaultDataSubIdChanged(subId);
- if (previousDefaultSub != getDefaultSubId()) {
- sendDefaultChangedBroadcast(getDefaultSubId());
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void broadcastDefaultDataSubIdChanged(int subId) {
- // Broadcast an Intent for default data sub change
- if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
- Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- SubscriptionManager.putSubscriptionIdExtra(intent, subId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /* Sets the default subscription. If only one sub is active that
- * sub is set as default subId. If two or more sub's are active
- * the first sub is set as default subscription
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected void setDefaultFallbackSubId(int subId, int subscriptionType) {
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
- }
- if (DBG) {
- logdl("[setDefaultFallbackSubId] subId=" + subId + ", subscriptionType="
- + subscriptionType);
- }
- int previousDefaultSub = getDefaultSubId();
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- sDefaultFallbackSubId.set(subId);
- return;
- }
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- int phoneId = getPhoneId(subId);
- if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount()
- || mTelephonyManager.getSimCount() == 1)) {
- if (DBG) logdl("[setDefaultFallbackSubId] set sDefaultFallbackSubId=" + subId);
- sDefaultFallbackSubId.set(subId);
- // Update MCC MNC device configuration information
- String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
- MccTable.updateMccMncConfiguration(mContext, defaultMccMnc);
- } else {
- if (DBG) {
- logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId
- + " subId=" + subId);
- }
- }
- }
- if (previousDefaultSub != getDefaultSubId()) {
- sendDefaultChangedBroadcast(getDefaultSubId());
- }
- }
-
- public void sendDefaultChangedBroadcast(int subId) {
- // Broadcast an Intent for default sub change
- int phoneId = SubscriptionManager.getPhoneId(subId);
- Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
- if (DBG) {
- logdl("[sendDefaultChangedBroadcast] broadcast default subId changed phoneId="
- + phoneId + " subId=" + subId);
- }
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /**
- * Whether a subscription is opportunistic or not.
- */
- public boolean isOpportunistic(int subId) {
- SubscriptionInfo info = getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
- mContext.getAttributionTag());
- return (info != null) && info.isOpportunistic();
- }
-
- // FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true
- // when there are multiple subscriptions per sim and probably for other reasons.
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int getSubIdUsingPhoneId(int phoneId) {
- int[] subIds = getSubId(phoneId);
- if (subIds == null || subIds.length == 0) {
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
- return subIds[0];
- }
-
- /** Must be public for access from instrumentation tests. */
- @VisibleForTesting
- public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex) {
- if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]+ slotIndex:" + slotIndex);
- if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
- slotIndex = getSlotIndex(getDefaultSubId());
- }
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- invalid slotIndex");
- return null;
- }
-
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
- new String[]{String.valueOf(slotIndex)}, null);
- ArrayList<SubscriptionInfo> subList = null;
- try {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- SubscriptionInfo subInfo = getSubInfoRecord(cursor);
- if (subInfo != null) {
- if (subList == null) {
- subList = new ArrayList<SubscriptionInfo>();
- }
- subList.add(subInfo);
- }
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
-
- return subList;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private void validateSubId(int subId) {
- if (DBG) logd("validateSubId subId: " + subId);
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- throw new IllegalArgumentException("Invalid sub id passed as parameter");
- } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- throw new IllegalArgumentException("Default sub id passed as parameter");
- }
- }
-
- private synchronized ArrayList<Integer> getActiveSubIdArrayList() {
- // Clone the sub id list so it can't change out from under us while iterating
- List<Entry<Integer, ArrayList<Integer>>> simInfoList =
- new ArrayList<>(sSlotIndexToSubIds.entrySet());
-
- // Put the set of sub ids in slot index order
- Collections.sort(simInfoList, (x, y) -> x.getKey().compareTo(y.getKey()));
-
- // Collect the sub ids for each slot in turn
- ArrayList<Integer> allSubs = new ArrayList<>();
- for (Entry<Integer, ArrayList<Integer>> slot : simInfoList) {
- allSubs.addAll(slot.getValue());
- }
- return allSubs;
- }
-
- private boolean isSubscriptionVisible(int subId) {
- synchronized (mSubInfoListLock) {
- for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
- if (info.getSubscriptionId() == subId) {
- // If group UUID is null, it's stand alone opportunistic profile. So it's
- // visible. Otherwise, it's bundled opportunistic profile, and is not visible.
- return info.getGroupUuid() == null;
- }
- }
- }
-
- return true;
- }
-
- /**
- * @return the list of subId's that are active, is never null but the length maybe 0.
- */
- @Override
- public int[] getActiveSubIdList(boolean visibleOnly) {
- enforceReadPrivilegedPhoneState("getActiveSubIdList");
-
- final long token = Binder.clearCallingIdentity();
- try {
- List<Integer> allSubs = getActiveSubIdArrayList();
-
- if (visibleOnly) {
- // Grouped opportunistic subscriptions should be hidden.
- allSubs = allSubs.stream().filter(subId -> isSubscriptionVisible(subId))
- .collect(Collectors.toList());
- }
-
- int[] subIdArr = new int[allSubs.size()];
- int i = 0;
- for (int sub : allSubs) {
- subIdArr[i] = sub;
- i++;
- }
-
- if (VDBG) {
- logdl("[getActiveSubIdList] allSubs=" + allSubs + " subIdArr.length="
- + subIdArr.length);
- }
- return subIdArr;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
- callingFeatureId, "isActiveSubId")) {
- throw new SecurityException("Requires READ_PHONE_STATE permission.");
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- return isActiveSubId(subId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Deprecated // This should be moved into isActiveSubId(int, String)
- public boolean isActiveSubId(int subId) {
- boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
- && getActiveSubIdArrayList().contains(subId);
-
- if (VDBG) logdl("[isActiveSubId]- " + retVal);
- return retVal;
- }
-
- /**
- * Get the SIM state for the slot index.
- * For Remote-SIMs, this method returns {@link #IccCardConstants.State.UNKNOWN}
- * @return SIM state as the ordinal of {@See IccCardConstants.State}
- */
- @Override
- public int getSimStateForSlotIndex(int slotIndex) {
- State simState;
- String err;
- if (slotIndex < 0) {
- simState = IccCardConstants.State.UNKNOWN;
- err = "invalid slotIndex";
- } else {
- Phone phone = null;
- try {
- phone = PhoneFactory.getPhone(slotIndex);
- } catch (IllegalStateException e) {
- // ignore
- }
- if (phone == null) {
- simState = IccCardConstants.State.UNKNOWN;
- err = "phone == null";
- } else {
- IccCard icc = phone.getIccCard();
- if (icc == null) {
- simState = IccCardConstants.State.UNKNOWN;
- err = "icc == null";
- } else {
- simState = icc.getState();
- err = "";
- }
- }
- }
- if (VDBG) {
- logd("getSimStateForSlotIndex: " + err + " simState=" + simState
- + " ordinal=" + simState.ordinal() + " slotIndex=" + slotIndex);
- }
- return simState.ordinal();
- }
-
- /**
- * Store properties associated with SubscriptionInfo in database
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in database associated with SubscriptionInfo
- * @param propValue Value to store in DB for particular subId & column name
- *
- * @return number of rows updated.
- * @hide
- */
- @Override
- public int setSubscriptionProperty(int subId, String propKey, String propValue) {
- enforceModifyPhoneState("setSubscriptionProperty");
- final long token = Binder.clearCallingIdentity();
-
- try {
- validateSubId(subId);
- ContentResolver resolver = mContext.getContentResolver();
- int result = setSubscriptionPropertyIntoContentResolver(
- subId, propKey, propValue, resolver);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- return result;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private int setSubscriptionPropertyIntoContentResolver(
- int subId, String propKey, String propValue, ContentResolver resolver) {
- ContentValues value = new ContentValues();
- boolean updateEntireGroup = GROUP_SHARING_PROPERTIES.contains(propKey);
- switch (propKey) {
- case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
- case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
- case SubscriptionManager.CB_AMBER_ALERT:
- case SubscriptionManager.CB_EMERGENCY_ALERT:
- case SubscriptionManager.CB_ALERT_SOUND_DURATION:
- case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
- case SubscriptionManager.CB_ALERT_VIBRATE:
- case SubscriptionManager.CB_ALERT_SPEECH:
- case SubscriptionManager.CB_ETWS_TEST_ALERT:
- case SubscriptionManager.CB_CHANNEL_50_ALERT:
- case SubscriptionManager.CB_CMAS_TEST_ALERT:
- case SubscriptionManager.CB_OPT_OUT_DIALOG:
- case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
- case SubscriptionManager.IS_OPPORTUNISTIC:
- case SubscriptionManager.VT_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
- case SubscriptionManager.IMS_RCS_UCE_ENABLED:
- case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
- case SubscriptionManager.VOIMS_OPT_IN_STATUS:
- case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED:
- case SubscriptionManager.USAGE_SETTING:
- value.put(propKey, Integer.parseInt(propValue));
- break;
- case SubscriptionManager.ALLOWED_NETWORK_TYPES:
- case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER:
- case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS:
- value.put(propKey, propValue);
- break;
- default:
- if (DBG) logd("Invalid column name");
- break;
- }
-
- return updateDatabase(value, subId, updateEntireGroup);
- }
-
- /**
- * Get properties associated with SubscriptionInfo from database
- *
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @return Value associated with subId and propKey column in database
- */
- @Override
- public String getSubscriptionProperty(int subId, String propKey, String callingPackage,
- String callingFeatureId) {
- switch (propKey) {
- case SubscriptionManager.GROUP_UUID:
- if (mContext.checkCallingOrSelfPermission(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE) != PERMISSION_GRANTED) {
- EventLog.writeEvent(0x534e4554, "213457638", Binder.getCallingUid());
- return null;
- }
- break;
- default:
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
- callingPackage, callingFeatureId, "getSubscriptionProperty")) {
- return null;
- }
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- return getSubscriptionProperty(subId, propKey);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Get properties associated with SubscriptionInfo from database. Note this is the version
- * without permission check for telephony internal use only.
- *
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @return Value associated with subId and propKey column in database
- */
- public String getSubscriptionProperty(int subId, String propKey) {
- String resultValue = null;
- try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- new String[]{propKey},
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
- new String[]{subId + ""}, null)) {
- if (cursor != null) {
- if (cursor.moveToFirst()) {
- switch (propKey) {
- case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
- case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
- case SubscriptionManager.CB_AMBER_ALERT:
- case SubscriptionManager.CB_EMERGENCY_ALERT:
- case SubscriptionManager.CB_ALERT_SOUND_DURATION:
- case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
- case SubscriptionManager.CB_ALERT_VIBRATE:
- case SubscriptionManager.CB_ALERT_SPEECH:
- case SubscriptionManager.CB_ETWS_TEST_ALERT:
- case SubscriptionManager.CB_CHANNEL_50_ALERT:
- case SubscriptionManager.CB_CMAS_TEST_ALERT:
- case SubscriptionManager.CB_OPT_OUT_DIALOG:
- case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
- case SubscriptionManager.VT_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_ENABLED:
- case SubscriptionManager.WFC_IMS_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_MODE:
- case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
- case SubscriptionManager.IMS_RCS_UCE_ENABLED:
- case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
- case SubscriptionManager.IS_OPPORTUNISTIC:
- case SubscriptionManager.GROUP_UUID:
- case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
- case SubscriptionManager.ALLOWED_NETWORK_TYPES:
- case SubscriptionManager.D2D_STATUS_SHARING:
- case SubscriptionManager.VOIMS_OPT_IN_STATUS:
- case SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS:
- case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED:
- case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER:
- case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS:
- case SubscriptionManager.USAGE_SETTING:
- resultValue = cursor.getString(0);
- break;
- default:
- if(DBG) logd("Invalid column name");
- break;
- }
- } else {
- if(DBG) logd("Valid row not present in db");
- }
- } else {
- if(DBG) logd("Query failed");
- }
- }
-
- if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
- return resultValue;
- }
-
- private void printStackTrace(String msg) {
- RuntimeException re = new RuntimeException();
- logd("StackTrace - " + msg);
- StackTraceElement[] st = re.getStackTrace();
- boolean first = true;
- for (StackTraceElement ste : st) {
- if (first) {
- first = false;
- } else {
- logd(ste.toString());
- }
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
- "Requires DUMP");
- final long token = Binder.clearCallingIdentity();
- try {
- pw.println("SubscriptionController:");
- pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime);
- pw.println(" defaultSubId=" + getDefaultSubId());
- pw.println(" defaultDataSubId=" + getDefaultDataSubId());
- pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
- pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
-
- pw.println(" defaultDataPhoneId=" + SubscriptionManager
- .from(mContext).getDefaultDataPhoneId());
- pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
- pw.println(" defaultSmsPhoneId=" + SubscriptionManager
- .from(mContext).getDefaultSmsPhoneId());
- pw.flush();
-
- for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
- pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subIds=" + entry);
- }
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
-
- List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (sirl != null) {
- pw.println(" ActiveSubInfoList:");
- for (SubscriptionInfo entry : sirl) {
- pw.println(" " + entry.toString());
- }
- } else {
- pw.println(" ActiveSubInfoList: is null");
- }
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
-
- sirl = getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag());
- if (sirl != null) {
- pw.println(" AllSubInfoList:");
- for (SubscriptionInfo entry : sirl) {
- pw.println(" " + entry.toString());
- }
- } else {
- pw.println(" AllSubInfoList: is null");
- }
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
-
- mLocalLog.dump(fd, pw, args);
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
- pw.flush();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Migrating Ims settings from global setting to subscription DB, if not already done.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public void migrateImsSettings() {
- migrateImsSettingHelper(
- Settings.Global.ENHANCED_4G_MODE_ENABLED,
- SubscriptionManager.ENHANCED_4G_MODE_ENABLED);
- migrateImsSettingHelper(
- Settings.Global.VT_IMS_ENABLED,
- SubscriptionManager.VT_IMS_ENABLED);
- migrateImsSettingHelper(
- Settings.Global.WFC_IMS_ENABLED,
- SubscriptionManager.WFC_IMS_ENABLED);
- migrateImsSettingHelper(
- Settings.Global.WFC_IMS_MODE,
- SubscriptionManager.WFC_IMS_MODE);
- migrateImsSettingHelper(
- Settings.Global.WFC_IMS_ROAMING_MODE,
- SubscriptionManager.WFC_IMS_ROAMING_MODE);
- migrateImsSettingHelper(
- Settings.Global.WFC_IMS_ROAMING_ENABLED,
- SubscriptionManager.WFC_IMS_ROAMING_ENABLED);
- }
-
- private void migrateImsSettingHelper(String settingGlobal, String subscriptionProperty) {
- ContentResolver resolver = mContext.getContentResolver();
- int defaultSubId = getDefaultVoiceSubId();
- if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return;
- }
- try {
- int prevSetting = Settings.Global.getInt(resolver, settingGlobal);
-
- if (prevSetting != DEPRECATED_SETTING) {
- // Write previous setting into Subscription DB.
- setSubscriptionPropertyIntoContentResolver(defaultSubId, subscriptionProperty,
- Integer.toString(prevSetting), resolver);
- // Write global setting value with DEPRECATED_SETTING making sure
- // migration only happen once.
- Settings.Global.putInt(resolver, settingGlobal, DEPRECATED_SETTING);
- }
- } catch (Settings.SettingNotFoundException e) {
- }
- }
-
- /**
- * Set whether a subscription is opportunistic.
- *
- * Throws SecurityException if doesn't have required permission.
- *
- * @param opportunistic whether it’s opportunistic subscription.
- * @param subId the unique SubscriptionInfo index in database
- * @param callingPackage The package making the IPC.
- * @return the number of records updated
- */
- @Override
- public int setOpportunistic(boolean opportunistic, int subId, String callingPackage) {
- try {
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
- mContext, subId, callingPackage);
- } catch (SecurityException e) {
- // The subscription may be inactive eSIM profile. If so, check the access rule in
- // database.
- enforceCarrierPrivilegeOnInactiveSub(subId, callingPackage,
- "Caller requires permission on sub " + subId);
- }
-
- long token = Binder.clearCallingIdentity();
- try {
- int ret = setSubscriptionProperty(subId, SubscriptionManager.IS_OPPORTUNISTIC,
- String.valueOf(opportunistic ? 1 : 0));
-
- if (ret != 0) notifySubscriptionInfoChanged();
-
- return ret;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Get subscription info from database, and check whether caller has carrier privilege
- * permission with it. If checking fails, throws SecurityException.
- */
- private void enforceCarrierPrivilegeOnInactiveSub(int subId, String callingPackage,
- String message) {
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-
- SubscriptionManager subManager = (SubscriptionManager)
- mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- List<SubscriptionInfo> subInfo = getSubInfo(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
-
- try {
- if (!isActiveSubId(subId) && subInfo != null && subInfo.size() == 1
- && subManager.canManageSubscription(subInfo.get(0), callingPackage)) {
- return;
- }
- throw new SecurityException(message);
- } catch (IllegalArgumentException e) {
- // canManageSubscription will throw IllegalArgumentException if sub is not embedded
- // or package name is unknown. In this case, we also see it as permission check failure
- // and throw a SecurityException.
- throw new SecurityException(message);
- }
- }
-
- @Override
- public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
- ISetOpportunisticDataCallback callback) {
- enforceModifyPhoneState("setPreferredDataSubscriptionId");
- final long token = Binder.clearCallingIdentity();
-
- try {
- PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
- if (phoneSwitcher == null) {
- logd("Set preferred data sub: phoneSwitcher is null.");
- AnomalyReporter.reportAnomaly(
- UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
- "Set preferred data sub: phoneSwitcher is null.");
- if (callback != null) {
- try {
- callback.onComplete(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
- } catch (RemoteException exception) {
- logd("RemoteException " + exception);
- }
- }
- return;
- }
-
- phoneSwitcher.trySetOpportunisticDataSubscription(subId, needValidation, callback);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public int getPreferredDataSubscriptionId() {
- enforceReadPrivilegedPhoneState("getPreferredDataSubscriptionId");
- final long token = Binder.clearCallingIdentity();
-
- try {
- PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
- if (phoneSwitcher == null) {
- AnomalyReporter.reportAnomaly(
- UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
- "Get preferred data sub: phoneSwitcher is null.");
- return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- }
-
- return phoneSwitcher.getOpportunisticDataSubscriptionId();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage,
- String callingFeatureId) {
- return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
- makeCacheListCopyWithLock(mCacheOpportunisticSubInfoList));
- }
-
- /**
- * Inform SubscriptionManager that subscriptions in the list are bundled
- * as a group. Typically it's a primary subscription and an opportunistic
- * subscription. It should only affect multi-SIM scenarios where primary
- * and opportunistic subscriptions can be activated together.
- * Being in the same group means they might be activated or deactivated
- * together, some of them may be invisible to the users, etc.
- *
- * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or had carrier privilege permission on the subscriptions:
- * {@link TelephonyManager#hasCarrierPrivileges(int)} or
- * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)}
- *
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
- *
- * @param subIdList list of subId that will be in the same group
- * @return groupUUID a UUID assigned to the subscription group. It returns
- * null if fails.
- *
- */
- @Override
- public ParcelUuid createSubscriptionGroup(int[] subIdList, String callingPackage) {
- if (subIdList == null || subIdList.length == 0) {
- throw new IllegalArgumentException("Invalid subIdList " + subIdList);
- }
-
- // Makes sure calling package matches caller UID.
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- // If it doesn't have modify phone state permission, or carrier privilege permission,
- // a SecurityException will be thrown.
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- != PERMISSION_GRANTED && !checkCarrierPrivilegeOnSubList(
- subIdList, callingPackage)) {
- throw new SecurityException("CreateSubscriptionGroup needs MODIFY_PHONE_STATE or"
- + " carrier privilege permission on all specified subscriptions");
- }
-
- long identity = Binder.clearCallingIdentity();
-
- try {
- // Generate a UUID.
- ParcelUuid groupUUID = new ParcelUuid(UUID.randomUUID());
-
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.GROUP_UUID, groupUUID.toString());
- value.put(SubscriptionManager.GROUP_OWNER, callingPackage);
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, getSelectionForSubIdList(subIdList), null);
-
- if (DBG) logdl("createSubscriptionGroup update DB result: " + result);
-
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUUID);
-
- return groupUUID;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private String getOwnerPackageOfSubGroup(ParcelUuid groupUuid) {
- if (groupUuid == null) return null;
-
- List<SubscriptionInfo> infoList = getSubInfo(SubscriptionManager.GROUP_UUID
- + "=\'" + groupUuid.toString() + "\'", null);
-
- return ArrayUtils.isEmpty(infoList) ? null : infoList.get(0).getGroupOwner();
- }
-
- /**
- * @param groupUuid a UUID assigned to the subscription group.
- * @param callingPackage the package making the IPC.
- * @return if callingPackage has carrier privilege on sublist.
- *
- */
- public boolean canPackageManageGroup(ParcelUuid groupUuid, String callingPackage) {
- if (groupUuid == null) {
- throw new IllegalArgumentException("Invalid groupUuid");
- }
-
- if (TextUtils.isEmpty(callingPackage)) {
- throw new IllegalArgumentException("Empty callingPackage");
- }
-
- List<SubscriptionInfo> infoList;
-
- // Getting all subscriptions in the group.
- long identity = Binder.clearCallingIdentity();
- try {
- infoList = getSubInfo(SubscriptionManager.GROUP_UUID
- + "=\'" + groupUuid.toString() + "\'", null);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // If the group does not exist, then by default the UUID is up for grabs so no need to
- // restrict management of a group (that someone may be attempting to create).
- if (ArrayUtils.isEmpty(infoList)) {
- return true;
- }
-
- // If the calling package is the group owner, skip carrier permission check and return
- // true as it was done before.
- if (callingPackage.equals(infoList.get(0).getGroupOwner())) return true;
-
- // Check carrier privilege for all subscriptions in the group.
- int[] subIdArray = infoList.stream().mapToInt(info -> info.getSubscriptionId())
- .toArray();
- return (checkCarrierPrivilegeOnSubList(subIdArray, callingPackage));
- }
-
- private int updateGroupOwner(ParcelUuid groupUuid, String groupOwner) {
- // If the existing group owner is different from current caller, make caller the new
- // owner of all subscriptions in group.
- // This is for use-case of:
- // 1) Both package1 and package2 has permission (MODIFY_PHONE_STATE or carrier
- // privilege permission) of all related subscriptions.
- // 2) Package 1 created a group.
- // 3) Package 2 wants to add a subscription into it.
- // Step 3 should be granted as all operations are permission based. Which means as
- // long as the package passes the permission check, it can modify the subscription
- // and the group. And package 2 becomes the new group owner as it's the last to pass
- // permission checks on all members.
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.GROUP_OWNER, groupOwner);
- return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, SubscriptionManager.GROUP_UUID + "=\"" + groupUuid + "\"", null);
- }
-
- @Override
- public void addSubscriptionsIntoGroup(int[] subIdList, ParcelUuid groupUuid,
- String callingPackage) {
- if (subIdList == null || subIdList.length == 0) {
- throw new IllegalArgumentException("Invalid subId list");
- }
-
- if (groupUuid == null || groupUuid.equals(INVALID_GROUP_UUID)) {
- throw new IllegalArgumentException("Invalid groupUuid");
- }
-
- // Makes sure calling package matches caller UID.
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- // If it doesn't have modify phone state permission, or carrier privilege permission,
- // a SecurityException will be thrown.
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
- && canPackageManageGroup(groupUuid, callingPackage))) {
- throw new SecurityException("Requires MODIFY_PHONE_STATE or carrier privilege"
- + " permissions on subscriptions and the group.");
- }
-
- long identity = Binder.clearCallingIdentity();
-
- try {
- if (DBG) {
- logdl("addSubscriptionsIntoGroup sub list "
- + Arrays.toString(subIdList) + " into group " + groupUuid);
- }
-
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, getSelectionForSubIdList(subIdList), null);
-
- if (DBG) logdl("addSubscriptionsIntoGroup update DB result: " + result);
-
- if (result > 0) {
- updateGroupOwner(groupUuid, callingPackage);
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Remove a list of subscriptions from their subscription group.
- * See {@link SubscriptionManager#createSubscriptionGroup(List<Integer>)} for more details.
- *
- * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or had carrier privilege permission on the subscriptions:
- * {@link TelephonyManager#hasCarrierPrivileges()} or
- * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)}
- *
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong
- * the specified group.
- *
- * @param subIdList list of subId that need removing from their groups.
- *
- */
- public void removeSubscriptionsFromGroup(int[] subIdList, ParcelUuid groupUuid,
- String callingPackage) {
- if (subIdList == null || subIdList.length == 0) {
- return;
- }
-
- // Makes sure calling package matches caller UID.
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- // If it doesn't have modify phone state permission, or carrier privilege permission,
- // a SecurityException will be thrown. If it's due to invalid parameter or internal state,
- // it will return null.
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
- && canPackageManageGroup(groupUuid, callingPackage))) {
- throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or"
- + " carrier privilege permission on all specified subscriptions");
- }
-
- long identity = Binder.clearCallingIdentity();
-
- try {
- List<SubscriptionInfo> subInfoList = getSubInfo(getSelectionForSubIdList(subIdList),
- null);
- for (SubscriptionInfo info : subInfoList) {
- if (!groupUuid.equals(info.getGroupUuid())) {
- throw new IllegalArgumentException("Subscription " + info.getSubscriptionId()
- + " doesn't belong to group " + groupUuid);
- }
- }
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.GROUP_UUID, (String) null);
- value.put(SubscriptionManager.GROUP_OWNER, (String) null);
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, getSelectionForSubIdList(subIdList), null);
-
- if (DBG) logdl("removeSubscriptionsFromGroup update DB result: " + result);
-
- if (result > 0) {
- updateGroupOwner(groupUuid, callingPackage);
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Helper function to check if the caller has carrier privilege permissions on a list of subId.
- * The check can either be processed against access rules on currently active SIM cards, or
- * the access rules we keep in our database for currently inactive eSIMs.
- *
- * @throws IllegalArgumentException if the some subId is invalid or doesn't exist.
- *
- * @return true if checking passes on all subId, false otherwise.
- */
- private boolean checkCarrierPrivilegeOnSubList(int[] subIdList, String callingPackage) {
- // Check carrier privilege permission on active subscriptions first.
- // If it fails, they could be inactive. So keep them in a HashSet and later check
- // access rules in our database.
- Set<Integer> checkSubList = new HashSet<>();
- for (int subId : subIdList) {
- if (isActiveSubId(subId)) {
- if (!mTelephonyManager.hasCarrierPrivileges(subId)) {
- return false;
- }
- } else {
- checkSubList.add(subId);
- }
- }
-
- if (checkSubList.isEmpty()) {
- return true;
- }
-
- long identity = Binder.clearCallingIdentity();
-
- try {
- // Check access rules for each sub info.
- SubscriptionManager subscriptionManager = (SubscriptionManager)
- mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- List<SubscriptionInfo> subInfoList = getSubInfo(
- getSelectionForSubIdList(subIdList), null);
-
- // Didn't find all the subscriptions specified in subIdList.
- if (subInfoList == null || subInfoList.size() != subIdList.length) {
- throw new IllegalArgumentException("Invalid subInfoList.");
- }
-
- for (SubscriptionInfo subInfo : subInfoList) {
- if (checkSubList.contains(subInfo.getSubscriptionId())) {
- if (subInfo.isEmbedded() && subscriptionManager.canManageSubscription(
- subInfo, callingPackage)) {
- checkSubList.remove(subInfo.getSubscriptionId());
- } else {
- return false;
- }
- }
- }
-
- return checkSubList.isEmpty();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Helper function to create selection argument of a list of subId.
- * The result should be: "in (subId1, subId2, ...)".
- */
- public static String getSelectionForSubIdList(int[] subId) {
- StringBuilder selection = new StringBuilder();
- selection.append(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID);
- selection.append(" IN (");
- for (int i = 0; i < subId.length - 1; i++) {
- selection.append(subId[i] + ", ");
- }
- selection.append(subId[subId.length - 1]);
- selection.append(")");
-
- return selection.toString();
- }
-
- /**
- * Helper function to create selection argument of a list of subId.
- * The result should be: "in (iccId1, iccId2, ...)".
- */
- private String getSelectionForIccIdList(String[] iccIds) {
- StringBuilder selection = new StringBuilder();
- selection.append(SubscriptionManager.ICC_ID);
- selection.append(" IN (");
- for (int i = 0; i < iccIds.length - 1; i++) {
- selection.append("'" + iccIds[i] + "', ");
- }
- selection.append("'" + iccIds[iccIds.length - 1] + "'");
- selection.append(")");
-
- return selection.toString();
- }
-
- /**
- * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
- * See {@link #createSubscriptionGroup(int[], String)} for more details.
- *
- * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
- * permission or had carrier privilege permission on the subscription.
- * {@link TelephonyManager#hasCarrierPrivileges(int)}
- *
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- *
- * @param groupUuid of which list of subInfo will be returned.
- * @return list of subscriptionInfo that belong to the same group, including the given
- * subscription itself. It will return an empty list if no subscription belongs to the group.
- *
- */
- @Override
- public List<SubscriptionInfo> getSubscriptionsInGroup(ParcelUuid groupUuid,
- String callingPackage, String callingFeatureId) {
- long identity = Binder.clearCallingIdentity();
- List<SubscriptionInfo> subInfoList;
-
- try {
- // need to bypass removing identifier check because that will remove the subList without
- // group id.
- subInfoList = getAllSubInfoList(mContext.getOpPackageName(),
- mContext.getAttributionTag(), true);
- if (groupUuid == null || subInfoList == null || subInfoList.isEmpty()) {
- return new ArrayList<>();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- return subInfoList.stream().filter(info -> {
- if (!groupUuid.equals(info.getGroupUuid())) return false;
- int subId = info.getSubscriptionId();
- return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
- callingPackage, callingFeatureId, "getSubscriptionsInGroup")
- || info.canManageSubscription(mContext, callingPackage);
- }).map(subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
- callingPackage, callingFeatureId, "getSubscriptionsInGroup"))
- .collect(Collectors.toList());
-
- }
-
- /**
- * Check if the passed in phoneId has a sub that belongs to the same group as the sub
- * corresponding to the passed in iccid.
- * @param phoneId phone id to check
- * @param iccid ICCID to check
- * @return true if sub/group is the same, false otherwise
- */
- public boolean checkPhoneIdAndIccIdMatch(int phoneId, String iccid) {
- int subId = getSubIdUsingPhoneId(phoneId);
- if (!SubscriptionManager.isUsableSubIdValue(subId)) return false;
- ParcelUuid groupUuid = getGroupUuid(subId);
- List<SubscriptionInfo> subInfoList;
- if (groupUuid != null) {
- subInfoList = getSubInfo(SubscriptionManager.GROUP_UUID
- + "=\'" + groupUuid.toString() + "\'", null);
- } else {
- subInfoList = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
- + "=" + subId, null);
- }
- return subInfoList != null && subInfoList.stream().anyMatch(
- subInfo -> IccUtils.stripTrailingFs(subInfo.getIccId()).equals(
- IccUtils.stripTrailingFs(iccid)));
- }
-
- public ParcelUuid getGroupUuid(int subId) {
- ParcelUuid groupUuid;
- List<SubscriptionInfo> subInfo = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
- + "=" + subId, null);
- if (subInfo == null || subInfo.size() == 0) {
- groupUuid = null;
- } else {
- groupUuid = subInfo.get(0).getGroupUuid();
- }
-
- return groupUuid;
- }
-
-
- /**
- * Enable/Disable a subscription
- * @param enable true if enabling, false if disabling
- * @param subId the unique SubInfoRecord index in database
- *
- * @return true if success, false if fails or the further action is
- * needed hence it's redirected to Euicc.
- */
- @Override
- public boolean setSubscriptionEnabled(boolean enable, int subId) {
- enforceModifyPhoneState("setSubscriptionEnabled");
-
- final long identity = Binder.clearCallingIdentity();
- try {
- logd("setSubscriptionEnabled" + (enable ? " enable " : " disable ")
- + " subId " + subId);
-
- // Error checking.
- if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
- throw new IllegalArgumentException(
- "setSubscriptionEnabled not usable subId " + subId);
- }
-
- // Nothing to do if it's already active or inactive.
- if (enable == isActiveSubscriptionId(subId)) return true;
-
- SubscriptionInfo info = SubscriptionController.getInstance()
- .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag())
- .stream()
- .filter(subInfo -> subInfo.getSubscriptionId() == subId)
- .findFirst()
- .get();
-
- if (info == null) {
- logd("setSubscriptionEnabled subId " + subId + " doesn't exist.");
- return false;
- }
-
- // TODO: make sure after slot mapping, we enable the uicc applications for the
- // subscription we are enabling.
- if (info.isEmbedded()) {
- return enableEmbeddedSubscription(info, enable);
- } else {
- return enablePhysicalSubscription(info, enable);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private boolean enableEmbeddedSubscription(SubscriptionInfo info, boolean enable) {
- // We need to send intents to Euicc for operations:
-
- // 1) In single SIM mode, turning on a eSIM subscription while pSIM is the active slot.
- // Euicc will ask user to switch to DSDS if supported or to confirm SIM slot
- // switching.
- // 2) In DSDS mode, turning on / off an eSIM profile. Euicc can ask user whether
- // to turn on DSDS, or whether to switch from current active eSIM profile to it, or
- // to simply show a progress dialog.
- // 3) In future, similar operations on triple SIM devices.
- enableSubscriptionOverEuiccManager(info.getSubscriptionId(), enable,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- // returning false to indicate state is not changed. If changed, a subscriptionInfo
- // change will be filed separately.
- return false;
-
- // TODO: uncomment or clean up if we decide whether to support standalone CBRS for Q.
- // subId = enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- // updateEnabledSubscriptionGlobalSetting(subId, physicalSlotIndex);
- }
-
- private boolean enablePhysicalSubscription(SubscriptionInfo info, boolean enable) {
- if (info == null || !SubscriptionManager.isValidSubscriptionId(info.getSubscriptionId())) {
- return false;
- }
-
- int subId = info.getSubscriptionId();
-
- UiccSlotInfo slotInfo = null;
- int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
- if (slotsInfo == null) return false;
- for (int i = 0; i < slotsInfo.length; i++) {
- UiccSlotInfo curSlotInfo = slotsInfo[i];
- if (curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT) {
- if (TextUtils.equals(IccUtils.stripTrailingFs(curSlotInfo.getCardId()),
- IccUtils.stripTrailingFs(info.getCardString()))) {
- slotInfo = curSlotInfo;
- physicalSlotIndex = i;
- break;
- }
- }
- }
-
- // Can't find the existing SIM.
- if (slotInfo == null) return false;
-
- // this for physical slot which has only one port
- if (enable && !slotInfo.getPorts().stream().findFirst().get().isActive()) {
- // We need to send intents to Euicc if we are turning on an inactive slot.
- // Euicc will decide whether to ask user to switch to DSDS, or change SIM
- // slot mapping.
- EuiccManager euiccManager =
- (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
- if (euiccManager != null && euiccManager.isEnabled()) {
- enableSubscriptionOverEuiccManager(subId, enable, physicalSlotIndex);
- } else {
- // Enable / disable uicc applications.
- if (!info.areUiccApplicationsEnabled()) setUiccApplicationsEnabled(enable, subId);
- // If euiccManager is not enabled, we try to switch to DSDS if possible,
- // or switch slot if not.
- if (mTelephonyManager.isMultiSimSupported() == MULTISIM_ALLOWED) {
- PhoneConfigurationManager.getInstance().switchMultiSimConfig(
- mTelephonyManager.getSupportedModemCount());
- } else {
- List<UiccSlotMapping> slotMapping = new ArrayList<>();
- // As this is single sim mode, set port index to 0 and logical slot index is 0
- slotMapping.add(new UiccSlotMapping(TelephonyManager.DEFAULT_PORT_INDEX,
- physicalSlotIndex, 0));
- UiccController.getInstance().switchSlots(slotMapping, null);
- }
- }
- return true;
- } else {
- // Enable / disable uicc applications.
- setUiccApplicationsEnabled(enable, subId);
- return true;
- }
- }
-
- private void enableSubscriptionOverEuiccManager(int subId, boolean enable,
- int physicalSlotIndex) {
- logdl("enableSubscriptionOverEuiccManager" + (enable ? " enable " : " disable ")
- + "subId " + subId + " on slotIndex " + physicalSlotIndex);
- Intent intent = new Intent(EuiccManager.ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(EuiccManager.EXTRA_SUBSCRIPTION_ID, subId);
- intent.putExtra(EuiccManager.EXTRA_ENABLE_SUBSCRIPTION, enable);
- if (physicalSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- intent.putExtra(EuiccManager.EXTRA_PHYSICAL_SLOT_ID, physicalSlotIndex);
- }
- mContext.startActivity(intent);
- }
-
- private void updateEnabledSubscriptionGlobalSetting(int subId, int physicalSlotIndex) {
- // Write the value which subscription is enabled into global setting.
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex, subId);
- }
-
- private void updateModemStackEnabledGlobalSetting(boolean enabled, int physicalSlotIndex) {
- // Write the whether a modem stack is disabled into global setting.
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT
- + physicalSlotIndex, enabled ? 1 : 0);
- }
-
- private int getPhysicalSlotIndexFromLogicalSlotIndex(int logicalSlotIndex) {
- int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
- for (int i = 0; i < slotInfos.length; i++) {
- for (UiccPortInfo portInfo : slotInfos[i].getPorts()) {
- if (portInfo.getLogicalSlotIndex() == logicalSlotIndex) {
- physicalSlotIndex = i;
- break;
- }
- }
- }
-
- return physicalSlotIndex;
- }
-
- @Override
- public boolean isSubscriptionEnabled(int subId) {
- // TODO: b/123314365 support multi-eSIM and removable eSIM.
- enforceReadPrivilegedPhoneState("isSubscriptionEnabled");
-
- long identity = Binder.clearCallingIdentity();
- try {
- // Error checking.
- if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
- throw new IllegalArgumentException(
- "isSubscriptionEnabled not usable subId " + subId);
- }
-
- List<SubscriptionInfo> infoList = getSubInfo(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
- if (infoList == null || infoList.isEmpty()) {
- // Subscription doesn't exist.
- return false;
- }
-
- boolean isEmbedded = infoList.get(0).isEmbedded();
-
- if (isEmbedded) {
- return isActiveSubId(subId);
- } else {
- // For pSIM, we also need to check if modem is disabled or not.
- return isActiveSubId(subId) && PhoneConfigurationManager.getInstance()
- .getPhoneStatus(PhoneFactory.getPhone(getPhoneId(subId)));
- }
-
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public int getEnabledSubscriptionId(int logicalSlotIndex) {
- // TODO: b/123314365 support multi-eSIM and removable eSIM.
- enforceReadPrivilegedPhoneState("getEnabledSubscriptionId");
-
- long identity = Binder.clearCallingIdentity();
- try {
- if (!SubscriptionManager.isValidPhoneId(logicalSlotIndex)) {
- throw new IllegalArgumentException(
- "getEnabledSubscriptionId with invalid logicalSlotIndex "
- + logicalSlotIndex);
- }
-
- // Getting and validating the physicalSlotIndex.
- int physicalSlotIndex = getPhysicalSlotIndexFromLogicalSlotIndex(logicalSlotIndex);
- if (physicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
-
- // if modem stack is disabled, return INVALID_SUBSCRIPTION_ID without reading
- // Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT.
- int modemStackEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT + physicalSlotIndex, 1);
- if (modemStackEnabled != 1) {
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
-
- int subId;
- try {
- subId = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex);
- } catch (Settings.SettingNotFoundException e) {
- // Value never set. Return whether it's currently active.
- subId = getSubIdUsingPhoneId(logicalSlotIndex);
- }
-
- return subId;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
- * They are doing similar things except operating on different cache.
- *
- * NOTE: the cacheSubList passed in is a *copy* of mCacheActiveSubInfoList or
- * mCacheOpportunisticSubInfoList, so mSubInfoListLock is not required to access it. Also, this
- * method may modify cacheSubList depending on the permissions the caller has.
- */
- private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
- String callingPackage, String callingFeatureId, List<SubscriptionInfo> cacheSubList) {
- boolean canReadPhoneState = false;
- boolean canReadIdentifiers = false;
- boolean canReadPhoneNumber = false;
- try {
- canReadPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
- Binder.getCallingUid(), callingPackage, callingFeatureId,
- "getSubscriptionInfoList");
- // If the calling package has the READ_PHONE_STATE permission then check if the caller
- // also has access to subscriber identifiers and the phone number to ensure that the ICC
- // ID and any other unique identifiers are removed if the caller should not have access.
- if (canReadPhoneState) {
- canReadIdentifiers = hasSubscriberIdentifierAccess(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getSubscriptionInfoList", false);
- canReadPhoneNumber = hasPhoneNumberAccess(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getSubscriptionInfoList");
- }
- } catch (SecurityException e) {
- // If a SecurityException is thrown during the READ_PHONE_STATE check then the only way
- // to access a subscription is to have carrier privileges for its subId; an app with
- // carrier privileges for a subscription is also granted access to all identifiers so
- // the identifier and phone number access checks are not required.
- }
-
- if (canReadIdentifiers && canReadPhoneNumber) {
- return cacheSubList;
- }
- // Filter the list to only include subscriptions which the caller can manage.
- for (int subIndex = cacheSubList.size() - 1; subIndex >= 0; subIndex--) {
- SubscriptionInfo subscriptionInfo = cacheSubList.get(subIndex);
-
- int subId = subscriptionInfo.getSubscriptionId();
- boolean hasCarrierPrivileges = TelephonyPermissions.checkCarrierPrivilegeForSubId(
- mContext, subId);
- // If the caller has carrier privileges then they are granted access to all
- // identifiers for their subscription.
- if (hasCarrierPrivileges) continue;
-
- cacheSubList.remove(subIndex);
- if (canReadPhoneState) {
- // The caller does not have carrier privileges for this subId, filter the
- // identifiers in the subscription based on the results of the initial
- // permission checks.
- cacheSubList.add(subIndex, conditionallyRemoveIdentifiers(
- subscriptionInfo, canReadIdentifiers, canReadPhoneNumber));
- }
- }
- return cacheSubList;
- }
-
- /**
- * Conditionally removes identifiers from the provided {@code subInfo} if the {@code
- * callingPackage} does not meet the access requirements for identifiers and returns the
- * potentially modified object..
- *
- * <p>If the caller does not meet the access requirements for identifiers a clone of the
- * provided SubscriptionInfo is created and modified to avoid altering SubscriptionInfo objects
- * in a cache.
- */
- private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo,
- String callingPackage, String callingFeatureId, String message) {
- SubscriptionInfo result = subInfo;
- int subId = subInfo.getSubscriptionId();
- boolean hasIdentifierAccess = hasSubscriberIdentifierAccess(subId, callingPackage,
- callingFeatureId, message, true);
- boolean hasPhoneNumberAccess = hasPhoneNumberAccess(subId, callingPackage, callingFeatureId,
- message);
- return conditionallyRemoveIdentifiers(subInfo, hasIdentifierAccess, hasPhoneNumberAccess);
- }
-
- /**
- * Conditionally removes identifiers from the provided {@code subInfo} based on if the calling
- * package {@code hasIdentifierAccess} and {@code hasPhoneNumberAccess} and returns the
- * potentially modified object.
- *
- * <p>If the caller specifies the package does not have identifier or phone number access
- * a clone of the provided SubscriptionInfo is created and modified to avoid altering
- * SubscriptionInfo objects in a cache.
- */
- private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo,
- boolean hasIdentifierAccess, boolean hasPhoneNumberAccess) {
- if (hasIdentifierAccess && hasPhoneNumberAccess) {
- return subInfo;
- }
- SubscriptionInfo result = new SubscriptionInfo(subInfo);
- if (!hasIdentifierAccess) {
- result.clearIccId();
- result.clearCardString();
- result.clearGroupUuid();
- }
- if (!hasPhoneNumberAccess) {
- result.clearNumber();
- }
- return result;
- }
-
- private synchronized boolean addToSubIdList(int slotIndex, int subId, int subscriptionType) {
- ArrayList<Integer> subIdsList = sSlotIndexToSubIds.getCopy(slotIndex);
- if (subIdsList == null) {
- subIdsList = new ArrayList<>();
- sSlotIndexToSubIds.put(slotIndex, subIdsList);
- }
-
- // add the given subId unless it already exists
- if (subIdsList.contains(subId)) {
- logdl("slotIndex, subId combo already exists in the map. Not adding it again.");
- return false;
- }
- if (isSubscriptionForRemoteSim(subscriptionType)) {
- // For Remote SIM subscriptions, a slot can have multiple subscriptions.
- sSlotIndexToSubIds.addToSubIdList(slotIndex, subId);
- } else {
- // for all other types of subscriptions, a slot can have only one subscription at a time
- sSlotIndexToSubIds.clearSubIdList(slotIndex);
- sSlotIndexToSubIds.addToSubIdList(slotIndex, subId);
- }
-
-
- // Remove the slot from sSlotIndexToSubIds if it has the same sub id with the added slot
- for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
- if (entry.getKey() != slotIndex && entry.getValue() != null
- && entry.getValue().contains(subId)) {
- logdl("addToSubIdList - remove " + entry.getKey());
- sSlotIndexToSubIds.remove(entry.getKey());
- }
- }
-
- if (DBG) logdl("slotIndex, subId combo is added to the map.");
- return true;
- }
-
- private boolean isSubscriptionForRemoteSim(int subscriptionType) {
- return subscriptionType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
- }
-
- /**
- * This is only for testing
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public Map<Integer, ArrayList<Integer>> getSlotIndexToSubIdsMap() {
- return sSlotIndexToSubIds.getMap();
- }
-
- /**
- * This is only for testing
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public void resetStaticMembers() {
- sDefaultFallbackSubId.set(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
- }
-
- private void notifyOpportunisticSubscriptionInfoChanged() {
- TelephonyRegistryManager trm =
- (TelephonyRegistryManager)
- mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
- if (DBG) logd("notifyOpptSubscriptionInfoChanged:");
- trm.notifyOpportunisticSubscriptionInfoChanged();
- }
-
- private void refreshCachedOpportunisticSubscriptionInfoList() {
- List<SubscriptionInfo> subList = getSubInfo(
- SubscriptionManager.IS_OPPORTUNISTIC + "=1 AND ("
- + SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
- + SubscriptionManager.IS_EMBEDDED + "=1)", null);
- synchronized (mSubInfoListLock) {
- List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList;
-
- if (subList != null) {
- subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
- } else {
- subList = new ArrayList<>();
- }
-
- mCacheOpportunisticSubInfoList = subList;
-
- for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
- if (shouldDisableSubGroup(info.getGroupUuid())) {
- info.setGroupDisabled(true);
- }
- }
-
- if (DBG_CACHE) {
- if (!mCacheOpportunisticSubInfoList.isEmpty()) {
- for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) {
- logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info="
- + si);
- }
- } else {
- logdl("[refreshCachedOpptSubscriptionInfoList]- no info return");
- }
- }
-
- if (!oldOpptCachedList.equals(mCacheOpportunisticSubInfoList)) {
- mOpptSubInfoListChangedDirtyBit.set(true);
- }
- }
- }
-
- private boolean shouldDisableSubGroup(ParcelUuid groupUuid) {
- if (groupUuid == null) return false;
-
- synchronized (mSubInfoListLock) {
- for (SubscriptionInfo activeInfo : mCacheActiveSubInfoList) {
- if (!activeInfo.isOpportunistic() && groupUuid.equals(activeInfo.getGroupUuid())) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Set allowing mobile data during voice call.
- *
- * @param subId Subscription index
- * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride}
- * for details.
- * @return {@code true} if settings changed, otherwise {@code false}.
- */
- public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) {
- if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId);
-
- validateSubId(subId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules);
-
- boolean result = updateDatabase(value, subId, true) > 0;
-
- if (result) {
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- }
-
- return result;
- }
-
- /**
- * Get data enabled override rules.
- *
- * @param subId Subscription index
- * @return Data enabled override rules in string
- */
- @NonNull
- public String getDataEnabledOverrideRules(int subId) {
- return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId,
- SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
- }
-
- /**
- * Get active data subscription id.
- *
- * @return Active data subscription id
- *
- * @hide
- */
- @Override
- public int getActiveDataSubscriptionId() {
- final long token = Binder.clearCallingIdentity();
-
- try {
- PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
- if (phoneSwitcher != null) {
- int activeDataSubId = phoneSwitcher.getActiveDataSubId();
- if (SubscriptionManager.isUsableSubscriptionId(activeDataSubId)) {
- return activeDataSubId;
- }
- }
- // If phone switcher isn't ready, or active data sub id is not available, use default
- // sub id from settings.
- return getDefaultDataSubId();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM.
- */
- @Override
- public boolean canDisablePhysicalSubscription() {
- enforceReadPrivilegedPhoneState("canToggleUiccApplicationsEnablement");
-
- final long identity = Binder.clearCallingIdentity();
- try {
- Phone phone = PhoneFactory.getDefaultPhone();
- return phone != null && phone.canDisablePhysicalSubscription();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /*
- * Returns the phone number for the given {@code subId} and {@code source},
- * or an empty string if not available.
- */
- @Override
- public String getPhoneNumber(int subId, int source,
- String callingPackage, String callingFeatureId) {
- TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
- mContext, subId, Binder.getCallingUid(), "getPhoneNumber",
- READ_PHONE_NUMBERS, READ_PRIVILEGED_PHONE_STATE);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- String number = getPhoneNumber(subId, source);
- return number == null ? "" : number;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /*
- * Returns the phone number for the given {@code subId} or an empty string if not available.
- *
- * <p>Built up on getPhoneNumber(int subId, int source) this API picks the 1st available
- * source based on a priority order.
- */
- @Override
- public String getPhoneNumberFromFirstAvailableSource(int subId,
- String callingPackage, String callingFeatureId) {
- TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
- mContext, subId, Binder.getCallingUid(), "getPhoneNumberFromFirstAvailableSource",
- READ_PHONE_NUMBERS, READ_PRIVILEGED_PHONE_STATE);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- String numberFromCarrier = getPhoneNumber(
- subId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
- if (!TextUtils.isEmpty(numberFromCarrier)) {
- return numberFromCarrier;
- }
- String numberFromUicc = getPhoneNumber(
- subId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
- if (!TextUtils.isEmpty(numberFromUicc)) {
- return numberFromUicc;
- }
- String numberFromIms = getPhoneNumber(
- subId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
- if (!TextUtils.isEmpty(numberFromIms)) {
- return numberFromIms;
- }
- return "";
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- // Internal helper method for implementing getPhoneNumber() API.
- @Nullable
- private String getPhoneNumber(int subId, int source) {
- if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_UICC) {
- Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
- return phone != null ? phone.getLine1Number() : null;
- }
- if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER) {
- return getSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER);
- }
- if (source == SubscriptionManager.PHONE_NUMBER_SOURCE_IMS) {
- return getSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS);
- }
- throw new IllegalArgumentException("setPhoneNumber doesn't accept source " + source);
- }
-
- /**
- * Sets the phone number for the given {@code subId}.
- *
- * <p>The only accepted {@code source} is {@link
- * SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER}.
- */
- @Override
- public void setPhoneNumber(int subId, int source, String number,
- String callingPackage, String callingFeatureId) {
- if (source != SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER) {
- throw new IllegalArgumentException("setPhoneNumber doesn't accept source " + source);
- }
- if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
- throw new SecurityException("setPhoneNumber for CARRIER needs carrier privilege");
- }
- if (number == null) {
- throw new NullPointerException("invalid number null");
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- setSubscriptionProperty(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER, number);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Set the Usage Setting for this subscription.
- *
- * @param usageSetting the cellular usage setting
- * @param subId the unique SubscriptionInfo index in database
- * @param callingPackage the package making the IPC
- * @return the number of records updated
- *
- * @throws SecurityException if doesn't have required permission.
- */
- @Override
- public int setUsageSetting(@UsageSetting int usageSetting, int subId, String callingPackage) {
- try {
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
- mContext, subId, callingPackage);
- } catch (SecurityException e) {
- enforceCarrierPrivilegeOnInactiveSub(subId, callingPackage,
- "Caller requires permission on sub " + subId);
- }
-
- if (usageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
- || usageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
- throw new IllegalArgumentException("setUsageSetting: Invalid usage setting: "
- + usageSetting);
- }
-
- final long token = Binder.clearCallingIdentity();
- int ret;
- try {
- ret = setSubscriptionProperty(subId, SubscriptionManager.USAGE_SETTING,
- String.valueOf(usageSetting));
-
- // ret is the number of records updated in the DB, which should always be 1.
- // TODO(b/205027930): move this check prior to the database mutation request
- if (ret != 1) throw new IllegalArgumentException(
- "Invalid SubscriptionId for setUsageSetting");
- } finally {
- Binder.restoreCallingIdentity(token);
- // FIXME(b/205726099) return void
- }
- return ret;
- }
-
- /**
- * @hide
- */
- private void setGlobalSetting(String name, int value) {
- Settings.Global.putInt(mContext.getContentResolver(), name, value);
- if (name == Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION) {
- invalidateDefaultDataSubIdCaches();
- invalidateActiveDataSubIdCaches();
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- } else if (name == Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION) {
- invalidateDefaultSubIdCaches();
- invalidateSlotIndexCaches();
- } else if (name == Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION) {
- invalidateDefaultSmsSubIdCaches();
- }
- }
-
- /**
- * @hide
- */
- private static void invalidateDefaultSubIdCaches() {
- if (sCachingEnabled) {
- SubscriptionManager.invalidateDefaultSubIdCaches();
- }
- }
-
- /**
- * @hide
- */
- private static void invalidateDefaultDataSubIdCaches() {
- if (sCachingEnabled) {
- SubscriptionManager.invalidateDefaultDataSubIdCaches();
- }
- }
-
- /**
- * @hide
- */
- private static void invalidateDefaultSmsSubIdCaches() {
- if (sCachingEnabled) {
- SubscriptionManager.invalidateDefaultSmsSubIdCaches();
- }
- }
-
- /**
- * @hide
- */
- public static void invalidateActiveDataSubIdCaches() {
- if (sCachingEnabled) {
- SubscriptionManager.invalidateActiveDataSubIdCaches();
- }
- }
-
- /**
- * @hide
- */
- protected static void invalidateSlotIndexCaches() {
- if (sCachingEnabled) {
- SubscriptionManager.invalidateSlotIndexCaches();
- }
- }
-
- /**
- * @hide
- */
- @VisibleForTesting
- public static void disableCaching() {
- sCachingEnabled = false;
- }
-
- /**
- * @hide
- */
- @VisibleForTesting
- public static void enableCaching() {
- sCachingEnabled = true;
- }
-}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
deleted file mode 100644
index 3ab229a..0000000
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ /dev/null
@@ -1,1392 +0,0 @@
-/*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.telephony;
-
-import android.Manifest;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.AsyncResult;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.os.UserHandle;
-import android.preference.PreferenceManager;
-import android.service.carrier.CarrierIdentifier;
-import android.service.euicc.EuiccProfileInfo;
-import android.service.euicc.EuiccService;
-import android.service.euicc.GetEuiccProfileInfoListResult;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.UsageSetting;
-import android.telephony.TelephonyManager;
-import android.telephony.TelephonyManager.SimState;
-import android.telephony.UiccAccessRule;
-import android.telephony.euicc.EuiccManager;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.euicc.EuiccController;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccCard;
-import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccPort;
-import com.android.internal.telephony.uicc.UiccSlot;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-/**
- *@hide
- */
-public class SubscriptionInfoUpdater extends Handler {
- private static final String LOG_TAG = "SubscriptionInfoUpdater";
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static final int SUPPORTED_MODEM_COUNT = TelephonyManager.getDefault()
- .getSupportedModemCount();
-
- private static final boolean DBG = true;
-
- private static final int EVENT_INVALID = -1;
- private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 2;
- private static final int EVENT_SIM_LOADED = 3;
- private static final int EVENT_SIM_ABSENT = 4;
- private static final int EVENT_SIM_LOCKED = 5;
- private static final int EVENT_SIM_IO_ERROR = 6;
- private static final int EVENT_SIM_UNKNOWN = 7;
- private static final int EVENT_SIM_RESTRICTED = 8;
- private static final int EVENT_SIM_NOT_READY = 9;
- private static final int EVENT_SIM_READY = 10;
- private static final int EVENT_SIM_IMSI = 11;
- private static final int EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS = 12;
- private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 13;
- private static final int EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED = 14;
-
- private static final String ICCID_STRING_FOR_NO_SIM = "";
-
- private static final ParcelUuid REMOVE_GROUP_UUID =
- ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING);
-
- // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED.
- public static final String CURR_SUBID = "curr_subid";
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static Context sContext = null;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-
- protected static String[] sIccId = new String[SUPPORTED_MODEM_COUNT];
- protected SubscriptionController mSubscriptionController = null;
- private static String[] sInactiveIccIds = new String[SUPPORTED_MODEM_COUNT];
- private static int[] sSimCardState = new int[SUPPORTED_MODEM_COUNT];
- private static int[] sSimApplicationState = new int[SUPPORTED_MODEM_COUNT];
- private static boolean sIsSubInfoInitialized = false;
- private SubscriptionManager mSubscriptionManager = null;
- private EuiccManager mEuiccManager;
- private Handler mBackgroundHandler;
-
- // The current foreground user ID.
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private int mCurrentlyActiveUserId;
- private CarrierServiceBindHelper mCarrierServiceBindHelper;
-
- private volatile boolean shouldRetryUpdateEmbeddedSubscriptions = false;
- private final CopyOnWriteArraySet<Integer> retryUpdateEmbeddedSubscriptionCards =
- new CopyOnWriteArraySet<>();
- private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- // The LPA may not have been ready before user unlock, and so previous attempts
- // to refresh the list of embedded subscriptions may have failed. This retries
- // the refresh operation after user unlock.
- if (shouldRetryUpdateEmbeddedSubscriptions) {
- logd("Retrying refresh embedded subscriptions after user unlock.");
- for (int cardId : retryUpdateEmbeddedSubscriptionCards){
- requestEmbeddedSubscriptionInfoListRefresh(cardId, null);
- }
- retryUpdateEmbeddedSubscriptionCards.clear();
- sContext.unregisterReceiver(mUserUnlockedReceiver);
- }
- }
- }
- };
-
- /**
- * Runnable with a boolean parameter. This is used in
- * updateEmbeddedSubscriptions(List<Integer> cardIds, @Nullable UpdateEmbeddedSubsCallback).
- */
- protected interface UpdateEmbeddedSubsCallback {
- /**
- * Callback of the Runnable.
- * @param hasChanges Whether there is any subscription info change. If yes, we need to
- * notify the listeners.
- */
- void run(boolean hasChanges);
- }
-
- @VisibleForTesting
- public SubscriptionInfoUpdater(Looper looper, Context context, SubscriptionController sc) {
- logd("Constructor invoked");
- mBackgroundHandler = new Handler(looper);
-
- sContext = context;
- mSubscriptionController = sc;
- mSubscriptionManager = SubscriptionManager.from(sContext);
- mEuiccManager = (EuiccManager) sContext.getSystemService(Context.EUICC_SERVICE);
-
- mCarrierServiceBindHelper = new CarrierServiceBindHelper(sContext);
-
- sContext.registerReceiver(
- mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
-
- initializeCarrierApps();
-
- PhoneConfigurationManager.registerForMultiSimConfigChange(
- this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
- }
-
- private void initializeCarrierApps() {
- // Initialize carrier apps:
- // -Now (on system startup)
- // -Whenever new carrier privilege rules might change (new SIM is loaded)
- // -Whenever we switch to a new user
- mCurrentlyActiveUserId = 0;
- sContext.registerReceiverForAllUsers(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Remove this line after testing
- if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) {
- UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
- // If couldn't get current user ID, guess it's 0.
- mCurrentlyActiveUserId = userHandle != null ? userHandle.getIdentifier() : 0;
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
- TelephonyManager.getDefault(), mCurrentlyActiveUserId, sContext);
- }
- }
- }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null);
- ActivityManager am = (ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE);
- mCurrentlyActiveUserId = am.getCurrentUser();
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
- TelephonyManager.getDefault(), mCurrentlyActiveUserId, sContext);
- }
-
- /**
- * Update subscriptions when given a new ICC state.
- */
- public void updateInternalIccState(String simStatus, String reason, int phoneId) {
- logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason
- + " phoneId " + phoneId);
- int message = internalIccStateToMessage(simStatus);
- if (message != EVENT_INVALID) {
- sendMessage(obtainMessage(message, phoneId, 0, reason));
- }
- }
-
- /**
- * Update subscriptions if needed when there's a change in inactive port.
- * @param prevActivePhoneId is the corresponding phoneId of the port if port was previously
- * active. It could be INVALID if it was already inactive.
- * @param iccId iccId in that port, if any.
- */
- public void updateInternalIccStateForInactivePort(int prevActivePhoneId, String iccId) {
- sendMessage(obtainMessage(EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED, prevActivePhoneId,
- 0, iccId));
- }
-
- private int internalIccStateToMessage(String simStatus) {
- switch(simStatus) {
- case IccCardConstants.INTENT_VALUE_ICC_ABSENT: return EVENT_SIM_ABSENT;
- case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN: return EVENT_SIM_UNKNOWN;
- case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: return EVENT_SIM_IO_ERROR;
- case IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED: return EVENT_SIM_RESTRICTED;
- case IccCardConstants.INTENT_VALUE_ICC_NOT_READY: return EVENT_SIM_NOT_READY;
- case IccCardConstants.INTENT_VALUE_ICC_LOCKED: return EVENT_SIM_LOCKED;
- case IccCardConstants.INTENT_VALUE_ICC_LOADED: return EVENT_SIM_LOADED;
- case IccCardConstants.INTENT_VALUE_ICC_READY: return EVENT_SIM_READY;
- case IccCardConstants.INTENT_VALUE_ICC_IMSI: return EVENT_SIM_IMSI;
- default:
- logd("Ignoring simStatus: " + simStatus);
- return EVENT_INVALID;
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected boolean isAllIccIdQueryDone() {
- for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) {
- UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(i);
- int slotId = UiccController.getInstance().getSlotIdFromPhoneId(i);
- // When psim card is absent there is no port object even the port state is active.
- // We should check the slot state for psim and port state for esim(MEP eUICC).
- if (sIccId[i] == null || slot == null || !slot.isActive()
- || (slot.isEuicc() && UiccController.getInstance().getUiccPort(i) == null)) {
- if (sIccId[i] == null) {
- logd("Wait for SIM " + i + " Iccid");
- } else {
- logd(String.format("Wait for port corresponding to phone %d to be active, "
- + "slotId is %d" + " , portIndex is %d", i, slotId,
- slot.getPortIndexFromPhoneId(i)));
- }
- return false;
- }
- }
- logd("All IccIds query complete");
-
- return true;
- }
-
- @Override
- public void handleMessage(Message msg) {
- List<Integer> cardIds = new ArrayList<>();
- switch (msg.what) {
- case EVENT_GET_NETWORK_SELECTION_MODE_DONE: {
- AsyncResult ar = (AsyncResult)msg.obj;
- Integer slotId = (Integer)ar.userObj;
- if (ar.exception == null && ar.result != null) {
- int[] modes = (int[])ar.result;
- if (modes[0] == 1) { // Manual mode.
- PhoneFactory.getPhone(slotId).setNetworkSelectionModeAutomatic(null);
- }
- } else {
- logd("EVENT_GET_NETWORK_SELECTION_MODE_DONE: error getting network mode.");
- }
- break;
- }
-
- case EVENT_SIM_LOADED:
- handleSimLoaded(msg.arg1);
- break;
-
- case EVENT_SIM_ABSENT:
- handleSimAbsent(msg.arg1);
- break;
-
- case EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED:
- handleInactivePortIccStateChange(msg.arg1, (String) msg.obj);
- break;
-
- case EVENT_SIM_LOCKED:
- handleSimLocked(msg.arg1, (String) msg.obj);
- break;
-
- case EVENT_SIM_UNKNOWN:
- broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null);
- broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
- broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
- updateSubscriptionCarrierId(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
- updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
- break;
-
- case EVENT_SIM_IO_ERROR:
- handleSimError(msg.arg1);
- break;
-
- case EVENT_SIM_RESTRICTED:
- broadcastSimStateChanged(msg.arg1,
- IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED,
- IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
- broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_CARD_RESTRICTED);
- broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY);
- updateSubscriptionCarrierId(msg.arg1,
- IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
- updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
- break;
-
- case EVENT_SIM_READY:
- handleSimReady(msg.arg1);
- break;
-
- case EVENT_SIM_IMSI:
- broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_IMSI, null);
- break;
-
- case EVENT_SIM_NOT_READY:
- // an eUICC with no active subscriptions never becomes ready, so we need to trigger
- // the embedded subscriptions update here
- cardIds.add(getCardIdFromPhoneId(msg.arg1));
- updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- mSubscriptionController.notifySubscriptionInfoChanged();
- }
- });
- handleSimNotReady(msg.arg1);
- break;
-
- case EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS:
- cardIds.add(msg.arg1);
- Runnable r = (Runnable) msg.obj;
- updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- mSubscriptionController.notifySubscriptionInfoChanged();
- }
- if (r != null) {
- r.run();
- }
- });
- break;
-
- case EVENT_MULTI_SIM_CONFIG_CHANGED:
- onMultiSimConfigChanged();
- break;
-
- default:
- logd("Unknown msg:" + msg.what);
- }
- }
-
- private void onMultiSimConfigChanged() {
- int activeModemCount = ((TelephonyManager) sContext.getSystemService(
- Context.TELEPHONY_SERVICE)).getActiveModemCount();
- // For inactive modems, reset its states.
- for (int phoneId = activeModemCount; phoneId < SUPPORTED_MODEM_COUNT; phoneId++) {
- sIccId[phoneId] = null;
- sSimCardState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
- sSimApplicationState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
- }
- }
-
- protected int getCardIdFromPhoneId(int phoneId) {
- UiccController uiccController = UiccController.getInstance();
- UiccCard card = uiccController.getUiccCardForPhone(phoneId);
- if (card != null) {
- return uiccController.convertToPublicCardId(card.getCardId());
- }
- return TelephonyManager.UNINITIALIZED_CARD_ID;
- }
-
- void requestEmbeddedSubscriptionInfoListRefresh(int cardId, @Nullable Runnable callback) {
- sendMessage(obtainMessage(
- EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback));
- }
-
- protected void handleSimLocked(int phoneId, String reason) {
- if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- logd("SIM" + (phoneId + 1) + " hot plug in");
- sIccId[phoneId] = null;
- }
-
- IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
- if (iccCard == null) {
- logd("handleSimLocked: IccCard null");
- return;
- }
- IccRecords records = iccCard.getIccRecords();
- if (records == null) {
- logd("handleSimLocked: IccRecords null");
- return;
- }
- if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) {
- logd("handleSimLocked: IccID null");
- return;
- }
- sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
-
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
-
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
- broadcastSimApplicationStateChanged(phoneId, getSimStateFromLockedReason(reason));
- updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- }
-
- private static int getSimStateFromLockedReason(String lockedReason) {
- switch (lockedReason) {
- case IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN:
- return TelephonyManager.SIM_STATE_PIN_REQUIRED;
- case IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK:
- return TelephonyManager.SIM_STATE_PUK_REQUIRED;
- case IccCardConstants.INTENT_VALUE_LOCKED_NETWORK:
- return TelephonyManager.SIM_STATE_NETWORK_LOCKED;
- case IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED:
- return TelephonyManager.SIM_STATE_PERM_DISABLED;
- default:
- Rlog.e(LOG_TAG, "Unexpected SIM locked reason " + lockedReason);
- return TelephonyManager.SIM_STATE_UNKNOWN;
- }
- }
-
- protected void handleSimReady(int phoneId) {
- List<Integer> cardIds = new ArrayList<>();
- logd("handleSimReady: phoneId: " + phoneId);
-
- if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- logd(" SIM" + (phoneId + 1) + " hot plug in");
- sIccId[phoneId] = null;
- }
-
- // ICCID is not available in IccRecords by the time SIM Ready event received
- // hence get ICCID from UiccPort.
- UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
- String iccId = (port == null) ? null : IccUtils.stripTrailingFs(port.getIccId());
-
- if (!TextUtils.isEmpty(iccId)) {
- sIccId[phoneId] = iccId;
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
- }
-
- cardIds.add(getCardIdFromPhoneId(phoneId));
- updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- mSubscriptionController.notifySubscriptionInfoChanged();
- }
- });
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_READY, null);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
- }
-
- protected void handleSimNotReady(int phoneId) {
- logd("handleSimNotReady: phoneId: " + phoneId);
- boolean isFinalState = false;
-
- IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
- boolean uiccAppsDisabled = areUiccAppsDisabledOnCard(phoneId);
- if (iccCard.isEmptyProfile() || uiccAppsDisabled) {
- if (uiccAppsDisabled) {
- UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
- String iccId = (port == null) ? null : port.getIccId();
- sInactiveIccIds[phoneId] = IccUtils.stripTrailingFs(iccId);
- }
- isFinalState = true;
- // ICC_NOT_READY is a terminal state for
- // 1) It's an empty profile as there's no uicc applications. Or
- // 2) Its uicc applications are set to be disabled.
- // At this phase, the subscription list is accessible. Treating NOT_READY
- // as equivalent to ABSENT, once the rest of the system can handle it.
- sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
- updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
- } else {
- sIccId[phoneId] = null;
- }
-
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
- null);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
- if (isFinalState) {
- updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY);
- }
- }
-
- private boolean areUiccAppsDisabledOnCard(int phoneId) {
- // When uicc apps are disabled(supported in IRadio 1.5), we will still get IccId from
- // cardStatus (since IRadio 1.2). Amd upon cardStatus change we'll receive another
- // handleSimNotReady so this will be evaluated again.
- UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
- if (slot == null) return false;
- UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
- String iccId = (port == null) ? null : port.getIccId();
- if (iccId == null) {
- return false;
- }
- SubscriptionInfo info =
- mSubscriptionController.getSubInfoForIccId(
- IccUtils.stripTrailingFs(iccId));
- return info != null && !info.areUiccApplicationsEnabled();
- }
-
- protected void handleSimLoaded(int phoneId) {
- logd("handleSimLoaded: phoneId: " + phoneId);
-
- // The SIM should be loaded at this state, but it is possible in cases such as SIM being
- // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
- // not broadcast the SIM loaded.
- IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
- if (iccCard == null) { // Possibly a race condition.
- logd("handleSimLoaded: IccCard null");
- return;
- }
- IccRecords records = iccCard.getIccRecords();
- if (records == null) { // Possibly a race condition.
- logd("handleSimLoaded: IccRecords null");
- return;
- }
- if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) {
- logd("handleSimLoaded: IccID null");
- return;
- }
-
- // Call updateSubscriptionInfoByIccId() only if was not done earlier from SIM READY event
- if (sIccId[phoneId] == null) {
- sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
-
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
- }
-
- List<SubscriptionInfo> subscriptionInfos =
- mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId);
- if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
- loge("empty subinfo for phoneId: " + phoneId + "could not update ContentResolver");
- } else {
- for (SubscriptionInfo sub : subscriptionInfos) {
- int subId = sub.getSubscriptionId();
- TelephonyManager tm = (TelephonyManager)
- sContext.getSystemService(Context.TELEPHONY_SERVICE);
- String operator = tm.getSimOperatorNumeric(subId);
-
- if (!TextUtils.isEmpty(operator)) {
- if (subId == mSubscriptionController.getDefaultSubId()) {
- MccTable.updateMccMncConfiguration(sContext, operator);
- }
- mSubscriptionController.setMccMnc(operator, subId);
- } else {
- logd("EVENT_RECORDS_LOADED Operator name is null");
- }
-
- String iso = tm.getSimCountryIsoForPhone(phoneId);
-
- if (!TextUtils.isEmpty(iso)) {
- mSubscriptionController.setCountryIso(iso, subId);
- } else {
- logd("EVENT_RECORDS_LOADED sim country iso is null");
- }
-
- String msisdn = tm.getLine1Number(subId);
- if (msisdn != null) {
- mSubscriptionController.setDisplayNumber(msisdn, subId);
- }
-
- String imsi = tm.createForSubscriptionId(subId).getSubscriberId();
- if (imsi != null) {
- mSubscriptionController.setImsi(imsi, subId);
- }
-
- String[] ehplmns = records.getEhplmns();
- String[] hplmns = records.getPlmnsFromHplmnActRecord();
- if (ehplmns != null || hplmns != null) {
- mSubscriptionController.setAssociatedPlmns(ehplmns, hplmns, subId);
- }
-
- /* Update preferred network type and network selection mode on SIM change.
- * Storing last subId in SharedPreference for now to detect SIM change.
- */
- SharedPreferences sp =
- PreferenceManager.getDefaultSharedPreferences(sContext);
- int storedSubId = sp.getInt(CURR_SUBID + phoneId, -1);
-
- if (storedSubId != subId) {
- // Only support automatic selection mode on SIM change.
- PhoneFactory.getPhone(phoneId).getNetworkSelectionMode(
- obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
- new Integer(phoneId)));
- // Update stored subId
- SharedPreferences.Editor editor = sp.edit();
- editor.putInt(CURR_SUBID + phoneId, subId);
- editor.apply();
- }
- }
- }
-
- /**
- * The sim loading sequence will be
- * 1. ACTION_SUBINFO_CONTENT_CHANGE happens through updateSubscriptionInfoByIccId() above.
- * 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
- * /ACTION_SIM_APPLICATION_STATE_CHANGED
- * 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
- * 4. restore sim-specific settings
- * 5. ACTION_CARRIER_CONFIG_CHANGED
- */
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_LOADED);
- updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
- /* Sim-specific settings restore depends on knowing both the mccmnc and the carrierId of the
- sim which is why it must be done after #updateSubscriptionCarrierId(). It is done before
- carrier config update to avoid any race conditions with user settings that depend on
- carrier config*/
- restoreSimSpecificSettingsForPhone(phoneId);
- updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
- }
-
- /**
- * Calculate the usage setting based on the carrier request.
- *
- * @param currentUsageSetting the current setting in the subscription DB
- * @param preferredUsageSetting provided by the carrier config
- * @return the calculated usage setting.
- */
- @VisibleForTesting
- @UsageSetting public int calculateUsageSetting(
- @UsageSetting int currentUsageSetting, @UsageSetting int preferredUsageSetting) {
- int defaultUsageSetting;
- int[] supportedUsageSettings;
-
- // Load the resources to provide the device capability
- try {
- defaultUsageSetting = sContext.getResources().getInteger(
- com.android.internal.R.integer.config_default_cellular_usage_setting);
- supportedUsageSettings = sContext.getResources().getIntArray(
- com.android.internal.R.array.config_supported_cellular_usage_settings);
- // If usage settings are not supported, return the default setting, which is UNKNOWN.
- if (supportedUsageSettings == null
- || supportedUsageSettings.length < 1) return currentUsageSetting;
- } catch (Resources.NotFoundException nfe) {
- loge("Failed to load usage setting resources!");
- return currentUsageSetting;
- }
-
- // If the current setting is invalid, including the first time the value is set,
- // update it to default (this will trigger a change in the DB).
- if (currentUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
- || currentUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
- logd("Updating usage setting for current subscription");
- currentUsageSetting = SubscriptionManager.USAGE_SETTING_DEFAULT;
- }
-
- // Range check the inputs, and on failure, make no changes
- if (preferredUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
- || preferredUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
- loge("Invalid usage setting!" + preferredUsageSetting);
- return currentUsageSetting;
- }
-
- // Default is always allowed
- if (preferredUsageSetting == SubscriptionManager.USAGE_SETTING_DEFAULT) {
- return preferredUsageSetting;
- }
-
- // Forced setting must be explicitly supported
- for (int i = 0; i < supportedUsageSettings.length; i++) {
- if (preferredUsageSetting == supportedUsageSettings[i]) return preferredUsageSetting;
- }
-
- // If the preferred setting is not possible, just keep the current setting.
- return currentUsageSetting;
- }
-
- private void restoreSimSpecificSettingsForPhone(int phoneId) {
- SubscriptionManager subManager = SubscriptionManager.from(sContext);
- subManager.restoreSimSpecificSettingsForIccIdFromBackup(sIccId[phoneId]);
- }
-
- private void updateCarrierServices(int phoneId, String simState) {
- if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- logd("Ignore updateCarrierServices request with invalid phoneId " + phoneId);
- return;
- }
- CarrierConfigManager configManager =
- (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- configManager.updateConfigForPhoneId(phoneId, simState);
- mCarrierServiceBindHelper.updateForPhoneId(phoneId, simState);
- }
-
- private void updateSubscriptionCarrierId(int phoneId, String simState) {
- if (PhoneFactory.getPhone(phoneId) != null) {
- PhoneFactory.getPhone(phoneId).resolveSubscriptionCarrierId(simState);
- }
- }
-
- /**
- * PhoneId is the corresponding phoneId of the port if port was previously active.
- * It could be INVALID if it was already inactive.
- */
- private void handleInactivePortIccStateChange(int phoneId, String iccId) {
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- // If phoneId is valid, it means the physical slot was previously active in that
- // phoneId. In this case, found the subId and set its phoneId to invalid.
- if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- logd("Slot of SIM" + (phoneId + 1) + " becomes inactive");
- }
- cleanSubscriptionInPhone(phoneId, false);
- }
- if (!TextUtils.isEmpty(iccId)) {
- // If iccId is new, add a subscription record in the db.
- String strippedIccId = IccUtils.stripTrailingFs(iccId);
- if (mSubscriptionController.getSubInfoForIccId(strippedIccId) == null) {
- mSubscriptionController.insertEmptySubInfoRecord(
- strippedIccId, "CARD", SubscriptionManager.INVALID_PHONE_INDEX,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
- }
- }
- }
-
- /**
- * Clean subscription info when sim state becomes ABSENT. There are 2 scenarios for this:
- * 1. SIM is actually removed
- * 2. Slot becomes inactive, which results in SIM being treated as ABSENT, but SIM may not
- * have been removed.
- * @param phoneId phoneId for which the cleanup needs to be done
- * @param isSimAbsent boolean to indicate if the SIM is actually ABSENT (case 1 above)
- */
- private void cleanSubscriptionInPhone(int phoneId, boolean isSimAbsent) {
- if (sInactiveIccIds[phoneId] != null || (isSimAbsent && sIccId[phoneId] != null
- && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM))) {
- // When a SIM is unplugged, mark uicc applications enabled. This is to make sure when
- // user unplugs and re-inserts the SIM card, we re-enable it.
- // In certain cases this can happen before sInactiveIccIds is updated, which is why we
- // check for sIccId as well (in case of isSimAbsent). The scenario is: after SIM
- // deactivate request is sent to RIL, SIM is removed before SIM state is updated to
- // NOT_READY. We do not need to check if this exact scenario is hit, because marking
- // uicc applications enabled when SIM is removed should be okay to do regardless.
- logd("cleanSubscriptionInPhone: " + phoneId + ", inactive iccid "
- + sInactiveIccIds[phoneId]);
- if (sInactiveIccIds[phoneId] == null) {
- logd("cleanSubscriptionInPhone: " + phoneId + ", isSimAbsent=" + isSimAbsent
- + ", iccid=" + sIccId[phoneId]);
- }
- String iccId = sInactiveIccIds[phoneId] != null
- ? sInactiveIccIds[phoneId] : sIccId[phoneId];
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, true);
- if (isSimAbsent) {
- // When sim is absent, set the port index to invalid port index -1;
- value.put(SubscriptionManager.PORT_INDEX, TelephonyManager.INVALID_PORT_INDEX);
- }
- sContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null);
- sInactiveIccIds[phoneId] = null;
- }
- sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
- }
-
- protected void handleSimAbsent(int phoneId) {
- if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- logd("handleSimAbsent on invalid phoneId");
- return;
- }
- if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- logd("SIM" + (phoneId + 1) + " hot plug out");
- }
- cleanSubscriptionInPhone(phoneId, true);
-
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_UNKNOWN);
- updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- }
-
- protected void handleSimError(int phoneId) {
- if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- logd("SIM" + (phoneId + 1) + " Error ");
- }
- sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
- IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
- updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
- updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
- }
-
- protected synchronized void updateSubscriptionInfoByIccId(int phoneId,
- boolean updateEmbeddedSubs) {
- logd("updateSubscriptionInfoByIccId:+ Start - phoneId: " + phoneId);
- if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- loge("[updateSubscriptionInfoByIccId]- invalid phoneId=" + phoneId);
- return;
- }
- logd("updateSubscriptionInfoByIccId: removing subscription info record: phoneId "
- + phoneId);
- // Clear phoneId only when sim absent is not enough. It's possible to switch SIM profile
- // within the same slot. Need to clear the slot index of the previous sub. Thus always clear
- // for the changing slot first.
- mSubscriptionController.clearSubInfoRecord(phoneId);
-
- // If SIM is not absent, insert new record or update existing record.
- if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId]) && sIccId[phoneId] != null) {
- logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
- + sIccId[phoneId] + ", phoneId:" + phoneId);
- mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId);
- }
-
- List<SubscriptionInfo> subInfos =
- mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId);
- if (subInfos != null) {
- boolean changed = false;
- for (int i = 0; i < subInfos.size(); i++) {
- SubscriptionInfo temp = subInfos.get(i);
- ContentValues value = new ContentValues(1);
-
- String msisdn = TelephonyManager.getDefault().getLine1Number(
- temp.getSubscriptionId());
-
- if (!TextUtils.equals(msisdn, temp.getNumber())) {
- value.put(SubscriptionManager.NUMBER, msisdn);
- sContext.getContentResolver().update(SubscriptionManager
- .getUriForSubscriptionId(temp.getSubscriptionId()), value, null, null);
- changed = true;
- }
- }
- if (changed) {
- // refresh Cached Active Subscription Info List
- mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
- }
- }
-
- // TODO investigate if we can update for each slot separately.
- if (isAllIccIdQueryDone()) {
- // Ensure the modems are mapped correctly
- if (mSubscriptionManager.isActiveSubId(
- mSubscriptionManager.getDefaultDataSubscriptionId())) {
- mSubscriptionManager.setDefaultDataSubId(
- mSubscriptionManager.getDefaultDataSubscriptionId());
- } else {
- logd("bypass reset default data sub if inactive");
- }
- setSubInfoInitialized();
- }
-
- UiccController uiccController = UiccController.getInstance();
- UiccSlot[] uiccSlots = uiccController.getUiccSlots();
- if (uiccSlots != null && updateEmbeddedSubs) {
- List<Integer> cardIds = new ArrayList<>();
- for (UiccSlot uiccSlot : uiccSlots) {
- if (uiccSlot != null && uiccSlot.getUiccCard() != null) {
- int cardId = uiccController.convertToPublicCardId(
- uiccSlot.getUiccCard().getCardId());
- cardIds.add(cardId);
- }
- }
- updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- mSubscriptionController.notifySubscriptionInfoChanged();
- }
- if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
- });
- }
-
- mSubscriptionController.notifySubscriptionInfoChanged();
- if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
- }
-
- private void setSubInfoInitialized() {
- // Should only be triggered once.
- if (!sIsSubInfoInitialized) {
- if (DBG) logd("SubInfo Initialized");
- sIsSubInfoInitialized = true;
- mSubscriptionController.notifySubInfoReady();
- }
- MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded();
- }
-
- /**
- * Whether subscriptions of all SIMs are initialized.
- */
- public static boolean isSubInfoInitialized() {
- return sIsSubInfoInitialized;
- }
-
- /**
- * Updates the cached list of embedded subscription for the eUICC with the given list of card
- * IDs {@code cardIds}. The step of reading the embedded subscription list from eUICC card is
- * executed in background thread. The callback {@code callback} is executed after the cache is
- * refreshed. The callback is executed in main thread.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public void updateEmbeddedSubscriptions(List<Integer> cardIds,
- @Nullable UpdateEmbeddedSubsCallback callback) {
- // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but they
- // are filtered out of list calls as long as EuiccManager.isEnabled returns false).
- if (!mEuiccManager.isEnabled()) {
- if (DBG) logd("updateEmbeddedSubscriptions: eUICC not enabled");
- callback.run(false /* hasChanges */);
- return;
- }
-
- mBackgroundHandler.post(() -> {
- List<Pair<Integer, GetEuiccProfileInfoListResult>> results = new ArrayList<>();
- for (int cardId : cardIds) {
- GetEuiccProfileInfoListResult result =
- EuiccController.get().blockingGetEuiccProfileInfoList(cardId);
- if (DBG) logd("blockingGetEuiccProfileInfoList cardId " + cardId);
- results.add(Pair.create(cardId, result));
- }
-
- // The runnable will be executed in the main thread.
- this.post(() -> {
- boolean hasChanges = false;
- for (Pair<Integer, GetEuiccProfileInfoListResult> cardIdAndResult : results) {
- if (updateEmbeddedSubscriptionsCache(cardIdAndResult.first,
- cardIdAndResult.second)) {
- hasChanges = true;
- }
- }
- // The latest state in the main thread may be changed when the callback is
- // triggered.
- if (callback != null) {
- callback.run(hasChanges);
- }
- });
- });
- }
-
- /**
- * Update the cached list of embedded subscription based on the passed in
- * GetEuiccProfileInfoListResult {@code result}.
- *
- * @return true if changes may have been made. This is not a guarantee that changes were made,
- * but notifications about subscription changes may be skipped if this returns false as an
- * optimization to avoid spurious notifications.
- */
- private boolean updateEmbeddedSubscriptionsCache(int cardId,
- GetEuiccProfileInfoListResult result) {
- if (DBG) logd("updateEmbeddedSubscriptionsCache");
-
- if (result == null) {
- if (DBG) logd("updateEmbeddedSubscriptionsCache: IPC to the eUICC controller failed");
- retryUpdateEmbeddedSubscriptionCards.add(cardId);
- shouldRetryUpdateEmbeddedSubscriptions = true;
- return false;
- }
-
- // If the returned result is not RESULT_OK or the profile list is null, don't update cache.
- // Otherwise, update the cache.
- final EuiccProfileInfo[] embeddedProfiles;
- List<EuiccProfileInfo> list = result.getProfiles();
- if (result.getResult() == EuiccService.RESULT_OK && list != null) {
- embeddedProfiles = list.toArray(new EuiccProfileInfo[list.size()]);
- if (DBG) {
- logd("blockingGetEuiccProfileInfoList: got " + result.getProfiles().size()
- + " profiles");
- }
- } else {
- if (DBG) {
- logd("blockingGetEuiccProfileInfoList returns an error. "
- + "Result code=" + result.getResult()
- + ". Null profile list=" + (result.getProfiles() == null));
- }
- return false;
- }
-
- final boolean isRemovable = result.getIsRemovable();
-
- final String[] embeddedIccids = new String[embeddedProfiles.length];
- for (int i = 0; i < embeddedProfiles.length; i++) {
- embeddedIccids[i] = embeddedProfiles[i].getIccid();
- }
-
- if (DBG) logd("Get eUICC profile list of size " + embeddedProfiles.length);
-
- // Note that this only tracks whether we make any writes to the DB. It's possible this will
- // be set to true for an update even when the row contents remain exactly unchanged from
- // before, since we don't compare against the previous value. Since this is only intended to
- // avoid some spurious broadcasts (particularly for users who don't use eSIM at all), this
- // is fine.
- boolean hasChanges = false;
-
- // Update or insert records for all embedded subscriptions (except non-removable ones if the
- // current eUICC is non-removable, since we assume these are still accessible though not
- // returned by the eUICC controller).
- List<SubscriptionInfo> existingSubscriptions =
- mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
- embeddedIccids, isRemovable);
- ContentResolver contentResolver = sContext.getContentResolver();
- for (EuiccProfileInfo embeddedProfile : embeddedProfiles) {
- int index =
- findSubscriptionInfoForIccid(existingSubscriptions, embeddedProfile.getIccid());
- int prevCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
- int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
- if (index < 0) {
- // No existing entry for this ICCID; create an empty one.
- mSubscriptionController.insertEmptySubInfoRecord(
- embeddedProfile.getIccid(), SubscriptionManager.SIM_NOT_INSERTED);
- } else {
- nameSource = existingSubscriptions.get(index).getNameSource();
- prevCarrierId = existingSubscriptions.get(index).getCarrierId();
- existingSubscriptions.remove(index);
- }
-
- if (DBG) {
- logd("embeddedProfile " + embeddedProfile + " existing record "
- + (index < 0 ? "not found" : "found"));
- }
-
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- List<UiccAccessRule> ruleList = embeddedProfile.getUiccAccessRules();
- boolean isRuleListEmpty = false;
- if (ruleList == null || ruleList.size() == 0) {
- isRuleListEmpty = true;
- }
- values.put(SubscriptionManager.ACCESS_RULES,
- isRuleListEmpty ? null : UiccAccessRule.encodeRules(
- ruleList.toArray(new UiccAccessRule[ruleList.size()])));
- values.put(SubscriptionManager.IS_REMOVABLE, isRemovable);
- // override DISPLAY_NAME if the priority of existing nameSource is <= carrier
- if (SubscriptionController.getNameSourcePriority(nameSource)
- <= SubscriptionController.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_CARRIER)) {
- values.put(SubscriptionManager.DISPLAY_NAME, embeddedProfile.getNickname());
- values.put(SubscriptionManager.NAME_SOURCE,
- SubscriptionManager.NAME_SOURCE_CARRIER);
- }
- values.put(SubscriptionManager.PROFILE_CLASS, embeddedProfile.getProfileClass());
- values.put(SubscriptionManager.PORT_INDEX,
- getEmbeddedProfilePortIndex(embeddedProfile.getIccid()));
- CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier();
- if (cid != null) {
- // Due to the limited subscription information, carrier id identified here might
- // not be accurate compared with CarrierResolver. Only update carrier id if there
- // is no valid carrier id present.
- if (prevCarrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
- values.put(SubscriptionManager.CARRIER_ID,
- CarrierResolver.getCarrierIdFromIdentifier(sContext, cid));
- }
- String mcc = cid.getMcc();
- String mnc = cid.getMnc();
- values.put(SubscriptionManager.MCC_STRING, mcc);
- values.put(SubscriptionManager.MCC, mcc);
- values.put(SubscriptionManager.MNC_STRING, mnc);
- values.put(SubscriptionManager.MNC, mnc);
- }
- // If cardId = unsupported or unitialized, we have no reason to update DB.
- // Additionally, if the device does not support cardId for default eUICC, the CARD_ID
- // field should not contain the EID
- UiccController uiccController = UiccController.getInstance();
- if (cardId >= 0 && uiccController.getCardIdForDefaultEuicc()
- != TelephonyManager.UNSUPPORTED_CARD_ID) {
- values.put(SubscriptionManager.CARD_ID, uiccController.convertToCardString(cardId));
- }
- hasChanges = true;
- contentResolver.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.ICC_ID + "='" + embeddedProfile.getIccid() + "'", null);
-
- // refresh Cached Active Subscription Info List
- mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
- }
-
- // Remove all remaining subscriptions which have embedded = true. We set embedded to false
- // to ensure they are not returned in the list of embedded subscriptions (but keep them
- // around in case the subscription is added back later, which is equivalent to a removable
- // SIM being removed and reinserted).
- if (!existingSubscriptions.isEmpty()) {
- if (DBG) {
- logd("Removing existing embedded subscriptions of size"
- + existingSubscriptions.size());
- }
- List<String> iccidsToRemove = new ArrayList<>();
- for (int i = 0; i < existingSubscriptions.size(); i++) {
- SubscriptionInfo info = existingSubscriptions.get(i);
- if (info.isEmbedded()) {
- if (DBG) logd("Removing embedded subscription of IccId " + info.getIccId());
- iccidsToRemove.add("'" + info.getIccId() + "'");
- }
- }
- String whereClause = SubscriptionManager.ICC_ID + " IN ("
- + TextUtils.join(",", iccidsToRemove) + ")";
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 0);
- hasChanges = true;
- contentResolver.update(SubscriptionManager.CONTENT_URI, values, whereClause, null);
-
- // refresh Cached Active Subscription Info List
- mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
- }
-
- if (DBG) logd("updateEmbeddedSubscriptions done hasChanges=" + hasChanges);
- return hasChanges;
- }
-
- private int getEmbeddedProfilePortIndex(String iccId) {
- UiccSlot[] slots = UiccController.getInstance().getUiccSlots();
- for (UiccSlot slot : slots) {
- if (slot != null && slot.isEuicc()
- && slot.getPortIndexFromIccId(iccId) != TelephonyManager.INVALID_PORT_INDEX) {
- return slot.getPortIndexFromIccId(iccId);
- }
- }
- return TelephonyManager.INVALID_PORT_INDEX;
- }
- /**
- * Called by CarrierConfigLoader to update the subscription before sending a broadcast.
- */
- public void updateSubscriptionByCarrierConfigAndNotifyComplete(int phoneId,
- String configPackageName, PersistableBundle config, Message onComplete) {
- post(() -> {
- updateSubscriptionByCarrierConfig(phoneId, configPackageName, config);
- onComplete.sendToTarget();
- });
- }
-
- private String getDefaultCarrierServicePackageName() {
- CarrierConfigManager configManager =
- (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- return configManager.getDefaultCarrierServicePackageName();
- }
-
- private boolean isCarrierServicePackage(int phoneId, String pkgName) {
- if (pkgName.equals(getDefaultCarrierServicePackageName())) return false;
-
- String carrierPackageName = TelephonyManager.from(sContext)
- .getCarrierServicePackageNameForLogicalSlot(phoneId);
- if (DBG) logd("Carrier service package for subscription = " + carrierPackageName);
- return pkgName.equals(carrierPackageName);
- }
-
- /**
- * Update the currently active Subscription based on information from CarrierConfig
- */
- @VisibleForTesting
- public void updateSubscriptionByCarrierConfig(
- int phoneId, String configPackageName, PersistableBundle config) {
- if (!SubscriptionManager.isValidPhoneId(phoneId)
- || TextUtils.isEmpty(configPackageName) || config == null) {
- if (DBG) {
- logd("In updateSubscriptionByCarrierConfig(): phoneId=" + phoneId
- + " configPackageName=" + configPackageName + " config="
- + ((config == null) ? "null" : config.hashCode()));
- }
- return;
- }
-
- int currentSubId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
- if (!SubscriptionManager.isValidSubscriptionId(currentSubId)
- || currentSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- if (DBG) logd("No subscription is active for phone being updated");
- return;
- }
-
- SubscriptionInfo currentSubInfo = mSubscriptionController.getSubscriptionInfo(currentSubId);
- if (currentSubInfo == null) {
- loge("Couldn't retrieve subscription info for current subscription");
- return;
- }
-
- ContentValues cv = new ContentValues();
- ParcelUuid groupUuid = null;
-
- // carrier certificates are not subscription-specific, so we want to load them even if
- // this current package is not a CarrierServicePackage
- String[] certs = config.getStringArray(
- CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
- UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRulesFromCarrierConfig(
- certs);
- cv.put(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS,
- UiccAccessRule.encodeRules(carrierConfigAccessRules));
-
- if (!isCarrierServicePackage(phoneId, configPackageName)) {
- loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName);
- } else {
- boolean isOpportunistic = config.getBoolean(
- CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
- if (currentSubInfo.isOpportunistic() != isOpportunistic) {
- if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic);
- cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0");
- }
-
- String groupUuidString =
- config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
- if (!TextUtils.isEmpty(groupUuidString)) {
- try {
- // Update via a UUID Structure to ensure consistent formatting
- groupUuid = ParcelUuid.fromString(groupUuidString);
- if (groupUuid.equals(REMOVE_GROUP_UUID)
- && currentSubInfo.getGroupUuid() != null) {
- cv.put(SubscriptionManager.GROUP_UUID, (String) null);
- if (DBG) logd("Group Removed for" + currentSubId);
- } else if (mSubscriptionController.canPackageManageGroup(
- groupUuid, configPackageName)) {
- cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
- cv.put(SubscriptionManager.GROUP_OWNER, configPackageName);
- if (DBG) logd("Group Added for" + currentSubId);
- } else {
- loge("configPackageName " + configPackageName + " doesn't own grouUuid "
- + groupUuid);
- }
- } catch (IllegalArgumentException e) {
- loge("Invalid Group UUID=" + groupUuidString);
- }
- }
- }
-
- final int preferredUsageSetting =
- config.getInt(
- CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT,
- SubscriptionManager.USAGE_SETTING_UNKNOWN);
-
- @UsageSetting int newUsageSetting = calculateUsageSetting(
- currentSubInfo.getUsageSetting(),
- preferredUsageSetting);
-
- if (newUsageSetting != currentSubInfo.getUsageSetting()) {
- cv.put(SubscriptionManager.USAGE_SETTING, newUsageSetting);
- if (DBG) {
- logd("UsageSetting changed,"
- + " oldSetting=" + currentSubInfo.getUsageSetting()
- + " preferredSetting=" + preferredUsageSetting
- + " newSetting=" + newUsageSetting);
- }
- } else {
- if (DBG) {
- logd("UsageSetting unchanged,"
- + " oldSetting=" + currentSubInfo.getUsageSetting()
- + " preferredSetting=" + preferredUsageSetting
- + " newSetting=" + newUsageSetting);
- }
- }
-
- if (cv.size() > 0 && sContext.getContentResolver().update(SubscriptionManager
- .getUriForSubscriptionId(currentSubId), cv, null, null) > 0) {
- mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
- mSubscriptionController.notifySubscriptionInfoChanged();
- MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
- }
- }
-
- private static int findSubscriptionInfoForIccid(List<SubscriptionInfo> list, String iccid) {
- for (int i = 0; i < list.size(); i++) {
- if (TextUtils.equals(iccid, list.get(i).getIccId())) {
- return i;
- }
- }
- return -1;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected void broadcastSimStateChanged(int phoneId, String state, String reason) {
- // Note: This intent is way deprecated and is only being kept around because there's no
- // graceful way to deprecate a sticky broadcast that has a lot of listeners.
- // DO NOT add any new extras to this broadcast -- it is not protected by any permissions.
- Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
- i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
- SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
- logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason +
- " for phone: " + phoneId);
- IntentBroadcaster.getInstance().broadcastStickyIntent(sContext, i, phoneId);
- }
-
- protected void broadcastSimCardStateChanged(int phoneId, int state) {
- if (state != sSimCardState[phoneId]) {
- sSimCardState[phoneId] = state;
- Intent i = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
- i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
- SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
- // TODO(b/130664115) we manually populate this intent with the slotId. In the future we
- // should do a review of whether to make this public
- UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
- int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId);
- i.putExtra(PhoneConstants.SLOT_KEY, slotId);
- if (slot != null) {
- i.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId));
- }
- logd("Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED " + simStateString(state)
- + " for phone: " + phoneId + " slot: " + slotId + " port: "
- + slot.getPortIndexFromPhoneId(phoneId));
- sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- TelephonyMetrics.getInstance().updateSimState(phoneId, state);
- }
- }
-
- protected void broadcastSimApplicationStateChanged(int phoneId, int state) {
- // Broadcast if the state has changed, except if old state was UNKNOWN and new is NOT_READY,
- // because that's the initial state and a broadcast should be sent only on a transition
- // after SIM is PRESENT. The only exception is eSIM boot profile, where NOT_READY is the
- // terminal state.
- boolean isUnknownToNotReady =
- (sSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN
- && state == TelephonyManager.SIM_STATE_NOT_READY);
- IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
- boolean emptyProfile = iccCard != null && iccCard.isEmptyProfile();
- if (state != sSimApplicationState[phoneId] && (!isUnknownToNotReady || emptyProfile)) {
- sSimApplicationState[phoneId] = state;
- Intent i = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
- i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
- SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
- // TODO(b/130664115) we populate this intent with the actual slotId. In the future we
- // should do a review of whether to make this public
- UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
- int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId);
- i.putExtra(PhoneConstants.SLOT_KEY, slotId);
- if (slot != null) {
- i.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId));
- }
- logd("Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED " + simStateString(state)
- + " for phone: " + phoneId + " slot: " + slotId + "port: "
- + slot.getPortIndexFromPhoneId(phoneId));
- sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- TelephonyMetrics.getInstance().updateSimState(phoneId, state);
- }
- }
-
- /**
- * Convert SIM state into string
- *
- * @param state SIM state
- * @return SIM state in string format
- */
- public static String simStateString(@SimState int state) {
- switch (state) {
- case TelephonyManager.SIM_STATE_UNKNOWN:
- return "UNKNOWN";
- case TelephonyManager.SIM_STATE_ABSENT:
- return "ABSENT";
- case TelephonyManager.SIM_STATE_PIN_REQUIRED:
- return "PIN_REQUIRED";
- case TelephonyManager.SIM_STATE_PUK_REQUIRED:
- return "PUK_REQUIRED";
- case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
- return "NETWORK_LOCKED";
- case TelephonyManager.SIM_STATE_READY:
- return "READY";
- case TelephonyManager.SIM_STATE_NOT_READY:
- return "NOT_READY";
- case TelephonyManager.SIM_STATE_PERM_DISABLED:
- return "PERM_DISABLED";
- case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
- return "CARD_IO_ERROR";
- case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
- return "CARD_RESTRICTED";
- case TelephonyManager.SIM_STATE_LOADED:
- return "LOADED";
- case TelephonyManager.SIM_STATE_PRESENT:
- return "PRESENT";
- default:
- return "INVALID";
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static void logd(String message) {
- Rlog.d(LOG_TAG, message);
- }
-
- private static void loge(String message) {
- Rlog.e(LOG_TAG, message);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("SubscriptionInfoUpdater:");
- mCarrierServiceBindHelper.dump(fd, pw, args);
- }
-}
diff --git a/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java b/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java
new file mode 100644
index 0000000..994405b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TelephonyAdminReceiver.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserManager;
+import android.telephony.Rlog;
+
+/**
+ * A BroadcastReceiver for device administration events.
+ */
+public class TelephonyAdminReceiver extends BroadcastReceiver {
+ private static final String TAG = "TelephonyAdminReceiver";
+ private final Phone mPhone;
+ // We keep track of the last value to avoid updating when unrelated user restrictions change
+ private boolean mDisallowCellular2gRestriction = false;
+ private final Context mContext;
+ private UserManager mUserManager;
+
+ public TelephonyAdminReceiver(Context context, Phone phone) {
+ mContext = context;
+ mPhone = phone;
+ mUserManager = null;
+ if (ensureUserManagerExists()) {
+ mDisallowCellular2gRestriction = mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_CELLULAR_2G);
+ }
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Rlog.d(TAG, "Processing onReceive");
+ if (context == null || intent == null) return;
+ if (!intent.getAction().equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
+ Rlog.d(TAG, "Ignoring unexpected action: " + intent.getAction());
+ return;
+ }
+ if (!ensureUserManagerExists()) {
+ return;
+ }
+ boolean shouldDisallow2g = mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_CELLULAR_2G);
+
+ if (shouldDisallow2g != mDisallowCellular2gRestriction) {
+ Rlog.i(TAG,
+ "Updating allowed network types with new admin 2g restriction. no_cellular_2g: "
+ + shouldDisallow2g);
+ mDisallowCellular2gRestriction = shouldDisallow2g;
+ mPhone.sendSubscriptionSettings(false);
+ } else {
+ Rlog.i(TAG, "Skipping update of allowed network types. Restriction no_cellular_2g "
+ + "unchanged: " + mDisallowCellular2gRestriction);
+ }
+ }
+
+ /**
+ * Returns the current state of the {@link UserManager#DISALLOW_CELLULAR_2G} user restriction.
+ */
+ public boolean isCellular2gDisabled() {
+ return mDisallowCellular2gRestriction;
+ }
+
+ /**
+ * Tries to resolve the user manager system service. Returns true if successful, false
+ * otherwise.
+ */
+ private boolean ensureUserManagerExists() {
+ if (mUserManager == null) {
+ Rlog.d(TAG, "No user manager. Attempting to resolve one.");
+ mUserManager = mContext.getSystemService(UserManager.class);
+ }
+ if (mUserManager == null) {
+ Rlog.e(TAG,
+ "Could not get a user manager instance. All operations will be no-ops until "
+ + "one is resolved");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index dcbd2d5..f7f24a2 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -27,7 +27,6 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStatVfs;
-import android.telephony.AccessNetworkConstants.TransportType;
import android.text.TextUtils;
import com.android.ims.ImsManager;
@@ -40,11 +39,9 @@
import com.android.internal.telephony.data.DataSettingsManager;
import com.android.internal.telephony.data.LinkBandwidthEstimator;
import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
+import com.android.internal.telephony.imsphone.ImsNrSaModeHandler;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.nitz.NitzStateMachineImpl;
@@ -313,10 +310,6 @@
return new SimActivationTracker(phone);
}
- public DcTracker makeDcTracker(Phone phone, @TransportType int transportType) {
- return new DcTracker(phone, transportType);
- }
-
public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
return new CarrierSignalAgent(phone);
}
@@ -394,6 +387,14 @@
}
/**
+ * Create an ImsNrSaModeHandler.
+ */
+ public ImsNrSaModeHandler makeImsNrSaModeHandler(ImsPhone imsPhone) {
+
+ return new ImsNrSaModeHandler(imsPhone, imsPhone.getLooper());
+ }
+
+ /**
* Create an AppSmsManager for per-app SMS message.
*/
public AppSmsManager makeAppSmsManager(Context context) {
@@ -404,10 +405,6 @@
return new DeviceStateMonitor(phone);
}
- public TransportManager makeTransportManager(Phone phone) {
- return new TransportManager(phone);
- }
-
/**
* Make access networks manager
*
@@ -430,10 +427,6 @@
return new LocaleTracker(phone, nitzStateMachine, looper);
}
- public DataEnabledSettings makeDataEnabledSettings(Phone phone) {
- return new DataEnabledSettings(phone);
- }
-
public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
int phoneId, int precisePhoneType,
TelephonyComponentFactory telephonyComponentFactory) {
@@ -441,10 +434,6 @@
telephonyComponentFactory);
}
- public SubscriptionController initSubscriptionController(Context c) {
- return SubscriptionController.init(c);
- }
-
public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context,
Looper looper) {
return PhoneSwitcher.make(maxDataAttachModemCount, context, looper);
@@ -457,9 +446,14 @@
return new DisplayInfoController(phone);
}
- public MultiSimSettingController initMultiSimSettingController(Context c,
- SubscriptionController sc) {
- return MultiSimSettingController.init(c, sc);
+ /**
+ * Initialize multi sim settings controller.
+ *
+ * @param c The context.
+ * @return The multi sim settings controller instance.
+ */
+ public MultiSimSettingController initMultiSimSettingController(Context c) {
+ return MultiSimSettingController.init(c);
}
/**
@@ -469,11 +463,6 @@
return new SignalStrengthController(phone);
}
- public SubscriptionInfoUpdater makeSubscriptionInfoUpdater(Looper looper, Context context,
- SubscriptionController sc) {
- return new SubscriptionInfoUpdater(looper, context, sc);
- }
-
/**
* Create a new LinkBandwidthEstimator.
*/
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 40bb212..b9e04c8 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -62,7 +63,6 @@
*
*/
public class TelephonyTester {
- private static final String LOG_TAG = "TelephonyTester";
private static final boolean DBG = true;
/**
@@ -144,6 +144,7 @@
"com.android.internal.telephony.TestServiceState";
private static final String EXTRA_ACTION = "action";
+ private static final String EXTRA_PHONE_ID = "phone_id";
private static final String EXTRA_VOICE_RAT = "voice_rat";
private static final String EXTRA_DATA_RAT = "data_rat";
private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state";
@@ -157,6 +158,8 @@
private static final String ACTION_RESET = "reset";
+ private String mLogTag;
+
private static List<ImsExternalCallState> mImsExternalCallStates = null;
private Intent mServiceStateTestIntent;
@@ -198,11 +201,7 @@
sendTestSuppServiceNotification(intent);
} else if (action.equals(ACTION_TEST_SERVICE_STATE)) {
log("handle test service state changed intent");
- // Trigger the service state update. The replacement will be done in
- // overrideServiceState().
- mServiceStateTestIntent = intent;
- mPhone.getServiceStateTracker().sendEmptyMessage(
- ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
+ setServiceStateTestIntent(intent);
} else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
log("handle test IMS ecall intent");
testImsECall();
@@ -216,7 +215,7 @@
if (DBG) log("onReceive: unknown action=" + action);
}
} catch (BadParcelableException e) {
- Rlog.w(LOG_TAG, e);
+ Rlog.w(mLogTag, e);
}
}
};
@@ -225,6 +224,7 @@
mPhone = phone;
if (TelephonyUtils.IS_DEBUGGABLE) {
+ mLogTag = "TelephonyTester-" + mPhone.getPhoneId();
IntentFilter filter = new IntentFilter();
filter.addAction(mPhone.getActionDetached());
@@ -261,8 +261,8 @@
}
}
- private static void log(String s) {
- Rlog.d(LOG_TAG, s);
+ private void log(String s) {
+ Rlog.d(mLogTag, s);
}
private void handleSuppServiceFailedIntent(Intent intent) {
@@ -385,8 +385,25 @@
}
}
+ /**
+ * Set the service state test intent.
+ *
+ * @param intent The service state test intent.
+ */
+ public void setServiceStateTestIntent(@NonNull Intent intent) {
+ mServiceStateTestIntent = intent;
+ // Trigger the service state update. The replacement will be done in
+ // overrideServiceState().
+ mPhone.getServiceStateTracker().sendEmptyMessage(
+ ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
+ }
+
void overrideServiceState(ServiceState ss) {
if (mServiceStateTestIntent == null || ss == null) return;
+ if (mPhone.getPhoneId() != mServiceStateTestIntent.getIntExtra(
+ EXTRA_PHONE_ID, mPhone.getPhoneId())) {
+ return;
+ }
if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION)
&& ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) {
log("Service state override reset");
@@ -394,13 +411,36 @@
}
if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
+ int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
+ ServiceState.STATE_OUT_OF_SERVICE);
ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
ServiceState.STATE_OUT_OF_SERVICE));
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
+ if (state == ServiceState.STATE_IN_SERVICE) {
+ builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ } else {
+ builder.setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+ }
+ ss.addNetworkRegistrationInfo(builder.build());
log("Override voice service state with " + ss.getState());
}
if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
- ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
- ServiceState.STATE_OUT_OF_SERVICE));
+ int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
+ ServiceState.STATE_OUT_OF_SERVICE);
+ ss.setDataRegState(state);
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
+ if (state == ServiceState.STATE_IN_SERVICE) {
+ builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ } else {
+ builder.setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+ }
+ ss.addNetworkRegistrationInfo(builder.build());
log("Override data service state with " + ss.getDataRegistrationState());
}
if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
index 5b55c35..96fd208 100644
--- a/src/java/com/android/internal/telephony/UiccPhoneBookController.java
+++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -19,11 +19,12 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentValues;
import android.os.Build;
import android.os.TelephonyServiceManager.ServiceRegisterer;
import android.telephony.TelephonyFrameworkInitializer;
-import android.content.ContentValues;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.telephony.Rlog;
@@ -146,7 +147,7 @@
private IccPhoneBookInterfaceManager
getIccPhoneBookInterfaceManager(int subId) {
- int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ int phoneId = SubscriptionManagerService.getInstance().getPhoneId(subId);
try {
return PhoneFactory.getPhone(phoneId).getIccPhoneBookInterfaceManager();
} catch (NullPointerException e) {
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index 5bbe8b4..e374811 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -15,10 +15,13 @@
*/
package com.android.internal.telephony;
+import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
+
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
import android.provider.VoicemailContract;
import android.telecom.PhoneAccountHandle;
import android.telephony.PhoneNumberUtils;
@@ -58,7 +61,7 @@
/**
* Convert the subId to a {@link PhoneAccountHandle}
*/
- PhoneAccountHandle fromSubId(int subId);
+ PhoneAccountHandle fromSubId(int subId, Context context);
}
private static final String TAG = "VvmSmsFilter";
@@ -75,7 +78,7 @@
new PhoneAccountHandleConverter() {
@Override
- public PhoneAccountHandle fromSubId(int subId) {
+ public PhoneAccountHandle fromSubId(int subId, Context context) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
return null;
}
@@ -83,6 +86,15 @@
if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
return null;
}
+ SubscriptionManager subscriptionManager =
+ (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ UserHandle userHandle = subscriptionManager.getSubscriptionUserHandle(subId);
+ if (userHandle != null) {
+ return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+ Integer.toString(PhoneFactory.getPhone(phoneId).getSubId()),
+ userHandle);
+ }
return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
Integer.toString(PhoneFactory.getPhone(phoneId).getSubId()));
}
@@ -136,7 +148,8 @@
return false;
}
- PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId);
+ PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId,
+ context);
if (phoneAccountHandle == null) {
Log.e(TAG, "Unable to convert subId " + subId + " to PhoneAccountHandle");
@@ -294,7 +307,19 @@
result.firstMessage = message;
}
String body = message.getMessageBody();
- if (body == null && message.getUserData() != null) {
+
+ /*
+ * For visual voice mail SMS message, UTF-8 is used by default
+ * {@link com.android.internal.telephony.SmsController#sendVisualVoicemailSmsForSubscriber}
+ *
+ * If config_sms_decode_gsm_8bit_data is enabled, GSM-8bit will be used to decode the
+ * received message. However, the message is most likely encoded with UTF-8. Therefore,
+ * we need to retry decoding the received message with UTF-8.
+ */
+ if ((body == null || (message.is3gpp()
+ && message.getReceivedEncodingType() == ENCODING_8BIT))
+ && message.getUserData() != null) {
+ Log.d(TAG, "getFullMessage decode using UTF-8");
// Attempt to interpret the user data as UTF-8. UTF-8 string over data SMS using
// 8BIT data coding scheme is our recommended way to send VVM SMS and is used in CTS
// Tests. The OMTP visual voicemail specification does not specify the SMS type and
@@ -303,7 +328,8 @@
try {
body = decoder.decode(byteBuffer).toString();
} catch (CharacterCodingException e) {
- // User data is not decode-able as UTF-8. Ignoring.
+ Log.e(TAG, "getFullMessage: got CharacterCodingException"
+ + " when decoding with UTF-8, e = " + e);
return null;
}
}
diff --git a/src/java/com/android/internal/telephony/VoiceIndication.java b/src/java/com/android/internal/telephony/VoiceIndication.java
index 4ed82a6..9720bb7 100644
--- a/src/java/com/android/internal/telephony/VoiceIndication.java
+++ b/src/java/com/android/internal/telephony/VoiceIndication.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_CALL_WAITING;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_INFO_REC;
@@ -63,7 +65,7 @@
*/
public void callRing(int indicationType, boolean isGsm,
android.hardware.radio.voice.CdmaSignalInfoRecord record) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
char[] response = null;
@@ -78,7 +80,7 @@
mRil.writeMetricsCallRing(response);
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response);
if (mRil.mRingRegistrant != null) {
mRil.mRingRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
@@ -90,9 +92,9 @@
* @param indicationType Type of radio indication
*/
public void callStateChanged(int indicationType) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
mRil.mCallStateRegistrants.notifyRegistrants();
}
@@ -104,7 +106,7 @@
*/
public void cdmaCallWaiting(int indicationType,
android.hardware.radio.voice.CdmaCallWaiting callWaitingRecord) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
// TODO: create a CdmaCallWaitingNotification constructor that takes in these fields to make
// sure no fields are missing
@@ -121,7 +123,7 @@
notification.numberType = callWaitingRecord.numberType;
notification.numberPlan = callWaitingRecord.numberPlan;
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification);
mRil.mCallWaitingInfoRegistrants.notifyRegistrants(
new AsyncResult(null, notification, null));
@@ -134,7 +136,7 @@
*/
public void cdmaInfoRec(int indicationType,
android.hardware.radio.voice.CdmaInformationRecord[] records) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
for (int i = 0; i < records.length; i++) {
android.hardware.radio.voice.CdmaInformationRecord record = records[i];
@@ -220,7 +222,9 @@
+ CdmaInformationRecords.idToString(id) + " ");
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords);
+ }
mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords);
}
}
@@ -231,11 +235,13 @@
* @param status CDMA OTA provision status
*/
public void cdmaOtaProvisionStatus(int indicationType, int status) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
int[] response = new int[] {status};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response);
+ if (mRil.isLogOrTrace()) {
+ mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response);
+ }
mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
@@ -248,7 +254,7 @@
*/
public void currentEmergencyNumberList(int indicationType,
android.hardware.radio.voice.EmergencyNumber[] emergencyNumberList) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.length);
for (android.hardware.radio.voice.EmergencyNumber enHal : emergencyNumberList) {
@@ -259,7 +265,7 @@
response.add(emergencyNumber);
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NUMBER_LIST, response);
// Cache emergency number list from last indication.
mRil.cacheEmergencyNumberListIndication(response);
@@ -275,9 +281,9 @@
* @param indicationType Type of radio indication
*/
public void enterEmergencyCallbackMode(int indicationType) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
if (mRil.mEmergencyCallbackModeRegistrant != null) {
mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
@@ -290,9 +296,9 @@
* @param indicationType Type of radio indication
*/
public void exitEmergencyCallbackMode(int indicationType) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants();
}
@@ -303,9 +309,9 @@
* @param start true = start play ringback tone, false = stop playing ringback tone
*/
public void indicateRingbackTone(int indicationType, boolean start) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
+ if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null));
}
@@ -318,7 +324,7 @@
*/
public void onSupplementaryServiceIndication(int indicationType,
android.hardware.radio.voice.StkCcUnsolSsResult ss) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
int num;
SsData ssData = new SsData();
@@ -355,7 +361,7 @@
}
}
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData);
if (mRil.mSsRegistrant != null) {
mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null));
@@ -370,9 +376,9 @@
* @param msg Message string in UTF-8, if applicable
*/
public void onUssd(int indicationType, int ussdModeType, String msg) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
+ if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
// TODO: Clean this up with a parcelable class for better self-documentation
String[] resp = new String[]{"" + ussdModeType, msg};
@@ -386,9 +392,9 @@
* @param indicationType Type of radio indication
*/
public void resendIncallMute(int indicationType) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
+ if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
mRil.mResendIncallMuteRegistrants.notifyRegistrants();
}
@@ -399,11 +405,11 @@
* @param state New SRVCC State
*/
public void srvccStateNotify(int indicationType, int state) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
int[] response = new int[] {state};
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response);
mRil.writeMetricsSrvcc(state);
mRil.mSrvccStateRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -415,9 +421,9 @@
* @param alpha ALPHA string from UICC in UTF-8 format
*/
public void stkCallControlAlphaNotify(int indicationType, String alpha) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
if (mRil.mCatCcAlphaRegistrant != null) {
mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult(null, alpha, null));
@@ -430,9 +436,9 @@
* @param timeout Timeout value in milliseconds for setting up voice call
*/
public void stkCallSetup(int indicationType, long timeout) {
- mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+ mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
+ if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
if (mRil.mCatCallSetUpRegistrant != null) {
mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult(null, timeout, null));
diff --git a/src/java/com/android/internal/telephony/VoiceResponse.java b/src/java/com/android/internal/telephony/VoiceResponse.java
index b1ba9d9..d1e060e 100644
--- a/src/java/com/android/internal/telephony/VoiceResponse.java
+++ b/src/java/com/android/internal/telephony/VoiceResponse.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.voice.IRadioVoiceResponse;
@@ -47,49 +49,49 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void acceptCallResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void cancelPendingUssdResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void conferenceResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void dialResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void emergencyDialResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void exitEmergencyCallbackModeResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void explicitCallTransferResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
@@ -99,7 +101,7 @@
*/
public void getCallForwardStatusResponse(RadioResponseInfo responseInfo,
android.hardware.radio.voice.CallForwardInfo[] callForwardInfos) {
- RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
if (rr != null) {
CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.length];
for (int i = 0; i < callForwardInfos.length; i++) {
@@ -129,7 +131,7 @@
public void getCallWaitingResponse(RadioResponseInfo responseInfo, boolean enable,
int serviceClass) {
RadioResponse.responseInts(
- RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0, serviceClass);
+ HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0, serviceClass);
}
/**
@@ -137,7 +139,7 @@
* @param status indicates CLIP status
*/
public void getClipResponse(RadioResponseInfo responseInfo, int status) {
- RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, status);
+ RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, status);
}
/**
@@ -146,7 +148,7 @@
* @param m is "m" parameter from TS 27.007 7.7
*/
public void getClirResponse(RadioResponseInfo responseInfo, int n, int m) {
- RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, n, m);
+ RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, n, m);
}
/**
@@ -155,7 +157,7 @@
*/
public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
android.hardware.radio.voice.Call[] calls) {
- RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
if (rr != null) {
int num = calls.length;
@@ -198,7 +200,7 @@
*/
public void getLastCallFailCauseResponse(RadioResponseInfo responseInfo,
android.hardware.radio.voice.LastCallFailCauseInfo fcInfo) {
- RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
if (rr != null) {
LastCallFailCause ret = new LastCallFailCause();
@@ -216,7 +218,7 @@
* @param enable true for "mute enabled" and false for "mute disabled"
*/
public void getMuteResponse(RadioResponseInfo responseInfo, boolean enable) {
- RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0);
+ RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0);
}
/**
@@ -225,7 +227,7 @@
* true for Enhanced Privacy Mode (Private Long Code Mask)
*/
public void getPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo, boolean enable) {
- RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0);
+ RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0);
}
/**
@@ -233,35 +235,35 @@
* @param mode TTY mode
*/
public void getTtyModeResponse(RadioResponseInfo responseInfo, int mode) {
- RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, mode);
+ RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, mode);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void handleStkCallSetupRequestFromSimResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void hangupConnectionResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void hangupForegroundResumeBackgroundResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void hangupWaitingOrBackgroundResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
@@ -269,7 +271,7 @@
* @param enable true for "vonr enabled" and false for "vonr disabled"
*/
public void isVoNrEnabledResponse(RadioResponseInfo responseInfo, boolean enable) {
- RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+ RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
if (rr != null) {
if (responseInfo.error == RadioError.NONE) {
@@ -283,112 +285,112 @@
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void rejectCallResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendBurstDtmfResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendCdmaFeatureCodeResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendDtmfResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void sendUssdResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void separateConnectionResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCallForwardResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCallWaitingResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setClirResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setMuteResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setTtyModeResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setVoNrEnabledResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void startDtmfResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void stopDtmfResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void switchWaitingOrHoldingAndActiveResponse(RadioResponseInfo responseInfo) {
- RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+ RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
}
@Override
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
old mode 100755
new mode 100644
index d6f69e2..d6ea4ad
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -46,6 +46,7 @@
import android.text.TextUtils;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import com.google.android.mms.pdu.GenericPdu;
@@ -245,9 +246,10 @@
System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
}
- int[] subIds = SubscriptionManager.getSubId(phoneId);
- int subId = (subIds != null) && (subIds.length > 0) ? subIds[0]
- : SmsManager.getDefaultSmsSubscriptionId();
+ int subId = SubscriptionManager.getSubscriptionId(phoneId);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ subId = SmsManager.getDefaultSmsSubscriptionId();
+ }
// Continue if PDU parsing fails: the default messaging app may successfully parse the
// same PDU.
@@ -391,7 +393,10 @@
// Direct the intent to only the default MMS app. If we can't find a default MMS app
// then sent it to all broadcast receivers.
- ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
+ UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, subId);
+ ComponentName componentName = SmsApplication.getDefaultMmsApplicationAsUser(mContext,
+ true, userHandle);
+
Bundle options = null;
if (componentName != null) {
// Deliver MMS message only to this receiver
@@ -409,9 +414,12 @@
options = bopts.toBundle();
}
+ if (userHandle == null) {
+ userHandle = UserHandle.SYSTEM;
+ }
handler.dispatchIntent(intent, getPermissionForType(result.mimeType),
getAppOpsStringPermissionForIntent(result.mimeType), options, receiver,
- UserHandle.SYSTEM, subId);
+ userHandle, subId);
return Activity.RESULT_OK;
}
diff --git a/src/java/com/android/internal/telephony/WspTypeDecoder.java b/src/java/com/android/internal/telephony/WspTypeDecoder.java
old mode 100755
new mode 100644
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
old mode 100755
new mode 100644
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 3d21270..4447c07 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -40,6 +40,7 @@
private ToneSettings mToneSettings = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CallSettings mCallSettings = null;
+ private SMSSettings mSMSSettings = null;
private SetupEventListSettings mSetupEventListSettings = null;
private boolean mLoadIconFailed = false;
@@ -61,6 +62,14 @@
public TextMessage callMsg;
}
+ /**
+ * Container for SEND SMS command settings.
+ */
+ public class SMSSettings {
+ public TextMessage smsText;
+ public TextMessage destAddr;
+ }
+
public class SetupEventListSettings {
@UnsupportedAppUsage
public int[] eventList;
@@ -84,57 +93,69 @@
mCmdDet = cmdParams.mCmdDet;
mLoadIconFailed = cmdParams.mLoadIconFailed;
switch(getCmdType()) {
- case SET_UP_MENU:
- case SELECT_ITEM:
- mMenu = ((SelectItemParams) cmdParams).mMenu;
- break;
- case DISPLAY_TEXT:
- case SET_UP_IDLE_MODE_TEXT:
- case SEND_DTMF:
- case SEND_SMS:
- case REFRESH:
- case RUN_AT:
- case SEND_SS:
- case SEND_USSD:
- mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
- break;
- case GET_INPUT:
- case GET_INKEY:
- mInput = ((GetInputParams) cmdParams).mInput;
- break;
- case LAUNCH_BROWSER:
- mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg;
- mBrowserSettings = new BrowserSettings();
- mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl;
- mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode;
- break;
- case PLAY_TONE:
- PlayToneParams params = (PlayToneParams) cmdParams;
- mToneSettings = params.mSettings;
- mTextMsg = params.mTextMsg;
- break;
- case GET_CHANNEL_STATUS:
- mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
- break;
- case SET_UP_CALL:
- mCallSettings = new CallSettings();
- mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
- mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg;
- break;
- case OPEN_CHANNEL:
- case CLOSE_CHANNEL:
- case RECEIVE_DATA:
- case SEND_DATA:
- BIPClientParams param = (BIPClientParams) cmdParams;
- mTextMsg = param.mTextMsg;
- break;
- case SET_UP_EVENT_LIST:
- mSetupEventListSettings = new SetupEventListSettings();
- mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
- break;
- case PROVIDE_LOCAL_INFORMATION:
- default:
- break;
+ case SET_UP_MENU:
+ case SELECT_ITEM:
+ mMenu = ((SelectItemParams) cmdParams).mMenu;
+ break;
+ case SEND_SMS:
+ /* If cmdParams is an instanceof SendSMSParams , then it means config value
+ * config_stk_sms_send_support is true and the SMS should be sent by framework
+ */
+ if (cmdParams instanceof SendSMSParams) {
+ mSMSSettings = new SMSSettings();
+ mSMSSettings.smsText = ((SendSMSParams) cmdParams).mTextSmsMsg;
+ mSMSSettings.destAddr = ((SendSMSParams) cmdParams).mDestAddress;
+ mTextMsg = ((SendSMSParams) cmdParams).mDisplayText.mTextMsg;
+ } else {
+ mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
+ }
+ break;
+ case DISPLAY_TEXT:
+ case SET_UP_IDLE_MODE_TEXT:
+ case SEND_DTMF:
+ case REFRESH:
+ case RUN_AT:
+ case SEND_SS:
+ case SEND_USSD:
+ mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
+ break;
+ case GET_INPUT:
+ case GET_INKEY:
+ mInput = ((GetInputParams) cmdParams).mInput;
+ break;
+ case LAUNCH_BROWSER:
+ mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg;
+ mBrowserSettings = new BrowserSettings();
+ mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl;
+ mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode;
+ break;
+ case PLAY_TONE:
+ PlayToneParams params = (PlayToneParams) cmdParams;
+ mToneSettings = params.mSettings;
+ mTextMsg = params.mTextMsg;
+ break;
+ case GET_CHANNEL_STATUS:
+ mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
+ break;
+ case SET_UP_CALL:
+ mCallSettings = new CallSettings();
+ mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
+ mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg;
+ break;
+ case OPEN_CHANNEL:
+ case CLOSE_CHANNEL:
+ case RECEIVE_DATA:
+ case SEND_DATA:
+ BIPClientParams param = (BIPClientParams) cmdParams;
+ mTextMsg = param.mTextMsg;
+ break;
+ case SET_UP_EVENT_LIST:
+ mSetupEventListSettings = new SetupEventListSettings();
+ mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ default:
+ break;
}
}
@@ -145,29 +166,34 @@
mInput = in.readParcelable(Input.class.getClassLoader());
mLoadIconFailed = (in.readByte() == 1);
switch (getCmdType()) {
- case LAUNCH_BROWSER:
- mBrowserSettings = new BrowserSettings();
- mBrowserSettings.url = in.readString();
- mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()];
- break;
- case PLAY_TONE:
- mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader());
- break;
- case SET_UP_CALL:
- mCallSettings = new CallSettings();
- mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader());
- mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader());
- break;
- case SET_UP_EVENT_LIST:
- mSetupEventListSettings = new SetupEventListSettings();
- int length = in.readInt();
- mSetupEventListSettings.eventList = new int[length];
- for (int i = 0; i < length; i++) {
- mSetupEventListSettings.eventList[i] = in.readInt();
- }
- break;
- default:
- break;
+ case LAUNCH_BROWSER:
+ mBrowserSettings = new BrowserSettings();
+ mBrowserSettings.url = in.readString();
+ mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()];
+ break;
+ case PLAY_TONE:
+ mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader());
+ break;
+ case SET_UP_CALL:
+ mCallSettings = new CallSettings();
+ mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader());
+ mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader());
+ break;
+ case SET_UP_EVENT_LIST:
+ mSetupEventListSettings = new SetupEventListSettings();
+ int length = in.readInt();
+ mSetupEventListSettings.eventList = new int[length];
+ for (int i = 0; i < length; i++) {
+ mSetupEventListSettings.eventList[i] = in.readInt();
+ }
+ break;
+ case SEND_SMS:
+ mSMSSettings = new SMSSettings();
+ mSMSSettings.smsText = in.readParcelable(SendSMSParams.class.getClassLoader());
+ mSMSSettings.destAddr = in.readParcelable(SendSMSParams.class.getClassLoader());
+ break;
+ default:
+ break;
}
}
@@ -178,23 +204,29 @@
dest.writeParcelable(mMenu, 0);
dest.writeParcelable(mInput, 0);
dest.writeByte((byte) (mLoadIconFailed ? 1 : 0));
- switch(getCmdType()) {
- case LAUNCH_BROWSER:
- dest.writeString(mBrowserSettings.url);
- dest.writeInt(mBrowserSettings.mode.ordinal());
- break;
- case PLAY_TONE:
- dest.writeParcelable(mToneSettings, 0);
- break;
- case SET_UP_CALL:
- dest.writeParcelable(mCallSettings.confirmMsg, 0);
- dest.writeParcelable(mCallSettings.callMsg, 0);
- break;
- case SET_UP_EVENT_LIST:
- dest.writeIntArray(mSetupEventListSettings.eventList);
- break;
- default:
- break;
+ switch (getCmdType()) {
+ case LAUNCH_BROWSER:
+ dest.writeString(mBrowserSettings.url);
+ dest.writeInt(mBrowserSettings.mode.ordinal());
+ break;
+ case PLAY_TONE:
+ dest.writeParcelable(mToneSettings, 0);
+ break;
+ case SET_UP_CALL:
+ dest.writeParcelable(mCallSettings.confirmMsg, 0);
+ dest.writeParcelable(mCallSettings.callMsg, 0);
+ break;
+ case SET_UP_EVENT_LIST:
+ dest.writeIntArray(mSetupEventListSettings.eventList);
+ break;
+ case SEND_SMS:
+ if (mSMSSettings != null) {
+ dest.writeParcelable(mSMSSettings.smsText, 0);
+ dest.writeParcelable(mSMSSettings.destAddr, 0);
+ }
+ break;
+ default:
+ break;
}
}
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 9ed5eb9..fa2b19b 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -20,25 +20,36 @@
import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT;
+import android.app.Activity;
import android.app.ActivityManager;
+import android.app.PendingIntent;
import android.app.backup.BackupManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources.NotFoundException;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.ProxyController;
+import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
@@ -138,12 +149,21 @@
static final String STK_DEFAULT = "Default Message";
+ private static final String SMS_DELIVERY_ACTION =
+ "com.android.internal.telephony.cat.SMS_DELIVERY_ACTION";
+ private static final String SMS_SENT_ACTION =
+ "com.android.internal.telephony.cat.SMS_SENT_ACTION";
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mSlotId;
+ private static HandlerThread sCatServiceThread;
/* For multisim catservice should not be singleton */
private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
- Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId) {
+ Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId,
+ Looper looper) {
+ //creating new thread to avoid deadlock conditions with the framework thread.
+ super(looper);
if (ci == null || ca == null || ir == null || context == null || fh == null
|| uiccProfile == null) {
throw new NullPointerException(
@@ -187,6 +207,9 @@
CatLog.d(this, "Running CAT service on Slotid: " + mSlotId +
". STK app installed:" + mStkAppInstalled);
+
+ mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_DELIVERY_ACTION));
+ mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_SENT_ACTION));
}
/**
@@ -200,6 +223,10 @@
*/
public static CatService getInstance(CommandsInterface ci,
Context context, UiccProfile uiccProfile, int slotId) {
+ if (sCatServiceThread == null) {
+ sCatServiceThread = new HandlerThread("CatServiceThread");
+ sCatServiceThread.start();
+ }
UiccCardApplication ca = null;
IccFileHandler fh = null;
IccRecords ir = null;
@@ -227,8 +254,8 @@
|| uiccProfile == null) {
return null;
}
-
- sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId);
+ sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId,
+ sCatServiceThread.getLooper());
} else if ((ir != null) && (mIccRecords != ir)) {
if (mIccRecords != null) {
mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]);
@@ -447,8 +474,49 @@
((DisplayTextParams)cmdParams).mTextMsg.text = null;
}
break;
- case SEND_DTMF:
case SEND_SMS:
+ /* If cmdParams is an instanceof SendSMSParams , then it means config value
+ * config_stk_sms_send_support is true and the SMS should be sent by framework
+ */
+ if (cmdParams instanceof SendSMSParams) {
+ String text = null, destAddr = null;
+ if (((SendSMSParams) cmdParams).mTextSmsMsg != null) {
+ text = ((SendSMSParams) cmdParams).mTextSmsMsg.text;
+ }
+ if (((SendSMSParams) cmdParams).mDestAddress != null) {
+ destAddr = ((SendSMSParams) cmdParams).mDestAddress.text;
+ }
+ if (text != null && destAddr != null) {
+ ProxyController proxyController = ProxyController.getInstance(mContext);
+ SubscriptionManager subscriptionManager = (SubscriptionManager)
+ mContext.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ SubscriptionInfo subInfo =
+ subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
+ mSlotId);
+ if (subInfo != null) {
+ sendStkSms(text, destAddr, subInfo.getSubscriptionId(), cmdParams,
+ proxyController);
+ } else {
+ sendTerminalResponse(cmdParams.mCmdDet,
+ ResultCode.CMD_DATA_NOT_UNDERSTOOD, false, 0x00, null);
+ CatLog.d(this, "Subscription info is null");
+ }
+ } else {
+ sendTerminalResponse(cmdParams.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
+ false, 0x00, null);
+ CatLog.d(this, "Sms text or Destination Address is null");
+ }
+ } else {
+ if ((((DisplayTextParams) cmdParams).mTextMsg.text != null)
+ && (((DisplayTextParams) cmdParams).mTextMsg.text.equals(
+ STK_DEFAULT))) {
+ message = mContext.getText(com.android.internal.R.string.sending);
+ ((DisplayTextParams) cmdParams).mTextMsg.text = message.toString();
+ }
+ }
+ break;
+ case SEND_DTMF:
case SEND_SS:
case SEND_USSD:
if ((((DisplayTextParams)cmdParams).mTextMsg.text != null)
@@ -536,6 +604,94 @@
broadcastCatCmdIntent(cmdMsg);
}
+ /**
+ * Used to send STK based sms via CATService
+ * @param text The message body
+ * @param destAddr The destination Address
+ * @param subId Subscription Id
+ * @param cmdParams Send SMS Command Params
+ * @param proxyController ProxyController
+ * @hide
+ */
+ public void sendStkSms(String text, String destAddr, int subId, CommandParams cmdParams,
+ ProxyController proxyController) {
+ PendingIntent sentPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(SMS_SENT_ACTION)
+ .putExtra("cmdDetails", cmdParams.mCmdDet)
+ .setPackage(mContext.getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
+ PendingIntent deliveryPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(SMS_DELIVERY_ACTION)
+ .putExtra("cmdDetails", cmdParams.mCmdDet)
+ .setPackage(mContext.getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
+ SmsController smsController = proxyController.getSmsController();
+ smsController.sendTextForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), destAddr, null, text, sentPendingIntent,
+ deliveryPendingIntent, false, 0L, true, true);
+ }
+
+ /**
+ * BroadcastReceiver class to handle error and success cases of
+ * SEND and DELIVERY pending intents used for sending of STK SMS
+ */
+ @VisibleForTesting
+ public final BroadcastReceiver mSmsBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ CommandDetails commandDetails = (CommandDetails) intent.getExtra("cmdDetails");
+ if (intent.getAction().equals(SMS_SENT_ACTION)) {
+ int resultCode = getResultCode();
+ ResultCode terminalResponseResultCode = ResultCode.NETWORK_CRNTLY_UNABLE_TO_PROCESS;
+ CatLog.d(this, "STK SMS errorCode : " + resultCode);
+ int additionalInfo = 0;
+ if (resultCode != Activity.RESULT_OK) {
+ /**
+ * The Terminal Response Result code is assigned as per Section 12.12.3
+ * and 12.12.5 TS 101.267. The Result code SMS_RP_ERROR is added in Ims Case
+ * and additional information is added as per RP-Cause Values in TS 124.011.
+ * The Result code NETWORK_CRNTLY_UNABLE_TO_PROCESS is added in non-Ims Case
+ * and additional information added as per cause values in TS 04.08.
+ */
+ if (intent.hasExtra("ims") && intent.getBooleanExtra("ims", false)) {
+ terminalResponseResultCode = ResultCode.SMS_RP_ERROR;
+ //Additional information's 8th bit is 0 as per section 12.12.5 of TS 101.267
+ if (intent.hasExtra("errorCode")) {
+ additionalInfo = (int) intent.getExtra("errorCode");
+ if ((additionalInfo & 0x80) != 0) additionalInfo = 0;
+ }
+ } else {
+ //Additional information's 8th bit is 1 as per section 12.12.3 of TS 101.267
+ if (intent.hasExtra("errorCode")) {
+ additionalInfo = (int) intent.getExtra("errorCode");
+ additionalInfo |= 0x80;
+ }
+ }
+ CatLog.d(this, "Error delivering STK SMS errorCode : " + additionalInfo
+ + " terminalResponseResultCode = " + terminalResponseResultCode);
+ sendTerminalResponse(commandDetails, terminalResponseResultCode,
+ true, additionalInfo, null);
+ } else {
+ CatLog.d(this, " STK SMS sent successfully ");
+ }
+ }
+ if (intent.getAction().equals(SMS_DELIVERY_ACTION)) {
+ int resultCode = getResultCode();
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ sendTerminalResponse(commandDetails, ResultCode.OK, false, 0, null);
+ CatLog.d(this, " STK SMS delivered successfully ");
+ break;
+ default:
+ CatLog.d(this, "Error delivering STK SMS : " + resultCode);
+ sendTerminalResponse(commandDetails,
+ ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, false,
+ 0, null);
+ }
+ }
+ }
+ };
private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) {
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
@@ -808,9 +964,9 @@
//TODO Need to take care for MSIM
public static AppInterface getInstance() {
int slotId = PhoneConstants.DEFAULT_SLOT_INDEX;
- SubscriptionController sControl = SubscriptionController.getInstance();
- if (sControl != null) {
- slotId = sControl.getSlotIndex(sControl.getDefaultSubId());
+ if (SubscriptionManagerService.getInstance() != null) {
+ slotId = SubscriptionManagerService.getInstance().getSlotIndex(
+ SubscriptionManagerService.getInstance().getDefaultSubId());
}
return getInstance(null, null, null, slotId);
}
@@ -845,7 +1001,11 @@
}
}
}
- mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
+ if (mMsgDecoder != null) {
+ mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
+ } else {
+ CatLog.e(this, "Error in handleMessage (" + msg.what + ") mMsgDecoder is NULL");
+ }
break;
case MSG_ID_CALL_SETUP:
mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
diff --git a/src/java/com/android/internal/telephony/cat/CommandParams.java b/src/java/com/android/internal/telephony/cat/CommandParams.java
old mode 100755
new mode 100644
index b9de4d1..8530ee2
--- a/src/java/com/android/internal/telephony/cat/CommandParams.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParams.java
@@ -22,16 +22,16 @@
/**
* Container class for proactive command parameters.
- *
+ * @hide
*/
-class CommandParams {
+public class CommandParams {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
CommandDetails mCmdDet;
// Variable to track if an optional icon load has failed.
boolean mLoadIconFailed = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- CommandParams(CommandDetails cmdDet) {
+ public CommandParams(CommandDetails cmdDet) {
mCmdDet = cmdDet;
}
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 7fbebfa..65f3c4a 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -29,6 +29,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.telephony.SmsMessage;
import android.text.TextUtils;
import com.android.internal.telephony.GsmAlphabet;
@@ -40,9 +41,9 @@
/**
* Factory class, used for decoding raw byte arrays, received from baseband,
* into a CommandParams object.
- *
+ * @hide
*/
-class CommandParamsFactory extends Handler {
+public class CommandParamsFactory extends Handler {
private static CommandParamsFactory sInstance = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IconLoader mIconLoader;
@@ -53,6 +54,7 @@
private String mSavedLanguage;
private String mRequestedLanguage;
private boolean mNoAlphaUsrCnf = false;
+ private boolean mStkSmsSendViaTelephony = false;
// constants
static final int MSG_ID_LOAD_ICON_DONE = 1;
@@ -86,7 +88,15 @@
private static final int MAX_GSM7_DEFAULT_CHARS = 239;
private static final int MAX_UCS2_CHARS = 118;
- static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
+ /**
+ * Returns a singleton instance of CommandParamsFactory
+ * @param caller Class used for queuing raw ril messages, decoding them into
+ * CommandParams objects and sending the result back to the CAT Service.
+ * @param fh IccFileHandler Object
+ * @param context The Context
+ * @return CommandParamsFactory instance
+ */
+ public static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
IccFileHandler fh, Context context) {
if (sInstance != null) {
return sInstance;
@@ -106,6 +116,12 @@
} catch (NotFoundException e) {
mNoAlphaUsrCnf = false;
}
+ try {
+ mStkSmsSendViaTelephony = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_stk_sms_send_support);
+ } catch (NotFoundException e) {
+ mStkSmsSendViaTelephony = false;
+ }
}
private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) {
@@ -187,8 +203,14 @@
case GET_INPUT:
cmdPending = processGetInput(cmdDet, ctlvs);
break;
- case SEND_DTMF:
case SEND_SMS:
+ if (mStkSmsSendViaTelephony) {
+ cmdPending = processSMSEventNotify(cmdDet, ctlvs);
+ } else {
+ cmdPending = processEventNotify(cmdDet, ctlvs);
+ }
+ break;
+ case SEND_DTMF:
case REFRESH:
case RUN_AT:
case SEND_SS:
@@ -735,6 +757,62 @@
return false;
}
+
+ /**
+ * Processes SMS_EVENT_NOTIFY message from baseband.
+ *
+ * Method extracts values such as Alpha Id,Icon Id,Sms Tpdu etc from the ComprehensionTlv,
+ * in order to create the CommandParams i.e. SendSMSParams.
+ *
+ * @param cmdDet Command Details container object.
+ * @param ctlvs List of ComprehensionTlv objects following Command Details
+ * object and Device Identities object within the proactive command
+ * @return true if the command is processing is pending and additional
+ * asynchronous processing is required.
+ * @hide
+ */
+ public boolean processSMSEventNotify(CommandDetails cmdDet,
+ List<ComprehensionTlv> ctlvs) throws ResultException {
+ CatLog.d(this, "processSMSEventNotify");
+
+ TextMessage textMsg = new TextMessage();
+ IconId iconId = null;
+
+ ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
+ ctlvs);
+ /* Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV object.
+ *
+ * String corresponding to the alpha identifier is obtained and saved as part of
+ * the DisplayTextParams.
+ */
+ textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
+
+ ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
+ if (ctlv != null) {
+ // Retrieves icon id from the Icon Identifier COMPREHENSION-TLV object
+ iconId = ValueParser.retrieveIconId(ctlv);
+ textMsg.iconSelfExplanatory = iconId.selfExplanatory;
+ }
+
+ textMsg.responseNeeded = false;
+ DisplayTextParams displayTextParams = new DisplayTextParams(cmdDet, textMsg);
+ ComprehensionTlv ctlvTpdu = searchForTag(ComprehensionTlvTag.SMS_TPDU,
+ ctlvs);
+ // Retrieves smsMessage from the SMS TPDU COMPREHENSION-TLV object
+ SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlvTpdu);
+ if (smsMessage != null) {
+ TextMessage smsText = new TextMessage();
+ // Obtains the sms message content.
+ smsText.text = smsMessage.getMessageBody();
+ TextMessage destAddr = new TextMessage();
+ // Obtains the destination Address.
+ destAddr.text = smsMessage.getRecipientAddress();
+ mCmdParams = new SendSMSParams(cmdDet, smsText, destAddr, displayTextParams);
+ return false;
+ }
+ return true;
+ }
+
/**
* Processes SET_UP_EVENT_LIST proactive command from the SIM card.
*
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index 5542b65..416c669 100644
--- a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -50,7 +50,7 @@
* @param data Byte array containing the value
* @param valueIndex Index in data at which the value starts
*/
- protected ComprehensionTlv(int tag, boolean cr, int length, byte[] data,
+ public ComprehensionTlv(int tag, boolean cr, int length, byte[] data,
int valueIndex) {
mTag = tag;
mCr = cr;
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
old mode 100755
new mode 100644
index c25b59e..4b10cae
--- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -32,8 +32,9 @@
/**
* Class used for queuing raw ril messages, decoding them into CommanParams
* objects and sending the result back to the CAT Service.
+ * @hide
*/
-class RilMessageDecoder extends StateMachine {
+public class RilMessageDecoder extends StateMachine {
// constants
private static final int CMD_START = 1;
diff --git a/src/java/com/android/internal/telephony/cat/SendSMSParams.java b/src/java/com/android/internal/telephony/cat/SendSMSParams.java
new file mode 100644
index 0000000..f5108f0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/cat/SendSMSParams.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.cat;
+
+class SendSMSParams extends CommandParams {
+
+ TextMessage mTextSmsMsg;
+ TextMessage mDestAddress;
+ DisplayTextParams mDisplayText;
+
+ SendSMSParams(CommandDetails cmdDet, TextMessage textMsg, TextMessage destAddress,
+ DisplayTextParams displayText) {
+ super(cmdDet);
+ mTextSmsMsg = textMsg;
+ mDestAddress = destAddress;
+ mDisplayText = displayText;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/cat/ValueParser.java b/src/java/com/android/internal/telephony/cat/ValueParser.java
index 7c09136..bd17f48 100644
--- a/src/java/com/android/internal/telephony/cat/ValueParser.java
+++ b/src/java/com/android/internal/telephony/cat/ValueParser.java
@@ -18,16 +18,24 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.telephony.SmsMessage;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.cat.Duration.TimeUnit;
import com.android.internal.telephony.uicc.IccUtils;
+import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-abstract class ValueParser {
+
+/**
+ * Util class that parses different entities from the ctlvs ComprehensionTlv List
+ * @hide
+ */
+public abstract class ValueParser {
/**
* Search for a Command Details object from a list.
@@ -352,4 +360,42 @@
throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
}
}
+
+ /**
+ * Retrieve's the tpdu from the ctlv and creates the SmsMessage from pdu.
+ * @param ctlv ComprehensionTlv value
+ * @return message SmsMessage to retrieve the destAddress and Text
+ * @throws ResultException
+ * @hide
+ */
+ public static SmsMessage retrieveTpduAsSmsMessage(ComprehensionTlv ctlv)
+ throws ResultException {
+ if (ctlv != null) {
+ byte[] rawValue = ctlv.getRawValue();
+ int valueIndex = ctlv.getValueIndex();
+ int length = ctlv.getLength();
+ if (length != 0) {
+ try {
+ byte[] pdu = Arrays.copyOfRange(rawValue, valueIndex, (valueIndex + length));
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(pdu.length + 1);
+ /* Framework's TPdu Parser expects the TPdu be prepended with SC-Address.
+ * else the parser will throw an exception. So prepending TPdu with 0,
+ * which indicates that there is no SC address and its length is 0.
+ * This way Parser will skip parsing for SC-Address
+ */
+ bo.write(0x00);
+ bo.write(pdu, 0, pdu.length);
+ byte[] frameworkPdu = bo.toByteArray();
+ //ToDO handle for 3GPP2 format bug: b/243123533
+ SmsMessage message = SmsMessage.createFromPdu(frameworkPdu,
+ SmsMessage.FORMAT_3GPP);
+ return message;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
+ }
+ }
+ }
+ return null;
+ }
+
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index eb5f866..784c974 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.SystemProperties;
@@ -77,8 +78,8 @@
* Create a new inbound SMS handler for CDMA.
*/
private CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
- Phone phone, CdmaSMSDispatcher smsDispatcher) {
- super("CdmaInboundSmsHandler", context, storageMonitor, phone);
+ Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper) {
+ super("CdmaInboundSmsHandler", context, storageMonitor, phone, looper);
mSmsDispatcher = smsDispatcher;
phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null);
@@ -169,9 +170,10 @@
* Wait for state machine to enter startup state. We can't send any messages until then.
*/
public static CdmaInboundSmsHandler makeInboundSmsHandler(Context context,
- SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher) {
+ SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher,
+ Looper looper) {
CdmaInboundSmsHandler handler = new CdmaInboundSmsHandler(context, storageMonitor,
- phone, smsDispatcher);
+ phone, smsDispatcher, looper);
handler.start();
return handler;
}
@@ -194,7 +196,8 @@
* @return true if the message was handled here; false to continue processing
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource,
+ int token) {
SmsMessage sms = (SmsMessage) smsb;
boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 5a830a8..ebc6342 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -93,6 +93,21 @@
}
@Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, message,
+ statusReportRequested, smsHeader, priority);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, destPort, message,
+ statusReportRequested);
+ }
+
+ @Override
protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly);
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java b/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java
old mode 100755
new mode 100644
index b31df59..24ee56d
--- a/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony.cdma;
+import java.util.Objects;
+
/**
* CdmaSmsBroadcastConfigInfo defines one configuration of Cdma Broadcast
* Message to be received by the ME
@@ -84,4 +86,22 @@
mFromServiceCategory + ", " + mToServiceCategory + "] " +
(isSelected() ? "ENABLED" : "DISABLED");
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFromServiceCategory, mToServiceCategory, mLanguage, mSelected);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CdmaSmsBroadcastConfigInfo)) {
+ return false;
+ }
+
+ CdmaSmsBroadcastConfigInfo other = (CdmaSmsBroadcastConfigInfo) obj;
+
+ return mFromServiceCategory == other.mFromServiceCategory
+ && mToServiceCategory == other.mToServiceCategory
+ && mLanguage == other.mLanguage && mSelected == other.mSelected;
+ }
}
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 6299dac..0ae1b5c 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -482,7 +482,7 @@
}
} else if (mPhone.getServiceStateTracker().getCombinedRegState(getServiceState())
== ServiceState.STATE_IN_SERVICE) {
- if (mPhone.isWifiCallingEnabled()) {
+ if (mPhone.isWifiCallingEnabled() && mPhone.isImsRegistered()) {
data = getCarrierDisplayNameFromWifiCallingOverride(data);
if (DBG) {
Rlog.d(TAG, "CarrierName override by wifi-calling " + data);
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index 2a12ecc..267f70b 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -18,23 +18,20 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
-import android.annotation.StringDef;
-import android.content.BroadcastReceiver;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
+import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
@@ -55,17 +52,13 @@
import android.util.SparseArray;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RIL;
-import com.android.internal.telephony.dataconnection.DataThrottler;
+import com.android.internal.telephony.SlidingWindowEventCounter;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -81,35 +74,14 @@
*/
public class AccessNetworksManager extends Handler {
private static final boolean DBG = false;
- public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE =
- "ro.telephony.iwlan_operation_mode";
- @Retention(RetentionPolicy.SOURCE)
- @StringDef(prefix = {"IWLAN_OPERATION_MODE_"},
- value = {
- IWLAN_OPERATION_MODE_DEFAULT,
- IWLAN_OPERATION_MODE_LEGACY,
- IWLAN_OPERATION_MODE_AP_ASSISTED})
- public @interface IwlanOperationMode {}
+ /** Event to guide a transport type for initial data connection of emergency data network. */
+ private static final int EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY = 1;
/**
- * IWLAN default mode. On device that has IRadio 1.4 or above, it means
- * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.3 or below, it means
- * {@link #IWLAN_OPERATION_MODE_LEGACY}.
+ * The counters to detect frequent QNS attempt to change preferred network transport by ApnType.
*/
- public static final String IWLAN_OPERATION_MODE_DEFAULT = "default";
-
- /**
- * IWLAN legacy mode. IWLAN is completely handled by the modem, and when the device is on
- * IWLAN, modem reports IWLAN as a RAT.
- */
- public static final String IWLAN_OPERATION_MODE_LEGACY = "legacy";
-
- /**
- * IWLAN application processor assisted mode. IWLAN is handled by the bound IWLAN data service
- * and network service separately.
- */
- public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted";
+ private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter;
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(64);
@@ -124,13 +96,16 @@
ApnSetting.TYPE_CBS,
ApnSetting.TYPE_SUPL,
ApnSetting.TYPE_EMERGENCY,
- ApnSetting.TYPE_XCAP
+ ApnSetting.TYPE_XCAP,
+ ApnSetting.TYPE_DUN
};
private final Phone mPhone;
private final CarrierConfigManager mCarrierConfigManager;
+ private @Nullable DataConfigManager mDataConfigManager;
+
private IQualifiedNetworksService mIQualifiedNetworksService;
private AccessNetworksManagerDeathRecipient mDeathRecipient;
@@ -146,38 +121,10 @@
private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList();
- private final Set<DataThrottler> mDataThrottlers = new HashSet<>();
-
- private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
- && mPhone.getPhoneId() == intent.getIntExtra(
- CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
- // We should wait for carrier config changed event because the target binding
- // package name can come from the carrier config. Note that we still get this event
- // even when SIM is absent.
- if (DBG) log("Carrier config changed. Try to bind qualified network service.");
- bindQualifiedNetworksService();
- }
- }
- };
-
- /**
- * The current transport of the APN type. The key is the APN type, and the value is the
- * transport.
- */
- private final Map<Integer, Integer> mCurrentTransports = new ConcurrentHashMap<>();
-
/**
* The preferred transport of the APN type. The key is the APN type, and the value is the
- * transport. The preferred transports are updated as soon as QNS changes the preference, while
- * the current transports are updated after handover complete.
+ * transport. The preferred transports are updated as soon as QNS changes the preference.
*/
- // TODO: Deprecate mPreferredTransports. Should expose mAvailableNetworks to
- // DataNetworkController after we support multi preferred access networks (i.e.
- // DataNetworkController might select 2nd preferred access network in some scenarios.)
private final Map<Integer, Integer> mPreferredTransports = new ConcurrentHashMap<>();
/**
@@ -187,21 +134,6 @@
new ArraySet<>();
/**
- * Registers the data throttler in order to receive APN status changes.
- *
- * @param dataThrottler the data throttler to register
- */
- public void registerDataThrottler(DataThrottler dataThrottler) {
- this.post(() -> {
- QualifiedNetworksServiceConnection serviceConnection = mServiceConnection;
- this.mDataThrottlers.add(dataThrottler);
- if (serviceConnection != null) {
- serviceConnection.registerDataThrottler(dataThrottler);
- }
- });
- }
-
- /**
* Represents qualified network types list on a specific APN type.
*/
public static class QualifiedNetworks {
@@ -229,29 +161,32 @@
}
}
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ int transport = (int) ar.result;
+ onEmergencyDataNetworkPreferredTransportChanged(transport);
+ break;
+ default:
+ loge("Unexpected event " + msg.what);
+ }
+ }
+
private class AccessNetworksManagerDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
// TODO: try to rebind the service.
String message = "Qualified network service " + mLastBoundPackageName + " died.";
+ // clear the anomaly report counters when QNS crash
+ mApnTypeToQnsChangeNetworkCounter.clear();
loge(message);
AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId());
}
}
private final class QualifiedNetworksServiceConnection implements ServiceConnection {
-
- /**
- * The APN throttle status callback is attached to the service connection so that they have
- * the same life cycle.
- */
- @NonNull
- private final ThrottleStatusChangedCallback mThrottleStatusCallback;
-
- QualifiedNetworksServiceConnection() {
- mThrottleStatusCallback = new ThrottleStatusChangedCallback();
- }
-
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DBG) log("onServiceConnected " + name);
@@ -263,9 +198,6 @@
service.linkToDeath(mDeathRecipient, 0 /* flags */);
mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(),
new QualifiedNetworksServiceCallback());
-
- registerDataThrottlersFirstTime();
-
} catch (RemoteException e) {
loge("Remote exception. " + e);
}
@@ -274,55 +206,9 @@
@Override
public void onServiceDisconnected(ComponentName name) {
if (DBG) log("onServiceDisconnected " + name);
- unregisterForThrottleCallbacks();
mTargetBindingPackageName = null;
}
- /**
- * Runs on all of the data throttlers when the service is connected
- */
- private void registerDataThrottlersFirstTime() {
- post(() -> {
- for (DataThrottler dataThrottler : mDataThrottlers) {
- dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
- }
- });
- }
-
- private void registerDataThrottler(DataThrottler dataThrottler) {
- post(() -> {
- dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
- });
- }
-
- private void unregisterForThrottleCallbacks() {
- post(() -> {
- for (DataThrottler dataThrottler : mDataThrottlers) {
- dataThrottler.unregisterForThrottleStatusChanges(mThrottleStatusCallback);
- }
- });
- }
- }
-
- private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
- @Override
- public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
- post(() -> {
- try {
- List<ThrottleStatus> throttleStatusesBySlot =
- throttleStatuses
- .stream()
- .filter(x -> x.getSlotIndex() == mPhone.getPhoneId())
- .collect(Collectors.toList());
- if (mIQualifiedNetworksService != null) {
- mIQualifiedNetworksService.reportThrottleStatusChanged(mPhone.getPhoneId(),
- throttleStatusesBySlot);
- }
- } catch (Exception ex) {
- loge("onThrottleStatusChanged", ex);
- }
- });
- }
}
private final class QualifiedNetworksServiceCallback extends
@@ -344,12 +230,23 @@
if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork
-> !DataUtils.isValidAccessNetwork(accessNetwork))) {
loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes));
+ if (mDataConfigManager != null
+ && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) {
+ reportAnomaly("QNS requested invalid Network Type",
+ "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4");
+ }
return;
}
List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
+ int satisfiedApnTypes = 0;
for (int apnType : SUPPORTED_APN_TYPES) {
if ((apnTypes & apnType) == apnType) {
+ // skip the APN anomaly detection if not using the T data stack
+ if (mDataConfigManager != null) {
+ satisfiedApnTypes |= apnType;
+ }
+
if (mAvailableNetworks.get(apnType) != null) {
if (Arrays.equals(mAvailableNetworks.get(apnType),
qualifiedNetworkTypes)) {
@@ -382,6 +279,16 @@
}
}
+ // Report anomaly if any requested APN types are unsatisfied
+ if (satisfiedApnTypes != apnTypes
+ && mDataConfigManager != null
+ && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) {
+ int unsatisfied = satisfiedApnTypes ^ apnTypes;
+ reportAnomaly("QNS requested unsupported APN Types:"
+ + Integer.toBinaryString(unsatisfied),
+ "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec5");
+ }
+
if (!qualifiedNetworksList.isEmpty()) {
setPreferredTransports(qualifiedNetworksList);
mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList);
@@ -389,6 +296,20 @@
}
}
+ private void onEmergencyDataNetworkPreferredTransportChanged(
+ @AccessNetworkConstants.TransportType int transportType) {
+ try {
+ logl("onEmergencyDataNetworkPreferredTransportChanged: "
+ + AccessNetworkConstants.transportTypeToString(transportType));
+ if (mIQualifiedNetworksService != null) {
+ mIQualifiedNetworksService.reportEmergencyDataNetworkPreferredTransportChanged(
+ mPhone.getPhoneId(), transportType);
+ }
+ } catch (Exception ex) {
+ loge("onEmergencyDataNetworkPreferredTransportChanged: ", ex);
+ }
+ }
+
/**
* Access networks manager callback. This should be only used by {@link DataNetworkController}.
*/
@@ -422,33 +343,27 @@
mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
Context.CARRIER_CONFIG_SERVICE);
mLogTag = "ANM-" + mPhone.getPhoneId();
+ mApnTypeToQnsChangeNetworkCounter = new SparseArray<>();
+ mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
- if (isInLegacyMode()) {
- log("operates in legacy mode.");
- // For legacy mode, WWAN is the only transport to handle all data connections, even
- // the IWLAN ones.
- mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN};
- } else {
- log("operates in AP-assisted mode.");
- mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- try {
- Context contextAsUser = phone.getContext().createPackageContextAsUser(
- phone.getContext().getPackageName(), 0, UserHandle.ALL);
- contextAsUser.registerReceiver(mConfigChangedReceiver, intentFilter,
- null /* broadcastPermission */, null);
- } catch (PackageManager.NameNotFoundException e) {
- loge("Package name not found: ", e);
- }
- bindQualifiedNetworksService();
- }
+ // bindQualifiedNetworksService posts real work to handler thread. So here we can
+ // let the callback execute in binder thread to avoid post twice.
+ mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex != mPhone.getPhoneId()) return;
+ // We should wait for carrier config changed event because the target binding
+ // package name can come from the carrier config. Note that we still get this
+ // event even when SIM is absent.
+ if (DBG) log("Carrier config changed. Try to bind qualified network service.");
+ bindQualifiedNetworksService();
+ });
+ bindQualifiedNetworksService();
- if (phone.isUsingNewDataStack()) {
- // Using post to delay the registering because data retry manager instance is created
- // later than access networks manager.
- post(() -> mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
+ // Using post to delay the registering because data retry manager and data config
+ // manager instances are created later than access networks manager.
+ post(() -> {
+ mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
new DataRetryManager.DataRetryManagerCallback(this::post) {
@Override
public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
@@ -462,8 +377,29 @@
loge("onThrottleStatusChanged: ", ex);
}
}
- }));
- }
+ });
+ mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager();
+ mDataConfigManager.registerCallback(
+ new DataConfigManager.DataConfigManagerCallback(this::post) {
+ @Override
+ public void onDeviceConfigChanged() {
+ mApnTypeToQnsChangeNetworkCounter.clear();
+ }
+ });
+ mPhone.registerForEmergencyDomainSelected(
+ this, EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY, null);
+ });
+ }
+
+ /**
+ * Trigger the anomaly report with the specified UUID.
+ *
+ * @param anomalyMsg Description of the event
+ * @param uuid UUID associated with that event
+ */
+ private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) {
+ logl(anomalyMsg);
+ AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId());
}
/**
@@ -527,24 +463,29 @@
/**
* Get the qualified network service package.
*
- * @return package name of the qualified networks service package. Return empty string when in
- * legacy mode (i.e. Dedicated IWLAN data/network service is not supported).
+ * @return package name of the qualified networks service package.
*/
private String getQualifiedNetworksServicePackageName() {
// Read package name from the resource
String packageName = mPhone.getContext().getResources().getString(
com.android.internal.R.string.config_qualified_networks_service_package);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null) {
- // If carrier config overrides it, use the one from carrier config
- String carrierConfigPackageName = b.getString(CarrierConfigManager
- .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
- if (!TextUtils.isEmpty(carrierConfigPackageName)) {
- if (DBG) log("Found carrier config override " + carrierConfigPackageName);
- packageName = carrierConfigPackageName;
+ PersistableBundle b;
+ try {
+ b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
+ CarrierConfigManager
+ .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
+ if (b != null && !b.isEmpty()) {
+ // If carrier config overrides it, use the one from carrier config
+ String carrierConfigPackageName = b.getString(CarrierConfigManager
+ .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
+ if (!TextUtils.isEmpty(carrierConfigPackageName)) {
+ if (DBG) log("Found carrier config override " + carrierConfigPackageName);
+ packageName = carrierConfigPackageName;
+ }
}
+ } catch (RuntimeException e) {
+ loge("Carrier config loader is not available.");
}
return packageName;
@@ -560,16 +501,22 @@
String className = mPhone.getContext().getResources().getString(
com.android.internal.R.string.config_qualified_networks_service_class);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null) {
- // If carrier config overrides it, use the one from carrier config
- String carrierConfigClassName = b.getString(CarrierConfigManager
- .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
- if (!TextUtils.isEmpty(carrierConfigClassName)) {
- if (DBG) log("Found carrier config override " + carrierConfigClassName);
- className = carrierConfigClassName;
+ PersistableBundle b;
+ try {
+ b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
+ CarrierConfigManager
+ .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
+ if (b != null && !b.isEmpty()) {
+ // If carrier config overrides it, use the one from carrier config
+ String carrierConfigClassName = b.getString(CarrierConfigManager
+ .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
+ if (!TextUtils.isEmpty(carrierConfigClassName)) {
+ if (DBG) log("Found carrier config override " + carrierConfigClassName);
+ className = carrierConfigClassName;
+ }
}
+ } catch (RuntimeException e) {
+ loge("Carrier config loader is not available.");
}
return className;
@@ -605,87 +552,12 @@
}
/**
- * @return {@code true} if the device operates in legacy mode, otherwise {@code false}.
+ * @return The available transports.
*/
- public boolean isInLegacyMode() {
- // Get IWLAN operation mode from the system property. If the system property is configured
- // to default or not configured, the mode is tied to IRadio version. For 1.4 or above, it's
- // AP-assisted mode, for 1.3 or below, it's legacy mode.
- String mode = SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE);
-
- if (mode.equals(IWLAN_OPERATION_MODE_AP_ASSISTED)) {
- return false;
- } else if (mode.equals(IWLAN_OPERATION_MODE_LEGACY)) {
- return true;
- }
-
- return mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
- }
-
- /**
- * @return The available transports. Note that on legacy devices, the only available transport
- * would be WWAN only. If the device is configured as AP-assisted mode, the available transport
- * will always be WWAN and WLAN (even if the device is not camped on IWLAN).
- * See {@link #isInLegacyMode()} for mode details.
- */
- public synchronized @NonNull int[] getAvailableTransports() {
+ public @NonNull int[] getAvailableTransports() {
return mAvailableTransports;
}
- /**
- * Get the transport based on the network capability.
- *
- * @param netCap The network capability.
- * @return The transport type.
- */
- public @TransportType int getCurrentTransportByNetworkCapability(@NetCapability int netCap) {
- return getCurrentTransport(DataUtils.networkCapabilityToApnType(netCap));
- }
-
- /**
- * Get the transport based on the APN type.
- *
- * @param apnType APN type
- * @return The transport type
- */
- // TODO: Remove this after TransportManager is removed.
- public @TransportType int getCurrentTransport(@ApnType int apnType) {
- // In legacy mode, always route to cellular.
- if (isInLegacyMode()) {
- return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
- }
-
- // If we can't find the corresponding transport, always route to cellular.
- return mCurrentTransports.get(apnType) == null
- ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mCurrentTransports.get(apnType);
- }
-
- /**
- * Set the current transport of a network capability.
- *
- * @param netCap The network capability.
- * @param transport The transport.
- */
- public void setCurrentTransportByNetworkCapability(@NetCapability int netCap,
- @TransportType int transport) {
- setCurrentTransport(DataUtils.networkCapabilityToApnType(netCap), transport);
- }
-
- /**
- * Set the current transport of apn type.
- *
- * @param apnType The APN type
- * @param transport The transport.
- */
- // TODO: Remove this after TransportManager is removed.
- public void setCurrentTransport(@ApnType int apnType, @TransportType int transport) {
- Integer previousTransport = mCurrentTransports.put(apnType, transport);
- if (previousTransport == null || previousTransport != transport) {
- logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
- + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
- }
- }
-
private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
return accessNetwork == AccessNetworkType.IWLAN
? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
@@ -717,11 +589,6 @@
* @return The preferred transport.
*/
public @TransportType int getPreferredTransport(@ApnType int apnType) {
- // In legacy mode, always preferred on cellular.
- if (isInLegacyMode()) {
- return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
- }
-
return mPreferredTransports.get(apnType) == null
? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType);
}
@@ -744,20 +611,14 @@
}
/**
- * Check if there is any APN type's current transport is on IWLAN.
+ * Check if there is any APN type preferred on IWLAN.
*
* @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}.
*/
public boolean isAnyApnOnIwlan() {
for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
- if (mPhone.isUsingNewDataStack()) {
- if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- return true;
- }
- } else {
- if (getCurrentTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- return true;
- }
+ if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ return true;
}
}
return false;
@@ -821,14 +682,6 @@
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println(AccessNetworksManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":");
pw.increaseIndent();
- pw.println("current transports=");
- pw.increaseIndent();
- for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
- pw.println(ApnSetting.getApnTypeString(apnType)
- + ": " + AccessNetworkConstants.transportTypeToString(
- getCurrentTransport(apnType)));
- }
- pw.decreaseIndent();
pw.println("preferred transports=");
pw.increaseIndent();
for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
@@ -838,9 +691,6 @@
}
pw.decreaseIndent();
- pw.println("isInLegacy=" + isInLegacyMode());
- pw.println("IWLAN operation mode="
- + SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE));
pw.println("Local logs=");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
index c63676f..c1d1203 100644
--- a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
@@ -17,7 +17,7 @@
package com.android.internal.telephony.data;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG;
+import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
import android.content.Context;
@@ -40,9 +40,10 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConfigurationManager;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import java.util.Comparator;
import java.util.HashMap;
@@ -164,9 +165,9 @@
private String getValidationNetworkIdentity(int subId) {
if (!SubscriptionManager.isUsableSubscriptionId(subId)) return null;
- SubscriptionController subController = SubscriptionController.getInstance();
- if (subController == null) return null;
- Phone phone = PhoneFactory.getPhone(subController.getPhoneId(subId));
+ if (SubscriptionManagerService.getInstance() == null) return null;
+ Phone phone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
+ .getPhoneId(subId));
if (phone == null || phone.getServiceState() == null) return null;
NetworkRegistrationInfo regInfo = phone.getServiceState().getNetworkRegistrationInfo(
@@ -192,7 +193,7 @@
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(subId);
if (b != null) {
- ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG);
+ ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG);
}
}
// Ttl can't be bigger than one day for now.
@@ -257,7 +258,9 @@
// If it's already validating the same subscription, do nothing.
if (subId == mSubId) return;
- if (!SubscriptionController.getInstance().isActiveSubId(subId)) {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null || !subInfo.isActive()) {
logd("Failed to start validation. Inactive subId " + subId);
callback.onValidationDone(false, subId);
return;
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 82a4ccd..78450a8 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -16,12 +16,9 @@
package com.android.internal.telephony.data;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.StringDef;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
@@ -29,7 +26,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
-import android.os.RegistrantList;
import android.provider.DeviceConfig;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetCapability;
@@ -41,6 +37,7 @@
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,9 +56,11 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
@@ -132,8 +131,7 @@
private static final String DATA_CONFIG_NETWORK_TYPE_IDEN = "iDEN";
/** Network type LTE. Should not be used outside of DataConfigManager. */
- // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
- public static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE";
+ private static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE";
/** Network type HSPA+. Should not be used outside of DataConfigManager. */
private static final String DATA_CONFIG_NETWORK_TYPE_HSPAP = "HSPA+";
@@ -151,12 +149,10 @@
private static final String DATA_CONFIG_NETWORK_TYPE_LTE_CA = "LTE_CA";
/** Network type NR_NSA. Should not be used outside of DataConfigManager. */
- // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
- public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA";
+ private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA";
/** Network type NR_NSA_MMWAVE. Should not be used outside of DataConfigManager. */
- // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
- public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
+ private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
/** Network type NR_SA. Should not be used outside of DataConfigManager. */
private static final String DATA_CONFIG_NETWORK_TYPE_NR_SA = "NR_SA";
@@ -192,6 +188,10 @@
@Retention(RetentionPolicy.SOURCE)
private @interface DataConfigNetworkType {}
+ /** Data config update callbacks. */
+ private final @NonNull Set<DataConfigManagerCallback> mDataConfigManagerCallbacks =
+ new ArraySet<>();
+
/** DeviceConfig key of anomaly report threshold for back to back ims release-request. */
private static final String KEY_ANOMALY_IMS_RELEASE_REQUEST = "anomaly_ims_release_request";
/** DeviceConfig key of anomaly report threshold for frequent setup data failure. */
@@ -199,6 +199,10 @@
"anomaly_setup_data_call_failure";
/** DeviceConfig key of anomaly report threshold for frequent network-unwanted call. */
private static final String KEY_ANOMALY_NETWORK_UNWANTED = "anomaly_network_unwanted";
+ /** DeviceConfig key of anomaly report threshold for frequent change of preferred network. */
+ private static final String KEY_ANOMALY_QNS_CHANGE_NETWORK = "anomaly_qns_change_network";
+ /** DeviceConfig key of anomaly report threshold for invalid QNS params. */
+ private static final String KEY_ANOMALY_QNS_PARAM = "anomaly_qns_param";
/** DeviceConfig key of anomaly report threshold for DataNetwork stuck in connecting state. */
private static final String KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT =
"anomaly_network_connecting_timeout";
@@ -208,6 +212,14 @@
/** DeviceConfig key of anomaly report threshold for DataNetwork stuck in handover state. */
private static final String KEY_ANOMALY_NETWORK_HANDOVER_TIMEOUT =
"anomaly_network_handover_timeout";
+ /** DeviceConfig key of anomaly report: True for enabling APN config invalidity detection */
+ private static final String KEY_ANOMALY_APN_CONFIG_ENABLED = "anomaly_apn_config_enabled";
+ /** DeviceConfig key of the time threshold in ms for defining a network status to be stable. **/
+ private static final String KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD =
+ "auto_data_switch_availability_stability_time_threshold";
+ /** DeviceConfig key of the maximum number of retries when a validation for switching failed.**/
+ private static final String KEY_AUTO_DATA_SWITCH_VALIDATION_MAX_RETRY =
+ "auto_data_switch_validation_max_retry";
/** Anomaly report thresholds for frequent setup data call failure. */
private EventFrequency mSetupDataCallAnomalyReportThreshold;
@@ -222,6 +234,12 @@
private EventFrequency mNetworkUnwantedAnomalyReportThreshold;
/**
+ * {@code true} if enabled anomaly detection for param when QNS wants to change preferred
+ * network at {@link AccessNetworksManager}.
+ */
+ private boolean mIsInvalidQnsParamAnomalyReportEnabled;
+
+ /**
* Timeout in ms before creating an anomaly report for a DataNetwork stuck in
* {@link DataNetwork.ConnectingState}.
*/
@@ -239,12 +257,14 @@
*/
private int mNetworkHandoverTimeout;
+ /**
+ * True if enabled anomaly detection for APN config that's read from {@link DataProfileManager}
+ */
+ private boolean mIsApnConfigAnomalyReportEnabled;
+
private @NonNull final Phone mPhone;
private @NonNull final String mLogTag;
- /** The registrants list for config update event. */
- private @NonNull final RegistrantList mConfigUpdateRegistrants = new RegistrantList();
-
private @NonNull final CarrierConfigManager mCarrierConfigManager;
private @NonNull PersistableBundle mCarrierConfig = null;
private @NonNull Resources mResources = null;
@@ -263,6 +283,8 @@
/** The network types that only support single data networks */
private @NonNull final @NetworkType List<Integer> mSingleDataNetworkTypeList =
new ArrayList<>();
+ private @NonNull final @NetCapability Set<Integer> mCapabilitiesExemptFromSingleDataList =
+ new HashSet<>();
/** The network types that support temporarily not metered */
private @NonNull final @DataConfigNetworkType Set<String> mUnmeteredNetworkTypes =
new HashSet<>();
@@ -277,6 +299,8 @@
new ConcurrentHashMap<>();
/** Rules for handover between IWLAN and cellular network. */
private @NonNull final List<HandoverRule> mHandoverRuleList = new ArrayList<>();
+ /** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/
+ private boolean mShouldKeepNetworkUpInNonVops = false;
/**
* Constructor
@@ -292,22 +316,13 @@
log("DataConfigManager created.");
mCarrierConfigManager = mPhone.getContext().getSystemService(CarrierConfigManager.class);
-
- // Register for carrier configs update
- IntentFilter filter = new IntentFilter();
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mPhone.getContext().registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- if (mPhone.getPhoneId() == intent.getIntExtra(
- CarrierConfigManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ // Callback send msg to handler thread, so callback itself can be executed in binder thread.
+ mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex == mPhone.getPhoneId()) {
sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
}
- }
- }
- }, filter, null, mPhone);
+ });
// Register for device config update
DeviceConfig.addOnPropertiesChangedListener(
@@ -321,8 +336,46 @@
// Must be called to set mCarrierConfig and mResources to non-null values
updateCarrierConfig();
+ // Must be called to set anomaly report threshold to non-null values
updateDeviceConfig();
- mConfigUpdateRegistrants.notifyRegistrants();
+ }
+
+ /**
+ * The data config callback.
+ */
+ public static class DataConfigManagerCallback extends DataCallback {
+ /**
+ * Constructor
+ *
+ * @param executor The executor of the callback.
+ */
+ public DataConfigManagerCallback(@NonNull @CallbackExecutor Executor executor) {
+ super(executor);
+ }
+
+ /** Callback on carrier config update.*/
+ public void onCarrierConfigChanged() {}
+
+ /** Callback on device config update.*/
+ public void onDeviceConfigChanged() {}
+ }
+
+ /**
+ * Register the callback for receiving information from {@link DataConfigManager}.
+ *
+ * @param callback The callback.
+ */
+ public void registerCallback(@NonNull DataConfigManagerCallback callback) {
+ mDataConfigManagerCallbacks.add(callback);
+ }
+
+ /**
+ * Unregister the callback.
+ *
+ * @param callback The previously registered callback.
+ */
+ public void unregisterCallback(@NonNull DataConfigManagerCallback callback) {
+ mDataConfigManagerCallbacks.remove(callback);
}
@Override
@@ -331,12 +384,14 @@
case EVENT_CARRIER_CONFIG_CHANGED:
log("EVENT_CARRIER_CONFIG_CHANGED");
updateCarrierConfig();
- mConfigUpdateRegistrants.notifyRegistrants();
+ mDataConfigManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ callback::onCarrierConfigChanged));
break;
case EVENT_DEVICE_CONFIG_CHANGED:
log("EVENT_DEVICE_CONFIG_CHANGED");
updateDeviceConfig();
- mConfigUpdateRegistrants.notifyRegistrants();
+ mDataConfigManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ callback::onDeviceConfigChanged));
break;
default:
loge("Unexpected message " + msg.what);
@@ -349,17 +404,13 @@
DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY);
mImsReleaseRequestAnomalyReportThreshold = parseSlidingWindowCounterThreshold(
- properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null),
- 0,
- 12);
+ properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2);
mNetworkUnwantedAnomalyReportThreshold = parseSlidingWindowCounterThreshold(
- properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null),
- 0,
- 12);
+ properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12);
mSetupDataCallAnomalyReportThreshold = parseSlidingWindowCounterThreshold(
- properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null),
- 0,
- 2);
+ properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12);
+ mIsInvalidQnsParamAnomalyReportEnabled = properties.getBoolean(
+ KEY_ANOMALY_QNS_PARAM, false);
mNetworkConnectingTimeout = properties.getInt(
KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS);
mNetworkDisconnectingTimeout = properties.getInt(
@@ -367,6 +418,8 @@
DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS);
mNetworkHandoverTimeout = properties.getInt(
KEY_ANOMALY_NETWORK_HANDOVER_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS);
+ mIsApnConfigAnomalyReportEnabled = properties.getBoolean(
+ KEY_ANOMALY_APN_CONFIG_ENABLED, false);
}
/**
@@ -393,13 +446,14 @@
updateNetworkCapabilityPriority();
updateDataRetryRules();
updateMeteredApnTypes();
- updateSingleDataNetworkTypeList();
+ updateSingleDataNetworkTypeAndCapabilityExemption();
+ updateVopsConfig();
updateUnmeteredNetworkTypes();
updateBandwidths();
updateTcpBuffers();
updateHandoverRules();
- log("Data config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
+ log("Carrier config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
+ "carrier specific.");
}
@@ -413,7 +467,8 @@
CarrierConfigManager.KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY);
if (capabilityPriorityStrings != null) {
for (String capabilityPriorityString : capabilityPriorityStrings) {
- capabilityPriorityString = capabilityPriorityString.trim().toUpperCase();
+ capabilityPriorityString =
+ capabilityPriorityString.trim().toUpperCase(Locale.ROOT);
String[] tokens = capabilityPriorityString.split(":");
if (tokens.length != 2) {
loge("Invalid config \"" + capabilityPriorityString + "\"");
@@ -584,15 +639,32 @@
/**
* Update the network types for only single data networks from the carrier config.
*/
- private void updateSingleDataNetworkTypeList() {
+ private void updateSingleDataNetworkTypeAndCapabilityExemption() {
synchronized (this) {
mSingleDataNetworkTypeList.clear();
+ mCapabilitiesExemptFromSingleDataList.clear();
int[] singleDataNetworkTypeList = mCarrierConfig.getIntArray(
CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY);
if (singleDataNetworkTypeList != null) {
- Arrays.stream(singleDataNetworkTypeList)
- .forEach(mSingleDataNetworkTypeList::add);
+ Arrays.stream(singleDataNetworkTypeList).forEach(mSingleDataNetworkTypeList::add);
}
+
+ int[] singleDataCapabilitiesExemptList = mCarrierConfig.getIntArray(
+ CarrierConfigManager.KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY);
+ if (singleDataCapabilitiesExemptList != null) {
+ Arrays.stream(singleDataCapabilitiesExemptList)
+ .forEach(mCapabilitiesExemptFromSingleDataList::add);
+ }
+ }
+ }
+
+ /**
+ * Update the voice over PS related config from the carrier config.
+ */
+ private void updateVopsConfig() {
+ synchronized (this) {
+ mShouldKeepNetworkUpInNonVops = mCarrierConfig.getBoolean(CarrierConfigManager
+ .Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL);
}
}
@@ -604,6 +676,25 @@
}
/**
+ * @return The list of {@link android.net.NetworkCapabilities.NetCapability} that every of which
+ * is exempt from the single PDN check.
+ */
+ public @NonNull @NetCapability Set<Integer> getCapabilitiesExemptFromSingleDataNetwork() {
+ return Collections.unmodifiableSet(mCapabilitiesExemptFromSingleDataList);
+ }
+
+ /** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/
+ public boolean shouldKeepNetworkUpInNonVops() {
+ return mShouldKeepNetworkUpInNonVops;
+ }
+
+ /** {@code True} requires ping test to pass on the target slot before switching to it.*/
+ public boolean isPingTestBeforeAutoDataSwitchRequired() {
+ return mResources.getBoolean(com.android.internal.R.bool
+ .auto_data_switch_ping_test_before_switch);
+ }
+
+ /**
* @return Whether {@link NetworkCapabilities#NET_CAPABILITY_TEMPORARILY_NOT_METERED}
* is supported by the carrier.
*/
@@ -787,6 +878,14 @@
}
/**
+ * @return {@code true} if enabled anomaly report for invalid param when QNS wants to change
+ * preferred network at {@link AccessNetworksManager}.
+ */
+ public boolean isInvalidQnsParamAnomalyReportEnabled() {
+ return mIsInvalidQnsParamAnomalyReportEnabled;
+ }
+
+ /**
* @return Timeout in ms before creating an anomaly report for a DataNetwork stuck in
* {@link DataNetwork.ConnectingState}.
*/
@@ -811,6 +910,32 @@
}
/**
+ * @return {@code true} if enabled anomaly report for invalid APN config
+ * at {@link DataProfileManager}
+ */
+ public boolean isApnConfigAnomalyReportEnabled() {
+ return mIsApnConfigAnomalyReportEnabled;
+ }
+
+ /**
+ * @return The maximum number of retries when a validation for switching failed.
+ */
+ public int getAutoDataSwitchValidationMaxRetry() {
+ return mResources.getInteger(com.android.internal.R.integer
+ .auto_data_switch_validation_max_retry);
+ }
+
+ /**
+ * @return Time threshold in ms to define a internet connection status to be stable
+ * (e.g. out of service, in service, wifi is the default active network.etc), while -1 indicates
+ * auto switch feature disabled.
+ */
+ public long getAutoDataSwitchAvailabilityStabilityTimeThreshold() {
+ return mResources.getInteger(com.android.internal.R.integer
+ .auto_data_switch_availability_stability_time_threshold_millis);
+ }
+
+ /**
* Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}.
* The config string will have the following form, with values in bytes:
* "read_min,read_default,read_max,write_min,write_default,write_max"
@@ -865,7 +990,7 @@
* @return {@code true} if tearing down IMS data network should be delayed until the voice call
* ends.
*/
- public boolean isImsDelayTearDownEnabled() {
+ public boolean isImsDelayTearDownUntilVoiceCallEndEnabled() {
return mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL);
}
@@ -895,9 +1020,8 @@
* @param displayInfo The {@link TelephonyDisplayInfo} used to determine the type.
* @return The equivalent {@link DataConfigNetworkType}.
*/
- public static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
+ private static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
@NonNull TelephonyDisplayInfo displayInfo) {
- // TODO: Make method private once DataConnection is removed
int networkType = displayInfo.getNetworkType();
switch (displayInfo.getOverrideNetworkType()) {
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
@@ -1140,24 +1264,6 @@
}
/**
- * Registration point for subscription info ready.
- *
- * @param h handler to notify.
- * @param what what code of message when delivered.
- */
- public void registerForConfigUpdate(Handler h, int what) {
- mConfigUpdateRegistrants.addUnique(h, what, null);
- }
-
- /**
- *
- * @param h The original handler passed in {@link #registerForConfigUpdate(Handler, int)}.
- */
- public void unregisterForConfigUpdate(Handler h) {
- mConfigUpdateRegistrants.remove(h);
- }
-
- /**
* Log debug messages.
* @param s debug messages
*/
@@ -1203,15 +1309,27 @@
pw.println("mSetupDataCallAnomalyReport=" + mSetupDataCallAnomalyReportThreshold);
pw.println("mNetworkUnwantedAnomalyReport=" + mNetworkUnwantedAnomalyReportThreshold);
pw.println("mImsReleaseRequestAnomalyReport=" + mImsReleaseRequestAnomalyReportThreshold);
+ pw.println("mIsInvalidQnsParamAnomalyReportEnabled="
+ + mIsInvalidQnsParamAnomalyReportEnabled);
pw.println("mNetworkConnectingTimeout=" + mNetworkConnectingTimeout);
pw.println("mNetworkDisconnectingTimeout=" + mNetworkDisconnectingTimeout);
pw.println("mNetworkHandoverTimeout=" + mNetworkHandoverTimeout);
+ pw.println("mIsApnConfigAnomalyReportEnabled=" + mIsApnConfigAnomalyReportEnabled);
+ pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold="
+ + getAutoDataSwitchAvailabilityStabilityTimeThreshold());
+ pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry());
pw.println("Metered APN types=" + mMeteredApnTypes.stream()
.map(ApnSetting::getApnTypeString).collect(Collectors.joining(",")));
pw.println("Roaming metered APN types=" + mRoamingMeteredApnTypes.stream()
.map(ApnSetting::getApnTypeString).collect(Collectors.joining(",")));
pw.println("Single data network types=" + mSingleDataNetworkTypeList.stream()
.map(TelephonyManager::getNetworkTypeName).collect(Collectors.joining(",")));
+ pw.println("Capabilities exempt from single PDN=" + mCapabilitiesExemptFromSingleDataList
+ .stream().map(DataUtils::networkCapabilityToString)
+ .collect(Collectors.joining(",")));
+ pw.println("mShouldKeepNetworkUpInNoVops=" + mShouldKeepNetworkUpInNonVops);
+ pw.println("isPingTestBeforeAutoDataSwitchRequired="
+ + isPingTestBeforeAutoDataSwitchRequired());
pw.println("Unmetered network types=" + String.join(",", mUnmeteredNetworkTypes));
pw.println("Roaming unmetered network types="
+ String.join(",", mRoamingUnmeteredNetworkTypes));
@@ -1236,7 +1354,8 @@
+ shouldPersistIwlanDataNetworksWhenDataServiceRestarted());
pw.println("Bandwidth estimation source=" + mResources.getString(
com.android.internal.R.string.config_bandwidthEstimateSource));
- pw.println("isDelayTearDownImsEnabled=" + isImsDelayTearDownEnabled());
+ pw.println("isImsDelayTearDownUntilVoiceCallEndEnabled="
+ + isImsDelayTearDownUntilVoiceCallEndEnabled());
pw.println("isEnhancedIwlanHandoverCheckEnabled=" + isEnhancedIwlanHandoverCheckEnabled());
pw.println("isTetheringProfileDisabledForRoaming="
+ isTetheringProfileDisabledForRoaming());
diff --git a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java b/src/java/com/android/internal/telephony/data/DataEnabledOverride.java
deleted file mode 100644
index e639ba6..0000000
--- a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.telephony.Annotation.ApnType;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.data.DataEnabledOverride.OverrideConditions.Condition;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * This class represents the rules for overriding data enabled settings in different conditions.
- * When data is disabled by the user, data can still be turned on temporarily when conditions
- * satisfy any rule here.
- */
-public class DataEnabledOverride {
-
- private final Set<OverrideRule> mRules = new HashSet<>();
-
- /**
- * The rule for allowing data during voice call.
- */
- private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL =
- new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL
- | OverrideConditions.CONDITION_NON_DEFAULT
- | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED
- | OverrideConditions.CONDITION_DSDS_ENABLED);
-
- /**
- * The rule for always allowing mms. Without adding any condition to the rule, any condition can
- * satisfy this rule for mms.
- */
- private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS =
- new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY);
-
- /**
- * Data enabled override rule
- */
- private static class OverrideRule {
- /**
- * APN type of the rule. The rule is APN type specific. The override is applicable to the
- * specified APN type as well. For now we only support one APN type per rule. Can be
- * expanded to multiple APN types in the future.
- */
- private final @ApnType int mApnType;
-
- /** The required conditions for overriding */
- private final OverrideConditions mRequiredConditions;
-
- /**
- * Constructor
- *
- * @param rule The override rule string. For example, {@code mms=nonDefault} or
- * {@code default=voiceCall & nonDefault}
- */
- OverrideRule(@NonNull String rule) {
- String[] tokens = rule.trim().split("\\s*=\\s*");
- if (tokens.length != 2) {
- throw new IllegalArgumentException("Invalid data enabled override rule format: "
- + rule);
- }
-
- if (TextUtils.isEmpty(tokens[0])) {
- throw new IllegalArgumentException("APN type can't be empty");
- }
-
- mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]);
- if (mApnType == ApnSetting.TYPE_NONE) {
- throw new IllegalArgumentException("Invalid APN type. Rule=" + rule);
- }
-
- mRequiredConditions = new OverrideConditions(tokens[1]);
- }
-
- /**
- * Constructor
- *
- * @param apnType APN type of the rule
- * @param requiredConditions The required conditions for the rule
- */
- private OverrideRule(int apnType, int requiredConditions) {
- mApnType = apnType;
- mRequiredConditions = new OverrideConditions(requiredConditions);
- }
-
- /**
- * Check if this rule can be satisfied by the given APN type and provided conditions.
- *
- * @param apnType APN type to check
- * @param providedConditions The provided conditions to check
- * @return {@code true} if satisfied
- */
- boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) {
- return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL)
- && mRequiredConditions.allMet(providedConditions);
- }
-
- @Override
- public String toString() {
- return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OverrideRule that = (OverrideRule) o;
- return mApnType == that.mApnType
- && Objects.equals(mRequiredConditions, that.mRequiredConditions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mApnType, mRequiredConditions);
- }
- }
-
- /**
- * Represent the conditions for overriding data enabled settings
- */
- static class OverrideConditions {
- // Possible values for data enabled override condition. Note these flags are bitmasks.
- /** Unconditionally override enabled settings */
- static final int CONDITION_UNCONDITIONALLY = 0;
-
- /** Enable data only on subscription that is not user selected default data subscription */
- static final int CONDITION_NON_DEFAULT = 1 << 0;
-
- /** Enable data only when device has ongoing voice call */
- static final int CONDITION_IN_VOICE_CALL = 1 << 1;
-
- /** Enable data only when default data is on */
- static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2;
-
- /** Enable data only when device is in DSDS mode */
- static final int CONDITION_DSDS_ENABLED = 1 << 3;
-
- /** Enable data unconditionally in string format */
- static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally";
-
- /** Enable data only on subscription that is not default in string format */
- static final String CONDITION_NON_DEFAULT_STRING = "nonDefault";
-
- /** Enable data only when device has ongoing voice call in string format */
- static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall";
-
- /** Enable data only when default data is on in string format */
- static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn";
-
- /** Enable data only when device is in DSDS mode in string format */
- static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled";
-
- /** @hide */
- @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = {
- CONDITION_NON_DEFAULT,
- CONDITION_IN_VOICE_CALL,
- CONDITION_DEFAULT_DATA_ENABLED,
- CONDITION_DSDS_ENABLED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Condition {}
-
- private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>();
- private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>();
-
- static {
- OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT,
- CONDITION_NON_DEFAULT_STRING);
- OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL,
- CONDITION_VOICE_CALL_STRING);
- OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED,
- CONDITION_DEFAULT_DATA_ENABLED_STRING);
- OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED,
- CONDITION_DSDS_ENABLED_STRING);
-
- OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING,
- CONDITION_UNCONDITIONALLY);
- OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING,
- CONDITION_NON_DEFAULT);
- OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING,
- CONDITION_IN_VOICE_CALL);
- OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING,
- CONDITION_DEFAULT_DATA_ENABLED);
- OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING,
- CONDITION_DSDS_ENABLED);
- }
-
- private final @Condition int mConditions;
-
- /**
- * Conditions for overriding data enabled setting
- *
- * @param conditions Conditions in string format
- */
- OverrideConditions(@NonNull String conditions) {
- mConditions = getBitmaskFromString(conditions);
- }
-
- /**
- * Conditions for overriding data enabled setting
- *
- * @param conditions Conditions in bitmask
- */
- OverrideConditions(@Condition int conditions) {
- mConditions = conditions;
- }
-
- private static String getStringFromBitmask(@Condition int conditions) {
- if (conditions == CONDITION_UNCONDITIONALLY) {
- return CONDITION_UNCONDITIONALLY_STRING;
- }
- List<String> conditionsStrings = new ArrayList<>();
- for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) {
- if ((conditions & condition) == condition) {
- conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition));
- }
- }
- return TextUtils.join("&", conditionsStrings);
- }
-
- private static @Condition int getBitmaskFromString(@NonNull String str) {
- if (TextUtils.isEmpty(str)) {
- throw new IllegalArgumentException("Empty rule string");
- }
-
- String[] conditionStrings = str.trim().split("\\s*&\\s*");
- int bitmask = 0;
-
- for (String conditionStr : conditionStrings) {
- if (!TextUtils.isEmpty(conditionStr)) {
- if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) {
- throw new IllegalArgumentException("Invalid conditions: " + str);
- }
- bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr);
- }
- }
-
- return bitmask;
- }
-
- /**
- * Check if provided conditions can meet all conditions in the rule.
- *
- * @param providedConditions The provided conditions
- * @return {@code true} if all conditions are met.
- */
- boolean allMet(@Condition int providedConditions) {
- return (providedConditions & mConditions) == mConditions;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OverrideConditions that = (OverrideConditions) o;
- return mConditions == that.mConditions;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mConditions);
- }
-
- @Override
- public String toString() {
- return getStringFromBitmask(mConditions);
- }
- }
-
- /**
- * Constructor
- *
- * @param rules Data enabled override rules
- */
- public DataEnabledOverride(@NonNull String rules) {
- updateRules(rules);
- }
-
- /**
- * Update the data enabled override rules.
- *
- * @param newRules New override rules
- */
- @VisibleForTesting
- public void updateRules(@NonNull String newRules) {
- mRules.clear();
- String[] rulesString = newRules.trim().split("\\s*,\\s*");
- for (String rule : rulesString) {
- if (!TextUtils.isEmpty(rule)) {
- mRules.add(new OverrideRule(rule));
- }
- }
- }
-
- /**
- * Set always allowing MMS
- *
- * @param allow {@code true} if always allowing, otherwise {@code false}.
- */
- public void setAlwaysAllowMms(boolean allow) {
- if (allow) {
- mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
- } else {
- mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
- }
- }
-
- /**
- * Set allowing mobile data during voice call. This is used for allowing data on the non-default
- * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
- * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
- * non-default data SIM during the life cycle of the voice call.
- *
- * @param allow {@code true} if allowing using data during voice call, {@code false} if
- * disallowed.
- */
- public void setDataAllowedInVoiceCall(boolean allow) {
- if (allow) {
- mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
- } else {
- mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
- }
- }
-
- /**
- * Check if data is allowed during voice call.
- *
- * @return {@code true} if data is allowed during voice call.
- */
- public boolean isDataAllowedInVoiceCall() {
- return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
- }
-
- public boolean isMmsAlwaysAllowed() {
- return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
- }
-
- private boolean canSatisfyAnyRule(@ApnType int apnType,
- @Condition int providedConditions) {
- for (OverrideRule rule : mRules) {
- if (rule.isSatisfiedByConditions(apnType, providedConditions)) {
- return true;
- }
- }
- return false;
- }
-
- private @Condition int getCurrentConditions(Phone phone) {
- int conditions = 0;
-
- if (phone != null) {
- // Check if the device is on voice call
- if (phone.getState() != PhoneConstants.State.IDLE) {
- conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL;
- }
-
- int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId();
-
- if (phone.getSubId() != defaultDataSubId) {
- conditions |= OverrideConditions.CONDITION_NON_DEFAULT;
- }
-
- if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId);
- try {
- Phone defaultDataPhone = PhoneFactory.getPhone(phoneId);
- if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) {
- conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED;
- }
- } catch (IllegalStateException e) {
- //ignore the exception and do not add the condition
- Log.d("DataEnabledOverride", e.getMessage());
- }
- }
-
- if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) {
- conditions |= OverrideConditions.CONDITION_DSDS_ENABLED;
- }
- }
-
- return conditions;
- }
-
- /**
- * Check for given APN type if we should enable data.
- *
- * @param phone Phone object
- * @param apnType APN type
- * @return {@code true} if data should be enabled for the current condition.
- */
- public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) {
- return canSatisfyAnyRule(apnType, getCurrentConditions(phone));
- }
-
- /**
- * Get data enabled override rules.
- *
- * @return Get data enabled override rules in string format
- */
- @NonNull
- public String getRules() {
- List<String> ruleStrings = new ArrayList<>();
- for (OverrideRule rule : mRules) {
- ruleStrings.add(rule.toString());
- }
- return TextUtils.join(",", ruleStrings);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DataEnabledOverride that = (DataEnabledOverride) o;
- return mRules.equals(that.mRules);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mRules);
- }
-
- @Override
- public String toString() {
- return "DataEnabledOverride: [rules=\"" + getRules() + "\"]";
- }
-}
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index c061e16..2ba3fe4 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -195,50 +195,78 @@
@VisibleForTesting
public enum DataEvaluationReason {
/** New request from the apps. */
- NEW_REQUEST,
+ NEW_REQUEST(false),
/** Data config changed. */
- DATA_CONFIG_CHANGED,
+ DATA_CONFIG_CHANGED(true),
/** SIM is loaded. */
- SIM_LOADED,
+ SIM_LOADED(true),
/** SIM is removed. */
- SIM_REMOVAL,
+ SIM_REMOVAL(true),
/** Data profiles changed. */
- DATA_PROFILES_CHANGED,
+ DATA_PROFILES_CHANGED(true),
/** When service state changes.(For now only considering data RAT and data registration). */
- DATA_SERVICE_STATE_CHANGED,
+ DATA_SERVICE_STATE_CHANGED(true),
/** When data is enabled or disabled (by user, carrier, thermal, etc...) */
- DATA_ENABLED_CHANGED,
+ DATA_ENABLED_CHANGED(true),
/** When data enabled overrides are changed (MMS always allowed, data on non-DDS sub). */
- DATA_ENABLED_OVERRIDE_CHANGED,
+ DATA_ENABLED_OVERRIDE_CHANGED(true),
/** When data roaming is enabled or disabled. */
- ROAMING_ENABLED_CHANGED,
+ ROAMING_ENABLED_CHANGED(true),
/** When voice call ended (for concurrent voice/data not supported RAT). */
- VOICE_CALL_ENDED,
+ VOICE_CALL_ENDED(true),
/** When network restricts or no longer restricts mobile data. */
- DATA_RESTRICTED_CHANGED,
+ DATA_RESTRICTED_CHANGED(true),
/** Network capabilities changed. The unsatisfied requests might have chances to attach. */
- DATA_NETWORK_CAPABILITIES_CHANGED,
+ DATA_NETWORK_CAPABILITIES_CHANGED(true),
/** When emergency call started or ended. */
- EMERGENCY_CALL_CHANGED,
+ EMERGENCY_CALL_CHANGED(true),
/** When data disconnected, re-evaluate later to see if data could be brought up again. */
- RETRY_AFTER_DISCONNECTED,
+ RETRY_AFTER_DISCONNECTED(true),
/** Data setup retry. */
- DATA_RETRY,
+ DATA_RETRY(false),
/** For handover evaluation, or for network tearing down after handover succeeds/fails. */
- DATA_HANDOVER,
+ DATA_HANDOVER(true),
/** Preferred transport changed. */
- PREFERRED_TRANSPORT_CHANGED,
+ PREFERRED_TRANSPORT_CHANGED(true),
/** Slice config changed. */
- SLICE_CONFIG_CHANGED,
+ SLICE_CONFIG_CHANGED(true),
+ /** SRVCC state changed. */
+ SRVCC_STATE_CHANGED(true),
/**
* Single data network arbitration. On certain RATs, only one data network is allowed at the
* same time.
*/
- SINGLE_DATA_NETWORK_ARBITRATION,
+ SINGLE_DATA_NETWORK_ARBITRATION(true),
/** Query from {@link TelephonyManager#isDataConnectivityPossible()}. */
- EXTERNAL_QUERY,
+ EXTERNAL_QUERY(false),
/** Tracking area code changed. */
- TAC_CHANGED,
+ TAC_CHANGED(true),
+ /** Unsatisfied network request detached. */
+ UNSATISFIED_REQUEST_DETACHED(true);
+
+ /**
+ * {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
+ * registration state changes, etc....
+ */
+ private final boolean mIsConditionBased;
+
+ /**
+ * @return {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
+ * registration state changes, etc....
+ */
+ public boolean isConditionBased() {
+ return mIsConditionBased;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param isConditionBased {@code true} if the evaluation is due to environmental changes
+ * (i.e. SIM removal, registration state changes, etc....)
+ */
+ DataEvaluationReason(boolean isConditionBased) {
+ mIsConditionBased = isConditionBased;
+ }
}
/** Disallowed reasons. There could be multiple reasons if it is not allowed. */
@@ -295,7 +323,9 @@
/** Only one data network is allowed at one time. */
ONLY_ALLOWED_SINGLE_NETWORK(true),
/** Data enabled settings are not ready. */
- DATA_SETTINGS_NOT_READY(true);
+ DATA_SETTINGS_NOT_READY(true),
+ /** Handover max retry stopped but network is not on the preferred transport. */
+ HANDOVER_RETRY_STOPPED(true);
private final boolean mIsHardReason;
@@ -334,6 +364,10 @@
*/
NORMAL,
/**
+ * Data is allowed because an ongoing VoPS call depends on this network
+ */
+ IN_VOICE_CALL,
+ /**
* The network brought up by this network request is unmetered. Should allowed no matter
* the user enables or disables data.
*/
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index e483d2b..d533933 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.data;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -52,6 +54,7 @@
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.ValidationStatus;
import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
import android.telephony.DataFailCause;
import android.telephony.DataSpecificRegistrationInfo;
import android.telephony.LinkCapacityEstimate;
@@ -87,6 +90,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RIL;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry;
@@ -165,6 +169,9 @@
/** Event for detaching a network request. */
private static final int EVENT_DETACH_NETWORK_REQUEST = 3;
+ /** Event when detect radio not available. */
+ private static final int EVENT_RADIO_NOT_AVAILABLE = 4;
+
/** Event for allocating PDU session id response. */
private static final int EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE = 5;
@@ -189,9 +196,6 @@
/** Event for display info changed. This is for getting 5G NSA or mmwave information. */
private static final int EVENT_DISPLAY_INFO_CHANGED = 13;
- /** Event for initiating an handover between cellular and IWLAN. */
- private static final int EVENT_START_HANDOVER = 14;
-
/** Event for setup data call (for handover) response from the data service. */
private static final int EVENT_HANDOVER_RESPONSE = 15;
@@ -231,6 +235,24 @@
/** Event for CSS indicator changed. */
private static final int EVENT_CSS_INDICATOR_CHANGED = 24;
+ /**
+ * Event for notifying source transport that handover is about to be initiated on target
+ * transport.
+ */
+ private static final int EVENT_NOTIFY_HANDOVER_STARTED = 25;
+
+ /**
+ * Event for the response of notifying source transport that handover is about to be initiated
+ * on target transport.
+ */
+ private static final int EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE = 26;
+
+ /**
+ * Event for the response of notifying source transport that handover is cancelled/failed on the
+ * target transport.
+ */
+ private static final int EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE = 27;
+
/** Invalid context id. */
private static final int INVALID_CID = -1;
@@ -245,8 +267,9 @@
private static final int DEFAULT_INTERNET_NETWORK_SCORE = 50;
private static final int OTHER_NETWORK_SCORE = 45;
- @IntDef(prefix = {"DEACTIVATION_REASON_"},
+ @IntDef(prefix = {"TEAR_DOWN_REASON_"},
value = {
+ TEAR_DOWN_REASON_NONE,
TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED,
TEAR_DOWN_REASON_SIM_REMOVAL,
TEAR_DOWN_REASON_AIRPLANE_MODE_ON,
@@ -279,6 +302,9 @@
})
public @interface TearDownReason {}
+ /** Data network was not torn down. */
+ public static final int TEAR_DOWN_REASON_NONE = 0;
+
/** Data network tear down requested by connectivity service. */
public static final int TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED = 1;
@@ -356,7 +382,7 @@
/** Data network tear down due to data profile not preferred. */
public static final int TEAR_DOWN_REASON_DATA_PROFILE_NOT_PREFERRED = 26;
- /** Data network tear down due to not allowed by policy. */
+ /** Data network tear down due to handover not allowed by policy. */
public static final int TEAR_DOWN_REASON_NOT_ALLOWED_BY_POLICY = 27;
/** Data network tear down due to illegal state. */
@@ -526,6 +552,10 @@
/** Data network controller. */
private final @NonNull DataNetworkController mDataNetworkController;
+ /** Data network controller callback. */
+ private final @NonNull DataNetworkController.DataNetworkControllerCallback
+ mDataNetworkControllerCallback;
+
/** Data config manager. */
private final @NonNull DataConfigManager mDataConfigManager;
@@ -605,6 +635,11 @@
private @DataFailureCause int mFailCause = DataFailCause.NONE;
/**
+ * The tear down reason if the data call is voluntarily deactivated, not due to failure.
+ */
+ private @TearDownReason int mTearDownReason = TEAR_DOWN_REASON_NONE;
+
+ /**
* The retry delay in milliseconds from setup data failure.
*/
private long mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED;
@@ -624,6 +659,11 @@
*/
private @TransportType int mTransport;
+ /**
+ * The last known data network type.
+ */
+ private @NetworkType int mLastKnownDataNetworkType;
+
/** The reason that why setting up this data network is allowed. */
private @NonNull DataAllowedReason mDataAllowedReason;
@@ -641,6 +681,9 @@
*/
private @NonNull int[] mAdministratorUids = new int[0];
+ /** Carrier privileges callback to monitor administrator UID change. */
+ private @Nullable TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback;
+
/**
* Carrier service package uid. This UID will not change through the life cycle of data network.
*/
@@ -652,6 +695,11 @@
private @Nullable LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
/**
+ * Data config callback for carrier config update.
+ */
+ private @Nullable DataConfigManagerCallback mDataConfigManagerCallback;
+
+ /**
* The network bandwidth.
*/
public static class NetworkBandwidth {
@@ -754,9 +802,10 @@
*
* @param dataNetwork The data network.
* @param cause The disconnect cause.
+ * @param tearDownReason The reason the network was torn down
*/
public abstract void onDisconnected(@NonNull DataNetwork dataNetwork,
- @DataFailureCause int cause);
+ @DataFailureCause int cause, @TearDownReason int tearDownReason);
/**
* Called when handover between IWLAN and cellular network succeeded.
@@ -810,6 +859,14 @@
* @param dataNetwork The data network.
*/
public abstract void onTrackNetworkUnwanted(@NonNull DataNetwork dataNetwork);
+
+ /**
+ * Called when a network request is detached after no longer satisfied.
+ *
+ * @param networkRequest The detached network request.
+ */
+ public abstract void onRetryUnsatisfiedNetworkRequest(
+ @NonNull TelephonyNetworkRequest networkRequest);
}
/**
@@ -846,12 +903,14 @@
mAccessNetworksManager = phone.getAccessNetworksManager();
mVcnManager = mPhone.getContext().getSystemService(VcnManager.class);
mDataNetworkController = phone.getDataNetworkController();
+ mDataNetworkControllerCallback = new DataNetworkController.DataNetworkControllerCallback(
+ getHandler()::post) {
+ @Override
+ public void onSubscriptionPlanOverride() {
+ sendMessage(EVENT_SUBSCRIPTION_PLAN_OVERRIDE);
+ }};
mDataNetworkController.registerDataNetworkControllerCallback(
- new DataNetworkController.DataNetworkControllerCallback(getHandler()::post) {
- @Override
- public void onSubscriptionPlanOverride() {
- sendMessage(EVENT_SUBSCRIPTION_PLAN_OVERRIDE);
- }});
+ mDataNetworkControllerCallback);
mDataConfigManager = mDataNetworkController.getDataConfigManager();
mDataCallSessionStats = new DataCallSessionStats(mPhone);
mDataNetworkCallback = callback;
@@ -863,13 +922,15 @@
mTrafficDescriptors.add(dataProfile.getTrafficDescriptor());
}
mTransport = transport;
+ mLastKnownDataNetworkType = getDataNetworkType();
mDataAllowedReason = dataAllowedReason;
dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
mAttachedNetworkRequestList.addAll(networkRequestList);
- mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, INVALID_CID);
- mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, INVALID_CID);
- mTcpBufferSizes = mDataConfigManager.getDefaultTcpConfigString();
+ for (int transportType : mAccessNetworksManager.getAvailableTransports()) {
+ mCid.put(transportType, INVALID_CID);
+ }
mTelephonyDisplayInfo = mPhone.getDisplayInfoController().getTelephonyDisplayInfo();
+ mTcpBufferSizes = mDataConfigManager.getTcpConfigString(mTelephonyDisplayInfo);
for (TelephonyNetworkRequest networkRequest : networkRequestList) {
networkRequest.setAttachedNetwork(DataNetwork.this);
@@ -982,18 +1043,39 @@
@Override
public void enter() {
logv("Registering all events.");
- mDataConfigManager.registerForConfigUpdate(getHandler(), EVENT_DATA_CONFIG_UPDATED);
+ mDataConfigManagerCallback = new DataConfigManagerCallback(getHandler()::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ sendMessage(EVENT_DATA_CONFIG_UPDATED);
+ }
+ };
mRil.registerForPcoData(getHandler(), EVENT_PCO_DATA_RECEIVED, null);
+
+ mDataConfigManager.registerCallback(mDataConfigManagerCallback);
mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
getHandler(), EVENT_DISPLAY_INFO_CHANGED, null);
mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
- EVENT_SERVICE_STATE_CHANGED);
+ EVENT_SERVICE_STATE_CHANGED, null);
for (int transport : mAccessNetworksManager.getAvailableTransports()) {
mDataServiceManagers.get(transport)
.registerForDataCallListChanged(getHandler(), EVENT_DATA_STATE_CHANGED);
}
- mPhone.getCarrierPrivilegesTracker().registerCarrierPrivilegesListener(getHandler(),
- EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null);
+
+ mCarrierPrivilegesCallback =
+ (Set<String> privilegedPackageNames, Set<Integer> privilegedUids) -> {
+ log("onCarrierPrivilegesChanged, Uids=" + privilegedUids.toString());
+ Message message = obtainMessage(EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED);
+ AsyncResult.forMessage(
+ message,
+ privilegedUids.stream().mapToInt(i -> i).toArray(),
+ null /* ex */);
+ sendMessage(message);
+ };
+ TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ tm.registerCarrierPrivilegesCallback(
+ mPhone.getPhoneId(), getHandler()::post, mCarrierPrivilegesCallback);
+ }
mPhone.getServiceStateTracker().registerForCssIndicatorChanged(
getHandler(), EVENT_CSS_INDICATOR_CHANGED, null);
@@ -1027,7 +1109,10 @@
mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
- mPhone.getCarrierPrivilegesTracker().unregisterCarrierPrivilegesListener(getHandler());
+ TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class);
+ if (tm != null && mCarrierPrivilegesCallback != null) {
+ tm.unregisterCarrierPrivilegesCallback(mCarrierPrivilegesCallback);
+ }
for (int transport : mAccessNetworksManager.getAvailableTransports()) {
mDataServiceManagers.get(transport)
.unregisterForDataCallListChanged(getHandler());
@@ -1035,18 +1120,22 @@
mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(
getHandler());
- mDataConfigManager.unregisterForConfigUpdate(getHandler());
mRil.unregisterForPcoData(getHandler());
+ mDataConfigManager.unregisterCallback(mDataConfigManagerCallback);
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case EVENT_DATA_CONFIG_UPDATED:
- onDataConfigUpdated();
+ onCarrierConfigUpdated();
break;
case EVENT_SERVICE_STATE_CHANGED: {
- mDataCallSessionStats.onDrsOrRatChanged(getDataNetworkType());
+ int networkType = getDataNetworkType();
+ mDataCallSessionStats.onDrsOrRatChanged(networkType);
+ if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ mLastKnownDataNetworkType = networkType;
+ }
updateSuspendState();
updateNetworkCapabilities();
break;
@@ -1057,7 +1146,8 @@
break;
}
case EVENT_DETACH_NETWORK_REQUEST: {
- onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj);
+ onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj,
+ msg.arg1 != 0 /* shouldRetry */);
updateNetworkScore();
break;
}
@@ -1088,6 +1178,9 @@
onPcoDataReceived((PcoData) ar.result);
break;
}
+ case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE:
+ log("Notified handover cancelled.");
+ break;
case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED:
case EVENT_TEAR_DOWN_NETWORK:
case EVENT_STUCK_IN_TRANSIENT_STATE:
@@ -1099,10 +1192,23 @@
// Ignore the events when not in the correct state.
log("Ignored " + eventToString(msg.what));
break;
- case EVENT_START_HANDOVER:
- log("Ignore the handover to " + AccessNetworkConstants
+ case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
+ case EVENT_NOTIFY_HANDOVER_STARTED:
+ // We reach here if network is not in the right state.
+ if (msg.obj != null) {
+ // Cancel it because it's either HO in progress or will soon disconnect.
+ // Either case we want to clean up obsolete retry attempts.
+ DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) msg.obj;
+ retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
+ }
+ log("Ignore handover to " + AccessNetworkConstants
.transportTypeToString(msg.arg1) + " request.");
break;
+ case EVENT_RADIO_NOT_AVAILABLE:
+ mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
+ loge(eventToString(msg.what) + ": transition to disconnected state");
+ transitionTo(mDisconnectedState);
+ break;
default:
loge("Unhandled event " + eventToString(msg.what));
break;
@@ -1166,7 +1272,7 @@
msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
onSetupResponse(resultCode, dataCallResponse);
break;
- case EVENT_START_HANDOVER:
+ case EVENT_NOTIFY_HANDOVER_STARTED:
case EVENT_TEAR_DOWN_NETWORK:
case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
// Defer the request until connected or disconnected.
@@ -1234,13 +1340,14 @@
}
}
- // If we've ever received PCO data before connected, now it's the time to
- // process it.
+ // If we've ever received PCO data before connected, now it's the time to process it.
mPcoData.getOrDefault(mCid.get(mTransport), Collections.emptyMap())
.forEach((pcoId, pcoData) -> {
onPcoDataChanged(pcoData);
});
+ mDataNetworkCallback.invokeFromExecutor(
+ () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus));
notifyPreciseDataConnectionState();
updateSuspendState();
}
@@ -1256,17 +1363,6 @@
}
int tearDownReason = msg.arg1;
- // If the tear down request is from upper layer, for example, IMS service
- // releases network request, we don't need to delay. The purpose of the delay
- // is to have IMS service have time to perform IMS de-registration, so if this
- // request is from IMS service itself, that means IMS service is already aware
- // of the tear down. So there is no need to delay in this case.
- if (tearDownReason != TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED
- && shouldDelayImsTearDown()) {
- logl("Delay IMS tear down until call ends. reason="
- + tearDownReasonToString(tearDownReason));
- break;
- }
removeMessages(EVENT_TEAR_DOWN_NETWORK);
removeDeferredMessages(EVENT_TEAR_DOWN_NETWORK);
@@ -1285,8 +1381,21 @@
case EVENT_DISPLAY_INFO_CHANGED:
onDisplayInfoChanged();
break;
- case EVENT_START_HANDOVER:
- onStartHandover(msg.arg1, (DataHandoverRetryEntry) msg.obj);
+ case EVENT_NOTIFY_HANDOVER_STARTED:
+ // Notify source transport that handover is about to start. Note this will not
+ // initiate the handover process on target transport, but more for notifying
+ // the source transport so that PDU session id can be preserved if network
+ // notifies PDN lost during handover. The real handover process will kick off
+ // after receiving EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE.
+ log("Notifying source transport "
+ + AccessNetworkConstants.transportTypeToString(mTransport)
+ + " that handover is about to start.");
+ mDataServiceManagers.get(mTransport).startHandover(mCid.get(mTransport),
+ obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE, 0, msg.arg2,
+ msg.obj));
+ // We enter handover state here because this is the first action we do for
+ // handover.
+ transitionTo(mHandoverState);
break;
case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
updateMeteredAndCongested();
@@ -1339,8 +1448,15 @@
// Otherwise the deferred message might be incorrectly treated as "disconnected"
// signal. So we only defer the related data call list changed event, and drop
// the unrelated.
- if (shouldDeferDataStateChangedEvent(msg)) {
- log("Defer message " + eventToString(msg.what));
+ AsyncResult ar = (AsyncResult) msg.obj;
+ int transport = (int) ar.userObj;
+ List<DataCallResponse> responseList = (List<DataCallResponse>) ar.result;
+ if (transport != mTransport) {
+ log("Dropped unrelated "
+ + AccessNetworkConstants.transportTypeToString(transport)
+ + " data call list changed event. " + responseList);
+ } else {
+ log("Defer message " + eventToString(msg.what) + ":" + responseList);
deferMessage(msg);
}
break;
@@ -1354,6 +1470,9 @@
log("Defer message " + eventToString(msg.what));
deferMessage(msg);
break;
+ case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
+ onStartHandover(msg.arg2, (DataHandoverRetryEntry) msg.obj);
+ break;
case EVENT_HANDOVER_RESPONSE:
int resultCode = msg.arg1;
DataCallResponse dataCallResponse =
@@ -1362,11 +1481,11 @@
(DataHandoverRetryEntry) msg.obj);
break;
case EVENT_STUCK_IN_TRANSIENT_STATE:
+ // enable detection only for valid timeout range
reportAnomaly("Data service did not respond the handover request within "
- + TimeUnit.MILLISECONDS.toSeconds(
+ + TimeUnit.MILLISECONDS.toSeconds(
mDataConfigManager.getNetworkHandoverTimeoutMs()) + " seconds.",
"1afe68cb-8b41-4964-a737-4f34372429ea");
-
// Handover failed. Use the retry logic defined in
// CarrierConfigManager.KEY_TELEPHONY_DATA_HANDOVER_RETRY_RULES_STRING_ARRAY.
long retry = DataCallResponse.RETRY_DURATION_UNDEFINED;
@@ -1384,39 +1503,6 @@
}
return HANDLED;
}
-
- /**
- * Check if the data call list changed event should be deferred or dropped when handover
- * is in progress.
- *
- * @param msg The data call list changed message.
- *
- * @return {@code true} if the message should be deferred.
- */
- private boolean shouldDeferDataStateChangedEvent(@NonNull Message msg) {
- // The data call list changed event should be conditionally deferred.
- // Otherwise the deferred message might be incorrectly treated as "disconnected"
- // signal. So we only defer the related data call list changed event, and drop
- // the unrelated.
- AsyncResult ar = (AsyncResult) msg.obj;
- int transport = (int) ar.userObj;
- List<DataCallResponse> responseList = (List<DataCallResponse>) ar.result;
- if (transport != mTransport) {
- log("Dropped unrelated " + AccessNetworkConstants.transportTypeToString(transport)
- + " data call list changed event. " + responseList);
- return false;
- }
-
- // Check if the data call list changed event are related to the current data network.
- boolean related = responseList.stream().anyMatch(
- r -> mCid.get(mTransport) == r.getId());
- if (related) {
- log("Deferred the related data call list changed event." + responseList);
- } else {
- log("Dropped unrelated data call list changed event. " + responseList);
- }
- return related;
- }
}
/**
@@ -1460,6 +1546,7 @@
case EVENT_STUCK_IN_TRANSIENT_STATE:
// After frameworks issues deactivate data call request, RIL should report
// data disconnected through data call list changed event subsequently.
+
reportAnomaly("RIL did not send data call list changed event after "
+ "deactivate data call request within "
+ TimeUnit.MILLISECONDS.toSeconds(
@@ -1512,8 +1599,11 @@
//************************************************************//
if (mEverConnected) {
+ mLinkStatus = DataCallResponse.LINK_STATUS_INACTIVE;
mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
- .onDisconnected(DataNetwork.this, mFailCause));
+ .onLinkStatusChanged(DataNetwork.this, mLinkStatus));
+ mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
+ .onDisconnected(DataNetwork.this, mFailCause, mTearDownReason));
if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
unregisterForWwanEvents();
}
@@ -1524,6 +1614,8 @@
}
notifyPreciseDataConnectionState();
mNetworkAgent.unregister();
+ mDataNetworkController.unregisterDataNetworkControllerCallback(
+ mDataNetworkControllerCallback);
mDataCallSessionStats.onDataCallDisconnected(mFailCause);
if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
@@ -1549,6 +1641,7 @@
private void registerForWwanEvents() {
registerForBandwidthUpdate();
mKeepaliveTracker.registerForKeepaliveStatus();
+ mRil.registerForNotAvailable(this.getHandler(), EVENT_RADIO_NOT_AVAILABLE, null);
}
/**
@@ -1557,6 +1650,7 @@
private void unregisterForWwanEvents() {
unregisterForBandwidthUpdate();
mKeepaliveTracker.unregisterForKeepaliveStatus();
+ mRil.unregisterForNotAvailable(this.getHandler());
}
@Override
@@ -1623,12 +1717,21 @@
* Called when detaching the network request from this data network.
*
* @param networkRequest Network request to detach.
+ * @param shouldRetry {@code true} if the detached network request should be retried.
*/
- private void onDetachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) {
+ private void onDetachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest,
+ boolean shouldRetry) {
mAttachedNetworkRequestList.remove(networkRequest);
networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED);
networkRequest.setAttachedNetwork(null);
+ if (shouldRetry) {
+ // Inform DataNetworkController that a network request was detached and should be
+ // scheduled to retry.
+ mDataNetworkCallback.invokeFromExecutor(
+ () -> mDataNetworkCallback.onRetryUnsatisfiedNetworkRequest(networkRequest));
+ }
+
if (mAttachedNetworkRequestList.isEmpty()) {
log("All network requests are detached.");
@@ -1649,12 +1752,15 @@
* network.
*
* @param networkRequest Network request to detach.
+ * @param shouldRetry {@code true} if the detached network request should be retried.
*/
- public void detachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) {
+ public void detachNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest,
+ boolean shouldRetry) {
if (getCurrentState() == null || isDisconnected()) {
return;
}
- sendMessage(obtainMessage(EVENT_DETACH_NETWORK_REQUEST, networkRequest));
+ sendMessage(obtainMessage(EVENT_DETACH_NETWORK_REQUEST, shouldRetry ? 1 : 0, 0,
+ networkRequest));
}
/**
@@ -1705,13 +1811,15 @@
/**
* Remove network requests that can't be satisfied anymore.
+ *
+ * @param shouldRetry {@code true} if the detached network requests should be retried.
*/
- private void removeUnsatisfiedNetworkRequests() {
+ private void removeUnsatisfiedNetworkRequests(boolean shouldRetry) {
for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) {
if (!networkRequest.canBeSatisfiedBy(mNetworkCapabilities)) {
log("removeUnsatisfiedNetworkRequests: " + networkRequest
+ " can't be satisfied anymore. Will be detached.");
- detachNetworkRequest(networkRequest);
+ detachNetworkRequest(networkRequest, shouldRetry);
}
}
}
@@ -1780,6 +1888,31 @@
}
/**
+ * In some rare cases we need to re-create the network agent, for example, underlying network
+ * IP changed, or when we unfortunately need to remove/add a immutable network capability.
+ */
+ private void recreateNetworkAgent() {
+ if (isConnecting() || isDisconnected() || isDisconnecting()) {
+ loge("Incorrect state for re-creating the network agent.");
+ return;
+ }
+
+ // Abandon the network agent because we are going to create a new one.
+ mNetworkAgent.abandon();
+ // Create a new network agent and register with connectivity service. Note that the agent
+ // will always be registered with NOT_SUSPENDED capability.
+ mNetworkAgent = createNetworkAgent();
+ mNetworkAgent.markConnected();
+ // Because network agent is always created with NOT_SUSPENDED, we need to update
+ // the suspended if it's was in suspended state.
+ if (mSuspended) {
+ log("recreateNetworkAgent: The network is in suspended state. Update the network"
+ + " capability again. nc=" + mNetworkCapabilities);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+ }
+
+ /**
* Update the network capabilities.
*/
private void updateNetworkCapabilities() {
@@ -1821,7 +1954,8 @@
DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
// Check if the network is non-VoPS.
if (dsri != null && dsri.getVopsSupportInfo() != null
- && !dsri.getVopsSupportInfo().isVopsSupported()) {
+ && !dsri.getVopsSupportInfo().isVopsSupported()
+ && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) {
builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL);
}
log("updateNetworkCapabilities: dsri=" + dsri);
@@ -1977,13 +2111,8 @@
logl("updateNetworkCapabilities: Immutable capabilities changed. Re-create the "
+ "network agent. Attempted to change from " + mNetworkCapabilities + " to "
+ nc);
- // Abandon the network agent because we are going to create a new one.
- mNetworkAgent.abandon();
- // Update the capabilities first so the new network agent would be created with the
- // new capabilities.
mNetworkCapabilities = nc;
- mNetworkAgent = createNetworkAgent();
- mNetworkAgent.markConnected();
+ recreateNetworkAgent();
} else {
// Now we need to inform connectivity service and data network controller
// about the capabilities changed.
@@ -1992,7 +2121,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
- removeUnsatisfiedNetworkRequests();
+ // Only retry the request when the network is in connected or handover state. This is to
+ // prevent request is detached during connecting state, and then become a setup/detach
+ // infinite loop.
+ boolean shouldRetry = isConnected() || isHandoverInProgress();
+ removeUnsatisfiedNetworkRequests(shouldRetry);
mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
.onNetworkCapabilitiesChanged(DataNetwork.this));
} else {
@@ -2173,8 +2306,13 @@
if (mLinkStatus != response.getLinkStatus()) {
mLinkStatus = response.getLinkStatus();
log("Link status updated to " + DataUtils.linkStatusToString(mLinkStatus));
- mDataNetworkCallback.invokeFromExecutor(
- () -> mDataNetworkCallback.onLinkStatusChanged(DataNetwork.this, mLinkStatus));
+ if (isConnected()) {
+ // If the data network is in a transition state, the link status will be notified
+ // upon entering connected or disconnected state. If the data network is already
+ // connected, send the updated link status from the updated data call response.
+ mDataNetworkCallback.invokeFromExecutor(() -> mDataNetworkCallback
+ .onLinkStatusChanged(DataNetwork.this, mLinkStatus));
+ }
}
// Set link addresses
@@ -2272,20 +2410,13 @@
// If the new link properties is not compatible (e.g. IP changes, interface changes),
// then we should de-register the network agent and re-create a new one.
if ((isConnected() || isHandoverInProgress())
- && !isLinkPropertiesCompatible(linkProperties, mLinkProperties)) {
+ && !isLinkPropertiesCompatible(mLinkProperties, linkProperties)) {
logl("updateDataNetwork: Incompatible link properties detected. Re-create the "
+ "network agent. Changed from " + mLinkProperties + " to "
+ linkProperties);
mLinkProperties = linkProperties;
-
- // Abandon the network agent because we are going to create a new one.
- mNetworkAgent.abandon();
- // Update the link properties first so the new network agent would be created with
- // the new link properties.
- mLinkProperties = linkProperties;
- mNetworkAgent = createNetworkAgent();
- mNetworkAgent.markConnected();
+ recreateNetworkAgent();
} else {
mLinkProperties = linkProperties;
log("sendLinkProperties " + mLinkProperties);
@@ -2307,14 +2438,16 @@
logl("onSetupResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode)
+ ", response=" + response);
mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
- validateDataCallResponse(response);
+ validateDataCallResponse(response, true /*isSetupResponse*/);
if (mFailCause == DataFailCause.NONE) {
- if (mDataNetworkController.isNetworkInterfaceExisting(response.getInterfaceName())) {
- logl("Interface " + response.getInterfaceName() + " already existing. Silently "
- + "tear down now.");
+ DataNetwork dataNetwork = mDataNetworkController.getDataNetworkByInterface(
+ response.getInterfaceName());
+ if (dataNetwork != null) {
+ logl("Interface " + response.getInterfaceName() + " has been already used by "
+ + dataNetwork + ". Silently tear down now.");
// If this is a pre-5G data setup, that means APN database has some problems. For
// example, different APN settings have the same APN name.
- if (response.getTrafficDescriptors().isEmpty()) {
+ if (response.getTrafficDescriptors().isEmpty() && dataNetwork.isConnected()) {
reportAnomaly("Duplicate network interface " + response.getInterfaceName()
+ " detected.", "62f66e7e-8d71-45de-a57b-dc5c78223fd5");
}
@@ -2370,15 +2503,18 @@
getDataNetworkType(),
apnTypeBitmask,
protocol,
- mFailCause);
+ // Log the raw fail cause to avoid large amount of UNKNOWN showing on metrics.
+ response != null ? response.getCause() : mFailCause);
}
/**
* If the {@link DataCallResponse} contains invalid info, triggers an anomaly report.
*
* @param response The response to be validated
+ * @param isSetupResponse {@code true} if the response is for initial data call setup
*/
- private void validateDataCallResponse(@Nullable DataCallResponse response) {
+ private void validateDataCallResponse(@Nullable DataCallResponse response,
+ boolean isSetupResponse) {
if (response == null
|| response.getLinkStatus() == DataCallResponse.LINK_STATUS_INACTIVE) return;
int failCause = response.getCause();
@@ -2398,6 +2534,37 @@
reportAnomaly("Invalid DataCallResponse detected",
"1f273e9d-b09c-46eb-ad1c-421d01f61164");
}
+ // Check IP for initial setup response
+ NetworkRegistrationInfo nri = getNetworkRegistrationInfo();
+ if (isSetupResponse
+ && mDataProfile.getApnSetting() != null && nri != null && nri.isInService()) {
+ boolean isRoaming = nri.getNetworkRegistrationState()
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ int protocol = isRoaming ? mDataProfile.getApnSetting().getRoamingProtocol()
+ : mDataProfile.getApnSetting().getProtocol();
+ String underlyingDataService = mTransport
+ == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+ ? "RIL" : "IWLAN data service";
+ if (protocol == ApnSetting.PROTOCOL_IP) {
+ if (response.getAddresses().stream().anyMatch(
+ la -> la.getAddress() instanceof java.net.Inet6Address)) {
+ loge("Invalid DataCallResponse. Requested IPv4 but got IPv6 address. "
+ + response);
+ reportAnomaly(underlyingDataService + " reported mismatched IP "
+ + "type. Requested IPv4 but got IPv6 address.",
+ "7744f920-fb64-4db0-ba47-de0eae485a80");
+ }
+ } else if (protocol == ApnSetting.PROTOCOL_IPV6) {
+ if (response.getAddresses().stream().anyMatch(
+ la -> la.getAddress() instanceof java.net.Inet4Address)) {
+ loge("Invalid DataCallResponse. Requested IPv6 but got IPv4 address. "
+ + response);
+ reportAnomaly(underlyingDataService + " reported mismatched IP "
+ + "type. Requested IPv6 but got IPv4 address.",
+ "7744f920-fb64-4db0-ba47-de0eae485a80");
+ }
+ }
+ }
} else if (!DataFailCause.isFailCauseExisting(failCause)) { // Setup data failed.
loge("Invalid DataFailCause in " + response);
reportAnomaly("Invalid DataFailCause: (0x" + Integer.toHexString(failCause)
@@ -2418,9 +2585,9 @@
log("Remove network since deactivate request returned an error.");
mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
transitionTo(mDisconnectedState);
- } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_2_0)) {
+ } else if (mPhone.getHalVersion(HAL_SERVICE_DATA).less(RIL.RADIO_HAL_VERSION_2_0)) {
log("Remove network on deactivate data response on old HAL "
- + mPhone.getHalVersion());
+ + mPhone.getHalVersion(HAL_SERVICE_DATA));
mFailCause = DataFailCause.LOST_CONNECTION;
transitionTo(mDisconnectedState);
}
@@ -2435,6 +2602,7 @@
if (getCurrentState() == null || isDisconnected()) {
return;
}
+ mTearDownReason = reason;
sendMessage(obtainMessage(EVENT_TEAR_DOWN_NETWORK, reason));
}
@@ -2454,16 +2622,17 @@
reason == TEAR_DOWN_REASON_AIRPLANE_MODE_ON ? DataService.REQUEST_REASON_SHUTDOWN
: DataService.REQUEST_REASON_NORMAL,
obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE));
- mDataCallSessionStats.setDeactivateDataCallReason(DataService.REQUEST_REASON_NORMAL);
+ mDataCallSessionStats.setDeactivateDataCallReason(reason);
mInvokedDataDeactivation = true;
}
/**
- * @return {@code true} if this is an IMS network and tear down should be delayed until call
- * ends on this data network.
+ * @return {@code true} if we shall delay tear down this network because an active voice call is
+ * relying on it and
+ * {@link CarrierConfigManager#KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL} is enabled.
*/
- public boolean shouldDelayImsTearDown() {
- return mDataConfigManager.isImsDelayTearDownEnabled()
+ public boolean shouldDelayImsTearDownDueToInCall() {
+ return mDataConfigManager.isImsDelayTearDownUntilVoiceCallEndEnabled()
&& mNetworkCapabilities != null
&& mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)
&& mPhone.getImsPhone() != null
@@ -2529,7 +2698,7 @@
if (response != null) {
if (!response.equals(mDataCallResponse)) {
log("onDataStateChanged: " + response);
- validateDataCallResponse(response);
+ validateDataCallResponse(response, false /*isSetupResponse*/);
mDataCallResponse = response;
if (response.getLinkStatus() != DataCallResponse.LINK_STATUS_INACTIVE) {
updateDataNetwork(response);
@@ -2557,10 +2726,10 @@
}
/**
- * Called when data config updated.
+ * Called when carrier config updated.
*/
- private void onDataConfigUpdated() {
- log("onDataConfigUpdated");
+ private void onCarrierConfigUpdated() {
+ log("onCarrierConfigUpdated");
updateBandwidthFromDataConfig();
updateTcpBufferSizes();
@@ -2686,9 +2855,12 @@
log("updateMeteredAndCongested: mTempNotMeteredSupported changed to "
+ mTempNotMeteredSupported);
}
- if ((mDataNetworkController.getUnmeteredOverrideNetworkTypes().contains(networkType)
- || isNetworkTypeUnmetered(networkType)) != mTempNotMetered) {
- mTempNotMetered = !mTempNotMetered;
+ boolean isTempNotMetered = mDataConfigManager.isNetworkTypeUnmetered(
+ mTelephonyDisplayInfo, mPhone.getServiceState())
+ && (mDataNetworkController.getUnmeteredOverrideNetworkTypes().contains(networkType)
+ || isNetworkTypeUnmetered(networkType));
+ if (isTempNotMetered != mTempNotMetered) {
+ mTempNotMetered = isTempNotMetered;
changed = true;
log("updateMeteredAndCongested: mTempNotMetered changed to " + mTempNotMetered);
}
@@ -2701,6 +2873,10 @@
if (changed) {
updateNetworkCapabilities();
}
+ if (mTempNotMetered && isInternetSupported()) {
+ // NR NSA and NR have the same network type: NR
+ mDataCallSessionStats.onUnmeteredUpdate(networkType);
+ }
}
/**
@@ -2965,8 +3141,6 @@
&& mNetworkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_TRUSTED)
&& mNetworkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- && mNetworkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
}
@@ -3008,6 +3182,12 @@
/**
* Request the data network to handover to the target transport.
*
+ * This is the starting point of initiating IWLAN/cellular handover. It will first call
+ * {@link DataServiceManager#startHandover(int, Message)} to notify source transport that
+ * handover is about to start, and then call {@link DataServiceManager#setupDataCall(int,
+ * DataProfile, boolean, boolean, int, LinkProperties, int, NetworkSliceInfo, TrafficDescriptor,
+ * boolean, Message)} on target transport to initiate the handover process.
+ *
* @param targetTransport The target transport.
* @param retryEntry Data handover retry entry. This would be {@code null} for first time
* handover attempt.
@@ -3020,12 +3200,16 @@
if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
return false;
}
- sendMessage(obtainMessage(EVENT_START_HANDOVER, targetTransport, 0, retryEntry));
+
+ // Before we really initiate the handover process on target transport, we need to notify
+ // source transport that handover is about to start. Handover will be eventually initiated
+ // in onStartHandover().
+ sendMessage(obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED, 0, targetTransport, retryEntry));
return true;
}
/**
- * Called when handover between IWLAN and cellular is needed.
+ * Called when starting IWLAN/cellular handover process on the target transport.
*
* @param targetTransport The target transport.
* @param retryEntry Data handover retry entry. This would be {@code null} for first time
@@ -3058,7 +3242,7 @@
&& !mAttachedNetworkRequestList.isEmpty()) {
TelephonyNetworkRequest networkRequest = mAttachedNetworkRequestList.get(0);
DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
- .getDataProfileForNetworkRequest(networkRequest, targetNetworkType);
+ .getDataProfileForNetworkRequest(networkRequest, targetNetworkType, false);
// Some carriers have different profiles between cellular and IWLAN. We need to
// dynamically switch profile, but only when those profiles have same APN name.
if (dataProfile != null && dataProfile.getApnSetting() != null
@@ -3080,7 +3264,6 @@
DataService.REQUEST_REASON_HANDOVER, mLinkProperties, mPduSessionId,
mNetworkSliceInfo, mHandoverDataProfile.getTrafficDescriptor(), true,
obtainMessage(EVENT_HANDOVER_RESPONSE, retryEntry));
- transitionTo(mHandoverState);
}
/**
@@ -3096,7 +3279,7 @@
logl("onHandoverResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode)
+ ", response=" + response);
mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
- validateDataCallResponse(response);
+ validateDataCallResponse(response, false /*isSetupResponse*/);
if (mFailCause == DataFailCause.NONE) {
// Handover succeeded.
@@ -3123,15 +3306,23 @@
() -> mDataNetworkCallback.onHandoverSucceeded(DataNetwork.this));
} else {
// Handover failed.
+
+ // Notify source transport that handover failed on target transport so that PDU session
+ // id can be released if it is preserved for handover.
+ mDataServiceManagers.get(mTransport).cancelHandover(mCid.get(mTransport),
+ obtainMessage(EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE));
+
long retry = response != null ? response.getRetryDurationMillis()
: DataCallResponse.RETRY_DURATION_UNDEFINED;
+ // If the handover mode is unspecified, default to HANDOVER_FAILURE_MODE_UNKNOWN,
+ // which will retry handover if retry rules are defined.
int handoverFailureMode = response != null ? response.getHandoverFailureMode()
- : DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
+ : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN;
if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED);
mDataNetworkCallback.invokeFromExecutor(
() -> mDataNetworkCallback.onHandoverFailed(DataNetwork.this,
mFailCause, retry, handoverFailureMode));
- mDataCallSessionStats.onHandoverFailure(mFailCause);
+ trackHandoverFailure(response != null ? response.getCause() : mFailCause);
}
// No matter handover succeeded or not, transit back to connected state.
@@ -3139,7 +3330,21 @@
}
/**
- * Called when PCO data changes.
+ * Called when handover failed. Record the source and target RAT{@link NetworkType} and the
+ * failure cause {@link android.telephony.DataFailCause}.
+ *
+ * @param cause The fail cause.
+ */
+ private void trackHandoverFailure(int cause) {
+ int sourceRat = getDataNetworkType();
+ int targetTransport = DataUtils.getTargetTransport(mTransport);
+ int targetRat = getDataNetworkType(targetTransport);
+
+ mDataCallSessionStats.onHandoverFailure(cause, sourceRat, targetRat);
+ }
+
+ /**
+ * Called when receiving PCO (Protocol Configuration Options) data from the cellular network.
*
* @param pcoData The PCO data.
*/
@@ -3179,8 +3384,14 @@
}
/**
- * @return The PCO data map of the network. The key is the PCO id, the value is the PCO data.
- * An empty map if PCO data is not available (or when the network is on IWLAN).
+ * @return The last known data network type of the data network.
+ */
+ public @NetworkType int getLastKnownDataNetworkType() {
+ return mLastKnownDataNetworkType;
+ }
+
+ /**
+ * @return The PCO data received from the network.
*/
public @NonNull Map<Integer, PcoData> getPcoData() {
if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
@@ -3205,6 +3416,17 @@
}
/**
+ * Check if any of the attached request has the specified network capability.
+ *
+ * @param netCapability The network capability to check.
+ * @return {@code true} if at least one network request has specified network capability.
+ */
+ public boolean hasNetworkCapabilityInNetworkRequests(@NetCapability int netCapability) {
+ return mAttachedNetworkRequestList.stream().anyMatch(
+ request -> request.hasCapability(netCapability));
+ }
+
+ /**
* Convert the data tear down reason to string.
*
* @param reason Data deactivation reason.
@@ -3212,6 +3434,8 @@
*/
public static @NonNull String tearDownReasonToString(@TearDownReason int reason) {
switch (reason) {
+ case TEAR_DOWN_REASON_NONE:
+ return "NONE";
case TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED:
return "CONNECTIVITY_SERVICE_UNWANTED";
case TEAR_DOWN_REASON_SIM_REMOVAL:
@@ -3289,6 +3513,8 @@
return "EVENT_ATTACH_NETWORK_REQUEST";
case EVENT_DETACH_NETWORK_REQUEST:
return "EVENT_DETACH_NETWORK_REQUEST";
+ case EVENT_RADIO_NOT_AVAILABLE:
+ return "EVENT_RADIO_NOT_AVAILABLE";
case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE:
return "EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE";
case EVENT_SETUP_DATA_NETWORK_RESPONSE:
@@ -3305,8 +3531,6 @@
return "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED";
case EVENT_DISPLAY_INFO_CHANGED:
return "EVENT_DISPLAY_INFO_CHANGED";
- case EVENT_START_HANDOVER:
- return "EVENT_START_HANDOVER";
case EVENT_HANDOVER_RESPONSE:
return "EVENT_HANDOVER_RESPONSE";
case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
@@ -3327,6 +3551,12 @@
return "EVENT_VOICE_CALL_ENDED";
case EVENT_CSS_INDICATOR_CHANGED:
return "EVENT_CSS_INDICATOR_CHANGED";
+ case EVENT_NOTIFY_HANDOVER_STARTED:
+ return "EVENT_NOTIFY_HANDOVER_STARTED";
+ case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
+ return "EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE";
+ case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE:
+ return "EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE";
default:
return "Unknown(" + event + ")";
}
@@ -3412,6 +3642,8 @@
pw.increaseIndent();
pw.println("mSubId=" + mSubId);
pw.println("mTransport=" + AccessNetworkConstants.transportTypeToString(mTransport));
+ pw.println("mLastKnownDataNetworkType=" + TelephonyManager
+ .getNetworkTypeName(mLastKnownDataNetworkType));
pw.println("WWAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
pw.println("WLAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
pw.println("mNetworkScore=" + mNetworkScore);
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 4b02428..127400b 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony.data;
-
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -35,6 +34,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
@@ -83,9 +83,9 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SlidingWindowEventCounter;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
import com.android.internal.telephony.data.DataEvaluation.DataDisallowedReason;
import com.android.internal.telephony.data.DataEvaluation.DataEvaluationReason;
@@ -111,6 +111,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -134,15 +135,15 @@
public class DataNetworkController extends Handler {
private static final boolean VDBG = false;
- /** Event for data config updated. */
- private static final int EVENT_DATA_CONFIG_UPDATED = 1;
-
/** Event for adding a network request. */
private static final int EVENT_ADD_NETWORK_REQUEST = 2;
/** Event for removing a network request. */
private static final int EVENT_REMOVE_NETWORK_REQUEST = 3;
+ /** Event for SRVCC state changed. */
+ private static final int EVENT_SRVCC_STATE_CHANGED = 4;
+
/** Re-evaluate all unsatisfied network requests. */
private static final int EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS = 5;
@@ -222,6 +223,13 @@
private static final long REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_TAC_CHANGED_DELAY_MILLIS =
TimeUnit.MILLISECONDS.toMillis(100);
+ /**
+ * The delay in milliseconds to re-evaluate unsatisfied network requests after network request
+ * detached.
+ */
+ private static final long REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_AFTER_DETACHED_DELAY_MILLIS =
+ TimeUnit.SECONDS.toMillis(1);
+
private final Phone mPhone;
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
@@ -233,6 +241,7 @@
private final @NonNull AccessNetworksManager mAccessNetworksManager;
private final @NonNull DataRetryManager mDataRetryManager;
private final @NonNull ImsManager mImsManager;
+ private final @NonNull TelecomManager mTelecomManager;
private final @NonNull NetworkPolicyManager mNetworkPolicyManager;
private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers =
new SparseArray<>();
@@ -299,12 +308,15 @@
private final @NonNull Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks =
new ArraySet<>();
- /** Indicates if packet switch data is restricted by the network. */
+ /** Indicates if packet switch data is restricted by the cellular network. */
private boolean mPsRestricted = false;
/** Indicates if NR advanced is allowed by PCO. */
private boolean mNrAdvancedCapableByPco = false;
+ /** Indicates if srvcc is going on. */
+ private boolean mIsSrvccHandoverInProcess = false;
+
/**
* Indicates if the data services are bound. Key if the transport type, and value is the boolean
* indicating service is bound or not.
@@ -463,6 +475,7 @@
}
return true;
}
+
/**
* Get the first network request that contains all the provided network capabilities.
*
@@ -548,10 +561,19 @@
/**
* Called when internet data network is connected.
*
- * @param dataProfiles The data profiles of the connected internet data network. It should
- * be only one in most of the cases.
+ * @param internetNetworks The connected internet data network. It should be only one in
+ * most of the cases.
*/
- public void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) {}
+ public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {}
+
+ /**
+ * Called when data network is connected.
+ *
+ * @param transport Transport for the connected network.
+ * @param dataProfile The data profile of the connected data network.
+ */
+ public void onDataNetworkConnected(@TransportType int transport,
+ @NonNull DataProfile dataProfile) {}
/** Called when internet data network is disconnected. */
public void onInternetDataNetworkDisconnected() {}
@@ -713,9 +735,9 @@
+ "\"" + ruleString + "\"");
}
- if (source.contains(AccessNetworkType.UNKNOWN)) {
- throw new IllegalArgumentException("Source access networks contains unknown. "
- + "\"" + ruleString + "\"");
+ if (source.contains(AccessNetworkType.UNKNOWN) && type != RULE_TYPE_DISALLOWED) {
+ throw new IllegalArgumentException("Unknown access network can be only specified in"
+ + " the disallowed rule. \"" + ruleString + "\"");
}
if (target.contains(AccessNetworkType.UNKNOWN)) {
@@ -772,13 +794,10 @@
log("DataNetworkController created.");
mAccessNetworksManager = phone.getAccessNetworksManager();
- mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- new DataServiceManager(mPhone, looper, AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
- if (!mAccessNetworksManager.isInLegacyMode()) {
- mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
- new DataServiceManager(mPhone, looper,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
+ for (int transport : mAccessNetworksManager.getAvailableTransports()) {
+ mDataServiceManagers.put(transport, new DataServiceManager(mPhone, looper, transport));
}
+
mDataConfigManager = new DataConfigManager(mPhone, looper);
// ========== Anomaly counters ==========
@@ -884,28 +903,12 @@
public void onDataNetworkHandoverRetryStopped(
@NonNull DataNetwork dataNetwork) {
Objects.requireNonNull(dataNetwork);
- int preferredTransport = mAccessNetworksManager
- .getPreferredTransportByNetworkCapability(
- dataNetwork.getApnTypeNetworkCapability());
- if (dataNetwork.getTransport() == preferredTransport) {
- log("onDataNetworkHandoverRetryStopped: " + dataNetwork + " is already "
- + "on the preferred transport "
- + AccessNetworkConstants.transportTypeToString(
- preferredTransport));
- return;
- }
- if (dataNetwork.shouldDelayImsTearDown()) {
- log("onDataNetworkHandoverRetryStopped: Delay IMS tear down until call "
- + "ends. " + dataNetwork);
- return;
- }
-
- tearDownGracefully(dataNetwork,
- DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED);
+ DataNetworkController.this.onDataNetworkHandoverRetryStopped(dataNetwork);
}
});
mImsManager = mPhone.getContext().getSystemService(ImsManager.class);
mNetworkPolicyManager = mPhone.getContext().getSystemService(NetworkPolicyManager.class);
+ mTelecomManager = mPhone.getContext().getSystemService(TelecomManager.class);
// Use the raw one from ServiceStateTracker instead of the combined one from
// mPhone.getServiceState().
@@ -961,8 +964,17 @@
});
mPhone.getServiceStateTracker().registerForServiceStateChanged(this,
- EVENT_SERVICE_STATE_CHANGED);
- mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
+ EVENT_SERVICE_STATE_CHANGED, null);
+ mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ DataNetworkController.this.onCarrierConfigUpdated();
+ }
+ @Override
+ public void onDeviceConfigChanged() {
+ DataNetworkController.this.onDeviceConfigUpdated();
+ }
+ });
mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
EVENT_PS_RESTRICT_ENABLED, null);
mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
@@ -972,12 +984,10 @@
mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED);
- if (!mAccessNetworksManager.isInLegacyMode()) {
- mPhone.getServiceStateTracker().registerForServiceStateChanged(this,
- EVENT_SERVICE_STATE_CHANGED);
- mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED);
- }
+ mPhone.getServiceStateTracker().registerForServiceStateChanged(this,
+ EVENT_SERVICE_STATE_CHANGED, null);
+ mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED);
mPhone.getContext().getSystemService(TelephonyRegistryManager.class)
.addOnSubscriptionsChangedListener(new OnSubscriptionsChangedListener() {
@@ -999,6 +1009,7 @@
this, EVENT_VOICE_CALL_ENDED, null);
}
mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICE_CONFIG_CHANGED, null);
+ mPhone.mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
mPhone.getLinkBandwidthEstimator().registerCallback(
new LinkBandwidthEstimatorCallback(this::post) {
@@ -1012,10 +1023,8 @@
@Override
public void handleMessage(@NonNull Message msg) {
+ AsyncResult ar;
switch (msg.what) {
- case EVENT_DATA_CONFIG_UPDATED:
- onDataConfigUpdated();
- break;
case EVENT_REGISTER_ALL_EVENTS:
onRegisterAllEvents();
break;
@@ -1049,6 +1058,12 @@
sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
DataEvaluationReason.SLICE_CONFIG_CHANGED));
break;
+ case EVENT_SRVCC_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ onSrvccStateChanged((int[]) ar.result);
+ }
+ break;
case EVENT_PS_RESTRICT_ENABLED:
mPsRestricted = true;
break;
@@ -1065,7 +1080,7 @@
REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_TAC_CHANGED_DELAY_MILLIS);
break;
case EVENT_DATA_SERVICE_BINDING_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
+ ar = (AsyncResult) msg.obj;
int transport = (int) ar.userObj;
boolean bound = (boolean) ar.result;
onDataServiceBindingChanged(transport, bound);
@@ -1078,7 +1093,13 @@
onTearDownAllDataNetworks(msg.arg1);
break;
case EVENT_REGISTER_DATA_NETWORK_CONTROLLER_CALLBACK:
- mDataNetworkControllerCallbacks.add((DataNetworkControllerCallback) msg.obj);
+ DataNetworkControllerCallback callback = (DataNetworkControllerCallback) msg.obj;
+ mDataNetworkControllerCallbacks.add(callback);
+ // Notify upon registering if no data networks currently exist.
+ if (mDataNetworkList.isEmpty()) {
+ callback.invokeFromExecutor(
+ () -> callback.onAnyDataNetworkExistingChanged(false));
+ }
break;
case EVENT_UNREGISTER_DATA_NETWORK_CONTROLLER_CALLBACK:
mDataNetworkControllerCallbacks.remove((DataNetworkControllerCallback) msg.obj);
@@ -1106,8 +1127,8 @@
log("Subscription plans changed: " + Arrays.toString(plans));
mSubscriptionPlans.clear();
mSubscriptionPlans.addAll(Arrays.asList(plans));
- mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
- () -> callback.onSubscriptionPlanOverride()));
+ mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
+ () -> cb.onSubscriptionPlanOverride()));
break;
case EVENT_SUBSCRIPTION_OVERRIDE:
int overrideMask = msg.arg1;
@@ -1126,8 +1147,8 @@
mUnmeteredOverrideNetworkTypes.remove(networkType);
}
}
- mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
- () -> callback.onSubscriptionPlanOverride()));
+ mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
+ () -> cb.onSubscriptionPlanOverride()));
} else if (overrideMask == NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED) {
log("Congested subscription override: override=" + override
+ ", networkTypes=" + Arrays.stream(networkTypes)
@@ -1140,8 +1161,8 @@
mCongestedOverrideNetworkTypes.remove(networkType);
}
}
- mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
- () -> callback.onSubscriptionPlanOverride()));
+ mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
+ () -> cb.onSubscriptionPlanOverride()));
} else {
loge("Unknown override mask: " + overrideMask);
}
@@ -1300,7 +1321,7 @@
if (nriRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
|| nriRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) return true;
- // If data is OOS on the non-DDS,
+ // If data is OOS as this device slot is not modem preferred(i.e. not active for internet),
// attempt to attach PS on 2G/3G if CS connection is available.
return ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
&& mPhone.getPhoneId() != PhoneSwitcher.getInstance().getPreferredDataPhoneId()
@@ -1329,6 +1350,17 @@
}
/**
+ * @param capabilities The Network Capabilities to be checked.
+ * @return {@code true} if the capabilities contain any capability that's exempt from the single
+ * PDN rule.
+ */
+ private boolean hasCapabilityExemptsFromSinglePdnRule(@NetCapability int[] capabilities) {
+ Set<Integer> exemptCapabilities =
+ mDataConfigManager.getCapabilitiesExemptFromSingleDataNetwork();
+ return Arrays.stream(capabilities).anyMatch(exemptCapabilities::contains);
+ }
+
+ /**
* Evaluate if telephony frameworks would allow data setup for internet in current environment.
*
* @return {@code true} if the environment is allowed for internet data. {@code false} if not
@@ -1343,13 +1375,31 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build(), mPhone);
+ // If one of the existing networks can satisfy the internet request, then internet is
+ // allowed.
+ if (mDataNetworkList.stream().anyMatch(dataNetwork -> internetRequest.canBeSatisfiedBy(
+ dataNetwork.getNetworkCapabilities()))) {
+ return true;
+ }
+
+ // If no existing network can satisfy the request, then check if we can possibly setup
+ // the internet network.
+
DataEvaluation evaluation = evaluateNetworkRequest(internetRequest,
DataEvaluationReason.EXTERNAL_QUERY);
+ if (evaluation.containsOnly(DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK)) {
+ // If the only failed reason is only single network allowed, then check if the request
+ // can trump the current network.
+ return internetRequest.getPriority() > mDataNetworkList.stream()
+ .map(DataNetwork::getPriority)
+ .max(Comparator.comparing(Integer::valueOf))
+ .orElse(0);
+ }
return !evaluation.containsDisallowedReasons();
}
/**
- * @return {@code true} internet is unmetered.
+ * @return {@code true} if internet is unmetered.
*/
public boolean isInternetUnmetered() {
return mDataNetworkList.stream()
@@ -1362,6 +1412,17 @@
}
/**
+ * @return {@code true} if all data networks are disconnected.
+ */
+ public boolean areAllDataDisconnected() {
+ if (!mDataNetworkList.isEmpty()) {
+ log("areAllDataDisconnected false due to: " + mDataNetworkList.stream()
+ .map(DataNetwork::name).collect(Collectors.joining(", ")));
+ }
+ return mDataNetworkList.isEmpty();
+ }
+
+ /**
* @return List of the reasons why internet data is not allowed. An empty list if internet
* is allowed.
*/
@@ -1394,7 +1455,7 @@
if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest(
- networkRequest, getDataNetworkType(transport)));
+ networkRequest, getDataNetworkType(transport), true));
networkRequest.setEvaluation(evaluation);
log(evaluation.toString());
return evaluation;
@@ -1446,8 +1507,8 @@
evaluation.addDataDisallowedReason(DataDisallowedReason.ROAMING_DISABLED);
}
- // Check if data is restricted by the network.
- if (mPsRestricted) {
+ // Check if data is restricted by the cellular network.
+ if (mPsRestricted && transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_RESTRICTED_BY_NETWORK);
}
@@ -1481,13 +1542,12 @@
}
// Check if only one data network is allowed.
- // Note any IMS network is ignored for the single-connection rule.
if (isOnlySingleDataNetworkAllowed(transport)
- && !networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- // if exists non-IMS network
+ && !hasCapabilityExemptsFromSinglePdnRule(networkRequest.getCapabilities())) {
+ // if exists not-exempt network.
if (mDataNetworkList.stream()
- .anyMatch(dataNetwork -> !dataNetwork.getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS))) {
+ .anyMatch(dataNetwork -> !hasCapabilityExemptsFromSinglePdnRule(
+ dataNetwork.getNetworkCapabilities().getCapabilities()))) {
evaluation.addDataDisallowedReason(
DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK);
}
@@ -1508,20 +1568,23 @@
if (!mDataSettingsManager.isDataEnabled()
&& networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
- && mDataSettingsManager.isMmsAlwaysAllowed()) {
+ && mDataSettingsManager.isMobileDataPolicyEnabled(TelephonyManager
+ .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) {
// We reach here when data is disabled, but MMS always-allowed is enabled.
// (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it
// would not generate any soft disallowed reason. We need to explicitly handle it.)
evaluation.addDataAllowedReason(DataAllowedReason.MMS_REQUEST);
}
} else if (!evaluation.containsHardDisallowedReasons()) {
- if ((mPhone.isInEmergencyCall() || mPhone.isInEcm())
+ if ((mTelecomManager.isInEmergencyCall() || mPhone.isInEcm())
&& networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
// Check if it's SUPL during emergency call.
evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_SUPL);
} else if (!networkRequest.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
- // Check if request is restricted.
+ NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) && !networkRequest
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ // Check if request is restricted and not for tethering, which always comes with
+ // a restricted network request.
evaluation.addDataAllowedReason(DataAllowedReason.RESTRICTED_REQUEST);
} else if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
// Check if request is unmetered (WiFi or unmetered APN).
@@ -1534,17 +1597,25 @@
}
// Check if there is any compatible data profile
+ int networkType = getDataNetworkType(transport);
+ if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN
+ && transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ // reach here when data is OOS but serviceStateAllowsPSAttach == true, so we adopt the
+ // voice RAT to select data profile
+ networkType = mServiceState.getVoiceNetworkType();
+ }
DataProfile dataProfile = mDataProfileManager
- .getDataProfileForNetworkRequest(networkRequest, getDataNetworkType(transport));
+ .getDataProfileForNetworkRequest(networkRequest, networkType,
+ // If the evaluation is due to environmental changes, then we should ignore
+ // the permanent failure reached earlier.
+ reason.isConditionBased());
if (dataProfile == null) {
evaluation.addDataDisallowedReason(DataDisallowedReason.NO_SUITABLE_DATA_PROFILE);
- } else if (reason == DataEvaluationReason.NEW_REQUEST
- && (mDataRetryManager.isAnySetupRetryScheduled(dataProfile, transport)
- || mDataRetryManager.isSimilarNetworkRequestRetryScheduled(
- networkRequest, transport))) {
- // If this is a new request, check if there is any retry already scheduled. For all
- // other evaluation reasons, since they are all condition changes, so if there is any
- // retry scheduled, we still want to go ahead and setup the data network.
+ } else if (// Check for new requests if we already self-scheduled(as opposed to modem
+ // demanded) retry for similar requests.
+ reason == DataEvaluationReason.NEW_REQUEST
+ && mDataRetryManager.isSimilarNetworkRequestRetryScheduled(
+ networkRequest, transport)) {
evaluation.addDataDisallowedReason(DataDisallowedReason.RETRY_SCHEDULED);
} else if (mDataRetryManager.isDataProfileThrottled(dataProfile, transport)) {
evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_THROTTLED);
@@ -1648,16 +1719,18 @@
}
// Check if there are other network that has higher priority, and only single data network
- // is allowed. Note IMS network is exempt from the single-connection rule.
+ // is allowed.
if (isOnlySingleDataNetworkAllowed(dataNetwork.getTransport())
- && !dataNetwork.getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ && !hasCapabilityExemptsFromSinglePdnRule(
+ dataNetwork.getNetworkCapabilities().getCapabilities())) {
// If there is network request that has higher priority than this data network, then
// tear down the network, regardless that network request is satisfied or not.
if (mAllNetworkRequestList.stream()
.filter(request -> dataNetwork.getTransport()
== mAccessNetworksManager.getPreferredTransportByNetworkCapability(
request.getApnTypeNetworkCapability()))
+ .filter(request
+ -> !hasCapabilityExemptsFromSinglePdnRule(request.getCapabilities()))
.anyMatch(request -> request.getPriority() > dataNetwork.getPriority())) {
evaluation.addDataDisallowedReason(
DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK);
@@ -1667,13 +1740,20 @@
}
}
- // If the data network is IMS that supports voice call, and has MMTEL request (client
- // specified VoPS is required.)
- if (dataNetwork.getAttachedNetworkRequestList().get(
- new int[]{NetworkCapabilities.NET_CAPABILITY_MMTEL}) != null) {
- // When reaching here, it means the network supports MMTEL, and also has MMTEL request
- // attached to it.
- if (!dataNetwork.shouldDelayImsTearDown()) {
+ boolean vopsIsRequired = dataNetwork.hasNetworkCapabilityInNetworkRequests(
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
+ // Check an active call relying on this network and config for "delay tear down due to vops
+ // call" is enabled.
+ if (dataNetwork.shouldDelayImsTearDownDueToInCall()) {
+ if (vopsIsRequired) {
+ log("Ignored VoPS check due to delay IMS tear down until call ends.");
+ }
+ } else {
+ // Reach here means we should ignore active calls even if there are any.
+
+ // Check if VoPS requirement is met.
+ if (vopsIsRequired) {
if (dataNetwork.getTransport() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
@@ -1681,22 +1761,27 @@
if (nri != null) {
DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
if (dsri != null && dsri.getVopsSupportInfo() != null
- && !dsri.getVopsSupportInfo().isVopsSupported()) {
+ && !dsri.getVopsSupportInfo().isVopsSupported()
+ && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) {
evaluation.addDataDisallowedReason(
DataDisallowedReason.VOPS_NOT_SUPPORTED);
}
}
}
- } else {
- log("Ignored VoPS check due to delay IMS tear down until call ends.");
+ }
+
+ // Check if handover retry stopped and preferred transport still not matched.
+ int preferredTransport = mAccessNetworksManager
+ .getPreferredTransportByNetworkCapability(
+ dataNetwork.getApnTypeNetworkCapability());
+ if (preferredTransport != dataNetwork.getTransport()
+ && mDataRetryManager.isDataNetworkHandoverRetryStopped(dataNetwork)) {
+ evaluation.addDataDisallowedReason(DataDisallowedReason.HANDOVER_RETRY_STOPPED);
}
}
// Check if data is disabled
- boolean dataDisabled = false;
- if (!mDataSettingsManager.isDataEnabled()) {
- dataDisabled = true;
- }
+ boolean dataDisabled = !mDataSettingsManager.isDataEnabled();
// Check if data roaming is disabled
if (mServiceState.getDataRoaming() && !mDataSettingsManager.isDataRoamingEnabled()) {
@@ -1711,10 +1796,9 @@
DataProfile dataProfile = dataNetwork.getDataProfile();
if (dataProfile.getApnSetting() != null) {
// Check if data is disabled for the APN type
- dataDisabled = !mDataSettingsManager.isDataEnabled(DataUtils
- .networkCapabilityToApnType(DataUtils
- .getHighestPriorityNetworkCapabilityFromDataProfile(
- mDataConfigManager, dataProfile)));
+ dataDisabled = !mDataSettingsManager.isDataEnabled(
+ DataUtils.networkCapabilityToApnType(
+ dataNetwork.getApnTypeNetworkCapability()));
// Sometimes network temporarily OOS and network type becomes UNKNOWN. We don't
// tear down network in that case.
@@ -1722,9 +1806,11 @@
&& !dataProfile.getApnSetting().canSupportLingeringNetworkType(networkType)) {
log("networkType=" + TelephonyManager.getNetworkTypeName(networkType)
+ ", networkTypeBitmask="
- + dataProfile.getApnSetting().getNetworkTypeBitmask()
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ dataProfile.getApnSetting().getNetworkTypeBitmask())
+ ", lingeringNetworkTypeBitmask="
- + dataProfile.getApnSetting().getLingeringNetworkTypeBitmask());
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ dataProfile.getApnSetting().getLingeringNetworkTypeBitmask()));
evaluation.addDataDisallowedReason(
DataDisallowedReason.DATA_NETWORK_TYPE_NOT_ALLOWED);
}
@@ -1734,20 +1820,18 @@
evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_DISABLED);
}
- // Check if the data profile is still valid, sometimes the users can remove it from the APN
- // editor. We use very loose check here because APN id can change after APN reset to
- // default
- if (mDataProfileManager.getDataProfile(
- dataProfile.getApnSetting() != null
- ? dataProfile.getApnSetting().getApnName() : null,
- dataProfile.getTrafficDescriptor()) == null) {
+ // Check if the data profile is still compatible, sometimes the users can remove it from the
+ // APN editor. If some of the important fields are changed in APN settings, we need to
+ // tear down the network. Note traffic descriptor from the data profile will not be checked.
+ if (!mDataProfileManager.isDataProfileCompatible(dataProfile)) {
evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_PROFILE_INVALID);
}
// If users switch preferred profile in APN editor, we need to tear down network.
if (dataNetwork.isInternetSupported()
&& !mDataProfileManager.isDataProfilePreferred(dataProfile)
- && mDataProfileManager.isAnyPreferredDataProfileExisting()) {
+ && mDataProfileManager.canPreferredDataProfileSatisfy(
+ dataNetwork.getAttachedNetworkRequestList())) {
evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_PROFILE_NOT_PREFERRED);
}
@@ -1759,12 +1843,16 @@
// If there are reasons we should tear down the network, check if those are hard reasons
// or soft reasons. In some scenarios, we can make exceptions if they are soft
// disallowed reasons.
- if ((mPhone.isInEmergencyCall() || mPhone.isInEcm()) && dataNetwork.isEmergencySupl()) {
+ if ((mTelecomManager.isInEmergencyCall() || mPhone.isInEcm())
+ && dataNetwork.isEmergencySupl()) {
// Check if it's SUPL during emergency call.
evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_SUPL);
} else if (!dataNetwork.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
- // Check if request is restricted
+ NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ && !dataNetwork.hasNetworkCapabilityInNetworkRequests(
+ NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ // Check if request is restricted and there are no DUN network requests attached to
+ // the network.
evaluation.addDataAllowedReason(DataAllowedReason.RESTRICTED_REQUEST);
} else if (dataNetwork.getTransport() == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
// Check if request is unmetered (WiFi or unmetered APN)
@@ -1779,7 +1867,16 @@
}
}
- log("Evaluated " + dataNetwork + ", " + evaluation.toString());
+ // Check if we allow additional lingering for active VoPS call network if
+ // a. this network is SRVCC handover in progress
+ // or b. "delay tear down due to active VoPS call" is enabled
+ boolean isInSrvcc = vopsIsRequired && mIsSrvccHandoverInProcess;
+ if (evaluation.containsOnly(DataDisallowedReason.DATA_NETWORK_TYPE_NOT_ALLOWED)
+ && (dataNetwork.shouldDelayImsTearDownDueToInCall() || isInSrvcc)) {
+ evaluation.addDataAllowedReason(DataAllowedReason.IN_VOICE_CALL);
+ }
+
+ log("Evaluated " + dataNetwork + ", " + evaluation);
return evaluation;
}
@@ -1821,11 +1918,6 @@
return dataEvaluation;
}
- if (mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) {
- dataEvaluation.addDataDisallowedReason(DataDisallowedReason.RETRY_SCHEDULED);
- return dataEvaluation;
- }
-
// If enhanced handover check is enabled, perform extra checks.
if (mDataConfigManager.isEnhancedIwlanHandoverCheckEnabled()) {
int targetTransport = DataUtils.getTargetTransport(dataNetwork.getTransport());
@@ -1845,7 +1937,8 @@
DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
// Check if the network is non-VoPS.
if (dsri != null && dsri.getVopsSupportInfo() != null
- && !dsri.getVopsSupportInfo().isVopsSupported()) {
+ && !dsri.getVopsSupportInfo().isVopsSupported()
+ && !mDataConfigManager.shouldKeepNetworkUpInNonVops()) {
dataEvaluation.addDataDisallowedReason(
DataDisallowedReason.VOPS_NOT_SUPPORTED);
}
@@ -1860,8 +1953,18 @@
if (mDataConfigManager.isIwlanHandoverPolicyEnabled()) {
List<HandoverRule> handoverRules = mDataConfigManager.getHandoverRules();
+ int sourceNetworkType = getDataNetworkType(dataNetwork.getTransport());
+ if (sourceNetworkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ // Using the data network type stored in the data network. We
+ // cache the last known network type in data network controller
+ // because data network has much shorter life cycle. It can prevent
+ // the obsolete last known network type cached in data network
+ // type controller.
+ sourceNetworkType = dataNetwork.getLastKnownDataNetworkType();
+ }
int sourceAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
- getDataNetworkType(dataNetwork.getTransport()));
+ sourceNetworkType);
+
int targetAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
getDataNetworkType(DataUtils.getTargetTransport(dataNetwork.getTransport())));
NetworkCapabilities capabilities = dataNetwork.getNetworkCapabilities();
@@ -1965,9 +2068,11 @@
return DataNetwork.TEAR_DOWN_REASON_VOPS_NOT_SUPPORTED;
case ONLY_ALLOWED_SINGLE_NETWORK:
return DataNetwork.TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK;
+ case HANDOVER_RETRY_STOPPED:
+ return DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED;
}
}
- return 0;
+ return DataNetwork.TEAR_DOWN_REASON_NONE;
}
/**
@@ -2050,7 +2155,8 @@
}
if (networkRequest.getAttachedNetwork() != null) {
- networkRequest.getAttachedNetwork().detachNetworkRequest(networkRequest);
+ networkRequest.getAttachedNetwork().detachNetworkRequest(
+ networkRequest, false /* shouldRetry */);
}
log("onRemoveNetworkRequest: Removed " + networkRequest);
}
@@ -2067,16 +2173,19 @@
}
/**
- * Check if there are existing networks having the same interface name.
+ * Get data network by interface name.
*
- * @param interfaceName The interface name to check.
- * @return {@code true} if the existing network has the same interface name.
+ * @param interfaceName The network interface name.
+ * @return The data network if found.
*/
- public boolean isNetworkInterfaceExisting(@NonNull String interfaceName) {
+ @Nullable
+ public DataNetwork getDataNetworkByInterface(@NonNull String interfaceName) {
return mDataNetworkList.stream()
.filter(dataNetwork -> !dataNetwork.isDisconnecting())
- .anyMatch(dataNetwork -> interfaceName.equals(
- dataNetwork.getLinkProperties().getInterfaceName()));
+ .filter(dataNetwork -> interfaceName.equals(
+ dataNetwork.getLinkProperties().getInterfaceName()))
+ .findFirst()
+ .orElse(null);
}
/**
@@ -2239,19 +2348,23 @@
}
/**
- * Called when data config was updated.
+ * Called when carrier config was updated.
*/
- private void onDataConfigUpdated() {
- log("onDataConfigUpdated: config is "
+ private void onCarrierConfigUpdated() {
+ log("onCarrierConfigUpdated: config is "
+ (mDataConfigManager.isConfigCarrierSpecific() ? "" : "not ")
+ "carrier specific. mSimState="
- + SubscriptionInfoUpdater.simStateString(mSimState)
- + ". DeviceConfig updated.");
-
- updateAnomalySlidingWindowCounters();
+ + TelephonyManager.simStateToString(mSimState));
updateNetworkRequestsPriority();
- sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
- DataEvaluationReason.DATA_CONFIG_CHANGED));
+ onReevaluateUnsatisfiedNetworkRequests(DataEvaluationReason.DATA_CONFIG_CHANGED);
+ }
+
+ /**
+ * Called when device config was updated.
+ */
+ private void onDeviceConfigUpdated() {
+ log("onDeviceConfigUpdated: DeviceConfig updated.");
+ updateAnomalySlidingWindowCounters();
}
/**
@@ -2326,9 +2439,12 @@
+ dataSetupRetryEntry + ", allowed reason=" + allowedReason + ", service state="
+ mServiceState);
for (DataNetwork dataNetwork : mDataNetworkList) {
- if (dataNetwork.getDataProfile().equals(dataProfile)) {
+ DataProfile currentDataProfile = dataNetwork.getDataProfile();
+ if (dataProfile.equals(currentDataProfile)
+ || mDataProfileManager.areDataProfilesSharingApn(
+ dataProfile, currentDataProfile)) {
log("onSetupDataNetwork: Found existing data network " + dataNetwork
- + " has the same data profile.");
+ + " using the same or a similar data profile.");
if (dataSetupRetryEntry != null) {
dataSetupRetryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
}
@@ -2399,9 +2515,9 @@
@Override
public void onDisconnected(@NonNull DataNetwork dataNetwork,
- @DataFailureCause int cause) {
+ @DataFailureCause int cause, @TearDownReason int tearDownReason) {
DataNetworkController.this.onDataNetworkDisconnected(
- dataNetwork, cause);
+ dataNetwork, cause, tearDownReason);
}
@Override
@@ -2437,6 +2553,13 @@
public void onTrackNetworkUnwanted(@NonNull DataNetwork dataNetwork) {
DataNetworkController.this.onTrackNetworkUnwanted();
}
+
+ @Override
+ public void onRetryUnsatisfiedNetworkRequest(
+ @NonNull TelephonyNetworkRequest networkRequest) {
+ DataNetworkController.this.onRetryUnsatisfiedNetworkRequest(
+ networkRequest);
+ }
}
));
if (!mAnyDataNetworkExisting) {
@@ -2460,7 +2583,7 @@
logl("onDataNetworkSetupDataFailed: " + dataNetwork + ", cause="
+ DataFailCause.toString(cause) + ", retryDelayMillis=" + retryDelayMillis + "ms.");
mDataNetworkList.remove(dataNetwork);
- trackSetupDataCallFailure(dataNetwork.getTransport());
+ trackSetupDataCallFailure(dataNetwork.getTransport(), cause);
if (mAnyDataNetworkExisting && mDataNetworkList.isEmpty()) {
mPendingTearDownAllNetworks = false;
mAnyDataNetworkExisting = false;
@@ -2485,8 +2608,10 @@
* {@link AccessNetworkConstants.TransportType} data service.
*
* @param transport The transport of the data service.
+ * @param cause The fail cause
*/
- private void trackSetupDataCallFailure(@TransportType int transport) {
+ private void trackSetupDataCallFailure(@TransportType int transport,
+ @DataFailureCause int cause) {
switch (transport) {
case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
// Skip when poor signal strength
@@ -2494,6 +2619,10 @@
<= CellSignalStrength.SIGNAL_STRENGTH_POOR) {
return;
}
+ if (cause == DataFailCause.ERROR_UNSPECIFIED || cause == DataFailCause.UNKNOWN) {
+ reportAnomaly("RIL set up data call fails: unknown/unspecified error",
+ "ce7d1465-d8e4-404a-b76f-de2c60bee843");
+ }
if (mSetupDataCallWwanFailureCounter.addOccurrence()) {
reportAnomaly("RIL fails setup data call request "
+ mSetupDataCallWwanFailureCounter.getFrequencyString(),
@@ -2501,6 +2630,10 @@
}
break;
case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
+ if (cause == DataFailCause.ERROR_UNSPECIFIED || cause == DataFailCause.UNKNOWN) {
+ reportAnomaly("IWLAN set up data call fails: unknown/unspecified error",
+ "a16fc15c-815b-4908-b8e6-5f3bc7cbc20b");
+ }
if (mSetupDataCallWlanFailureCounter.addOccurrence()) {
reportAnomaly("IWLAN data service fails setup data call request "
+ mSetupDataCallWlanFailureCounter.getFrequencyString(),
@@ -2530,6 +2663,11 @@
*/
private void onDataNetworkConnected(@NonNull DataNetwork dataNetwork) {
logl("onDataNetworkConnected: " + dataNetwork);
+
+ mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ () -> callback.onDataNetworkConnected(dataNetwork.getTransport(),
+ dataNetwork.getDataProfile())));
+
mPreviousConnectedDataNetworkList.add(0, dataNetwork);
// Preserve the connected data networks for debugging purposes.
if (mPreviousConnectedDataNetworkList.size() > MAX_HISTORICAL_CONNECTED_DATA_NETWORKS) {
@@ -2558,11 +2696,22 @@
NetworkRequestList requestList = new NetworkRequestList(
dataSetupRetryEntry.networkRequestList);
requestList.removeIf(request -> !mAllNetworkRequestList.contains(request));
+ // Retrieves the newly added unsatisfied NetworkRequest if all NetworkRequests in the
+ // DataSetupRetryEntry have already been removed.
+ if (requestList.isEmpty()) {
+ List<NetworkRequestList> groupRequestLists = getGroupedUnsatisfiedNetworkRequests();
+ dataSetupRetryEntry.networkRequestList.stream()
+ .filter(request -> groupRequestLists.stream()
+ .anyMatch(groupRequestList -> groupRequestList
+ .get(request.getCapabilities()) != null))
+ .forEach(requestList::add);
+ }
if (requestList.isEmpty()) {
loge("onDataNetworkSetupRetry: Request list is empty. Abort retry.");
dataSetupRetryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
return;
}
+ log("onDataNetworkSetupRetry: Request list:" + requestList);
TelephonyNetworkRequest telephonyNetworkRequest = requestList.get(0);
int networkCapability = telephonyNetworkRequest.getApnTypeNetworkCapability();
@@ -2632,10 +2781,36 @@
return;
}
- logl("Start handover " + dataNetwork + " to "
+ logl("onDataNetworkHandoverRetry: Start handover " + dataNetwork + " to "
+ AccessNetworkConstants.transportTypeToString(preferredTransport)
+ ", " + dataHandoverRetryEntry);
- dataNetwork.startHandover(preferredTransport, dataHandoverRetryEntry);
+ tryHandoverDataNetwork(dataNetwork, preferredTransport, dataHandoverRetryEntry);
+ }
+
+ /**
+ * Called when data network reached max handover retry count.
+ *
+ * @param dataNetwork The data network.
+ */
+ private void onDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) {
+ int preferredTransport = mAccessNetworksManager
+ .getPreferredTransportByNetworkCapability(
+ dataNetwork.getApnTypeNetworkCapability());
+ if (dataNetwork.getTransport() == preferredTransport) {
+ log("onDataNetworkHandoverRetryStopped: " + dataNetwork + " is already "
+ + "on the preferred transport "
+ + AccessNetworkConstants.transportTypeToString(
+ preferredTransport));
+ return;
+ }
+ if (dataNetwork.shouldDelayImsTearDownDueToInCall()) {
+ log("onDataNetworkHandoverRetryStopped: Delay IMS tear down until call "
+ + "ends. " + dataNetwork);
+ return;
+ }
+
+ tearDownGracefully(dataNetwork,
+ DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED);
}
/**
@@ -2708,11 +2883,13 @@
*
* @param dataNetwork The data network.
* @param cause The disconnect cause.
+ * @param tearDownReason The reason the network was torn down
*/
private void onDataNetworkDisconnected(@NonNull DataNetwork dataNetwork,
- @DataFailureCause int cause) {
+ @DataFailureCause int cause, @TearDownReason int tearDownReason) {
logl("onDataNetworkDisconnected: " + dataNetwork + ", cause="
- + DataFailCause.toString(cause) + "(" + cause + ")");
+ + DataFailCause.toString(cause) + "(" + cause + "), tearDownReason="
+ + DataNetwork.tearDownReasonToString(tearDownReason));
mDataNetworkList.remove(dataNetwork);
mPendingImsDeregDataNetworks.remove(dataNetwork);
mDataRetryManager.cancelPendingHandoverRetry(dataNetwork);
@@ -2733,11 +2910,13 @@
() -> callback.onAnyDataNetworkExistingChanged(mAnyDataNetworkExisting)));
}
+ // Immediately reestablish on target transport if network was torn down due to policy
+ long delayMillis = tearDownReason == DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED
+ ? 0 : mDataConfigManager.getRetrySetupAfterDisconnectMillis();
// Sometimes network was unsolicitedly reported lost for reasons. We should re-evaluate
// and see if data network can be re-established again.
sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
- DataEvaluationReason.RETRY_AFTER_DISCONNECTED),
- mDataConfigManager.getRetrySetupAfterDisconnectMillis());
+ DataEvaluationReason.RETRY_AFTER_DISCONNECTED), delayMillis);
}
/**
@@ -2802,6 +2981,16 @@
} else if (handoverFailureMode == DataCallResponse
.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL || handoverFailureMode
== DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY) {
+ int preferredTransport = mAccessNetworksManager
+ .getPreferredTransportByNetworkCapability(
+ dataNetwork.getApnTypeNetworkCapability());
+ if (dataNetwork.getTransport() == preferredTransport) {
+ log("onDataNetworkHandoverFailed: Already on preferred transport "
+ + AccessNetworkConstants.transportTypeToString(preferredTransport)
+ + ". No further actions needed.");
+ return;
+ }
+
int targetTransport = DataUtils.getTargetTransport(dataNetwork.getTransport());
mDataRetryManager.evaluateDataSetupRetry(dataNetwork.getDataProfile(), targetTransport,
dataNetwork.getAttachedNetworkRequestList(), cause, retryDelayMillis);
@@ -2825,6 +3014,20 @@
}
/**
+ * Called when a network request is detached from the data network and should be retried.
+ *
+ * @param networkRequest The detached network request.
+ */
+ private void onRetryUnsatisfiedNetworkRequest(
+ @NonNull TelephonyNetworkRequest networkRequest) {
+ if (!mAllNetworkRequestList.contains(networkRequest)) return;
+
+ sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
+ DataEvaluationReason.UNSATISFIED_REQUEST_DETACHED),
+ REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_AFTER_DETACHED_DELAY_MILLIS);
+ }
+
+ /**
* Called when data stall occurs and needed to tear down / setup a new data network for
* internet. This event is from {@link DataStallRecoveryManager}.
*/
@@ -2840,6 +3043,25 @@
}
/**
+ * Called when SRVCC handover state changes. To preserve the voice call, we don't tear down the
+ * IMS network while handover in process. We reevaluate the network when handover ends.
+ *
+ * @param state The handover state of SRVCC
+ */
+ private void onSrvccStateChanged(@NonNull int[] state) {
+ if (state != null && state.length != 0) {
+ log("onSrvccStateChanged: " + TelephonyManager.srvccStateToString(state[0]));
+ mIsSrvccHandoverInProcess = state[0] == TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+ // Reevaluate networks if SRVCC ends.
+ if (!mIsSrvccHandoverInProcess
+ && !hasMessages(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)) {
+ sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+ DataEvaluationReason.SRVCC_STATE_CHANGED));
+ }
+ }
+ }
+
+ /**
* Called when data service binding changed.
*
* @param transport The transport of the changed data service.
@@ -2871,7 +3093,7 @@
* @param simState SIM state. (Note this is mixed with card state and application state.)
*/
private void onSimStateChanged(@SimState int simState) {
- log("onSimStateChanged: state=" + SubscriptionInfoUpdater.simStateString(simState));
+ log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState));
if (mSimState != simState) {
mSimState = simState;
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
@@ -2912,34 +3134,64 @@
continue;
}
- DataEvaluation dataEvaluation = evaluateDataNetworkHandover(dataNetwork);
- log("onEvaluatePreferredTransport: " + dataEvaluation + ", " + dataNetwork);
- if (!dataEvaluation.containsDisallowedReasons()) {
- logl("Start handover " + dataNetwork + " to "
- + AccessNetworkConstants.transportTypeToString(preferredTransport));
- dataNetwork.startHandover(preferredTransport, null);
- } else if (dataEvaluation.containsAny(DataDisallowedReason.NOT_ALLOWED_BY_POLICY,
- DataDisallowedReason.NOT_IN_SERVICE,
- DataDisallowedReason.VOPS_NOT_SUPPORTED)) {
- logl("onEvaluatePreferredTransport: Handover not allowed. Tear "
- + "down " + dataNetwork + " so a new network can be setup on "
- + AccessNetworkConstants.transportTypeToString(preferredTransport)
- + ".");
- tearDownGracefully(dataNetwork,
- DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED);
- } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE,
- DataDisallowedReason.RETRY_SCHEDULED)) {
- logl("onEvaluatePreferredTransport: Handover not allowed. " + dataNetwork
- + " will remain on " + AccessNetworkConstants.transportTypeToString(
- dataNetwork.getTransport()));
- } else {
- loge("onEvaluatePreferredTransport: Unexpected handover evaluation result.");
- }
+ tryHandoverDataNetwork(dataNetwork, preferredTransport, null/*handoverRetryEntry*/);
}
}
}
/**
+ * Perform data network handover if condition allows, otherwise tear down the network to allow
+ * new network setup on the target transport.
+ *
+ * @param dataNetwork The network on which the handover occurs
+ * @param targetTransport The target transport of the handover
+ * @param dataHandoverRetryEntry {@code null} if the handover attempt is not due to scheduled
+ * retry
+ */
+ private void tryHandoverDataNetwork(@NonNull DataNetwork dataNetwork,
+ @TransportType int targetTransport,
+ @Nullable DataHandoverRetryEntry dataHandoverRetryEntry) {
+ if (dataHandoverRetryEntry == null // This handover is a new request
+ && mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) {
+ log("tryHandoverDataNetwork: retry scheduled for" + dataNetwork
+ + ", ignore this attempt");
+ return;
+ }
+ DataEvaluation dataEvaluation = evaluateDataNetworkHandover(dataNetwork);
+ log("tryHandoverDataNetwork: " + dataEvaluation + ", " + dataNetwork);
+ if (!dataEvaluation.containsDisallowedReasons()) {
+ logl("Start handover " + dataNetwork + " to "
+ + AccessNetworkConstants.transportTypeToString(targetTransport));
+ dataNetwork.startHandover(targetTransport, dataHandoverRetryEntry);
+ } else if (dataEvaluation.containsOnly(DataDisallowedReason.NOT_IN_SERVICE)
+ && dataNetwork.shouldDelayImsTearDownDueToInCall()) {
+ // We try to preserve voice call in the case of temporary preferred transport mismatch
+ if (dataHandoverRetryEntry != null) {
+ dataHandoverRetryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED);
+ }
+ mDataRetryManager.evaluateDataHandoverRetry(dataNetwork,
+ DataFailCause.HANDOVER_FAILED,
+ DataCallResponse.RETRY_DURATION_UNDEFINED /* retry mills */);
+ logl("tryHandoverDataNetwork: Scheduled retry due to in voice call and target OOS");
+ } else if (dataEvaluation.containsAny(DataDisallowedReason.NOT_ALLOWED_BY_POLICY,
+ DataDisallowedReason.NOT_IN_SERVICE,
+ DataDisallowedReason.VOPS_NOT_SUPPORTED)) {
+ logl("tryHandoverDataNetwork: Handover not allowed. Tear down"
+ + dataNetwork + " so a new network can be setup on "
+ + AccessNetworkConstants.transportTypeToString(targetTransport));
+ tearDownGracefully(dataNetwork,
+ DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED);
+ } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE,
+ DataDisallowedReason.RETRY_SCHEDULED)) {
+ logl("tryHandoverDataNetwork: Handover not allowed. " + dataNetwork
+ + " will remain on " + AccessNetworkConstants.transportTypeToString(
+ dataNetwork.getTransport()));
+ } else {
+ loge("tryHandoverDataNetwork: Unexpected handover evaluation result.");
+ }
+ }
+
+ /**
* Update {@link SubscriptionPlan}s from {@link NetworkPolicyManager}.
*/
private void updateSubscriptionPlans() {
@@ -3034,6 +3286,12 @@
log("Found more network requests that can be satisfied. " + networkRequestList);
dataNetwork.attachNetworkRequests(networkRequestList);
}
+
+ if (dataNetwork.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ // Update because DataNetwork#isInternetSupported might have changed with capabilities.
+ updateOverallInternetDataState();
+ }
}
/**
@@ -3184,9 +3442,9 @@
}
/**
- * Update the internet data network state. For now only {@link TelephonyManager#DATA_CONNECTED}
- * , {@link TelephonyManager#DATA_SUSPENDED}, and
- * {@link TelephonyManager#DATA_DISCONNECTED} are supported.
+ * Update the internet data network state. For now only {@link TelephonyManager#DATA_CONNECTED},
+ * {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED}
+ * are supported.
*/
private void updateOverallInternetDataState() {
boolean anyInternetConnected = mDataNetworkList.stream()
@@ -3219,9 +3477,7 @@
&& mInternetDataNetworkState == TelephonyManager.DATA_DISCONNECTED) {
mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onInternetDataNetworkConnected(
- allConnectedInternetDataNetworks.stream()
- .map(DataNetwork::getDataProfile)
- .collect(Collectors.toList()))));
+ allConnectedInternetDataNetworks)));
} else if (dataNetworkState == TelephonyManager.DATA_DISCONNECTED
&& mInternetDataNetworkState == TelephonyManager.DATA_CONNECTED) {
mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
@@ -3454,8 +3710,8 @@
/**
* Get the internet data network state. Note that this is the best effort if more than one
- * data network supports internet. For now only {@link TelephonyManager#DATA_CONNECTED}
- * , {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED}
+ * data network supports internet. For now only {@link TelephonyManager#DATA_CONNECTED},
+ * {@link TelephonyManager#DATA_SUSPENDED}, and {@link TelephonyManager#DATA_DISCONNECTED}
* are supported.
*
* @return The data network state.
@@ -3565,7 +3821,8 @@
pw.println("mImsDataNetworkState="
+ TelephonyUtils.dataStateToString(mImsDataNetworkState));
pw.println("mDataServiceBound=" + mDataServiceBound);
- pw.println("mSimState=" + SubscriptionInfoUpdater.simStateString(mSimState));
+ pw.println("mIsSrvccHandoverInProcess=" + mIsSrvccHandoverInProcess);
+ pw.println("mSimState=" + TelephonyManager.simStateToString(mSimState));
pw.println("mDataNetworkControllerCallbacks=" + mDataNetworkControllerCallbacks);
pw.println("Subscription plans:");
pw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 1ec345c..37f0de0 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -32,6 +32,7 @@
import android.provider.Telephony;
import android.telephony.Annotation;
import android.telephony.Annotation.NetworkType;
+import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -44,6 +45,7 @@
import android.util.LocalLog;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.telephony.Rlog;
@@ -54,6 +56,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -64,9 +67,6 @@
public class DataProfileManager extends Handler {
private static final boolean VDBG = true;
- /** Event for data config updated. */
- private static final int EVENT_DATA_CONFIG_UPDATED = 1;
-
/** Event for APN database changed. */
private static final int EVENT_APN_DATABASE_CHANGED = 2;
@@ -77,6 +77,13 @@
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
+ /**
+ * Should only be used by update updateDataProfiles() to indicate whether resend IA to modem
+ * regardless whether IA changed.
+ **/
+ private final boolean FORCED_UPDATE_IA = true;
+ private final boolean ONLY_UPDATE_IA_IF_CHANGED = false;
+
/** Data network controller. */
private final @NonNull DataNetworkController mDataNetworkController;
@@ -86,7 +93,11 @@
/** Cellular data service. */
private final @NonNull DataServiceManager mWwanDataServiceManager;
- /** All data profiles for the current carrier. */
+ /**
+ * All data profiles for the current carrier. Note only data profiles loaded from the APN
+ * database will be stored here. The on-demand data profiles (generated dynamically, for
+ * example, enterprise data profiles with differentiator) are not stored here.
+ */
private final @NonNull List<DataProfile> mAllDataProfiles = new ArrayList<>();
/** The data profile used for initial attach. */
@@ -95,6 +106,9 @@
/** The preferred data profile used for internet. */
private @Nullable DataProfile mPreferredDataProfile = null;
+ /** The last data profile that's successful for internet connection. */
+ private @Nullable DataProfile mLastInternetDataProfile = null;
+
/** Preferred data profile set id. */
private int mPreferredDataProfileSetId = Telephony.Carriers.NO_APN_SET_ID;
@@ -153,10 +167,16 @@
new DataNetworkControllerCallback(this::post) {
@Override
public void onInternetDataNetworkConnected(
- @NonNull List<DataProfile> dataProfiles) {
- DataProfileManager.this.onInternetDataNetworkConnected(dataProfiles);
- }});
- mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
+ @NonNull List<DataNetwork> internetNetworks) {
+ DataProfileManager.this.onInternetDataNetworkConnected(internetNetworks);
+ }
+ });
+ mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ DataProfileManager.this.onCarrierConfigUpdated();
+ }
+ });
mPhone.getContext().getContentResolver().registerContentObserver(
Telephony.Carriers.CONTENT_URI, true, new ContentObserver(this) {
@Override
@@ -171,16 +191,13 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case EVENT_DATA_CONFIG_UPDATED:
- onDataConfigUpdated();
- break;
case EVENT_SIM_REFRESH:
log("Update data profiles due to SIM refresh.");
- updateDataProfiles();
+ updateDataProfiles(FORCED_UPDATE_IA);
break;
case EVENT_APN_DATABASE_CHANGED:
log("Update data profiles due to APN db updated.");
- updateDataProfiles();
+ updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED);
break;
default:
loge("Unexpected event " + msg);
@@ -189,11 +206,11 @@
}
/**
- * Called when data config was updated.
+ * Called when carrier config was updated.
*/
- private void onDataConfigUpdated() {
- log("Update data profiles due to config updated.");
- updateDataProfiles();
+ private void onCarrierConfigUpdated() {
+ log("Update data profiles due to carrier config updated.");
+ updateDataProfiles(FORCED_UPDATE_IA);
//TODO: more works needed to be done here.
}
@@ -231,8 +248,10 @@
/**
* Update all data profiles, including preferred data profile, and initial attach data profile.
* Also send those profiles down to the modem if needed.
+ *
+ * @param forceUpdateIa If {@code true}, we should always send IA again to modem.
*/
- private void updateDataProfiles() {
+ private void updateDataProfiles(boolean forceUpdateIa) {
List<DataProfile> profiles = new ArrayList<>();
if (mDataConfigManager.isConfigCarrierSpecific()) {
Cursor cursor = mPhone.getContext().getContentResolver().query(
@@ -242,7 +261,7 @@
loge("Cannot access APN database through telephony provider.");
return;
}
-
+ boolean isInternetSupported = false;
while (cursor.moveToNext()) {
ApnSetting apn = ApnSetting.makeApnSetting(cursor);
if (apn != null) {
@@ -253,14 +272,44 @@
.build();
profiles.add(dataProfile);
log("Added " + dataProfile);
+
+ isInternetSupported |= apn.canHandleType(ApnSetting.TYPE_DEFAULT);
+ if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) {
+ checkApnSetting(apn);
+ }
}
}
cursor.close();
+
+ if (!isInternetSupported
+ && !profiles.isEmpty() // APN database has been read successfully
+ && mDataConfigManager.isApnConfigAnomalyReportEnabled()) {
+ reportAnomaly("Carrier doesn't support internet.",
+ "9af73e18-b523-4dc5-adab-363eb6613305");
+ }
+ }
+
+ DataProfile dataProfile;
+
+ if (!profiles.isEmpty()) { // APN database has been read successfully after SIM loaded
+ // Check if any of the profile already supports IMS, if not, add the default one.
+ dataProfile = profiles.stream()
+ .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS))
+ .findFirst()
+ .orElse(null);
+ if (dataProfile == null) {
+ profiles.add(new DataProfile.Builder()
+ .setApnSetting(buildDefaultApnSetting("DEFAULT IMS", "ims",
+ ApnSetting.TYPE_IMS))
+ .setTrafficDescriptor(new TrafficDescriptor("ims", null))
+ .build());
+ log("Added default IMS data profile.");
+ }
}
// Check if any of the profile already supports ENTERPRISE, if not, check if DPC has
// configured one and retrieve the same.
- DataProfile dataProfile = profiles.stream()
+ dataProfile = profiles.stream()
.filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE))
.findFirst()
.orElse(null);
@@ -272,20 +321,6 @@
}
}
- // Check if any of the profile already supports IMS, if not, add the default one.
- dataProfile = profiles.stream()
- .filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS))
- .findFirst()
- .orElse(null);
- if (dataProfile == null) {
- profiles.add(new DataProfile.Builder()
- .setApnSetting(buildDefaultApnSetting("DEFAULT IMS", "ims",
- ApnSetting.TYPE_IMS))
- .setTrafficDescriptor(new TrafficDescriptor("ims", null))
- .build());
- log("Added default IMS data profile.");
- }
-
// Check if any of the profile already supports EIMS, if not, add the default one.
dataProfile = profiles.stream()
.filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_EIMS))
@@ -302,6 +337,10 @@
dedupeDataProfiles(profiles);
+ if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) {
+ checkDataProfiles(profiles);
+ }
+
log("Found " + profiles.size() + " data profiles. profiles = " + profiles);
boolean profilesChanged = false;
@@ -323,7 +362,7 @@
}
updateDataProfilesAtModem();
- updateInitialAttachDataProfileAtModem();
+ updateInitialAttachDataProfileAtModem(forceUpdateIa);
if (profilesChanged) {
mDataProfileManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
@@ -365,23 +404,38 @@
/**
* Called when internet data is connected.
*
- * @param dataProfiles The connected internet data networks' profiles.
+ * @param internetNetworks The connected internet data networks.
*/
- private void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) {
- // If there is already a preferred data profile set, then we don't need to do anything.
- if (mPreferredDataProfile != null) return;
+ private void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {
+ DataProfile defaultProfile = null;
+ if (internetNetworks.size() == 1) {
+ // Most of the cases there should be only one.
+ defaultProfile = internetNetworks.get(0).getDataProfile();
+ } else if (internetNetworks.size() > 1) {
+ // but in case there are multiple, find the default internet network, and choose the
+ // one which has longest life cycle.
+ logv("onInternetDataNetworkConnected: mPreferredDataProfile=" + mPreferredDataProfile
+ + " internetNetworks=" + internetNetworks);
+ defaultProfile = internetNetworks.stream()
+ .filter(network -> mPreferredDataProfile == null
+ || canPreferredDataProfileSatisfy(
+ network.getAttachedNetworkRequestList()))
+ .map(DataNetwork::getDataProfile)
+ .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
+ .orElse(null);
+ }
- // If there is no preferred data profile, then we should use one of the data profiles,
- // which is good for internet, as the preferred data profile.
+ // Update a working internet data profile as a future candidate for preferred data profile
+ // after APNs are reset to default
+ mLastInternetDataProfile = defaultProfile;
- // Most of the cases there should be only one, but in case there are multiple, choose the
- // one which has longest life cycle.
- DataProfile dataProfile = dataProfiles.stream()
- .max(Comparator.comparingLong(DataProfile::getLastSetupTimestamp).reversed())
- .orElse(null);
+ // If the live default internet network is not using the preferred data profile, since
+ // brought up a network means it passed sophisticated checks, update the preferred data
+ // profile so that this network won't be torn down in future network evaluations.
+ if (defaultProfile == null || defaultProfile.equals(mPreferredDataProfile)) return;
// Save the preferred data profile into database.
- setPreferredDataProfile(dataProfile);
- updateDataProfiles();
+ setPreferredDataProfile(defaultProfile);
+ updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED);
}
/**
@@ -434,7 +488,7 @@
* the preferred data profile from database.
*/
private void setPreferredDataProfile(@Nullable DataProfile dataProfile) {
- log("setPreferredDataProfile: " + dataProfile);
+ logl("setPreferredDataProfile: " + dataProfile);
String subId = Long.toString(mPhone.getSubId());
Uri uri = Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, subId);
@@ -465,6 +519,16 @@
if (preferredDataProfile != null) {
// Save the carrier specified preferred data profile into database
setPreferredDataProfile(preferredDataProfile);
+ } else {
+ preferredDataProfile = mAllDataProfiles.stream()
+ .filter(dp -> areDataProfilesSharingApn(dp, mLastInternetDataProfile))
+ .findFirst()
+ .orElse(null);
+ if (preferredDataProfile != null) {
+ log("updatePreferredDataProfile: preferredDB is empty and no carrier "
+ + "default configured, setting preferred to be prev internet DP.");
+ setPreferredDataProfile(preferredDataProfile);
+ }
}
}
} else {
@@ -493,8 +557,10 @@
* Some carriers might explicitly require that using "user-added" APN for initial
* attach. In this case, exception can be configured through
* {@link CarrierConfigManager#KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY}.
+ *
+ * @param forceUpdateIa If {@code true}, we should always send IA again to modem.
*/
- private void updateInitialAttachDataProfileAtModem() {
+ private void updateInitialAttachDataProfileAtModem(boolean forceUpdateIa) {
DataProfile initialAttachDataProfile = null;
// Sort the data profiles so the preferred data profile is at the beginning.
@@ -510,11 +576,12 @@
if (initialAttachDataProfile != null) break;
}
- if (!Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) {
+ if (forceUpdateIa || !Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) {
mInitialAttachDataProfile = initialAttachDataProfile;
- logl("Initial attach data profile updated as " + mInitialAttachDataProfile);
+ logl("Initial attach data profile updated as " + mInitialAttachDataProfile
+ + " or forceUpdateIa= " + forceUpdateIa);
// TODO: Push the null data profile to modem on new AIDL HAL. Modem should clear the IA
- // APN.
+ // APN, tracking for U b/227579876, now using forceUpdateIa which always push to modem
if (mInitialAttachDataProfile != null) {
mWwanDataServiceManager.setInitialAttachApn(mInitialAttachDataProfile,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
@@ -557,14 +624,18 @@
*
* @param networkRequest The network request.
* @param networkType The current data network type.
+ * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
+ * This should be set to true for condition-based retry/setup.
* @return The data profile. {@code null} if can't find any satisfiable data profile.
*/
public @Nullable DataProfile getDataProfileForNetworkRequest(
- @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) {
+ @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
+ boolean ignorePermanentFailure) {
ApnSetting apnSetting = null;
if (networkRequest.hasAttribute(TelephonyNetworkRequest
.CAPABILITY_ATTRIBUTE_APN_SETTING)) {
- apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType);
+ apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType,
+ ignorePermanentFailure);
}
TrafficDescriptor.Builder trafficDescriptorBuilder = new TrafficDescriptor.Builder();
@@ -623,25 +694,41 @@
*
* @param networkRequest The network request.
* @param networkType The current data network type.
+ * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
+ * This should be set to true for condition-based retry/setup.
* @return The APN setting. {@code null} if can't find any satisfiable data profile.
*/
private @Nullable ApnSetting getApnSettingForNetworkRequest(
- @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) {
+ @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
+ boolean ignorePermanentFailure) {
if (!networkRequest.hasAttribute(
TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) {
loge("Network request does not have APN setting attribute.");
return null;
}
+ // If the preferred data profile can be used, always use it if it can satisfy the network
+ // request with current network type (even though it's been marked as permanent failed.)
+ if (mPreferredDataProfile != null
+ && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
+ && mPreferredDataProfile.getApnSetting() != null
+ && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) {
+ if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
+ .getPermanentFailed()) {
+ return mPreferredDataProfile.getApnSetting();
+ }
+ log("The preferred data profile is permanently failed. Only condition based retry "
+ + "can happen.");
+ return null;
+ }
+
// Filter out the data profile that can't satisfy the request.
// Preferred data profile should be returned in the top of the list.
List<DataProfile> dataProfiles = mAllDataProfiles.stream()
.filter(networkRequest::canBeSatisfiedBy)
- // Put the preferred data profile at the top of the list, then the longest time
- // hasn't used data profile will be in the front so all the data profiles can be
- // tried.
- .sorted(Comparator.comparing((DataProfile dp) -> !dp.equals(mPreferredDataProfile))
- .thenComparingLong(DataProfile::getLastSetupTimestamp))
+ // The longest time hasn't used data profile will be in the front so all the data
+ // profiles can be tried.
+ .sorted(Comparator.comparing(DataProfile::getLastSetupTimestamp))
.collect(Collectors.toList());
for (DataProfile dataProfile : dataProfiles) {
logv("Satisfied profile: " + dataProfile + ", last setup="
@@ -676,17 +763,39 @@
return null;
}
+ // Check if data profiles are permanently failed.
+ dataProfiles = dataProfiles.stream()
+ .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed())
+ .collect(Collectors.toList());
+ if (dataProfiles.size() == 0) {
+ log("The suitable data profiles are all in permanent failed state.");
+ return null;
+ }
+
return dataProfiles.get(0).getApnSetting();
}
/**
- * Check if the data profile is the preferred data profile.
+ * Check if the data profile is essentially the preferred data profile. The non-essential
+ * elements include e.g.APN Id.
*
* @param dataProfile The data profile to check.
- * @return {@code true} if the data profile is the preferred data profile.
+ * @return {@code true} if the data profile is essentially the preferred data profile.
*/
public boolean isDataProfilePreferred(@NonNull DataProfile dataProfile) {
- return dataProfile.equals(mPreferredDataProfile);
+ return areDataProfilesSharingApn(dataProfile, mPreferredDataProfile);
+ }
+
+ /**
+ * @param networkRequests The required network requests
+ * @return {@code true} if we currently have a preferred data profile that's capable of
+ * satisfying the required network requests; {@code false} if we have no preferred, or the
+ * preferred cannot satisfy the required requests.
+ */
+ public boolean canPreferredDataProfileSatisfy(
+ @NonNull DataNetworkController.NetworkRequestList networkRequests) {
+ return mPreferredDataProfile != null && networkRequests.stream()
+ .allMatch(request -> request.canBeSatisfiedBy(mPreferredDataProfile));
}
/**
@@ -707,19 +816,7 @@
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.build(), mPhone);
- return getDataProfileForNetworkRequest(networkRequest, networkType) != null;
- }
-
- /**
- * Check if any preferred data profile exists.
- *
- * @return {@code true} if any preferred data profile exists
- */
- public boolean isAnyPreferredDataProfileExisting() {
- for (DataProfile dataProfile : mAllDataProfiles) {
- if (dataProfile.isPreferred()) return true;
- }
- return false;
+ return getDataProfileForNetworkRequest(networkRequest, networkType, true) != null;
}
/**
@@ -750,6 +847,72 @@
}
/**
+ * Trigger anomaly report if APN Setting contains invalid info.
+ *
+ * @param setting The Apn setting to be checked.
+ */
+ private void checkApnSetting(@NonNull ApnSetting setting) {
+ if (setting.canHandleType(ApnSetting.TYPE_MMS)) {
+ if (setting.getMmsc() == null) {
+ reportAnomaly("MMS is supported but no MMSC configured " + setting,
+ "9af73e18-b523-4dc5-adab-19d86c6a3685");
+ } else if (!setting.getMmsc().toString().matches("^https?:\\/\\/.+")) {
+ reportAnomaly("Apn config mmsc should start with http but is "
+ + setting.getMmsc(),
+ "9af73e18-b523-4dc5-adab-ec754d959d4d");
+ }
+ if (!TextUtils.isEmpty(setting.getMmsProxyAddressAsString())
+ && setting.getMmsProxyAddressAsString().matches("^https?:\\/\\/.+")) {
+ reportAnomaly("Apn config mmsc_proxy should NOT start with http but is "
+ + setting.getMmsc(), "9af73e18-b523-4dc5-adab-ec754d959d4d");
+ }
+ }
+ }
+
+ /**
+ * Trigger anomaly report if any two Apn Settings share the same APN name while having
+ * overlapped network types.
+ *
+ * @param profiles The list of data profiles to be checked.
+ */
+ private void checkDataProfiles(List<DataProfile> profiles) {
+ for (int i = 0; i < profiles.size(); i++) {
+ ApnSetting a = profiles.get(i).getApnSetting();
+ if (a == null) continue;
+ if (// Lingering network is not the default and doesn't cover all the regular networks
+ (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN
+ != a.getLingeringNetworkTypeBitmask()
+ && (a.getNetworkTypeBitmask() | a.getLingeringNetworkTypeBitmask())
+ != a.getLingeringNetworkTypeBitmask()) {
+ reportAnomaly("Apn[" + a.getApnName() + "] network "
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ a.getNetworkTypeBitmask()) + " should be a subset of "
+ + "the lingering network "
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ a.getLingeringNetworkTypeBitmask()),
+ "9af73e18-b523-4dc5-adab-4bb24355d838");
+ }
+ for (int j = i + 1; j < profiles.size(); j++) {
+ ApnSetting b = profiles.get(j).getApnSetting();
+ if (b == null) continue;
+ String apnNameA = a.getApnName();
+ String apnNameB = b.getApnName();
+ if (TextUtils.equals(apnNameA, apnNameB)
+ // TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN means all network types
+ && (a.getNetworkTypeBitmask()
+ == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN
+ || b.getNetworkTypeBitmask()
+ == (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN
+ || (a.getNetworkTypeBitmask() & b.getNetworkTypeBitmask()) != 0)) {
+ reportAnomaly("Found overlapped network type under the APN name "
+ + a.getApnName(),
+ "9af73e18-b523-4dc5-adab-4bb24555d839");
+ }
+ }
+ }
+ }
+
+ /**
* Merge two data profiles if possible.
*
* @param dp1 Data profile 1 to be merged.
@@ -806,6 +969,9 @@
? apn1.getMtuV4() : apn2.getMtuV4());
apnBuilder.setMtuV6(apn2.getMtuV6() <= ApnSetting.UNSET_MTU
? apn1.getMtuV6() : apn2.getMtuV6());
+ // legacy properties that don't matter
+ apnBuilder.setMvnoType(apn1.getMvnoType());
+ apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData());
// The following fields in apn1 and apn2 should be the same, otherwise ApnSetting.similar()
// should fail earlier.
@@ -820,8 +986,6 @@
apnBuilder.setMaxConns(apn1.getMaxConns());
apnBuilder.setWaitTime(apn1.getWaitTime());
apnBuilder.setMaxConnsTime(apn1.getMaxConnsTime());
- apnBuilder.setMvnoType(apn1.getMvnoType());
- apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData());
apnBuilder.setApnSetId(apn1.getApnSetId());
apnBuilder.setCarrierId(apn1.getCarrierId());
apnBuilder.setSkip464Xlat(apn1.getSkip464Xlat());
@@ -834,39 +998,43 @@
}
/**
- * Get data profile by APN name and/or traffic descriptor.
+ * Check if the provided data profile is still compatible with the current environment. Note
+ * this method ignores APN id check and traffic descriptor check. A data profile with traffic
+ * descriptor only can always be used in any condition.
*
- * @param apnName APN name.
- * @param trafficDescriptor Traffic descriptor.
- *
- * @return Data profile by APN name and/or traffic descriptor. Either one of APN name or
- * traffic descriptor should be provided. {@code null} if data profile is not found.
+ * @param dataProfile The data profile to check.
+ * @return {@code true} if the provided data profile can be still used in current environment.
*/
- public @Nullable DataProfile getDataProfile(@Nullable String apnName,
- @Nullable TrafficDescriptor trafficDescriptor) {
- if (apnName == null && trafficDescriptor == null) return null;
-
- List<DataProfile> dataProfiles = mAllDataProfiles;
-
- // Check if any existing data profile has the same traffic descriptor.
- if (trafficDescriptor != null) {
- dataProfiles = mAllDataProfiles.stream()
- .filter(dp -> trafficDescriptor.equals(dp.getTrafficDescriptor()))
- .collect(Collectors.toList());
+ public boolean isDataProfileCompatible(@NonNull DataProfile dataProfile) {
+ if (dataProfile == null) {
+ return false;
}
- // Check if any existing data profile has the same APN name.
- if (apnName != null) {
- dataProfiles = dataProfiles.stream()
- .filter(dp -> dp.getApnSetting() != null
- && (dp.getApnSetting().getApnSetId()
- == Telephony.Carriers.MATCH_ALL_APN_SET_ID
- || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId))
- .filter(dp -> apnName.equals(dp.getApnSetting().getApnName()))
- .collect(Collectors.toList());
+ if (dataProfile.getApnSetting() == null && dataProfile.getTrafficDescriptor() != null) {
+ // A traffic descriptor only data profile can be always used. Traffic descriptors are
+ // always generated on the fly instead loaded from the database.
+ return true;
}
- return dataProfiles.isEmpty() ? null : dataProfiles.get(0);
+ // Check the APN from the profile is compatible and matches preferred data profile set id.
+ return mAllDataProfiles.stream()
+ .filter(dp -> dp.getApnSetting() != null
+ && (dp.getApnSetting().getApnSetId()
+ == Telephony.Carriers.MATCH_ALL_APN_SET_ID
+ || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId))
+ .anyMatch(dp -> areDataProfilesSharingApn(dataProfile, dp));
+ }
+
+ /**
+ * @return {@code true} if both data profiles' APN setting are non-null and essentially the same
+ * (non-essential elements include e.g.APN Id).
+ */
+ public boolean areDataProfilesSharingApn(@Nullable DataProfile a, @Nullable DataProfile b) {
+ return a != null
+ && b != null
+ && a.getApnSetting() != null
+ && a.getApnSetting().equals(b.getApnSetting(),
+ mPhone.getServiceState().getDataRoamingFromRegistration());
}
/**
@@ -888,6 +1056,17 @@
}
/**
+ * Trigger the anomaly report with the specified UUID.
+ *
+ * @param anomalyMsg Description of the event
+ * @param uuid UUID associated with that event
+ */
+ private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) {
+ logl(anomalyMsg);
+ AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId());
+ }
+
+ /**
* Log debug messages.
* @param s debug messages
*/
@@ -945,9 +1124,16 @@
pw.println("Preferred data profile from db=" + getPreferredDataProfileFromDb());
pw.println("Preferred data profile from config=" + getPreferredDataProfileFromConfig());
pw.println("Preferred data profile set id=" + mPreferredDataProfileSetId);
+ pw.println("Last internet data profile=" + mLastInternetDataProfile);
pw.println("Initial attach data profile=" + mInitialAttachDataProfile);
pw.println("isTetheringDataProfileExisting=" + isTetheringDataProfileExisting(
TelephonyManager.NETWORK_TYPE_LTE));
+ pw.println("Permanent failed profiles=");
+ pw.increaseIndent();
+ mAllDataProfiles.stream()
+ .filter(dp -> dp.getApnSetting() != null && dp.getApnSetting().getPermanentFailed())
+ .forEach(pw::println);
+ pw.decreaseIndent();
pw.println("Local logs:");
pw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index da86cf4..518689a 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -21,6 +21,12 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.NetworkCapabilities;
import android.os.AsyncResult;
import android.os.Handler;
@@ -46,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback;
@@ -55,7 +62,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -64,6 +70,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* DataRetryManager manages data network setup retry and its configurations.
@@ -71,8 +78,10 @@
public class DataRetryManager extends Handler {
private static final boolean VDBG = false;
- /** Event for data config updated. */
- private static final int EVENT_DATA_CONFIG_UPDATED = 1;
+ /** Intent of Alarm Manager for long retry timer. */
+ private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY";
+ /** The extra key for the hashcode of the retry entry for Alarm Manager. */
+ private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode";
/** Event for data setup retry. */
private static final int EVENT_DATA_SETUP_RETRY = 3;
@@ -100,6 +109,12 @@
/** The maximum entries to preserve. */
private static final int MAXIMUM_HISTORICAL_ENTRIES = 100;
+ /**
+ * The threshold of retry timer, longer than or equal to which we use alarm manager to schedule
+ * instead of handler.
+ */
+ private static final long RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS = TimeUnit
+ .MINUTES.toMillis(1);
@IntDef(prefix = {"RESET_REASON_"},
value = {
@@ -145,6 +160,9 @@
/** Local log. */
private final @NonNull LocalLog mLocalLog = new LocalLog(128);
+ /** Alarm Manager used to schedule long set up or handover retries. */
+ private final @NonNull AlarmManager mAlarmManager;
+
/**
* The data retry callback. This is only used to notify {@link DataNetworkController} to retry
* setup data network.
@@ -316,10 +334,6 @@
}
}
- if (mMaxRetries == 0) {
- mRetryIntervalsMillis = Collections.emptyList();
- }
-
if (mMaxRetries < 0) {
throw new IllegalArgumentException("Max retries should not be less than 0. "
+ "mMaxRetries=" + mMaxRetries);
@@ -360,39 +374,45 @@
}
/**
- * Represent a setup data network retry rule.
+ * Represent a rule for data setup retry.
*
* The syntax of the retry rule:
- * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network
- * capabilities are supported.
- * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...],
- * [maximum_retries=n]"
+ * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
+ * are supported. If the capabilities are not specified, then the retry rule only applies
+ * to the current failed APN used in setup data call request.
+ * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
* 2. Retry based on {@link DataFailCause}
- * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...],
- * [maximum_retries=n]"
+ * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
* 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only
* APN-type network capabilities are supported.
* "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
* [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
+ * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
+ * is specified for retrying the next available APN.
+ * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
+ * 2252|2253|2254, retry_interval=2500"
+ *
* For example,
* "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
* network request is emergency, then retry data network setup every 1 second for up to 20
* times.
*
- * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
- * , maximum_retries=0" means for those fail causes, never retry with timers. Note that
- * when environment changes, retry can still happen.
- *
* "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
* "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
- * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s,
- * 5s, 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
+ * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
+ * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
+ *
*/
public static class DataSetupRetryRule extends DataRetryRule {
+ private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes";
private static final String RULE_TAG_CAPABILITIES = "capabilities";
+
+ /** {@code true} if this rule is for permanent fail causes. */
+ private boolean mIsPermanentFailCauseRule;
+
/**
* Constructor
*
@@ -411,8 +431,23 @@
}
String key = tokens[0].trim();
String value = tokens[1].trim();
- if (key.equals(RULE_TAG_CAPABILITIES)) {
- mNetworkCapabilities = DataUtils.getNetworkCapabilitiesFromString(value);
+ try {
+ switch (key) {
+ case RULE_TAG_PERMANENT_FAIL_CAUSES:
+ mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*"))
+ .map(String::trim)
+ .map(Integer::valueOf)
+ .collect(Collectors.toSet());
+ mIsPermanentFailCauseRule = true;
+ break;
+ case RULE_TAG_CAPABILITIES:
+ mNetworkCapabilities = DataUtils
+ .getNetworkCapabilitiesFromString(value);
+ break;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e);
}
}
@@ -435,6 +470,13 @@
}
/**
+ * @return {@code true} if this rule is for permanent fail causes.
+ */
+ public boolean isPermanentFailCauseRule() {
+ return mIsPermanentFailCauseRule;
+ }
+
+ /**
* Check if this rule can be matched.
*
* @param networkCapability The network capability for matching.
@@ -930,12 +972,17 @@
mDataServiceManagers = dataServiceManagers;
mDataConfigManager = dataNetworkController.getDataConfigManager();
mDataProfileManager = dataNetworkController.getDataProfileManager();
- mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
+ mAlarmManager = mPhone.getContext().getSystemService(AlarmManager.class);
- mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .registerForApnUnthrottled(this, EVENT_DATA_PROFILE_UNTHROTTLED);
- if (!mPhone.getAccessNetworksManager().isInLegacyMode()) {
- mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ DataRetryManager.this.onCarrierConfigUpdated();
+ }
+ });
+
+ for (int transport : mPhone.getAccessNetworksManager().getAvailableTransports()) {
+ mDataServiceManagers.get(transport)
.registerForApnUnthrottled(this, EVENT_DATA_PROFILE_UNTHROTTLED);
}
mDataProfileManager.registerCallback(new DataProfileManagerCallback(this::post) {
@@ -946,14 +993,44 @@
});
dataNetworkController.registerDataNetworkControllerCallback(
new DataNetworkControllerCallback(this::post) {
+ /**
+ * Called when data service is bound.
+ *
+ * @param transport The transport of the data service.
+ */
@Override
public void onDataServiceBound(@TransportType int transport) {
onReset(RESET_REASON_DATA_SERVICE_BOUND);
}
+
+ /**
+ * Called when data network is connected.
+ *
+ * @param transport Transport for the connected network.
+ * @param dataProfile The data profile of the connected data network.
+ */
+ @Override
+ public void onDataNetworkConnected(@TransportType int transport,
+ @NonNull DataProfile dataProfile) {
+ DataRetryManager.this.onDataNetworkConnected(transport, dataProfile);
+ }
});
mRil.registerForOn(this, EVENT_RADIO_ON, null);
mRil.registerForModemReset(this, EVENT_MODEM_RESET, null);
+ // Register intent of alarm manager for long retry timer
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_RETRY);
+ mPhone.getContext().registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_RETRY.equals(intent.getAction())) {
+ DataRetryManager.this.onAlarmIntentRetry(
+ intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE, -1 /*Bad hashcode*/));
+ }
+ }
+ }, intentFilter);
+
if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) {
mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED,
null);
@@ -964,23 +1041,18 @@
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
- case EVENT_DATA_CONFIG_UPDATED:
- onDataConfigUpdated();
- break;
case EVENT_DATA_SETUP_RETRY:
DataSetupRetryEntry dataSetupRetryEntry = (DataSetupRetryEntry) msg.obj;
- Objects.requireNonNull(dataSetupRetryEntry);
- mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
- () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry)));
+ if (!isRetryCancelled(dataSetupRetryEntry)) {
+ mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry)));
+ }
break;
case EVENT_DATA_HANDOVER_RETRY:
DataHandoverRetryEntry dataHandoverRetryEntry = (DataHandoverRetryEntry) msg.obj;
- Objects.requireNonNull(dataHandoverRetryEntry);
- if (mDataRetryEntries.contains(dataHandoverRetryEntry)) {
+ if (!isRetryCancelled(dataHandoverRetryEntry)) {
mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onDataNetworkHandoverRetry(dataHandoverRetryEntry)));
- } else {
- log("Handover was cancelled earlier. " + dataHandoverRetryEntry);
}
break;
case EVENT_RADIO_ON:
@@ -1003,7 +1075,7 @@
} else if (ar.result instanceof DataProfile) {
dataProfile = (DataProfile) ar.result;
}
- onDataProfileUnthrottled(dataProfile, apn, transport, true);
+ onDataProfileUnthrottled(dataProfile, apn, transport, true, true);
break;
case EVENT_CANCEL_PENDING_HANDOVER_RETRY:
onCancelPendingHandoverRetry((DataNetwork) msg.obj);
@@ -1014,9 +1086,21 @@
}
/**
- * Called when data config is updated.
+ * @param retryEntry The retry entry to check.
+ * @return {@code true} if the retry is null or not in RETRY_STATE_NOT_RETRIED state.
*/
- private void onDataConfigUpdated() {
+ private boolean isRetryCancelled(@Nullable DataRetryEntry retryEntry) {
+ if (retryEntry != null && retryEntry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) {
+ return false;
+ }
+ log("Retry was removed earlier. " + retryEntry);
+ return true;
+ }
+
+ /**
+ * Called when carrier config is updated.
+ */
+ private void onCarrierConfigUpdated() {
onReset(RESET_REASON_DATA_CONFIG_CHANGED);
mDataSetupRetryRuleList = mDataConfigManager.getDataSetupRetryRules();
mDataHandoverRetryRuleList = mDataConfigManager.getDataHandoverRetryRules();
@@ -1025,6 +1109,21 @@
}
/**
+ * Called when data network is connected.
+ *
+ * @param transport Transport for the connected network.
+ * @param dataProfile The data profile of the connected data network.
+ */
+ public void onDataNetworkConnected(@TransportType int transport,
+ @NonNull DataProfile dataProfile) {
+ if (dataProfile.getApnSetting() != null) {
+ dataProfile.getApnSetting().setPermanentFailed(false);
+ }
+
+ onDataProfileUnthrottled(dataProfile, null, transport, true, false);
+ }
+
+ /**
* Evaluate if data setup retry is needed or not. If needed, retry will be scheduled
* automatically after evaluation.
*
@@ -1062,6 +1161,7 @@
// ThrottleStatus is just for API backwards compatibility reason.
updateThrottleStatus(dataProfile, requestList, null,
ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, Long.MAX_VALUE);
+ return;
} else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) {
// Network specifically asks retry the previous data profile again.
DataSetupRetryEntry dataSetupRetryEntry = new DataSetupRetryEntry.Builder<>()
@@ -1075,67 +1175,90 @@
ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport,
dataSetupRetryEntry.retryElapsedTime);
schedule(dataSetupRetryEntry);
- } else {
- // Network did not suggest any retry. Use the configured rules to perform retry.
- logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList);
+ return;
+ }
- // Support the legacy permanent failure configuration
- if (DataFailCause.isPermanentFailure(mPhone.getContext(), cause, mPhone.getSubId())) {
- log("Stopped timer-based retry. cause=" + DataFailCause.toString(cause));
+ // Network did not suggest any retry. Use the configured rules to perform retry.
+ logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList);
+
+ boolean retryScheduled = false;
+ List<NetworkRequestList> groupedNetworkRequestLists =
+ DataUtils.getGroupedNetworkRequestList(requestList);
+ for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) {
+ if (retryRule.isPermanentFailCauseRule() && retryRule.getFailCauses().contains(cause)) {
+ if (dataProfile.getApnSetting() != null) {
+ dataProfile.getApnSetting().setPermanentFailed(true);
+
+ // It seems strange to have retry timer in permanent failure rule, but since
+ // in this case permanent failure is only applicable to the failed profile, so
+ // we need to see if other profile can be selected for next data setup.
+ log("Marked " + dataProfile.getApnSetting().getApnName() + " permanently "
+ + "failed, but still schedule retry to see if another data profile "
+ + "can be used for setup data.");
+ // Schedule a data retry to see if another data profile could be selected.
+ // If the same data profile is selected again, since it's marked as
+ // permanent failure, it won't be used for setup data call.
+ schedule(new DataSetupRetryEntry.Builder<>()
+ .setRetryDelay(retryRule.getRetryIntervalsMillis().get(0))
+ .setAppliedRetryRule(retryRule)
+ .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS)
+ .setTransport(transport)
+ .setNetworkRequestList(requestList)
+ .build());
+ } else {
+ // For TD-based data profile, do not do anything for now. Should expand this in
+ // the future if needed.
+ log("Stopped timer-based retry for TD-based data profile. Will retry only when "
+ + "environment changes.");
+ }
return;
}
-
- boolean retryScheduled = false;
- List<NetworkRequestList> groupedNetworkRequestLists =
- DataUtils.getGroupedNetworkRequestList(requestList);
for (NetworkRequestList networkRequestList : groupedNetworkRequestLists) {
int capability = networkRequestList.get(0).getApnTypeNetworkCapability();
-
- for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) {
- if (retryRule.canBeMatched(capability, cause)) {
- // Check if there is already a similar network request retry scheduled.
- if (isSimilarNetworkRequestRetryScheduled(
- networkRequestList.get(0), transport)) {
- log(networkRequestList.get(0) + " already had similar retry "
- + "scheduled.");
- return;
- }
-
- int failedCount = getRetryFailedCount(capability, retryRule);
- log("For capability " + DataUtils.networkCapabilityToString(capability)
- + ", found matching rule " + retryRule + ", failed count="
- + failedCount);
- if (failedCount == retryRule.getMaxRetries()) {
- log("Data retry failed for " + failedCount + " times. Stopped "
- + "timer-based data retry for "
- + DataUtils.networkCapabilityToString(capability)
- + ". Condition-based retry will still happen when condition "
- + "changes.");
- return;
- }
-
- retryDelayMillis = retryRule.getRetryIntervalsMillis().get(
- Math.min(failedCount, retryRule
- .getRetryIntervalsMillis().size() - 1));
-
- // Schedule a data retry.
- schedule(new DataSetupRetryEntry.Builder<>()
- .setRetryDelay(retryDelayMillis)
- .setAppliedRetryRule(retryRule)
- .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS)
- .setTransport(transport)
- .setNetworkRequestList(networkRequestList)
- .build());
- retryScheduled = true;
- break;
+ if (retryRule.canBeMatched(capability, cause)) {
+ // Check if there is already a similar network request retry scheduled.
+ if (isSimilarNetworkRequestRetryScheduled(
+ networkRequestList.get(0), transport)) {
+ log(networkRequestList.get(0) + " already had similar retry "
+ + "scheduled.");
+ return;
}
+
+ int failedCount = getRetryFailedCount(capability, retryRule, transport);
+ log("For capability " + DataUtils.networkCapabilityToString(capability)
+ + ", found matching rule " + retryRule + ", failed count="
+ + failedCount);
+ if (failedCount == retryRule.getMaxRetries()) {
+ log("Data retry failed for " + failedCount + " times on "
+ + AccessNetworkConstants.transportTypeToString(transport)
+ + ". Stopped timer-based data retry for "
+ + DataUtils.networkCapabilityToString(capability)
+ + ". Condition-based retry will still happen when condition "
+ + "changes.");
+ return;
+ }
+
+ retryDelayMillis = retryRule.getRetryIntervalsMillis().get(
+ Math.min(failedCount, retryRule
+ .getRetryIntervalsMillis().size() - 1));
+
+ // Schedule a data retry.
+ schedule(new DataSetupRetryEntry.Builder<>()
+ .setRetryDelay(retryDelayMillis)
+ .setAppliedRetryRule(retryRule)
+ .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS)
+ .setTransport(transport)
+ .setNetworkRequestList(networkRequestList)
+ .build());
+ retryScheduled = true;
+ break;
}
}
+ }
- if (!retryScheduled) {
- log("onEvaluateDataSetupRetry: Did not match any retry rule. Stop timer-based "
- + "retry.");
- }
+ if (!retryScheduled) {
+ log("onEvaluateDataSetupRetry: Did not match any retry rule. Stop timer-based "
+ + "retry.");
}
}
@@ -1211,6 +1334,24 @@
}
}
+ /**
+ * @param dataNetwork The data network to check.
+ * @return {@code true} if the data network had failed the maximum number of attempts for
+ * handover according to any retry rules.
+ */
+ public boolean isDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) {
+ // Matching the rule in configured order.
+ for (DataHandoverRetryRule retryRule : mDataHandoverRetryRuleList) {
+ int failedCount = getRetryFailedCount(dataNetwork, retryRule);
+ if (failedCount == retryRule.getMaxRetries()) {
+ log("Data handover retry failed for " + failedCount + " times. Stopped "
+ + "handover retry.");
+ return true;
+ }
+ }
+ return false;
+ }
+
/** Cancel all retries and throttling entries. */
private void onReset(@RetryResetReason int reason) {
logl("Remove all retry and throttling entries, reason=" + resetReasonToString(reason));
@@ -1224,7 +1365,7 @@
DataProfile dataProfile = dataThrottlingEntry.dataProfile;
String apn = dataProfile.getApnSetting() != null
? dataProfile.getApnSetting().getApnName() : null;
- onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false);
+ onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false, true);
}
mDataThrottlingEntries.clear();
@@ -1263,16 +1404,18 @@
*
* @param networkCapability The network capability to check.
* @param dataRetryRule The data retry rule.
+ * @param transport The transport on which setup failure has occurred.
* @return The failed count since last successful data setup.
*/
private int getRetryFailedCount(@NetCapability int networkCapability,
- @NonNull DataSetupRetryRule dataRetryRule) {
+ @NonNull DataSetupRetryRule dataRetryRule, @TransportType int transport) {
int count = 0;
for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) {
if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) {
DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i);
// count towards the last succeeded data setup.
- if (entry.setupRetryType == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) {
+ if (entry.setupRetryType == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS
+ && entry.transport == transport) {
if (entry.networkRequestList.isEmpty()) {
String msg = "Invalid data retry entry detected";
logl(msg);
@@ -1304,20 +1447,50 @@
* @param dataRetryEntry The data retry entry.
*/
private void schedule(@NonNull DataRetryEntry dataRetryEntry) {
- logl("Scheduled data retry: " + dataRetryEntry);
+ logl("Scheduled data retry " + dataRetryEntry
+ + " hashcode=" + dataRetryEntry.hashCode());
mDataRetryEntries.add(dataRetryEntry);
if (mDataRetryEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
// Discard the oldest retry entry.
mDataRetryEntries.remove(0);
}
- // Using delayed message instead of alarm manager to schedule data retry is intentional.
- // When the device enters doze mode, the handler message might be extremely delayed than the
- // original scheduled time. There is no need to wake up the device to perform data retry in
- // that case.
- sendMessageDelayed(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
- ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry),
- dataRetryEntry.retryDelayMillis);
+ // When the device is in doze mode, the handler message might be extremely delayed because
+ // handler uses relative system time(not counting sleep) which is inaccurate even when we
+ // enter the maintenance window.
+ // Therefore, we use alarm manager when we need to schedule long timers.
+ if (dataRetryEntry.retryDelayMillis <= RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS) {
+ sendMessageDelayed(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+ ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry),
+ dataRetryEntry.retryDelayMillis);
+ } else {
+ Intent intent = new Intent(ACTION_RETRY);
+ intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode());
+ // No need to wake up the device at the exact time, the retry can wait util next time
+ // the device wake up to save power.
+ mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
+ dataRetryEntry.retryElapsedTime,
+ PendingIntent.getBroadcast(mPhone.getContext(),
+ dataRetryEntry.hashCode() /*Unique identifier of this retry attempt*/,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE));
+ }
+ }
+
+ /**
+ * Called when it's time to retry scheduled by Alarm Manager.
+ * @param retryHashcode The hashcode is the unique identifier of which retry entry to retry.
+ */
+ private void onAlarmIntentRetry(int retryHashcode) {
+ DataRetryEntry dataRetryEntry = mDataRetryEntries.stream()
+ .filter(entry -> entry.hashCode() == retryHashcode)
+ .findAny()
+ .orElse(null);
+ logl("onAlarmIntentRetry: found " + dataRetryEntry + " with hashcode " + retryHashcode);
+ if (dataRetryEntry != null) {
+ sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+ ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry));
+ }
}
/**
@@ -1340,15 +1513,20 @@
@TransportType int transport, @ElapsedRealtimeLong long expirationTime) {
DataThrottlingEntry entry = new DataThrottlingEntry(dataProfile, networkRequestList,
dataNetwork, transport, retryType, expirationTime);
- if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
- mDataThrottlingEntries.remove(0);
- }
-
- // Remove previous entry that contains the same data profile.
+ // Remove previous entry that contains the same data profile. Therefore it should always
+ // contain at maximum all the distinct data profiles of the current subscription.
mDataThrottlingEntries.removeIf(
throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile));
-
+ if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
+ // If we don't see the anomaly report after U release, we should remove this check for
+ // the commented reason above.
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("24fd4d46-1d0f-4b13-b7d6-7bad70b8289b"),
+ "DataRetryManager throttling more than 100 data profiles",
+ mPhone.getCarrierId());
+ mDataThrottlingEntries.remove(0);
+ }
logl("Add throttling entry " + entry);
mDataThrottlingEntries.add(entry);
@@ -1383,10 +1561,11 @@
* When this is set, {@code dataProfile} must be {@code null}.
* @param transport The transport that this unthrottling request is on.
* @param remove Whether to remove unthrottled entries from the list of entries.
+ * @param retry Whether schedule retry after unthrottling.
*/
private void onDataProfileUnthrottled(@Nullable DataProfile dataProfile, @Nullable String apn,
- int transport, boolean remove) {
- log("onDataProfileUnthrottled: data profile=" + dataProfile + ", apn=" + apn
+ @TransportType int transport, boolean remove, boolean retry) {
+ log("onDataProfileUnthrottled: dataProfile=" + dataProfile + ", apn=" + apn
+ ", transport=" + AccessNetworkConstants.transportTypeToString(transport)
+ ", remove=" + remove);
@@ -1398,18 +1577,18 @@
// equal to the data profiles kept in data profile manager (due to some fields missing
// in DataProfileInfo.aidl), so we need to get the equivalent data profile from data
// profile manager.
- final DataProfile dp = mDataProfileManager.getDataProfile(
- dataProfile.getApnSetting() != null
- ? dataProfile.getApnSetting().getApnName() : null,
- dataProfile.getTrafficDescriptor());
- log("onDataProfileUnthrottled: getDataProfile=" + dp);
- if (dp != null) {
- dataUnthrottlingEntries = mDataThrottlingEntries.stream()
- .filter(entry -> entry.expirationTimeMillis > now
- && entry.dataProfile.equals(dp)
- && entry.transport == transport)
- .collect(Collectors.toList());
+ Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream();
+ stream = stream.filter(entry -> entry.expirationTimeMillis > now);
+ if (dataProfile.getApnSetting() != null) {
+ stream = stream
+ .filter(entry -> entry.dataProfile.getApnSetting() != null)
+ .filter(entry -> entry.dataProfile.getApnSetting().getApnName()
+ .equals(dataProfile.getApnSetting().getApnName()));
}
+ stream = stream.filter(entry -> Objects.equals(entry.dataProfile.getTrafficDescriptor(),
+ dataProfile.getTrafficDescriptor()));
+
+ dataUnthrottlingEntries = stream.collect(Collectors.toList());
} else if (apn != null) {
// For HIDL 1.6 or below
dataUnthrottlingEntries = mDataThrottlingEntries.stream()
@@ -1441,6 +1620,7 @@
final int dataRetryType = retryType;
if (unthrottledProfile != null && unthrottledProfile.getApnSetting() != null) {
+ unthrottledProfile.getApnSetting().setPermanentFailed(false);
throttleStatusList.addAll(unthrottledProfile.getApnSetting().getApnTypes().stream()
.map(apnType -> new ThrottleStatus.Builder()
.setApnType(apnType)
@@ -1455,23 +1635,30 @@
mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onThrottleStatusChanged(throttleStatusList)));
+ if (unthrottledProfile != null) {
+ // cancel pending retries since we will soon schedule an immediate retry
+ cancelRetriesForDataProfile(unthrottledProfile, transport);
+ }
+
logl("onDataProfileUnthrottled: Removing the following throttling entries. "
+ dataUnthrottlingEntries);
- for (DataThrottlingEntry entry : dataUnthrottlingEntries) {
- if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) {
+ if (retry) {
+ for (DataThrottlingEntry entry : dataUnthrottlingEntries) {
// Immediately retry after unthrottling.
- schedule(new DataSetupRetryEntry.Builder<>()
- .setDataProfile(entry.dataProfile)
- .setTransport(entry.transport)
- .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE)
- .setNetworkRequestList(entry.networkRequestList)
- .setRetryDelay(0)
- .build());
- } else if (entry.retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) {
- schedule(new DataHandoverRetryEntry.Builder<>()
- .setDataNetwork(entry.dataNetwork)
- .setRetryDelay(0)
- .build());
+ if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) {
+ schedule(new DataSetupRetryEntry.Builder<>()
+ .setDataProfile(entry.dataProfile)
+ .setTransport(entry.transport)
+ .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE)
+ .setNetworkRequestList(entry.networkRequestList)
+ .setRetryDelay(0)
+ .build());
+ } else if (entry.retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) {
+ schedule(new DataHandoverRetryEntry.Builder<>()
+ .setDataNetwork(entry.dataNetwork)
+ .setRetryDelay(0)
+ .build());
+ }
}
}
if (remove) {
@@ -1480,6 +1667,34 @@
}
/**
+ * Cancel pending retries that uses the specified data profile, with specified target transport.
+ *
+ * @param dataProfile The data profile to cancel.
+ * @param transport The target {@link TransportType} on which the retry to cancel.
+ */
+ private void cancelRetriesForDataProfile(@NonNull DataProfile dataProfile,
+ @TransportType int transport) {
+ logl("cancelRetriesForDataProfile: Canceling pending retries for " + dataProfile);
+ mDataRetryEntries.stream()
+ .filter(entry -> {
+ if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) {
+ if (entry instanceof DataSetupRetryEntry) {
+ DataSetupRetryEntry retryEntry = (DataSetupRetryEntry) entry;
+ return dataProfile.equals(retryEntry.dataProfile)
+ && transport == retryEntry.transport;
+ } else if (entry instanceof DataHandoverRetryEntry) {
+ DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) entry;
+ return dataProfile.equals(retryEntry.dataNetwork.getDataProfile());
+ }
+ }
+ return false;
+ })
+ .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED));
+ }
+
+
+
+ /**
* Check if there is any similar network request scheduled to retry. The definition of similar
* is that network requests have same APN capability and on the same transport.
*
@@ -1489,12 +1704,14 @@
*/
public boolean isSimilarNetworkRequestRetryScheduled(
@NonNull TelephonyNetworkRequest networkRequest, @TransportType int transport) {
+ long now = SystemClock.elapsedRealtime();
for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) {
if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) {
DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i);
if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED
&& entry.setupRetryType
- == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) {
+ == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS
+ && entry.retryElapsedTime > now) {
if (entry.networkRequestList.isEmpty()) {
String msg = "Invalid data retry entry detected";
logl(msg);
@@ -1517,23 +1734,6 @@
}
/**
- * Check if there is any data setup retry scheduled with specified data profile.
- *
- * @param dataProfile The data profile to retry.
- * @param transport The transport that the request is on.
- * @return {@code true} if there is retry scheduled for this data profile.
- */
- public boolean isAnySetupRetryScheduled(@NonNull DataProfile dataProfile,
- @TransportType int transport) {
- return mDataRetryEntries.stream()
- .filter(DataSetupRetryEntry.class::isInstance)
- .map(DataSetupRetryEntry.class::cast)
- .anyMatch(entry -> entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED
- && dataProfile.equals(entry.dataProfile)
- && entry.transport == transport);
- }
-
- /**
* Check if a specific data profile is explicitly throttled by the network.
*
* @param dataProfile The data profile to check.
@@ -1563,9 +1763,11 @@
* @param dataNetwork The data network that was originally scheduled for handover retry.
*/
private void onCancelPendingHandoverRetry(@NonNull DataNetwork dataNetwork) {
- mDataRetryEntries.removeIf(entry -> entry instanceof DataHandoverRetryEntry
- && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork);
- mDataThrottlingEntries.removeIf(entry -> entry.dataNetwork == dataNetwork);
+ mDataRetryEntries.stream()
+ .filter(entry -> entry instanceof DataHandoverRetryEntry
+ && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork
+ && entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED)
+ .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED));
}
/**
diff --git a/src/java/com/android/internal/telephony/data/DataServiceManager.java b/src/java/com/android/internal/telephony/data/DataServiceManager.java
index 41190bf..2733aff 100644
--- a/src/java/com/android/internal/telephony/data/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/data/DataServiceManager.java
@@ -22,11 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -121,22 +119,6 @@
private List<DataCallResponse> mLastDataCallResponseList = Collections.EMPTY_LIST;
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
- && mPhone.getPhoneId() == intent.getIntExtra(
- CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
- // We should wait for carrier config changed event because the target binding
- // package name can come from the carrier config. Note that we still get this event
- // even when SIM is absent.
- if (DBG) log("Carrier config changed. Try to bind data service.");
- sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
- }
- }
- };
-
private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
@@ -409,16 +391,18 @@
Context.LEGACY_PERMISSION_SERVICE);
mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- try {
- Context contextAsUser = phone.getContext().createPackageContextAsUser(
- phone.getContext().getPackageName(), 0, UserHandle.ALL);
- contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
- null /* broadcastPermission */, null);
- } catch (PackageManager.NameNotFoundException e) {
- loge("Package name not found: " + e.getMessage());
- }
+ // Callback is executed in handler thread to directly handle config change.
+ mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) -> {
+ if (slotIndex == mPhone.getPhoneId()) {
+ // We should wait for carrier config changed event because the
+ // target binding package name can come from the carrier config.
+ // Note that we still get this event even when SIM is absent.
+ if (DBG) log("Carrier config changed. Try to bind data service.");
+ rebindDataService();
+ }
+ });
+
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_BIND_DATA_SERVICE, null);
@@ -587,9 +571,10 @@
// Read package name from resource overlay
packageName = mPhone.getContext().getResources().getString(resourceId);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+ if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
packageName = b.getString(carrierConfig, packageName);
}
@@ -636,9 +621,10 @@
// Read package name from resource overlay
className = mPhone.getContext().getResources().getString(resourceId);
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+ if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
// If carrier config overrides it, use the one from carrier config
className = b.getString(carrierConfig, className);
}
@@ -760,14 +746,19 @@
* @param onCompleteMessage The result callback for this request.
*/
public void startHandover(int cid, @NonNull Message onCompleteMessage) {
- DataServiceCallbackWrapper callback =
- setupCallbackHelper("startHandover", onCompleteMessage);
- if (callback == null) {
- loge("startHandover: callback == null");
+ if (DBG) log("startHandover");
+ if (!mBound) {
+ loge("Data service not bound.");
sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
return;
}
+ DataServiceCallbackWrapper callback =
+ new DataServiceCallbackWrapper("startHandover");
+ if (onCompleteMessage != null) {
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+
try {
sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
REQUEST_UNRESPONDED_TIMEOUT);
@@ -790,13 +781,19 @@
* @param onCompleteMessage The result callback for this request.
*/
public void cancelHandover(int cid, @NonNull Message onCompleteMessage) {
- DataServiceCallbackWrapper callback =
- setupCallbackHelper("cancelHandover", onCompleteMessage);
- if (callback == null) {
+ if (DBG) log("cancelHandover");
+ if (!mBound) {
+ loge("Data service not bound.");
sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
return;
}
+ DataServiceCallbackWrapper callback =
+ new DataServiceCallbackWrapper("cancelHandover");
+ if (onCompleteMessage != null) {
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+
try {
sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
REQUEST_UNRESPONDED_TIMEOUT);
@@ -808,26 +805,6 @@
}
}
- @Nullable
- private DataServiceCallbackWrapper setupCallbackHelper(
- @NonNull final String operationName, @NonNull final Message onCompleteMessage) {
- if (DBG) log(operationName);
- if (!mBound) {
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return null;
- }
-
- DataServiceCallbackWrapper callback =
- new DataServiceCallbackWrapper(operationName);
- if (onCompleteMessage != null) {
- if (DBG) log(operationName + ": onCompleteMessage set");
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- } else {
- if (DBG) log(operationName + ": onCompleteMessage not set");
- }
- return callback;
- }
-
/**
* Set an APN to initial attach network.
*
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index e2c1aff..5178ae4 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -19,7 +19,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
@@ -29,12 +28,13 @@
import android.provider.Settings;
import android.sysprop.TelephonyProperties;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.MobileDataPolicy;
import android.telephony.TelephonyRegistryManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ApnType;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -42,12 +42,19 @@
import com.android.internal.telephony.GlobalSettingsHelper;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SettingsObserver;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
+import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -58,8 +65,9 @@
* data roaming settings, etc...
*/
public class DataSettingsManager extends Handler {
- /** Event for data config updated. */
- private static final int EVENT_DATA_CONFIG_UPDATED = 1;
+ /** Invalid mobile data policy **/
+ private static final int INVALID_MOBILE_DATA_POLICY = -1;
+
/** Event for call state changed. */
private static final int EVENT_CALL_STATE_CHANGED = 2;
/** Event for subscriptions updated. */
@@ -68,10 +76,9 @@
private static final int EVENT_SET_DATA_ENABLED_FOR_REASON = 5;
/** Event for set data roaming enabled. */
private static final int EVENT_SET_DATA_ROAMING_ENABLED = 6;
- /** Event for set always allow MMS data. */
- private static final int EVENT_SET_ALWAYS_ALLOW_MMS_DATA = 7;
- /** Event for set allow data during voice call. */
- private static final int EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL = 8;
+ /** Event for set mobile data policy. */
+ private static final int EVENT_SET_MOBILE_DATA_POLICY = 7;
+
/** Event for device provisioned changed. */
private static final int EVENT_PROVISIONED_CHANGED = 9;
/** Event for provisioning data enabled setting changed. */
@@ -84,8 +91,8 @@
private final SettingsObserver mSettingsObserver;
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
+ private Set<Integer> mEnabledMobileDataPolicy = new HashSet<>();
private int mSubId;
- private DataEnabledOverride mDataEnabledOverride;
/** Data config manager */
private final @NonNull DataConfigManager mDataConfigManager;
@@ -172,7 +179,7 @@
mResolver = mPhone.getContext().getContentResolver();
registerCallback(callback);
mDataConfigManager = dataNetworkController.getDataConfigManager();
- mDataEnabledOverride = getDataEnabledOverride();
+ refreshEnabledMobileDataPolicy();
mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, true);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, true);
@@ -186,19 +193,13 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case EVENT_DATA_CONFIG_UPDATED: {
- if (mDataConfigManager.isConfigCarrierSpecific()) {
- setDefaultDataRoamingEnabled();
- }
- break;
- }
case EVENT_CALL_STATE_CHANGED: {
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
break;
}
case EVENT_SUBSCRIPTIONS_CHANGED: {
mSubId = (int) msg.obj;
- mDataEnabledOverride = getDataEnabledOverride();
+ refreshEnabledMobileDataPolicy();
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER);
mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
break;
@@ -232,34 +233,10 @@
setDataRoamingFromUserAction();
break;
}
- case EVENT_SET_ALWAYS_ALLOW_MMS_DATA: {
- boolean alwaysAllow = (boolean) msg.obj;
- if (alwaysAllow == isMmsAlwaysAllowed()) {
- break;
- }
- logl("AlwaysAllowMmsData changed to " + alwaysAllow);
- mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow);
- if (SubscriptionController.getInstance()
- .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) {
- updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
- notifyDataEnabledOverrideChanged(alwaysAllow,
- TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED);
- }
- break;
- }
- case EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL: {
- boolean allow = (boolean) msg.obj;
- if (allow == isDataAllowedInVoiceCall()) {
- break;
- }
- logl("AllowDataDuringVoiceCall changed to " + allow);
- mDataEnabledOverride.setDataAllowedInVoiceCall(allow);
- if (SubscriptionController.getInstance()
- .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) {
- updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
- notifyDataEnabledOverrideChanged(allow, TelephonyManager
- .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL);
- }
+ case EVENT_SET_MOBILE_DATA_POLICY: {
+ int mobileDataPolicy = msg.arg1;
+ boolean enable = msg.arg2 == 1;
+ onSetMobileDataPolicy(mobileDataPolicy, enable);
break;
}
case EVENT_PROVISIONED_CHANGED:
@@ -280,7 +257,14 @@
* Called when needed to register for all events that data network controller is interested.
*/
private void onInitialize() {
- mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
+ mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ if (mDataConfigManager.isConfigCarrierSpecific()) {
+ setDefaultDataRoamingEnabled();
+ }
+ }
+ });
mSettingsObserver.observe(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
EVENT_PROVISIONED_CHANGED);
mSettingsObserver.observe(
@@ -399,9 +383,9 @@
return isProvisioningDataEnabled();
} else {
boolean userDataEnabled = isUserDataEnabled();
- // Check if we should temporarily enable data in certain conditions.
- boolean isDataEnabledOverridden = mDataEnabledOverride
- .shouldOverrideDataEnabledSettings(mPhone, apnType);
+ // Check if we should temporarily enable data based on mobile data policy.
+ boolean isDataEnabledOverridden = isDataEnabledOverriddenForApn(apnType);
+
return ((userDataEnabled || isDataEnabledOverridden)
&& mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY)
@@ -410,10 +394,11 @@
}
}
- private static boolean isStandAloneOpportunistic(int subId, Context context) {
- SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo(
- subId, context.getOpPackageName(), context.getAttributionTag());
- return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null;
+ private boolean isStandAloneOpportunistic(int subId) {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && subInfo.isOpportunistic()
+ && TextUtils.isEmpty(subInfo.getGroupUuid());
}
/**
@@ -423,7 +408,7 @@
*/
private void setUserDataEnabled(boolean enabled, String callingPackage) {
// Can't disable data for stand alone opportunistic subscription.
- if (isStandAloneOpportunistic(mSubId, mPhone.getContext()) && !enabled) return;
+ if (isStandAloneOpportunistic(mSubId) && !enabled) return;
boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(),
Settings.Global.MOBILE_DATA, mSubId, (enabled ? 1 : 0));
log("Set user data enabled to " + enabled + ", changed=" + changed + ", callingPackage="
@@ -445,7 +430,7 @@
}
// User data should always be true for opportunistic subscription.
- if (isStandAloneOpportunistic(mSubId, mPhone.getContext())) return true;
+ if (isStandAloneOpportunistic(mSubId)) return true;
boolean defaultVal = TelephonyProperties.mobile_data().orElse(true);
@@ -544,42 +529,37 @@
* has not manually set the value. The default value is {@link #isDefaultDataRoamingEnabled()}.
*/
public void setDefaultDataRoamingEnabled() {
- // For SSSS, this is a per-phone property from DATA_ROAMING_IS_USER_SETTING_KEY.
- // For DSDS, this is a per-sub property from Settings.Global.DATA_ROAMING + subId.
// If the user has not manually set the value, use the default value.
- boolean useCarrierSpecificDefault = false;
- if (mPhone.getContext().getSystemService(TelephonyManager.class).getSimCount() != 1) {
- String setting = Settings.Global.DATA_ROAMING + mPhone.getSubId();
- try {
- Settings.Global.getInt(mResolver, setting);
- } catch (Settings.SettingNotFoundException ex) {
- // For multi-SIM phones, use the default value if uninitialized.
- useCarrierSpecificDefault = true;
- }
- } else if (!isDataRoamingFromUserAction()) {
- // For single-SIM phones, use the default value if user action is not set.
- useCarrierSpecificDefault = true;
- }
- log("setDefaultDataRoamingEnabled: useCarrierSpecificDefault=" + useCarrierSpecificDefault);
- if (useCarrierSpecificDefault) {
- boolean defaultVal = isDefaultDataRoamingEnabled();
- setDataRoamingEnabledInternal(defaultVal);
+ if (!isDataRoamingFromUserAction()) {
+ setDataRoamingEnabledInternal(isDefaultDataRoamingEnabled());
}
}
/**
- * Get whether the user has manually enabled or disabled data roaming from settings.
- * @return {@code true} if the user has enabled data roaming and {@code false} if they have not.
+ * Get whether the user has manually enabled or disabled data roaming from settings for the
+ * current subscription.
+ * @return {@code true} if the user has manually enabled data roaming for the current
+ * subscription and {@code false} if they have not.
*/
private boolean isDataRoamingFromUserAction() {
- final SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(mPhone.getContext());
- // Since we don't want to unset user preferences after a system update, default to true if
- // the preference does not exist and set it to false explicitly from factory reset.
- if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
- sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
+ String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId();
+ final SharedPreferences sp =
+ PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
+
+ // Set the default roaming from user action value if the preference doesn't exist
+ if (!sp.contains(key)) {
+ if (sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
+ log("Reusing previous roaming from user action value for backwards compatibility.");
+ sp.edit().putBoolean(key, true).commit();
+ } else {
+ log("Clearing roaming from user action value for new or upgrading devices.");
+ sp.edit().putBoolean(key, false).commit();
+ }
}
- return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
+
+ boolean isUserSetting = sp.getBoolean(key, true);
+ log("isDataRoamingFromUserAction: key=" + key + ", isUserSetting=" + isUserSetting);
+ return isUserSetting;
}
/**
@@ -588,49 +568,76 @@
* {@link #isDefaultDataRoamingEnabled()} will continue to be used.
*/
private void setDataRoamingFromUserAction() {
- final SharedPreferences.Editor sp = PreferenceManager
- .getDefaultSharedPreferences(mPhone.getContext()).edit();
- sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true).commit();
+ String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId();
+ log("setDataRoamingFromUserAction: key=" + key);
+ final SharedPreferences.Editor sp =
+ PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()).edit();
+ sp.putBoolean(key, true).commit();
}
- private @NonNull DataEnabledOverride getDataEnabledOverride() {
- return new DataEnabledOverride(SubscriptionController.getInstance()
- .getDataEnabledOverrideRules(mSubId));
+ /** Refresh the enabled mobile data policies from Telephony database */
+ private void refreshEnabledMobileDataPolicy() {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(mSubId);
+ if (subInfo != null) {
+ mEnabledMobileDataPolicy = getMobileDataPolicyEnabled(
+ subInfo.getEnabledMobileDataPolicies());
+ }
}
/**
- * Set whether to always allow the MMS data connection.
- * @param alwaysAllow {@code true} if MMS data is always allowed and {@code false} otherwise.
+ * @return {@code true} If the mobile data policy is enabled
*/
- public void setAlwaysAllowMmsData(boolean alwaysAllow) {
- obtainMessage(EVENT_SET_ALWAYS_ALLOW_MMS_DATA, alwaysAllow).sendToTarget();
+ public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int mobileDataPolicy) {
+ return mEnabledMobileDataPolicy.contains(mobileDataPolicy);
}
/**
- * Check whether MMS is always allowed.
- * @return {@code true} if MMS is always allowed and {@code false} otherwise.
+ * Set mobile data policy enabled status
+ * @param mobileDataPolicy The mobile data policy to set
+ * @param enable {@code true} to enable the policy; {@code false} to disable.
*/
- public boolean isMmsAlwaysAllowed() {
- return mDataEnabledOverride.isMmsAlwaysAllowed();
+ public void setMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
+ obtainMessage(EVENT_SET_MOBILE_DATA_POLICY, mobileDataPolicy, enable ? 1 : 0)
+ .sendToTarget();
}
/**
- * Set whether to allow mobile data during voice call. This is used for allowing data on the
- * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
- * devices, users will not be able to use mobile data. By calling this API, data will be
- * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
- * @param allow {@code true} if data is allowed during a voice call and {@code false} otherwise.
+ * Store data mobile policy to Telephony database.
+ *
+ * @param mobileDataPolicy The mobile data policy that overrides user data enabled setting.
+ * @param enable {@code true} to enable the policy; {@code false} to remove the policy.
*/
- public void setAllowDataDuringVoiceCall(boolean allow) {
- obtainMessage(EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL, allow).sendToTarget();
+ private void onSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
+ if (enable == isMobileDataPolicyEnabled(mobileDataPolicy)) {
+ return;
+ }
+ metricsRecordSetMobileDataPolicy(mobileDataPolicy);
+
+ if (enable) {
+ mEnabledMobileDataPolicy.add(mobileDataPolicy);
+ } else {
+ mEnabledMobileDataPolicy.remove(mobileDataPolicy);
+ }
+
+ String enabledMobileDataPolicies = mEnabledMobileDataPolicy.stream().map(String::valueOf)
+ .collect(Collectors.joining(","));
+ SubscriptionManagerService.getInstance().setEnabledMobileDataPolicies(mSubId,
+ enabledMobileDataPolicies);
+ logl(TelephonyUtils.mobileDataPolicyToString(mobileDataPolicy) + " changed to "
+ + enable);
+ updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+ notifyDataEnabledOverrideChanged(enable, mobileDataPolicy);
}
/**
- * Check whether data is allowed during a voice call.
- * @return {@code true} if data is allowed during voice call and {@code false} otherwise.
+ * Record the number of times a mobile data policy is toggled to metrics.
+ * @param mobileDataPolicy The mobile data policy that's toggled
*/
- public boolean isDataAllowedInVoiceCall() {
- return mDataEnabledOverride.isDataAllowedInVoiceCall();
+ private void metricsRecordSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy) {
+ if (mobileDataPolicy == TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) {
+ DeviceTelephonyPropertiesStats.recordAutoDataSwitchFeatureToggle();
+ }
}
/**
@@ -659,6 +666,85 @@
}
/**
+ * Return the parsed mobile data policies.
+ *
+ * @param policies New mobile data policies in String format.
+ * @return A Set of parsed mobile data policies.
+ */
+ public @NonNull @MobileDataPolicy Set<Integer> getMobileDataPolicyEnabled(
+ @NonNull String policies) {
+ Set<Integer> mobileDataPolicies = new HashSet<>();
+ String[] rulesString = policies.trim().split("\\s*,\\s*");
+ for (String rule : rulesString) {
+ if (!TextUtils.isEmpty(rule)) {
+ int parsedDataPolicy = parsePolicyFrom(rule);
+ if (parsedDataPolicy != INVALID_MOBILE_DATA_POLICY) {
+ mobileDataPolicies.add(parsedDataPolicy);
+ }
+ }
+ }
+ return mobileDataPolicies;
+ }
+
+ /**
+ * Parse a mobile data policy retrieved from Telephony db.
+ * If the policy is in legacy format, convert it into the corresponding mobile data policy.
+ *
+ * @param policy Mobile data policy to be parsed from.
+ * @return Parsed mobile data policy. {@link #INVALID_MOBILE_DATA_POLICY} if string can't be
+ * parsed into a mobile data policy.
+ */
+ private @MobileDataPolicy int parsePolicyFrom(@NonNull String policy) {
+ int dataPolicy;
+ try {
+ // parse as new override policy
+ dataPolicy = Integer.parseInt(policy);
+ } catch (NumberFormatException e) {
+ dataPolicy = INVALID_MOBILE_DATA_POLICY;
+ loge("parsePolicyFrom: invalid mobile data policy format: " + policy);
+ }
+ return dataPolicy;
+ }
+
+ /**
+ * Check if data enabled is temporarily overridden in certain conditions.
+ *
+ * @param apnType The APN type to check.
+ * @return {@code true} if data enabled should be overridden.
+ */
+ private boolean isDataEnabledOverriddenForApn(@ApnType int apnType) {
+ boolean overridden = false;
+
+ // mobile data policy : MMS always allowed
+ if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) {
+ overridden = apnType == ApnSetting.TYPE_MMS;
+ }
+
+ boolean isNonDds = mPhone.getSubId() != SubscriptionManagerService.getInstance()
+ .getDefaultDataSubId();
+
+ // mobile data policy : data during call
+ if (isMobileDataPolicyEnabled(TelephonyManager
+ .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL)) {
+ overridden = overridden || isNonDds && mPhone.getState() != PhoneConstants.State.IDLE;
+ }
+
+ // mobile data policy : auto data switch
+ if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
+ // check user enabled data on the default data phone
+ Phone defaultDataPhone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
+ .getPhoneId(SubscriptionManagerService.getInstance()
+ .getDefaultDataSubId()));
+ if (defaultDataPhone == null) {
+ loge("isDataEnabledOverriddenForApn: unexpected defaultDataPhone is null");
+ } else {
+ overridden = overridden || isNonDds && defaultDataPhone.isUserDataEnabled();
+ }
+ }
+ return overridden;
+ }
+
+ /**
* Register the callback for receiving information from {@link DataSettingsManager}.
*
* @param callback The callback.
@@ -700,7 +786,8 @@
+ ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
+ ", mIsDataEnabled=" + mIsDataEnabled
+ ", mDataEnabledSettings=" + mDataEnabledSettings
- + ", mDataEnabledOverride=" + mDataEnabledOverride
+ + ", mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
+ .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(","))
+ "]";
}
@@ -756,7 +843,8 @@
.map(entry ->
dataEnabledChangedReasonToString(entry.getKey()) + "=" + entry.getValue())
.collect(Collectors.joining(", ")));
- pw.println("mDataEnabledOverride=" + mDataEnabledOverride);
+ pw.println("mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
+ .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(",")));
pw.println("Local logs:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index 22d0498..6c6f064 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.data;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
import android.annotation.CallbackExecutor;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
@@ -26,18 +28,19 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.ValidationStatus;
import android.telephony.CellSignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.data.DataProfile;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
import com.android.internal.telephony.metrics.DataStallRecoveryStats;
@@ -124,9 +127,6 @@
/** The data stall recovered by user (Mobile Data Power off/on). */
private static final int RECOVERED_REASON_USER = 3;
- /** Event for data config updated. */
- private static final int EVENT_DATA_CONFIG_UPDATED = 1;
-
/** Event for triggering recovery action. */
private static final int EVENT_DO_RECOVERY = 2;
@@ -147,17 +147,20 @@
private final @NonNull DataServiceManager mWwanDataServiceManager;
/** The data stall recovery action. */
- private @RecoveryAction int mRecovryAction;
+ private @RecoveryAction int mRecoveryAction;
/** The elapsed real time of last recovery attempted */
private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs;
/** Whether current network is good or not */
private boolean mIsValidNetwork;
+ /** Whether data stall recovery is triggered or not */
+ private boolean mRecoveryTriggered = false;
/** Whether data stall happened or not. */
private boolean mDataStalled;
/** Whether the result of last action(RADIO_RESTART) reported. */
private boolean mLastActionReported;
/** The real time for data stall start. */
- private @ElapsedRealtimeLong long mDataStallStartMs;
+ @VisibleForTesting
+ public @ElapsedRealtimeLong long mDataStallStartMs;
/** Last data stall recovery action. */
private @RecoveryAction int mLastAction;
/** Last radio power state. */
@@ -166,12 +169,16 @@
private boolean mNetworkCheckTimerStarted = false;
/** Whether radio state changed during data stall. */
private boolean mRadioStateChangedDuringDataStall;
+ /** Whether airplane mode enabled during data stall. */
+ private boolean mIsAirPlaneModeEnableDuringDataStall;
/** Whether mobile data change to Enabled during data stall. */
private boolean mMobileDataChangedToEnabledDuringDataStall;
/** Whether attempted all recovery steps. */
private boolean mIsAttemptedAllSteps;
/** Whether internet network connected. */
private boolean mIsInternetNetworkConnected;
+ /** The durations for current recovery action */
+ private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;
/** The array for the timers between recovery actions. */
private @NonNull long[] mDataStallRecoveryDelayMillisArray;
@@ -245,7 +252,12 @@
/** Register for all events that data stall monitor is interested. */
private void registerAllEvents() {
- mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
+ mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
+ @Override
+ public void onCarrierConfigChanged() {
+ DataStallRecoveryManager.this.onCarrierConfigUpdated();
+ }
+ });
mDataNetworkController.registerDataNetworkControllerCallback(
new DataNetworkControllerCallback(this::post) {
@Override
@@ -256,7 +268,7 @@
@Override
public void onInternetDataNetworkConnected(
- @NonNull List<DataProfile> dataProfiles) {
+ @NonNull List<DataNetwork> internetNetworks) {
mIsInternetNetworkConnected = true;
logl("onInternetDataNetworkConnected");
}
@@ -274,9 +286,6 @@
public void handleMessage(Message msg) {
logv("handleMessage = " + msg);
switch (msg.what) {
- case EVENT_DATA_CONFIG_UPDATED:
- onDataConfigUpdated();
- break;
case EVENT_DO_RECOVERY:
doRecovery();
break;
@@ -285,6 +294,12 @@
if (mDataStalled) {
// Store the radio state changed flag only when data stall occurred.
mRadioStateChangedDuringDataStall = true;
+ if (Settings.Global.getInt(
+ mPhone.getContext().getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON,
+ 0) != 0) {
+ mIsAirPlaneModeEnableDuringDataStall = true;
+ }
}
break;
default:
@@ -319,8 +334,8 @@
return mSkipRecoveryActionArray[recoveryAction];
}
- /** Called when data config was updated. */
- private void onDataConfigUpdated() {
+ /** Called when carrier config was updated. */
+ private void onCarrierConfigUpdated() {
updateDataStallRecoveryConfigs();
}
@@ -343,19 +358,21 @@
*/
private void reset() {
mIsValidNetwork = true;
+ mRecoveryTriggered = false;
mIsAttemptedAllSteps = false;
mRadioStateChangedDuringDataStall = false;
+ mIsAirPlaneModeEnableDuringDataStall = false;
mMobileDataChangedToEnabledDuringDataStall = false;
cancelNetworkCheckTimer();
mTimeLastRecoveryStartMs = 0;
mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
- mRecovryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
+ mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
}
/**
* Called when internet validation status changed.
*
- * @param validationStatus Validation status.
+ * @param status Validation status.
*/
private void onInternetValidationStatusChanged(@ValidationStatus int status) {
logl("onInternetValidationStatusChanged: " + DataUtils.validationStatusToString(status));
@@ -363,15 +380,12 @@
setNetworkValidationState(isValid);
if (isValid) {
reset();
- } else {
- if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
- mIsValidNetwork = false;
- if (isRecoveryNeeded(true)) {
- log("trigger data stall recovery");
- mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
- sendMessage(obtainMessage(EVENT_DO_RECOVERY));
- }
- }
+ } else if (isRecoveryNeeded(true)) {
+ // Set the network as invalid, because recovery is needed
+ mIsValidNetwork = false;
+ log("trigger data stall recovery");
+ mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
+ sendMessage(obtainMessage(EVENT_DO_RECOVERY));
}
}
@@ -380,6 +394,7 @@
mTimeLastRecoveryStartMs = 0;
mMobileDataChangedToEnabledDuringDataStall = false;
mRadioStateChangedDuringDataStall = false;
+ mIsAirPlaneModeEnableDuringDataStall = false;
setRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
}
@@ -391,8 +406,8 @@
@VisibleForTesting
@RecoveryAction
public int getRecoveryAction() {
- log("getRecoveryAction: " + recoveryActionToString(mRecovryAction));
- return mRecovryAction;
+ log("getRecoveryAction: " + recoveryActionToString(mRecoveryAction));
+ return mRecoveryAction;
}
/**
@@ -402,24 +417,24 @@
*/
@VisibleForTesting
public void setRecoveryAction(@RecoveryAction int action) {
- mRecovryAction = action;
+ mRecoveryAction = action;
// Check if the mobile data enabled is TRUE, it means that the mobile data setting changed
// from DISABLED to ENABLED, we will set the next recovery action to
// RECOVERY_ACTION_RADIO_RESTART due to already did the RECOVERY_ACTION_CLEANUP.
if (mMobileDataChangedToEnabledDuringDataStall
- && mRecovryAction < RECOVERY_ACTION_RADIO_RESTART) {
- mRecovryAction = RECOVERY_ACTION_RADIO_RESTART;
+ && mRecoveryAction < RECOVERY_ACTION_RADIO_RESTART) {
+ mRecoveryAction = RECOVERY_ACTION_RADIO_RESTART;
}
// Check if the radio state changed from off to on, it means that the modem already
// did the radio restart, we will set the next action to RECOVERY_ACTION_RESET_MODEM.
if (mRadioStateChangedDuringDataStall
&& mRadioPowerState == TelephonyManager.RADIO_POWER_ON) {
- mRecovryAction = RECOVERY_ACTION_RESET_MODEM;
+ mRecoveryAction = RECOVERY_ACTION_RESET_MODEM;
}
// To check the flag from DataConfigManager if we need to skip the step.
- if (shouldSkipRecoveryAction(mRecovryAction)) {
- switch (mRecovryAction) {
+ if (shouldSkipRecoveryAction(mRecoveryAction)) {
+ switch (mRecoveryAction) {
case RECOVERY_ACTION_GET_DATA_CALL_LIST:
setRecoveryAction(RECOVERY_ACTION_CLEANUP);
break;
@@ -435,7 +450,7 @@
}
}
- log("setRecoveryAction: " + recoveryActionToString(mRecovryAction));
+ log("setRecoveryAction: " + recoveryActionToString(mRecoveryAction));
}
/**
@@ -444,7 +459,7 @@
* @return {@code true} if recovery already started, {@code false} recovery not started.
*/
private boolean isRecoveryAlreadyStarted() {
- return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST;
+ return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST || mRecoveryTriggered;
}
/**
@@ -457,6 +472,15 @@
}
/**
+ * Get duration time for current recovery action.
+ *
+ * @return the time duration for current recovery action.
+ */
+ private long getDurationOfCurrentRecoveryMs() {
+ return (SystemClock.elapsedRealtime() - mTimeElapsedOfCurrentAction);
+ }
+
+ /**
* Broadcast intent when data stall occurred.
*
* @param recoveryAction Send the data stall detected intent with RecoveryAction info.
@@ -466,7 +490,7 @@
Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
- mPhone.getContext().sendBroadcast(intent);
+ mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
}
/** Recovery Action: RECOVERY_ACTION_GET_DATA_CALL_LIST */
@@ -531,6 +555,12 @@
private boolean isRecoveryNeeded(boolean isNeedToCheckTimer) {
logv("enter: isRecoveryNeeded()");
+ // Skip if network is invalid and recovery was not started yet
+ if (!mIsValidNetwork && !isRecoveryAlreadyStarted()) {
+ logl("skip when network still remains invalid and recovery was not started yet");
+ return false;
+ }
+
// Skip recovery if we have already attempted all steps.
if (mIsAttemptedAllSteps) {
logl("skip retrying continue recovery action");
@@ -566,7 +596,6 @@
logl("skip data stall recovery as data not connected");
return false;
}
-
return true;
}
@@ -576,39 +605,65 @@
* @param isValid true for validation passed & false for validation failed
*/
private void setNetworkValidationState(boolean isValid) {
+ boolean isLogNeeded = false;
+ int timeDuration = 0;
+ int timeDurationOfCurrentAction = 0;
+ boolean isFirstDataStall = false;
+ boolean isFirstValidationAfterDoRecovery = false;
+ @RecoveredReason int reason = getRecoveredReason(isValid);
// Validation status is true and was not data stall.
if (isValid && !mDataStalled) {
return;
}
if (!mDataStalled) {
+ // First data stall
+ isLogNeeded = true;
mDataStalled = true;
+ isFirstDataStall = true;
mDataStallStartMs = SystemClock.elapsedRealtime();
logl("data stall: start time = " + DataUtils.elapsedTimeToString(mDataStallStartMs));
- return;
- }
-
- if (!mLastActionReported) {
- @RecoveredReason int reason = getRecoveredReason(isValid);
- int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
- logl(
- "data stall: lastaction = "
- + recoveryActionToString(mLastAction)
- + ", isRecovered = "
- + isValid
- + ", reason = "
- + recoveredReasonToString(reason)
- + ", TimeDuration = "
- + timeDuration);
- DataStallRecoveryStats.onDataStallEvent(
- mLastAction, mPhone, isValid, timeDuration, reason);
+ } else if (!mLastActionReported) {
+ // When the first validation status appears, enter this block.
+ isLogNeeded = true;
+ timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
mLastActionReported = true;
+ isFirstValidationAfterDoRecovery = true;
}
if (isValid) {
+ // When the validation passed(mobile data resume), enter this block.
+ isLogNeeded = true;
+ timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
mLastActionReported = false;
mDataStalled = false;
}
+
+ if (isLogNeeded) {
+ timeDurationOfCurrentAction =
+ ((getRecoveryAction() > RECOVERY_ACTION_GET_DATA_CALL_LIST
+ && !mIsAttemptedAllSteps)
+ || mLastAction == RECOVERY_ACTION_RESET_MODEM)
+ ? (int) getDurationOfCurrentRecoveryMs() : 0;
+ DataStallRecoveryStats.onDataStallEvent(
+ mLastAction, mPhone, isValid, timeDuration, reason,
+ isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction);
+ logl(
+ "data stall: "
+ + (isFirstDataStall == true ? "start" : isValid == false ? "in process" : "end")
+ + ", lastaction="
+ + recoveryActionToString(mLastAction)
+ + ", isRecovered="
+ + isValid
+ + ", reason="
+ + recoveredReasonToString(reason)
+ + ", isFirstValidationAfterDoRecovery="
+ + isFirstValidationAfterDoRecovery
+ + ", TimeDuration="
+ + timeDuration
+ + ", TimeDurationForCurrentRecoveryAction="
+ + timeDurationOfCurrentAction);
+ }
}
/**
@@ -628,6 +683,9 @@
if (mLastAction > RECOVERY_ACTION_CLEANUP) {
ret = RECOVERED_REASON_DSRM;
}
+ if (mIsAirPlaneModeEnableDuringDataStall) {
+ ret = RECOVERED_REASON_USER;
+ }
} else if (mMobileDataChangedToEnabledDuringDataStall) {
ret = RECOVERED_REASON_USER;
}
@@ -638,6 +696,7 @@
private void doRecovery() {
@RecoveryAction final int recoveryAction = getRecoveryAction();
final int signalStrength = mPhone.getSignalStrength().getLevel();
+ mRecoveryTriggered = true;
// DSRM used sendMessageDelayed to process the next event EVENT_DO_RECOVERY, so it need
// to check the condition if DSRM need to process the recovery action.
@@ -654,6 +713,7 @@
mLastActionReported = false;
broadcastDataStallDetected(recoveryAction);
mNetworkCheckTimerStarted = false;
+ mTimeElapsedOfCurrentAction = SystemClock.elapsedRealtime();
switch (recoveryAction) {
case RECOVERY_ACTION_GET_DATA_CALL_LIST:
@@ -799,6 +859,7 @@
pw.println("mIsValidNetwork=" + mIsValidNetwork);
pw.println("mIsInternetNetworkConnected=" + mIsInternetNetworkConnected);
+ pw.println("mIsAirPlaneModeEnableDuringDataStall=" + mIsAirPlaneModeEnableDuringDataStall);
pw.println("mDataStalled=" + mDataStalled);
pw.println("mLastAction=" + recoveryActionToString(mLastAction));
pw.println("mIsAttemptedAllSteps=" + mIsAttemptedAllSteps);
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 0bfddc2..fc0c4f1 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -41,6 +41,7 @@
import android.util.ArrayMap;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
+import com.android.telephony.Rlog;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -61,6 +62,7 @@
/** The time format for converting time to readable string. */
private static final SimpleDateFormat TIME_FORMAT =
new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);
+ private static final String TAG = "DataUtils";
/**
* Get the network capability from the string.
@@ -164,6 +166,7 @@
case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH:
return "PRIORITIZE_BANDWIDTH";
default:
+ loge("Unknown network capability(" + netCap + ")");
return "Unknown(" + netCap + ")";
}
}
@@ -209,7 +212,9 @@
switch (status) {
case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID";
case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID";
- default: return "UNKNOWN(" + status + ")";
+ default:
+ loge("Unknown validation status(" + status + ")");
+ return "UNKNOWN(" + status + ")";
}
}
@@ -366,6 +371,7 @@
case ImsFeature.FEATURE_MMTEL: return "MMTEL";
case ImsFeature.FEATURE_RCS: return "RCS";
default:
+ loge("Unknown IMS feature(" + imsFeature + ")");
return "Unknown(" + imsFeature + ")";
}
}
@@ -404,9 +410,27 @@
.boxed().collect(Collectors.toSet()),
v -> new NetworkRequestList()).add(networkRequest);
}
- // Sort the list, so the network request list contains higher priority will be in the front
- // of the list.
- return new ArrayList<>(requestsMap.values()).stream()
+ List<NetworkRequestList> requests = new ArrayList<>();
+ // Create separate groups for enterprise requests with different enterprise IDs.
+ for (NetworkRequestList requestList : requestsMap.values()) {
+ List<TelephonyNetworkRequest> enterpriseRequests = requestList.stream()
+ .filter(request ->
+ request.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE))
+ .collect(Collectors.toList());
+ if (enterpriseRequests.isEmpty()) {
+ requests.add(requestList);
+ continue;
+ }
+ // Key is the enterprise ID
+ Map<Integer, NetworkRequestList> enterpriseRequestsMap = new ArrayMap<>();
+ for (TelephonyNetworkRequest request : enterpriseRequests) {
+ enterpriseRequestsMap.computeIfAbsent(request.getCapabilityDifferentiator(),
+ v -> new NetworkRequestList()).add(request);
+ }
+ requests.addAll(enterpriseRequestsMap.values());
+ }
+ // Sort the requests so the network request list with higher priority will be at the front.
+ return requests.stream()
.sorted((list1, list2) -> Integer.compare(
list2.get(0).getPriority(), list1.get(0).getPriority()))
.collect(Collectors.toList());
@@ -450,7 +474,9 @@
case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE";
case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE";
case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT";
- default: return "UNKNOWN(" + linkStatus + ")";
+ default:
+ loge("Unknown link status(" + linkStatus + ")");
+ return "UNKNOWN(" + linkStatus + ")";
}
}
@@ -487,7 +513,13 @@
case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT";
case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT";
case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT";
- default: return "UNKNOWN(" + dataActivity + ")";
+ default:
+ loge("Unknown data activity(" + dataActivity + ")");
+ return "UNKNOWN(" + dataActivity + ")";
}
}
+
+ private static void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
}
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
index 919bbc3..5ed12aa 100644
--- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
+++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -32,8 +32,6 @@
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.OutcomeReceiver;
-import android.os.Registrant;
-import android.os.RegistrantList;
import android.preference.PreferenceManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.DataActivityType;
@@ -43,12 +41,14 @@
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
@@ -97,6 +97,9 @@
@VisibleForTesting
static final int MSG_DATA_REG_STATE_OR_RAT_CHANGED = 9;
+ @VisibleForTesting
+ static final int UNKNOWN_TAC = CellInfo.UNAVAILABLE;
+
// TODO: move the following parameters to xml file
private static final int TRAFFIC_STATS_POLL_INTERVAL_MS = 1_000;
private static final int MODEM_POLL_MIN_INTERVAL_MS = 5_000;
@@ -180,7 +183,7 @@
private int mSignalLevel;
private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
private int mTac;
- private String mPlmn = UNKNOWN_PLMN;
+ @NonNull private String mPlmn = UNKNOWN_PLMN;
private NetworkCapabilities mNetworkCapabilities;
private NetworkBandwidth mPlaceholderNetwork;
private long mFilterUpdateTimeMs;
@@ -191,7 +194,6 @@
private String mBandwidthUpdatePlmn = UNKNOWN_PLMN;
private BandwidthState mTxState = new BandwidthState(LINK_TX);
private BandwidthState mRxState = new BandwidthState(LINK_RX);
- private RegistrantList mBandwidthChangedRegistrants = new RegistrantList();
private long mLastPlmnOrRatChangeTimeMs;
private long mLastDrsOrRatChangeTimeMs;
@@ -351,33 +353,6 @@
}
/**
- * Registers for bandwidth estimation change. The bandwidth will be returned
- * * {@link AsyncResult#result} as a {@link Pair} Object.
- * * The {@link AsyncResult} will be in the notification {@link Message#obj}.
- * @param h handler to notify
- * @param what what code of message when delivered
- * @param obj placed in Message.obj
- *
- * @deprecated Use {@link #registerCallback(LinkBandwidthEstimatorCallback)}.
- */
- @Deprecated //TODO: Remove once old data stack is removed.
- public void registerForBandwidthChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mBandwidthChangedRegistrants.add(r);
- }
-
- /**
- * Unregisters for bandwidth estimation change.
- * @param h handler to notify
- *
- * @deprecated Use {@link #unregisterCallback(LinkBandwidthEstimatorCallback)}.
- */
- @Deprecated //TODO: Remove once old data stack is removed.
- public void unregisterForBandwidthChanged(Handler h) {
- mBandwidthChangedRegistrants.remove(h);
- }
-
- /**
* Register the callback for receiving information from {@link LinkBandwidthEstimator}.
*
* @param callback The callback.
@@ -441,6 +416,7 @@
return;
}
mIsOnDefaultRoute = isOnDefaultRoute;
+ logd("mIsOnDefaultRoute " + mIsOnDefaultRoute);
handleTrafficStatsPollConditionChanged();
}
@@ -465,6 +441,13 @@
if (mScreenOn && mIsOnDefaultRoute && mIsOnActiveData) {
updateDataRatCellIdentityBandwidth();
handleTrafficStatsPoll();
+ } else {
+ logd("Traffic status poll stopped");
+ if (mDataActivity != TelephonyManager.DATA_ACTIVITY_NONE) {
+ mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+ mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ () -> callback.onDataActivityChanged(mDataActivity)));
+ }
}
}
@@ -475,6 +458,23 @@
long txBytesDelta = mobileTxBytes - mLastMobileTxBytes;
long rxBytesDelta = mobileRxBytes - mLastMobileRxBytes;
+ int dataActivity;
+ if (txBytesDelta > 0 && rxBytesDelta > 0) {
+ dataActivity = TelephonyManager.DATA_ACTIVITY_INOUT;
+ } else if (rxBytesDelta > 0) {
+ dataActivity = TelephonyManager.DATA_ACTIVITY_IN;
+ } else if (txBytesDelta > 0) {
+ dataActivity = TelephonyManager.DATA_ACTIVITY_OUT;
+ } else {
+ dataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+ }
+
+ if (mDataActivity != dataActivity) {
+ mDataActivity = dataActivity;
+ mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ () -> callback.onDataActivityChanged(dataActivity)));
+ }
+
// Schedule the next traffic stats poll
sendEmptyMessageDelayed(MSG_TRAFFIC_STATS_POLL, TRAFFIC_STATS_POLL_INTERVAL_MS);
@@ -523,23 +523,6 @@
return;
}
- int dataActivity;
- if (txBytesDelta > 0 && rxBytesDelta > 0) {
- dataActivity = TelephonyManager.DATA_ACTIVITY_INOUT;
- } else if (rxBytesDelta > 0) {
- dataActivity = TelephonyManager.DATA_ACTIVITY_IN;
- } else if (txBytesDelta > 0) {
- dataActivity = TelephonyManager.DATA_ACTIVITY_OUT;
- } else {
- dataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- }
-
- if (mDataActivity != dataActivity) {
- mDataActivity = dataActivity;
- mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor(
- () -> callback.onDataActivityChanged(dataActivity)));
- }
-
long timeSinceLastFilterUpdateMs = currTimeMs - mFilterUpdateTimeMs;
// Update filter
if (timeSinceLastFilterUpdateMs >= FILTER_UPDATE_MAX_INTERVAL_MS) {
@@ -695,7 +678,7 @@
return;
}
int linkBandwidthKbps = (int) linkBandwidthLongKbps;
- mBwSampleValid = true;
+ mBwSampleValid = linkBandwidthKbps > 0;
mBwSampleKbps = linkBandwidthKbps;
String dataRatName = getDataRatName(mDataRat);
@@ -715,7 +698,7 @@
int filterInKbps = mBwSampleValid ? mBwSampleKbps : avgKbps;
long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
- int timeDeltaSec = (int) (currTimeMs - mBwSampleValidTimeMs) / 1000;
+ int timeDeltaSec = (int) ((currTimeMs - mBwSampleValidTimeMs) / 1000);
// If the operation condition changes significantly since the last update
// or the sample has higher BW, use a faster filter. Otherwise, use a slow filter
@@ -930,9 +913,6 @@
private void sendLinkBandwidthToDataConnection(int linkBandwidthTxKps, int linkBandwidthRxKps) {
logv("send to DC tx " + linkBandwidthTxKps + " rx " + linkBandwidthRxKps);
- Pair<Integer, Integer> bandwidthInfo =
- new Pair<Integer, Integer>(linkBandwidthTxKps, linkBandwidthRxKps);
- mBandwidthChangedRegistrants.notifyRegistrants(new AsyncResult(null, bandwidthInfo, null));
mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onBandwidthChanged(linkBandwidthTxKps, linkBandwidthRxKps)));
}
@@ -1054,22 +1034,28 @@
}
private boolean updateDataRatCellIdentityBandwidth() {
- boolean updatedPlmn = false;
- CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
- mTac = getTac(cellIdentity);
+ final ServiceState ss = mPhone.getServiceState();
+ final CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+
+ boolean hasChanged = false;
String plmn;
- if (mPhone.getServiceState().getOperatorNumeric() != null) {
- plmn = mPhone.getServiceState().getOperatorNumeric();
+ // Why does updating the TAC not result in hasChanged == true?
+ // Legacy behavior is currently being preserved for now, but it might very
+ // well be incorrect. The TAC is part of the network key. This is very fishy.
+ mTac = getTac(cellIdentity);
+
+ /* ss should always be non-null */
+ if (!TextUtils.isEmpty(ss.getOperatorNumeric())) {
+ plmn = ss.getOperatorNumeric();
+ } else if (cellIdentity != null && !TextUtils.isEmpty(cellIdentity.getPlmn())) {
+ plmn = cellIdentity.getPlmn();
} else {
- if (cellIdentity.getPlmn() != null) {
- plmn = cellIdentity.getPlmn();
- } else {
- plmn = UNKNOWN_PLMN;
- }
+ plmn = UNKNOWN_PLMN;
}
- if (mPlmn == null || !plmn.equals(mPlmn)) {
- updatedPlmn = true;
+
+ if (!mPlmn.equals(plmn)) {
+ hasChanged = true;
mPlmn = plmn;
}
@@ -1078,20 +1064,19 @@
if (nri != null) {
int dataRat = nri.getAccessNetworkTechnology();
if (dataRat != mDataRat) {
- updatedRat = true;
+ hasChanged = true;
mDataRat = dataRat;
updateStaticBwValue(mDataRat);
updateByteCountThr();
}
}
- boolean updatedPlmnOrRat = updatedPlmn || updatedRat;
- if (updatedPlmnOrRat) {
+ if (hasChanged) {
resetBandwidthFilter();
updateTxRxBandwidthFilterSendToDataConnection();
mLastPlmnOrRatChangeTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
}
- return updatedPlmnOrRat;
+ return hasChanged;
}
private int getTac(@NonNull CellIdentity cellIdentity) {
@@ -1110,7 +1095,7 @@
if (cellIdentity instanceof CellIdentityGsm) {
return ((CellIdentityGsm) cellIdentity).getLac();
}
- return 0;
+ return UNKNOWN_TAC;
}
private class TelephonyCallbackImpl extends TelephonyCallback implements
@@ -1135,16 +1120,17 @@
mLocalLog.log(msg);
}
- @VisibleForTesting
- static final int UNKNOWN_TAC = -1;
// Map with NetworkKey as the key and NetworkBandwidth as the value.
// NetworkKey is specified by the PLMN, data RAT and TAC of network.
// NetworkBandwidth represents the bandwidth related stats of each network.
private final Map<NetworkKey, NetworkBandwidth> mNetworkMap = new ArrayMap<>();
+
private static class NetworkKey {
+
private final String mPlmn;
private final String mDataRat;
private final int mTac;
+
NetworkKey(String plmn, int tac, String dataRat) {
mPlmn = plmn;
mTac = tac;
@@ -1175,22 +1161,21 @@
StringBuilder sb = new StringBuilder();
sb.append("Plmn").append(mPlmn)
.append("Rat").append(mDataRat)
- .append("Tac").append(mTac)
- .toString();
+ .append("Tac").append(mTac);
return sb.toString();
}
}
@NonNull
- private NetworkBandwidth lookupNetwork(String plmn, String dataRat) {
+ private NetworkBandwidth lookupNetwork(@NonNull String plmn, String dataRat) {
return lookupNetwork(plmn, UNKNOWN_TAC, dataRat);
}
/** Look up NetworkBandwidth and create a new one if it doesn't exist */
@VisibleForTesting
@NonNull
- public NetworkBandwidth lookupNetwork(String plmn, int tac, String dataRat) {
- if (plmn == null || dataRat.equals(
+ public NetworkBandwidth lookupNetwork(@NonNull String plmn, int tac, String dataRat) {
+ if (dataRat.equals(
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_UNKNOWN))) {
return mPlaceholderNetwork;
}
@@ -1206,7 +1191,9 @@
/** A class holding link bandwidth related stats */
@VisibleForTesting
public class NetworkBandwidth {
+
private final String mKey;
+
NetworkBandwidth(String key) {
mKey = key;
}
@@ -1302,5 +1289,4 @@
pw.println();
pw.flush();
}
-
}
diff --git a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java b/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java
deleted file mode 100644
index 554177a..0000000
--- a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import android.annotation.NonNull;
-import android.net.NetworkAgent;
-import android.net.QosSessionAttributes;
-
-/**
- * The temporary interface that is shared by
- * {@link com.android.internal.telephony.dataconnection.DcNetworkAgent} and
- * {@link com.android.internal.telephony.data.TelephonyNetworkAgent} so they can both interact
- * with {@link QosCallbackTracker}.
- */
-// TODO: Remove after DcNetworkAgent is removed.
-public interface NotifyQosSessionInterface {
- /**
- * Sends the attributes of Qos Session back to the Application. This method is create for
- * Mockito to mock since
- * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is
- * {@code final} that can't be mocked.
- *
- * @param qosCallbackId the callback id that the session belongs to.
- * @param sessionId the unique session id across all Qos Sessions.
- * @param attributes the attributes of the Qos Session.
- */
- void notifyQosSessionAvailable(int qosCallbackId, int sessionId,
- @NonNull QosSessionAttributes attributes);
-
- /**
- * Sends event that the Qos Session was lost. This method is create for Mockito to mock
- * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be
- * mocked..
- *
- * @param qosCallbackId the callback id that the session belongs to.
- * @param sessionId the unique session id across all Qos Sessions.
- * @param qosSessionType the session type {@code QosSession#QosSessionType}.
- */
- void notifyQosSessionLost(int qosCallbackId, int sessionId, int qosSessionType);
-}
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index cc5f447..5e13f6b 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -31,6 +31,10 @@
import static java.util.Arrays.copyOf;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -46,6 +50,7 @@
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -53,20 +58,23 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
-import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArrayMap;
import android.util.LocalLog;
+import android.util.Log;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
@@ -78,17 +86,17 @@
import com.android.internal.telephony.PhoneConfigurationManager;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RadioConfig;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.SubscriptionController.WatchedInt;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
-import com.android.internal.telephony.dataconnection.ApnConfigTypeRepository;
-import com.android.internal.telephony.dataconnection.DcRequest;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.WatchedInt;
+import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -96,7 +104,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -112,9 +119,23 @@
*/
public class PhoneSwitcher extends Handler {
private static final String LOG_TAG = "PhoneSwitcher";
- protected static final boolean VDBG = false;
+ protected static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
- private static final int DEFAULT_NETWORK_CHANGE_TIMEOUT_MS = 5000;
+ /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */
+ private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ /**
+ * When starting this activity, this extra can also be specified to supply a Bundle of arguments
+ * to pass to that fragment when it is instantiated during the initial creation of the activity.
+ */
+ private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
+ ":settings:show_fragment_args";
+ /** The res Id of the auto data switch fragment in settings. **/
+ private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
+ /** Notification tag **/
+ private static final String AUTO_DATA_SWITCH_NOTIFICATION_TAG = "auto_data_switch";
+ /** Notification ID **/
+ private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1;
+
private static final int MODEM_COMMAND_RETRY_PERIOD_MS = 5000;
// After the emergency call ends, wait for a few seconds to see if we enter ECBM before starting
// the countdown to remove the emergency DDS override.
@@ -182,10 +203,9 @@
}
}
- protected final List<DcRequest> mPrioritizedDcRequests = new ArrayList<>();
private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList();
protected final RegistrantList mActivePhoneRegistrants;
- protected final SubscriptionController mSubscriptionController;
+ private final SubscriptionManagerService mSubscriptionManagerService;
protected final Context mContext;
private final LocalLog mLocalLog;
protected PhoneState[] mPhoneStates;
@@ -194,6 +214,10 @@
@VisibleForTesting
protected final CellularNetworkValidator mValidator;
private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+ /** The reason for the last time changing preferred data sub **/
+ private int mLastSwitchPreferredDataReason = -1;
+ /** {@code true} if we've displayed the notification the first time auto switch occurs **/
+ private boolean mDisplayedAutoSwitchNotification = false;
private boolean mPendingSwitchNeedValidation;
@VisibleForTesting
public final CellularNetworkValidator.ValidationCallback mValidationCallback =
@@ -226,9 +250,15 @@
// Internet data if mOpptDataSubId is not set.
protected int mPrimaryDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- // mOpptDataSubId must be an active subscription. If it's set, it overrides mPrimaryDataSubId
- // to be used for Internet data.
- private int mOpptDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ // The automatically suggested preferred data subId (by e.g. CBRS or auto data switch), a
+ // candidate for preferred data subId, which is eventually presided by
+ // updatePreferredDataPhoneId().
+ // If CBRS/auto switch feature selects the primary data subId as the preferred data subId,
+ // its value will be DEFAULT_SUBSCRIPTION_ID.
+ private int mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+
+ /** The count of consecutive auto switch validation failure **/
+ private int mAutoSwitchRetryFailedCount = 0;
// The phone ID that has an active voice call. If set, and its mobile data setting is on,
// it will become the mPreferredDataPhoneId.
@@ -244,14 +274,8 @@
protected int mPreferredDataPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
// Subscription ID corresponds to mPreferredDataPhoneId.
- protected WatchedInt mPreferredDataSubId =
- new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- @Override
- public void set(int newValue) {
- super.set(newValue);
- SubscriptionController.invalidateActiveDataSubIdCaches();
- }
- };
+ protected WatchedInt mPreferredDataSubId = new WatchedInt(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
// If non-null, An emergency call is about to be started, is ongoing, or has just ended and we
// are overriding the DDS.
@@ -261,7 +285,7 @@
private ISetOpportunisticDataCallback mSetOpptSubCallback;
private static final int EVENT_PRIMARY_DATA_SUB_CHANGED = 101;
- protected static final int EVENT_SUBSCRIPTION_CHANGED = 102;
+ protected static final int EVENT_SUBSCRIPTION_CHANGED = 102;
private static final int EVENT_REQUEST_NETWORK = 103;
private static final int EVENT_RELEASE_NETWORK = 104;
// ECBM has started/ended. If we just ended an emergency call and mEmergencyOverride is not
@@ -276,14 +300,12 @@
// mEmergencyOverride, start the countdown to remove the override using the message
// EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE. The only exception to this is if the device moves to
// ECBM, which is detected by EVENT_EMERGENCY_TOGGLE.
- @VisibleForTesting
- public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109;
+ private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109;
private static final int EVENT_NETWORK_VALIDATION_DONE = 110;
- private static final int EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK = 111;
+ private static final int EVENT_EVALUATE_AUTO_SWITCH = 111;
private static final int EVENT_MODEM_COMMAND_DONE = 112;
private static final int EVENT_MODEM_COMMAND_RETRY = 113;
- @VisibleForTesting
- public static final int EVENT_DATA_ENABLED_CHANGED = 114;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 114;
// An emergency call is about to be originated and requires the DDS to be overridden.
// Uses EVENT_PRECISE_CALL_STATE_CHANGED message to start countdown to finish override defined
// in mEmergencyOverride. If EVENT_PRECISE_CALL_STATE_CHANGED does not come in
@@ -292,12 +314,11 @@
// If it exists, remove the current mEmergencyOverride DDS override.
private static final int EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE = 116;
// If it exists, remove the current mEmergencyOverride DDS override.
- @VisibleForTesting
- public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117;
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117;
private static final int EVENT_NETWORK_AVAILABLE = 118;
private static final int EVENT_PROCESS_SIM_STATE_CHANGE = 119;
- @VisibleForTesting
- public static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
+ private static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
+ private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 121;
// List of events triggers re-evaluations
private static final String EVALUATION_REASON_RADIO_ON = "EVENT_RADIO_ON";
@@ -317,13 +338,29 @@
// Default timeout value of network validation in millisecond.
private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
- private Boolean mHasRegisteredDefaultNetworkChangeCallback = false;
-
private ConnectivityManager mConnectivityManager;
private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
+ /**
+ * {@code true} if requires ping test before switching preferred data modem; otherwise, switch
+ * even if ping test fails.
+ */
+ private boolean mRequirePingTestBeforeDataSwitch = true;
+
+ /**
+ * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
+ * in service, wifi is the default active network.etc), while -1 indicates auto switch
+ * feature disabled.
+ */
+ private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
+
+ /**
+ * The maximum number of retries when a validation for switching failed.
+ */
+ private int mAutoDataSwitchValidationMaxRetry;
+
/** Data settings manager callback. Key is the phone id. */
private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
new ArrayMap<>();
@@ -331,18 +368,37 @@
private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
+ public boolean isDefaultNetworkOnCellular = false;
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
- if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
- && SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
- && mExpectedSubId == getSubIdFromNetworkSpecifier(
- networkCapabilities.getNetworkSpecifier())) {
- logDataSwitchEvent(
- mExpectedSubId,
- TelephonyEvent.EventState.EVENT_STATE_END,
- mSwitchReason);
- removeDefaultNetworkChangeCallback();
+ if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ isDefaultNetworkOnCellular = true;
+ if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
+ && mExpectedSubId == getSubIdFromNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier())) {
+ logDataSwitchEvent(
+ mExpectedSubId,
+ TelephonyEvent.EventState.EVENT_STATE_END,
+ mSwitchReason);
+ mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
+ }
+ } else {
+ if (isDefaultNetworkOnCellular) {
+ // non-cellular transport is active
+ isDefaultNetworkOnCellular = false;
+ log("default network is active on non cellular");
+ evaluateIfAutoSwitchIsNeeded();
+ }
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ // try find an active sub to switch to
+ if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+ sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
}
}
}
@@ -395,49 +451,26 @@
public static PhoneSwitcher make(int maxDataAttachModemCount, Context context, Looper looper) {
if (sPhoneSwitcher == null) {
sPhoneSwitcher = new PhoneSwitcher(maxDataAttachModemCount, context, looper);
- SubscriptionController.invalidateActiveDataSubIdCaches();
}
return sPhoneSwitcher;
}
- /**
- * Whether this phone IMS registration is on its original network. This result impacts
- * whether we want to do DDS switch to the phone having voice call.
- * If it's registered on IWLAN or cross SIM in multi-SIM case, return false. Otherwise,
- * return true.
- */
- private boolean isImsOnOriginalNetwork(Phone phone) {
- if (phone == null) return false;
- int phoneId = phone.getPhoneId();
- if (!SubscriptionManager.isValidPhoneId(phoneId)) return false;
-
- int imsRegTech = mImsRegTechProvider.get(mContext, phoneId);
- // If IMS is registered on IWLAN or cross SIM, return false.
- boolean isOnOriginalNetwork = (imsRegTech != REGISTRATION_TECH_IWLAN)
- && (imsRegTech != REGISTRATION_TECH_CROSS_SIM);
- if (!isOnOriginalNetwork) {
- log("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch");
- }
- return isOnOriginalNetwork;
- }
-
- private boolean isPhoneInVoiceCallChanged() {
+ private boolean updatesIfPhoneInVoiceCallChanged() {
int oldPhoneIdInVoiceCall = mPhoneIdInVoiceCall;
// If there's no active call, the value will become INVALID_PHONE_INDEX
// and internet data will be switched back to system selected or user selected
// subscription.
mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX;
for (Phone phone : PhoneFactory.getPhones()) {
- if (isPhoneInVoiceCall(phone) || (isPhoneInVoiceCall(phone.getImsPhone())
- && isImsOnOriginalNetwork(phone))) {
+ if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) {
mPhoneIdInVoiceCall = phone.getPhoneId();
break;
}
}
if (mPhoneIdInVoiceCall != oldPhoneIdInVoiceCall) {
- log("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall
+ logl("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall
+ " to phoneId " + mPhoneIdInVoiceCall);
return true;
} else {
@@ -464,13 +497,13 @@
}
}
- private void evaluateIfDataSwitchIsNeeded(String reason) {
- if (onEvaluate(REQUESTS_UNCHANGED, reason)) {
+ private void evaluateIfImmediateDataSwitchIsNeeded(String evaluationReason, int switchReason) {
+ if (onEvaluate(REQUESTS_UNCHANGED, evaluationReason)) {
logDataSwitchEvent(mPreferredDataSubId.get(),
TelephonyEvent.EventState.EVENT_STATE_START,
- DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ switchReason);
registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
- DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ switchReason);
}
}
@@ -484,7 +517,8 @@
mMaxDataAttachModemCount = maxActivePhones;
mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES);
- mSubscriptionController = SubscriptionController.getInstance();
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+
mRadioConfig = RadioConfig.getInstance();
mValidator = CellularNetworkValidator.getInstance();
@@ -494,9 +528,9 @@
mContext.registerReceiver(mSimStateIntentReceiver, filter);
mActivePhoneRegistrants = new RegistrantList();
- for (int i = 0; i < mActiveModemCount; i++) {
- mPhoneStates[i] = new PhoneState();
- Phone phone = PhoneFactory.getPhone(i);
+ for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
+ mPhoneStates[phoneId] = new PhoneState();
+ Phone phone = PhoneFactory.getPhone(phoneId);
if (phone != null) {
phone.registerForEmergencyCallToggle(
this, EVENT_EMERGENCY_TOGGLE, null);
@@ -507,23 +541,19 @@
phone.getImsPhone().registerForPreciseCallStateChanged(
this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
}
- if (phone.isUsingNewDataStack()) {
- mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
- v -> new DataSettingsManagerCallback(this::post) {
- @Override
- public void onDataEnabledChanged(boolean enabled,
- @TelephonyManager.DataEnabledChangedReason int reason,
- @NonNull String callingPackage) {
- evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
- }});
- phone.getDataSettingsManager().registerCallback(
- mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
- } else {
- phone.getDataEnabledSettings().registerForDataEnabledChanged(
- this, EVENT_DATA_ENABLED_CHANGED, null);
- }
-
- registerForImsRadioTechChange(context, i);
+ mDataSettingsManagerCallbacks.computeIfAbsent(phoneId,
+ v -> new DataSettingsManagerCallback(this::post) {
+ @Override
+ public void onDataEnabledChanged(boolean enabled,
+ @TelephonyManager.DataEnabledChangedReason int reason,
+ @NonNull String callingPackage) {
+ PhoneSwitcher.this.onDataEnabledChanged();
+ }});
+ phone.getDataSettingsManager().registerCallback(
+ mDataSettingsManagerCallbacks.get(phoneId));
+ phone.getServiceStateTracker().registerForServiceStateChanged(this,
+ EVENT_SERVICE_STATE_CHANGED, phoneId);
+ registerForImsRadioTechChange(context, phoneId);
}
Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
mCurrentDdsSwitchFailure.add(ddsFailure);
@@ -533,6 +563,8 @@
PhoneFactory.getPhone(0).mCi.registerForOn(this, EVENT_RADIO_ON, null);
}
+ readDeviceResourceConfig();
+
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
telephonyRegistryManager.addOnSubscriptionsChangedListener(
@@ -547,6 +579,8 @@
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+ mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
@@ -580,7 +614,7 @@
updateHalCommandToUse();
- log("PhoneSwitcher started");
+ logl("PhoneSwitcher started");
}
private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() {
@@ -600,7 +634,7 @@
TelephonyManager.SIM_STATE_UNKNOWN);
int slotIndex = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- log("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state);
+ logl("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state);
obtainMessage(EVENT_PROCESS_SIM_STATE_CHANGE, slotIndex, state).sendToTarget();
}
}
@@ -611,14 +645,14 @@
return false;
}
- SubscriptionInfo info = SubscriptionController.getInstance()
+ SubscriptionInfo info = mSubscriptionManagerService
.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
- mContext.getOpPackageName(), null);
+ mContext.getOpPackageName(), mContext.getAttributionTag());
boolean uiccAppsEnabled = info != null && info.areUiccApplicationsEnabled();
IccCard iccCard = PhoneFactory.getPhone(slotIndex).getIccCard();
if (!iccCard.isEmptyProfile() && uiccAppsEnabled) {
- log("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex);
+ logl("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex);
return true;
} else {
return false;
@@ -638,17 +672,25 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SUBSCRIPTION_CHANGED: {
- onEvaluate(REQUESTS_UNCHANGED, "subChanged");
+ onEvaluate(REQUESTS_UNCHANGED, "subscription changed");
+ break;
+ }
+ case EVENT_SERVICE_STATE_CHANGED: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ final int phoneId = (int) ar.userObj;
+ onServiceStateChanged(phoneId);
+ break;
+ }
+ case EVENT_MEETS_AUTO_DATA_SWITCH_STATE: {
+ final int targetSubId = msg.arg1;
+ final boolean needValidation = (boolean) msg.obj;
+ validate(targetSubId, needValidation,
+ DataSwitch.Reason.DATA_SWITCH_REASON_AUTO, null);
break;
}
case EVENT_PRIMARY_DATA_SUB_CHANGED: {
- if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) {
- logDataSwitchEvent(mPreferredDataSubId.get(),
- TelephonyEvent.EventState.EVENT_STATE_START,
- DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
- registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
- DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
- }
+ evaluateIfImmediateDataSwitchIsNeeded("primary data sub changed",
+ DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
break;
}
case EVENT_REQUEST_NETWORK: {
@@ -662,7 +704,7 @@
case EVENT_EMERGENCY_TOGGLE: {
boolean isInEcm = isInEmergencyCallbackMode();
if (mEmergencyOverride != null) {
- log("Emergency override - ecbm status = " + isInEcm);
+ logl("Emergency override - ecbm status = " + isInEcm);
if (isInEcm) {
// The device has gone into ECBM. Wait until it's out.
removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
@@ -676,6 +718,9 @@
onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
break;
}
+ case EVENT_EVALUATE_AUTO_SWITCH:
+ evaluateIfAutoSwitchIsNeeded();
+ break;
case EVENT_RADIO_CAPABILITY_CHANGED: {
final int phoneId = msg.arg1;
sendRilCommands(phoneId);
@@ -694,24 +739,25 @@
onEvaluate(REQUESTS_UNCHANGED, EVALUATION_REASON_RADIO_ON);
break;
}
- case EVENT_IMS_RADIO_TECH_CHANGED:
+ case EVENT_IMS_RADIO_TECH_CHANGED: {
// register for radio tech change to listen to radio tech handover in case previous
// attempt was not successful
registerForImsRadioTechChange();
- // If the phoneId in voice call didn't change, do nothing.
- if (!isPhoneInVoiceCallChanged()) {
- break;
+ // if voice call state changes or in voice call didn't change
+ // but RAT changes(e.g. Iwlan -> cross sim), reevaluate for data switch.
+ if (updatesIfPhoneInVoiceCallChanged() || isAnyVoiceCallActiveOnDevice()) {
+ evaluateIfImmediateDataSwitchIsNeeded("Ims radio tech changed",
+ DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
}
- evaluateIfDataSwitchIsNeeded("EVENT_IMS_RADIO_TECH_CHANGED");
break;
-
+ }
case EVENT_PRECISE_CALL_STATE_CHANGED: {
// register for radio tech change to listen to radio tech handover in case previous
// attempt was not successful
registerForImsRadioTechChange();
// If the phoneId in voice call didn't change, do nothing.
- if (!isPhoneInVoiceCallChanged()) {
+ if (!updatesIfPhoneInVoiceCallChanged()) {
break;
}
@@ -740,13 +786,16 @@
mEmergencyOverride.mPendingOriginatingCall = false;
}
}
- evaluateIfDataSwitchIsNeeded("EVENT_PRECISE_CALL_STATE_CHANGED");
+ // Always update data modem via data during call code path, because
+ // mAutoSelectedDataSubId doesn't know about any data switch due to voice call
+ evaluateIfImmediateDataSwitchIsNeeded("precise call state changed",
+ DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ if (!isAnyVoiceCallActiveOnDevice()) {
+ // consider auto switch on hang up all voice call
+ evaluateIfAutoSwitchIsNeeded();
+ }
break;
}
-
- case EVENT_DATA_ENABLED_CHANGED:
- evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
- break;
case EVENT_NETWORK_VALIDATION_DONE: {
int subId = msg.arg1;
boolean passed = (msg.arg2 == 1);
@@ -759,10 +808,6 @@
onNetworkAvailable(subId, network);
break;
}
- case EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK: {
- removeDefaultNetworkChangeCallback();
- break;
- }
case EVENT_MODEM_COMMAND_DONE: {
AsyncResult ar = (AsyncResult) msg.obj;
onDdsSwitchResponse(ar);
@@ -770,11 +815,14 @@
}
case EVENT_MODEM_COMMAND_RETRY: {
int phoneId = (int) msg.obj;
+ if (mActiveModemCount <= phoneId) {
+ break;
+ }
if (isPhoneIdValidForRetry(phoneId)) {
- log("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId);
+ logl("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId);
sendRilCommands(phoneId);
} else {
- log("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed");
+ logl("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed");
mCurrentDdsSwitchFailure.get(phoneId).clear();
}
break;
@@ -786,7 +834,7 @@
// being overridden, ignore. We should not try to switch DDS while already
// waiting for SUPL.
if (mEmergencyOverride.mPhoneId != req.mPhoneId) {
- log("emergency override requested for phone id " + req.mPhoneId + " when "
+ logl("emergency override requested for phone id " + req.mPhoneId + " when "
+ "there is already an override in place for phone id "
+ mEmergencyOverride.mPhoneId + ". Ignoring.");
if (req.isCallbackAvailable()) {
@@ -806,7 +854,7 @@
mEmergencyOverride = req;
}
- log("new emergency override - " + mEmergencyOverride);
+ logl("new emergency override - " + mEmergencyOverride);
// a new request has been created, remove any previous override complete scheduled.
removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
Message msg2 = obtainMessage(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
@@ -824,7 +872,7 @@
break;
}
case EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE: {
- log("Emergency override removed - " + mEmergencyOverride);
+ logl("Emergency override removed - " + mEmergencyOverride);
mEmergencyOverride = null;
onEvaluate(REQUESTS_UNCHANGED, "emer_rm_override_dds");
break;
@@ -835,23 +883,42 @@
break;
}
case EVENT_PROCESS_SIM_STATE_CHANGE: {
- int slotIndex = (int) msg.arg1;
- int simState = (int) msg.arg2;
+ int slotIndex = msg.arg1;
+ int simState = msg.arg2;
if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- log("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: "
+ logl("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: "
+ slotIndex);
- } else if (mCurrentDdsSwitchFailure.get(slotIndex).contains(
+ } else if (TelephonyManager.SIM_STATE_LOADED == simState) {
+ if (mCurrentDdsSwitchFailure.get(slotIndex).contains(
CommandException.Error.INVALID_SIM_STATE)
&& (TelephonyManager.SIM_STATE_LOADED == simState)
&& isSimApplicationReady(slotIndex)) {
- sendRilCommands(slotIndex);
+ sendRilCommands(slotIndex);
+ }
+ // SIM loaded after subscriptions slot mapping are done. Evaluate for auto
+ // data switch.
+ sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
}
break;
}
}
}
+ /**
+ * Read the default device config from any default phone because the resource config are per
+ * device. No need to register callback for the same reason.
+ */
+ private void readDeviceResourceConfig() {
+ Phone phone = PhoneFactory.getDefaultPhone();
+ DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
+ mRequirePingTestBeforeDataSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
+ mAutoDataSwitchAvailabilityStabilityTimeThreshold =
+ dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ mAutoDataSwitchValidationMaxRetry =
+ dataConfig.getAutoDataSwitchValidationMaxRetry();
+ }
+
private synchronized void onMultiSimConfigChanged(int activeModemCount) {
// No change.
if (mActiveModemCount == activeModemCount) return;
@@ -880,22 +947,19 @@
this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
}
- if (phone.isUsingNewDataStack()) {
- mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
- v -> new DataSettingsManagerCallback(this::post) {
- @Override
- public void onDataEnabledChanged(boolean enabled,
- @TelephonyManager.DataEnabledChangedReason int reason,
- @NonNull String callingPackage) {
- evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
- }
- });
- phone.getDataSettingsManager().registerCallback(
- mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
- } else {
- phone.getDataEnabledSettings().registerForDataEnabledChanged(
- this, EVENT_DATA_ENABLED_CHANGED, null);
- }
+ mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
+ v -> new DataSettingsManagerCallback(this::post) {
+ @Override
+ public void onDataEnabledChanged(boolean enabled,
+ @TelephonyManager.DataEnabledChangedReason int reason,
+ @NonNull String callingPackage) {
+ PhoneSwitcher.this.onDataEnabledChanged();
+ }
+ });
+ phone.getDataSettingsManager().registerCallback(
+ mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
+ phone.getServiceStateTracker().registerForServiceStateChanged(this,
+ EVENT_SERVICE_STATE_CHANGED, phoneId);
Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
mCurrentDdsSwitchFailure.add(ddsFailure);
@@ -903,6 +967,23 @@
}
}
+ /**
+ * Called when
+ * 1. user changed mobile data settings
+ * 2. OR user changed auto data switch feature
+ */
+ private void onDataEnabledChanged() {
+ logl("user changed data related settings");
+ if (isAnyVoiceCallActiveOnDevice()) {
+ // user changed data related settings during call, switch or turn off immediately
+ evaluateIfImmediateDataSwitchIsNeeded(
+ "user changed data settings during call",
+ DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ } else {
+ evaluateIfAutoSwitchIsNeeded();
+ }
+ }
+
private boolean isInEmergencyCallbackMode() {
for (Phone p : PhoneFactory.getPhones()) {
if (p == null) continue;
@@ -941,79 +1022,26 @@
}
private void onRequestNetwork(NetworkRequest networkRequest) {
- if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
- TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
- networkRequest, PhoneFactory.getDefaultPhone());
- if (!mNetworkRequestList.contains(telephonyNetworkRequest)) {
- mNetworkRequestList.add(telephonyNetworkRequest);
- onEvaluate(REQUESTS_CHANGED, "netRequest");
- }
- return;
- }
- final DcRequest dcRequest =
- DcRequest.create(networkRequest, createApnRepository(networkRequest));
- if (dcRequest != null) {
- if (!mPrioritizedDcRequests.contains(dcRequest)) {
- collectRequestNetworkMetrics(networkRequest);
- mPrioritizedDcRequests.add(dcRequest);
- Collections.sort(mPrioritizedDcRequests);
- onEvaluate(REQUESTS_CHANGED, "netRequest");
- if (VDBG) log("Added DcRequest, size: " + mPrioritizedDcRequests.size());
- }
+ TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
+ networkRequest, PhoneFactory.getDefaultPhone());
+ if (!mNetworkRequestList.contains(telephonyNetworkRequest)) {
+ mNetworkRequestList.add(telephonyNetworkRequest);
+ onEvaluate(REQUESTS_CHANGED, "netRequest");
}
}
private void onReleaseNetwork(NetworkRequest networkRequest) {
- if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
- TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
- networkRequest, PhoneFactory.getDefaultPhone());
- if (mNetworkRequestList.remove(telephonyNetworkRequest)) {
- onEvaluate(REQUESTS_CHANGED, "netReleased");
- collectReleaseNetworkMetrics(networkRequest);
- }
- return;
+ TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
+ networkRequest, PhoneFactory.getDefaultPhone());
+ if (mNetworkRequestList.remove(telephonyNetworkRequest)) {
+ onEvaluate(REQUESTS_CHANGED, "netReleased");
+ collectReleaseNetworkMetrics(networkRequest);
}
- final DcRequest dcRequest =
- DcRequest.create(networkRequest, createApnRepository(networkRequest));
- if (dcRequest != null) {
- if (mPrioritizedDcRequests.remove(dcRequest)) {
- onEvaluate(REQUESTS_CHANGED, "netReleased");
- collectReleaseNetworkMetrics(networkRequest);
- if (VDBG) log("Removed DcRequest, size: " + mPrioritizedDcRequests.size());
- }
- }
- }
-
- private ApnConfigTypeRepository createApnRepository(NetworkRequest networkRequest) {
- int phoneIdForRequest = phoneIdForRequest(networkRequest);
- int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneIdForRequest);
- CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-
- PersistableBundle carrierConfig;
- if (configManager != null) {
- carrierConfig = configManager.getConfigForSubId(subId);
- } else {
- carrierConfig = null;
- }
- return new ApnConfigTypeRepository(carrierConfig);
- }
-
- private void removeDefaultNetworkChangeCallback() {
- removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK);
- mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- mDefaultNetworkCallback.mSwitchReason =
- TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
- mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
}
private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) {
mDefaultNetworkCallback.mExpectedSubId = expectedSubId;
mDefaultNetworkCallback.mSwitchReason = reason;
- mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
- sendMessageDelayed(
- obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK),
- DEFAULT_NETWORK_CHANGE_TIMEOUT_MS);
}
private void collectRequestNetworkMetrics(NetworkRequest networkRequest) {
@@ -1040,6 +1068,195 @@
}
}
+ /**
+ * Called when service state changed.
+ */
+ private void onServiceStateChanged(int phoneId) {
+ Phone phone = findPhoneById(phoneId);
+ if (phone != null) {
+ int newRegState = phone.getServiceState()
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .getRegistrationState();
+ if (newRegState != mPhoneStates[phoneId].dataRegState) {
+ mPhoneStates[phoneId].dataRegState = newRegState;
+ logl("onServiceStateChanged: phoneId:" + phoneId + " dataReg-> "
+ + NetworkRegistrationInfo.registrationStateToString(newRegState));
+ if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+ sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ }
+ }
+ }
+ }
+
+ /**
+ * Evaluate if auto switch is suitable at the moment.
+ */
+ private void evaluateIfAutoSwitchIsNeeded() {
+ // auto data switch feature is disabled from server
+ if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return;
+ // check is valid DSDS
+ if (!isActiveSubId(mPrimaryDataSubId) || mSubscriptionManagerService
+ .getActiveSubIdList(true).length <= 1) {
+ return;
+ }
+
+ Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId);
+ if (primaryDataPhone == null) {
+ loge("evaluateIfAutoSwitchIsNeeded: cannot find primary data phone. subId="
+ + mPrimaryDataSubId);
+ return;
+ }
+
+ int primaryPhoneId = primaryDataPhone.getPhoneId();
+ log("evaluateIfAutoSwitchIsNeeded: primaryPhoneId: " + primaryPhoneId
+ + " preferredPhoneId: " + mPreferredDataPhoneId);
+ Phone secondaryDataPhone;
+
+ if (mPreferredDataPhoneId == primaryPhoneId) {
+ // on primary data sub
+
+ int candidateSubId = getAutoSwitchTargetSubIdIfExists();
+ if (candidateSubId != INVALID_SUBSCRIPTION_ID) {
+ startAutoDataSwitchStabilityCheck(candidateSubId, mRequirePingTestBeforeDataSwitch);
+ } else {
+ cancelPendingAutoDataSwitch();
+ }
+ } else if ((secondaryDataPhone = findPhoneById(mPreferredDataPhoneId)) != null) {
+ // on secondary data sub
+
+ if (!primaryDataPhone.isUserDataEnabled()
+ || !secondaryDataPhone.isDataAllowed()) {
+ // immediately switch back if user setting changes
+ mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
+ evaluateIfImmediateDataSwitchIsNeeded("User disabled data settings",
+ DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
+ return;
+ }
+
+ NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
+ .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
+ if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
+ .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ log("evaluateIfAutoSwitchIsNeeded: "
+ + "Default network is active on non-cellular transport");
+ startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
+ return;
+ }
+
+ if (mPhoneStates[secondaryDataPhone.getPhoneId()].dataRegState
+ != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+ // secondary phone lost its HOME availability
+ startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
+ return;
+ }
+
+ if (isInService(mPhoneStates[primaryPhoneId])) {
+ // primary becomes available
+ startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID,
+ mRequirePingTestBeforeDataSwitch);
+ return;
+ }
+
+ // cancel any previous attempts of switching back to primary
+ cancelPendingAutoDataSwitch();
+ }
+ }
+
+ /**
+ * @param phoneState The phone state to check
+ * @return {@code true} if the phone state is considered in service.
+ */
+ private boolean isInService(@NonNull PhoneState phoneState) {
+ return phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+ || phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ }
+
+ /**
+ * Called when the current environment suits auto data switch.
+ * Start pre-switch validation if the current environment suits auto data switch for
+ * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS.
+ * @param targetSubId the target sub Id.
+ * @param needValidation {@code true} if validation is needed.
+ */
+ private void startAutoDataSwitchStabilityCheck(int targetSubId, boolean needValidation) {
+ log("startAutoDataSwitchStabilityCheck: targetSubId=" + targetSubId
+ + " needValidation=" + needValidation);
+ if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) {
+ sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetSubId,
+ 0/*placeholder*/,
+ needValidation),
+ mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+ }
+ }
+
+ /**
+ * Cancel any auto switch attempts when the current environment is not suitable for auto switch.
+ */
+ private void cancelPendingAutoDataSwitch() {
+ mAutoSwitchRetryFailedCount = 0;
+ removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
+ if (mValidator.isValidating()) {
+ mValidator.stopValidation();
+
+ removeMessages(EVENT_NETWORK_VALIDATION_DONE);
+ removeMessages(EVENT_NETWORK_AVAILABLE);
+ mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+ mPendingSwitchNeedValidation = false;
+ }
+ }
+
+ /**
+ * Called when consider switching from primary default data sub to another data sub.
+ * @return the target subId if a suitable candidate is found, otherwise return
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+ */
+ private int getAutoSwitchTargetSubIdIfExists() {
+ Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId);
+ if (primaryDataPhone == null) {
+ log("getAutoSwitchTargetSubId: no sim loaded");
+ return INVALID_SUBSCRIPTION_ID;
+ }
+
+ int primaryPhoneId = primaryDataPhone.getPhoneId();
+
+ if (!primaryDataPhone.isUserDataEnabled()) {
+ log("getAutoSwitchTargetSubId: user disabled data");
+ return INVALID_SUBSCRIPTION_ID;
+ }
+
+ NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
+ .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
+ if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
+ .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ // Exists other active default transport
+ log("getAutoSwitchTargetSubId: Default network is active on non-cellular transport");
+ return INVALID_SUBSCRIPTION_ID;
+ }
+
+ // check whether primary and secondary signal status worth switching
+ if (isInService(mPhoneStates[primaryPhoneId])) {
+ log("getAutoSwitchTargetSubId: primary is in service");
+ return INVALID_SUBSCRIPTION_ID;
+ }
+ for (int phoneId = 0; phoneId < mPhoneStates.length; phoneId++) {
+ if (phoneId != primaryPhoneId) {
+ // the alternative phone must have HOME availability
+ if (mPhoneStates[phoneId].dataRegState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+ log("getAutoSwitchTargetSubId: found phone " + phoneId + " in HOME service");
+ Phone secondaryDataPhone = findPhoneById(phoneId);
+ if (secondaryDataPhone != null && // check auto switch feature enabled
+ secondaryDataPhone.isDataAllowed()) {
+ return secondaryDataPhone.getSubId();
+ }
+ }
+ }
+ }
+ return INVALID_SUBSCRIPTION_ID;
+ }
+
private TelephonyManager getTm() {
return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
@@ -1062,11 +1279,12 @@
boolean diffDetected = mHalCommandToUse != HAL_COMMAND_PREFERRED_DATA && requestsChanged;
// Check if user setting of default non-opportunistic data sub is changed.
- final int primaryDataSubId = mSubscriptionController.getDefaultDataSubId();
+ int primaryDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
if (primaryDataSubId != mPrimaryDataSubId) {
sb.append(" mPrimaryDataSubId ").append(mPrimaryDataSubId).append("->")
.append(primaryDataSubId);
mPrimaryDataSubId = primaryDataSubId;
+ mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL;
}
// Check to see if there is any active subscription on any phone
@@ -1074,14 +1292,21 @@
// Check if phoneId to subId mapping is changed.
for (int i = 0; i < mActiveModemCount; i++) {
- int sub = mSubscriptionController.getSubIdUsingPhoneId(i);
+ int sub = SubscriptionManager.getSubscriptionId(i);
if (SubscriptionManager.isValidSubscriptionId(sub)) hasAnyActiveSubscription = true;
if (sub != mPhoneSubscriptions[i]) {
sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]);
sb.append("->").append(sub);
+ if (mAutoSelectedDataSubId == mPhoneSubscriptions[i]) {
+ mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
+ }
mPhoneSubscriptions[i] = sub;
+ // Listen to IMS radio tech change for new sub
+ if (SubscriptionManager.isValidSubscriptionId(sub)) {
+ registerForImsRadioTechChange(mContext, i);
+ }
diffDetected = true;
}
}
@@ -1106,11 +1331,11 @@
if (hasAnyActiveSubscription) updatePreferredDataPhoneId();
if (oldPreferredDataPhoneId != mPreferredDataPhoneId) {
- sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId)
+ sb.append(" preferred data phoneId ").append(oldPreferredDataPhoneId)
.append("->").append(mPreferredDataPhoneId);
diffDetected = true;
} else if (oldPreferredDataSubId != mPreferredDataSubId.get()) {
- log("SIM refresh, notify dds change");
+ logl("SIM refresh, notify dds change");
// Inform connectivity about the active data phone
notifyPreferredDataSubIdChanged();
}
@@ -1119,7 +1344,7 @@
// DDS are out of sync after APM, AP should force DDS when radio on. long term solution
// should be having API to query preferred data modem to detect the out-of-sync scenarios.
if (diffDetected || EVALUATION_REASON_RADIO_ON.equals(reason)) {
- log("evaluating due to " + sb.toString());
+ logl("evaluating due to " + sb);
if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
// With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach.
// So marking all phone as active, and the phone with mPreferredDataPhoneId
@@ -1147,27 +1372,17 @@
}
if (newActivePhones.size() < mMaxDataAttachModemCount) {
- if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
- for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
- int phoneIdForRequest = phoneIdForRequest(networkRequest);
- if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
- if (newActivePhones.contains(phoneIdForRequest)) continue;
- newActivePhones.add(phoneIdForRequest);
- if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
- }
- } else {
- for (DcRequest dcRequest : mPrioritizedDcRequests) {
- int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
- if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
- if (newActivePhones.contains(phoneIdForRequest)) continue;
- newActivePhones.add(phoneIdForRequest);
- if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
- }
+ for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
+ int phoneIdForRequest = phoneIdForRequest(networkRequest);
+ if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
+ if (newActivePhones.contains(phoneIdForRequest)) continue;
+ newActivePhones.add(phoneIdForRequest);
+ if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
}
}
if (newActivePhones.size() < mMaxDataAttachModemCount
- && newActivePhones.contains(mPreferredDataPhoneId)
+ && !newActivePhones.contains(mPreferredDataPhoneId)
&& SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) {
newActivePhones.add(mPreferredDataPhoneId);
}
@@ -1175,7 +1390,7 @@
if (VDBG) {
log("mPrimaryDataSubId = " + mPrimaryDataSubId);
- log("mOpptDataSubId = " + mOpptDataSubId);
+ log("mAutoSelectedDataSubId = " + mAutoSelectedDataSubId);
for (int i = 0; i < mActiveModemCount; i++) {
log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
}
@@ -1200,6 +1415,8 @@
protected static class PhoneState {
public volatile boolean active = false;
+ public @NetworkRegistrationInfo.RegistrationState int dataRegState =
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
public long lastRequested = 0;
}
@@ -1217,7 +1434,7 @@
PhoneState state = mPhoneStates[phoneId];
if (state.active == active) return;
state.active = active;
- log((active ? "activate " : "deactivate ") + phoneId);
+ logl((active ? "activate " : "deactivate ") + phoneId);
state.lastRequested = System.currentTimeMillis();
sendRilCommands(phoneId);
}
@@ -1264,7 +1481,7 @@
protected void sendRilCommands(int phoneId) {
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- log("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId);
+ logl("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId);
return;
}
@@ -1276,7 +1493,7 @@
}
} else if (phoneId == mPreferredDataPhoneId) {
// Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId
- log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
+ logl("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, message);
}
}
@@ -1286,18 +1503,13 @@
.getNumberOfModemsWithSimultaneousDataConnections();
if (mMaxDataAttachModemCount != newMaxDataAttachModemCount) {
mMaxDataAttachModemCount = newMaxDataAttachModemCount;
- log("Max active phones changed to " + mMaxDataAttachModemCount);
+ logl("Max active phones changed to " + mMaxDataAttachModemCount);
onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged");
}
}
- // Merge phoneIdForRequest(NetworkRequest netRequest) after Phone.isUsingNewDataStack() is
- // cleaned up.
private int phoneIdForRequest(TelephonyNetworkRequest networkRequest) {
- return phoneIdForRequest(networkRequest.getNativeNetworkRequest());
- }
-
- private int phoneIdForRequest(NetworkRequest netRequest) {
+ NetworkRequest netRequest = networkRequest.getNativeNetworkRequest();
int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId;
@@ -1341,71 +1553,82 @@
return INVALID_SUBSCRIPTION_ID;
}
- private int getSubIdForDefaultNetworkRequests() {
- if (mSubscriptionController.isActiveSubId(mOpptDataSubId)) {
- return mOpptDataSubId;
- } else {
- return mPrimaryDataSubId;
- }
+ private boolean isActiveSubId(int subId) {
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && subInfo.isActive();
}
// This updates mPreferredDataPhoneId which decides which phone should handle default network
// requests.
protected void updatePreferredDataPhoneId() {
- Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall);
- boolean isDataEnabled = false;
- if (voicePhone != null) {
- if (voicePhone.isUsingNewDataStack()) {
- isDataEnabled = voicePhone.getDataSettingsManager()
- .isDataEnabled(ApnSetting.TYPE_DEFAULT);
- } else {
- isDataEnabled = voicePhone.getDataEnabledSettings()
- .isDataEnabled(ApnSetting.TYPE_DEFAULT);
- }
- }
-
if (mEmergencyOverride != null && findPhoneById(mEmergencyOverride.mPhoneId) != null) {
// Override DDS for emergency even if user data is not enabled, since it is an
// emergency.
// TODO: Provide a notification to the user that metered data is currently being
// used during this period.
- log("updatePreferredDataPhoneId: preferred data overridden for emergency."
+ logl("updatePreferredDataPhoneId: preferred data overridden for emergency."
+ " phoneId = " + mEmergencyOverride.mPhoneId);
mPreferredDataPhoneId = mEmergencyOverride.mPhoneId;
- } else if (isDataEnabled) {
- // If a phone is in call and user enabled its mobile data, we
- // should switch internet connection to it. Because the other modem
- // will lose data connection anyway.
- // TODO: validate network first.
- mPreferredDataPhoneId = mPhoneIdInVoiceCall;
+ mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
} else {
- int subId = getSubIdForDefaultNetworkRequests();
- int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
-
- if (SubscriptionManager.isUsableSubIdValue(subId)) {
- for (int i = 0; i < mActiveModemCount; i++) {
- if (mPhoneSubscriptions[i] == subId) {
- phoneId = i;
- break;
- }
+ int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall);
+ if (isAnyVoiceCallActiveOnDevice() && imsRegTech != REGISTRATION_TECH_IWLAN) {
+ if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) {
+ mPreferredDataPhoneId = shouldSwitchDataDueToInCall()
+ ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests();
+ } else {
+ logl("IMS call on cross-SIM, skip switching data to phone "
+ + mPhoneIdInVoiceCall);
}
+ } else {
+ mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests();
}
-
- mPreferredDataPhoneId = phoneId;
}
- mPreferredDataSubId.set(
- mSubscriptionController.getSubIdUsingPhoneId(mPreferredDataPhoneId));
+ mPreferredDataSubId.set(SubscriptionManager.getSubscriptionId(mPreferredDataPhoneId));
+ }
+
+ /**
+ * @return the default data phone Id (or auto selected phone Id in auto data switch/CBRS case)
+ */
+ private int getFallbackDataPhoneIdForInternetRequests() {
+ int fallbackSubId = isActiveSubId(mAutoSelectedDataSubId)
+ ? mAutoSelectedDataSubId : mPrimaryDataSubId;
+
+ if (SubscriptionManager.isUsableSubIdValue(fallbackSubId)) {
+ for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
+ if (mPhoneSubscriptions[phoneId] == fallbackSubId) {
+ return phoneId;
+ }
+ }
+ }
+ return SubscriptionManager.INVALID_PHONE_INDEX;
+ }
+
+ /**
+ * If a phone is in call and user enabled its mobile data and auto data switch feature, we
+ * should switch internet connection to it because the other modem will lose data connection
+ * anyway.
+ * @return {@code true} if should switch data to the phone in voice call
+ */
+ private boolean shouldSwitchDataDueToInCall() {
+ Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall);
+ Phone defaultDataPhone = getPhoneBySubId(mPrimaryDataSubId);
+ return defaultDataPhone != null // check user enabled data
+ && defaultDataPhone.isUserDataEnabled()
+ && voicePhone != null // check user enabled voice during call feature
+ && voicePhone.isDataAllowed();
}
protected void transitionToEmergencyPhone() {
if (mActiveModemCount <= 0) {
- log("No phones: unable to reset preferred phone for emergency");
+ logl("No phones: unable to reset preferred phone for emergency");
return;
}
if (mPreferredDataPhoneId != DEFAULT_EMERGENCY_PHONE_ID) {
- log("No active subscriptions: resetting preferred phone to 0 for emergency");
+ logl("No active subscriptions: resetting preferred phone to 0 for emergency");
mPreferredDataPhoneId = DEFAULT_EMERGENCY_PHONE_ID;
}
@@ -1415,6 +1638,10 @@
}
}
+ private Phone getPhoneBySubId(int subId) {
+ return findPhoneById(mSubscriptionManagerService.getPhoneId(subId));
+ }
+
private Phone findPhoneById(final int phoneId) {
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return null;
@@ -1426,15 +1653,16 @@
TelephonyNetworkRequest networkRequest, int phoneId) {
if (!SubscriptionManager.isValidPhoneId(phoneId)) return false;
+ int subId = SubscriptionManager.getSubscriptionId(phoneId);
+
// In any case, if phone state is inactive, don't apply the network request.
- if (!isPhoneActive(phoneId) || (
- mSubscriptionController.getSubIdUsingPhoneId(phoneId) == INVALID_SUBSCRIPTION_ID
+ if (!isPhoneActive(phoneId) || (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
&& !isEmergencyNetworkRequest(networkRequest))) {
return false;
}
NetworkRequest netRequest = networkRequest.getNativeNetworkRequest();
- int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
+ subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
//if this phone is an emergency networkRequest
//and subId is not specified that is invalid or default
@@ -1488,45 +1716,67 @@
*/
private void setOpportunisticDataSubscription(int subId, boolean needValidation,
ISetOpportunisticDataCallback callback) {
- if (!mSubscriptionController.isActiveSubId(subId)
- && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- log("Can't switch data to inactive subId " + subId);
+ validate(subId, needValidation,
+ DataSwitch.Reason.DATA_SWITCH_REASON_CBRS, callback);
+ }
+
+ /**
+ * Try setup a new internet connection on the subId that's pending validation. If the validation
+ * succeeds, this subId will be evaluated for being the preferred data subId; If fails, nothing
+ * happens.
+ * Callback will be updated with the validation result.
+ *
+ * @param subId Sub Id that's pending switch, awaiting validation.
+ * @param needValidation {@code false} if switch to the subId even if validation fails.
+ * @param switchReason The switch reason for this validation
+ * @param callback Optional - specific for external opportunistic sub validation request.
+ */
+ private void validate(int subId, boolean needValidation, int switchReason,
+ @Nullable ISetOpportunisticDataCallback callback) {
+ logl("Validate subId " + subId + " due to " + switchReasonToString(switchReason)
+ + " needValidation=" + needValidation);
+ int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+ ? mPrimaryDataSubId : subId;
+ if (!isActiveSubId(subIdToValidate)) {
+ logl("Can't switch data to inactive subId " + subIdToValidate);
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ // the default data sub is not selected yet, store the intent of switching to
+ // default subId once it becomes available.
+ mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ }
sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
return;
}
- // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId
- // if queued.
- removeMessages(EVENT_NETWORK_VALIDATION_DONE);
- removeMessages(EVENT_NETWORK_AVAILABLE);
-
- int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
- ? mPrimaryDataSubId : subId;
-
- mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
-
if (mValidator.isValidating()) {
mValidator.stopValidation();
sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
mSetOpptSubCallback = null;
}
- if (subId == mOpptDataSubId) {
+ // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previous subId
+ // if queued.
+ removeMessages(EVENT_NETWORK_VALIDATION_DONE);
+ removeMessages(EVENT_NETWORK_AVAILABLE);
+
+ mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+
+ if (subIdToValidate == mPreferredDataSubId.get()) {
sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
return;
}
- logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
+ mLastSwitchPreferredDataReason = switchReason;
+ logDataSwitchEvent(subIdToValidate,
TelephonyEvent.EventState.EVENT_STATE_START,
- DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
- registerDefaultNetworkChangeCallback(
- subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
- DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
+ switchReason);
+ registerDefaultNetworkChangeCallback(subIdToValidate,
+ switchReason);
// If validation feature is not supported, set it directly. Otherwise,
// start validation on the subscription first.
if (!mValidator.isValidationFeatureSupported()) {
- setOpportunisticSubscriptionInternal(subId);
+ setAutoSelectedDataSubIdInternal(subIdToValidate);
sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
return;
}
@@ -1563,36 +1813,46 @@
try {
callback.onComplete(result);
} catch (RemoteException exception) {
- log("RemoteException " + exception);
+ logl("RemoteException " + exception);
}
}
/**
- * Set opportunistic data subscription.
+ * Evaluate whether the specified sub Id can be set to be the preferred data sub Id.
+ *
+ * @param subId The subId that we tried to validate: could possibly be unvalidated if validation
+ * feature is not supported.
*/
- private void setOpportunisticSubscriptionInternal(int subId) {
- if (mOpptDataSubId != subId) {
- mOpptDataSubId = subId;
- onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed");
+ private void setAutoSelectedDataSubIdInternal(int subId) {
+ if (mAutoSelectedDataSubId != subId) {
+ mAutoSelectedDataSubId = subId;
+ onEvaluate(REQUESTS_UNCHANGED, switchReasonToString(mLastSwitchPreferredDataReason));
}
}
private void confirmSwitch(int subId, boolean confirm) {
- log("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled."));
+ logl("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled."));
int resultForCallBack;
- if (!mSubscriptionController.isActiveSubId(subId)) {
- log("confirmSwitch: subId " + subId + " is no longer active");
+ if (!isActiveSubId(subId)) {
+ logl("confirmSwitch: subId " + subId + " is no longer active");
resultForCallBack = SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
+ mAutoSwitchRetryFailedCount = 0;
} else if (!confirm) {
resultForCallBack = SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+
+ // retry for auto data switch validation failure
+ if (mLastSwitchPreferredDataReason == DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
+ scheduleAutoSwitchRetryEvaluation();
+ mAutoSwitchRetryFailedCount++;
+ }
} else {
- if (mSubscriptionController.isOpportunistic(subId)) {
- setOpportunisticSubscriptionInternal(subId);
+ if (subId == mPrimaryDataSubId) {
+ setAutoSelectedDataSubIdInternal(DEFAULT_SUBSCRIPTION_ID);
} else {
- // Switching data back to primary subscription.
- setOpportunisticSubscriptionInternal(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ setAutoSelectedDataSubIdInternal(subId);
}
resultForCallBack = SET_OPPORTUNISTIC_SUB_SUCCESS;
+ mAutoSwitchRetryFailedCount = 0;
}
// Trigger callback if needed
@@ -1601,6 +1861,23 @@
mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
}
+ /**
+ * Schedule auto data switch evaluation retry if haven't reached the max retry count.
+ */
+ private void scheduleAutoSwitchRetryEvaluation() {
+ if (mAutoSwitchRetryFailedCount < mAutoDataSwitchValidationMaxRetry) {
+ if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+ sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH),
+ mAutoDataSwitchAvailabilityStabilityTimeThreshold
+ << mAutoSwitchRetryFailedCount);
+ }
+ } else {
+ logl("scheduleAutoSwitchEvaluation: reached max auto switch retry count "
+ + mAutoDataSwitchValidationMaxRetry);
+ mAutoSwitchRetryFailedCount = 0;
+ }
+ }
+
private void onNetworkAvailable(int subId, Network network) {
log("onNetworkAvailable: on subId " + subId);
// Do nothing unless pending switch matches target subId and it doesn't require
@@ -1613,7 +1890,7 @@
}
private void onValidationDone(int subId, boolean passed) {
- log("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId);
+ logl("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId);
if (mPendingSwitchSubId == INVALID_SUBSCRIPTION_ID || mPendingSwitchSubId != subId) return;
// If validation failed and mPendingSwitch.mNeedValidation is false, we still confirm
@@ -1639,7 +1916,7 @@
*/
public void trySetOpportunisticDataSubscription(int subId, boolean needValidation,
ISetOpportunisticDataCallback callback) {
- log("Try set opportunistic data subscription to subId " + subId
+ logl("Try set opportunistic data subscription to subId " + subId
+ (needValidation ? " with " : " without ") + "validation");
PhoneSwitcher.this.obtainMessage(EVENT_OPPT_DATA_SUB_CHANGED,
subId, needValidation ? 1 : 0, callback).sendToTarget();
@@ -1660,21 +1937,38 @@
? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA;
}
- public int getOpportunisticDataSubscriptionId() {
- return mOpptDataSubId;
- }
-
public int getPreferredDataPhoneId() {
return mPreferredDataPhoneId;
}
+ /**
+ * Log debug messages and also log into the local log.
+ * @param l debug messages
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- protected void log(String l) {
- Rlog.d(LOG_TAG, l);
+ protected void logl(String l) {
+ log(l);
mLocalLog.log(l);
}
/**
+ * Log debug messages.
+ * @param s debug messages
+ */
+ private void log(@NonNull String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ /**
+ * Log debug error messages.
+ * @param s debug messages
+ */
+ private void loge(@NonNull String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+
+ /**
* Convert data switch reason into string.
*
* @param reason The switch reason.
@@ -1690,6 +1984,8 @@
return "IN_CALL";
case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_CBRS:
return "CBRS";
+ case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_AUTO:
+ return "AUTO";
default: return "UNKNOWN(" + reason + ")";
}
}
@@ -1720,8 +2016,8 @@
* @param reason The switching reason.
*/
private void logDataSwitchEvent(int subId, int state, int reason) {
- log("Data switch event. subId=" + subId + ", state=" + switchStateToString(state)
- + ", reason=" + switchReasonToString(reason));
+ logl("Data switch state=" + switchStateToString(state) + " due to reason="
+ + switchReasonToString(reason) + " on subId " + subId);
DataSwitch dataSwitch = new DataSwitch();
dataSwitch.state = state;
dataSwitch.reason = reason;
@@ -1734,7 +2030,7 @@
protected void notifyPreferredDataSubIdChanged() {
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) mContext
.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
- log("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get());
+ logl("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get());
telephonyRegistryManager.notifyActiveDataSubIdChanged(mPreferredDataSubId.get());
}
@@ -1745,6 +2041,13 @@
return mPreferredDataSubId.get();
}
+ /**
+ * @return The auto selected data subscription id.
+ */
+ public int getAutoSelectedDataSubId() {
+ return mAutoSelectedDataSubId;
+ }
+
// TODO (b/148396668): add an internal callback method to monitor phone capability change,
// and hook this call to that callback.
private void onPhoneCapabilityChanged(PhoneCapability capability) {
@@ -1759,23 +2062,32 @@
for (int i = 0; i < mActiveModemCount; i++) {
PhoneState ps = mPhoneStates[i];
c.setTimeInMillis(ps.lastRequested);
- pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" +
- (ps.lastRequested == 0 ? "never" :
+ pw.println("PhoneId(" + i + ") active=" + ps.active + ", dataRegState="
+ + NetworkRegistrationInfo.registrationStateToString(ps.dataRegState)
+ + ", lastRequest="
+ + (ps.lastRequested == 0 ? "never" :
String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)));
}
pw.println("mPreferredDataPhoneId=" + mPreferredDataPhoneId);
pw.println("mPreferredDataSubId=" + mPreferredDataSubId.get());
- pw.println("DefaultDataSubId=" + mSubscriptionController.getDefaultDataSubId());
- pw.println("DefaultDataPhoneId=" + mSubscriptionController.getPhoneId(
- mSubscriptionController.getDefaultDataSubId()));
+ pw.println("DefaultDataSubId=" + mSubscriptionManagerService.getDefaultDataSubId());
+ pw.println("DefaultDataPhoneId=" + mSubscriptionManagerService.getPhoneId(
+ mSubscriptionManagerService.getDefaultDataSubId()));
pw.println("mPrimaryDataSubId=" + mPrimaryDataSubId);
- pw.println("mOpptDataSubId=" + mOpptDataSubId);
+ pw.println("mAutoSelectedDataSubId=" + mAutoSelectedDataSubId);
pw.println("mIsRegisteredForImsRadioTechChange=" + mIsRegisteredForImsRadioTechChange);
pw.println("mPendingSwitchNeedValidation=" + mPendingSwitchNeedValidation);
pw.println("mMaxDataAttachModemCount=" + mMaxDataAttachModemCount);
pw.println("mActiveModemCount=" + mActiveModemCount);
pw.println("mPhoneIdInVoiceCall=" + mPhoneIdInVoiceCall);
pw.println("mCurrentDdsSwitchFailure=" + mCurrentDdsSwitchFailure);
+ pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
+ + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+ pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry);
+ pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeDataSwitch);
+ pw.println("mLastSwitchPreferredDataReason="
+ + switchReasonToString(mLastSwitchPreferredDataReason));
+ pw.println("mDisplayedAutoSwitchNotification=" + mDisplayedAutoSwitchNotification);
pw.println("Local logs:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
@@ -1793,64 +2105,109 @@
boolean commandSuccess = ar != null && ar.exception == null;
int phoneId = (int) ar.userObj;
if (mEmergencyOverride != null) {
- log("Emergency override result sent = " + commandSuccess);
+ logl("Emergency override result sent = " + commandSuccess);
mEmergencyOverride.sendOverrideCompleteCallbackResultAndClear(commandSuccess);
// Do not retry , as we do not allow changes in onEvaluate during an emergency
// call. When the call ends, we will start the countdown to remove the override.
} else if (!commandSuccess) {
- log("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception);
+ logl("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception);
if (ar.exception instanceof CommandException) {
CommandException.Error error = ((CommandException)
(ar.exception)).getCommandError();
mCurrentDdsSwitchFailure.get(phoneId).add(error);
if (error == CommandException.Error.OP_NOT_ALLOWED_DURING_VOICE_CALL) {
- log("onDdsSwitchResponse: Wait for call end indication");
+ logl("onDdsSwitchResponse: Wait for call end indication");
return;
} else if (error == CommandException.Error.INVALID_SIM_STATE) {
/* If there is a attach failure due to sim not ready then
hold the retry until sim gets ready */
- log("onDdsSwitchResponse: Wait for SIM to get READY");
+ logl("onDdsSwitchResponse: Wait for SIM to get READY");
return;
}
}
- log("onDdsSwitchResponse: Scheduling DDS switch retry");
+ logl("onDdsSwitchResponse: Scheduling DDS switch retry");
sendMessageDelayed(Message.obtain(this, EVENT_MODEM_COMMAND_RETRY,
phoneId), MODEM_COMMAND_RETRY_PERIOD_MS);
return;
}
- if (commandSuccess) log("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
+ if (commandSuccess) logl("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
mCurrentDdsSwitchFailure.get(phoneId).clear();
// Notify all registrants
mActivePhoneRegistrants.notifyRegistrants();
notifyPreferredDataSubIdChanged();
+ displayAutoDataSwitchNotification();
+ }
+
+ /**
+ * Display a notification the first time auto data switch occurs.
+ */
+ private void displayAutoDataSwitchNotification() {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (mDisplayedAutoSwitchNotification) {
+ // cancel posted notification if any exist
+ log("displayAutoDataSwitchNotification: canceling any notifications for subId "
+ + mAutoSelectedDataSubId);
+ notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+ AUTO_DATA_SWITCH_NOTIFICATION_ID);
+ return;
+ }
+ // proceed only the first time auto data switch occurs, which includes data during call
+ if (mLastSwitchPreferredDataReason != DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
+ log("displayAutoDataSwitchNotification: Ignore DDS switch due to "
+ + switchReasonToString(mLastSwitchPreferredDataReason));
+ return;
+ }
+ SubscriptionInfo subInfo = mSubscriptionManagerService
+ .getSubscriptionInfo(mAutoSelectedDataSubId);
+ if (subInfo == null || subInfo.isOpportunistic()) {
+ loge("displayAutoDataSwitchNotification: mAutoSelectedDataSubId="
+ + mAutoSelectedDataSubId + " unexpected subInfo " + subInfo);
+ return;
+ }
+ logl("displayAutoDataSwitchNotification: display for subId=" + mAutoSelectedDataSubId);
+ // "Mobile network settings" screen / dialog
+ Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ final Bundle fragmentArgs = new Bundle();
+ // Special contract for Settings to highlight permission row
+ fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
+ intent.putExtra(Settings.EXTRA_SUB_ID, mAutoSelectedDataSubId);
+ intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ mContext, mAutoSelectedDataSubId, intent, PendingIntent.FLAG_IMMUTABLE);
+
+ CharSequence activeCarrierName = subInfo.getDisplayName();
+ CharSequence contentTitle = mContext.getString(
+ com.android.internal.R.string.auto_data_switch_title, activeCarrierName);
+ CharSequence contentText = mContext.getText(
+ com.android.internal.R.string.auto_data_switch_content);
+
+ final Notification notif = new Notification.Builder(mContext)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setSmallIcon(android.R.drawable.stat_sys_warning)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+ .setContentIntent(contentIntent)
+ .setStyle(new Notification.BigTextStyle().bigText(contentText))
+ .build();
+ notificationManager.notify(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+ AUTO_DATA_SWITCH_NOTIFICATION_ID, notif);
+ mDisplayedAutoSwitchNotification = true;
}
private boolean isPhoneIdValidForRetry(int phoneId) {
- int phoneIdForRequest = INVALID_PHONE_INDEX;
- int ddsPhoneId = mSubscriptionController.getPhoneId(
- mSubscriptionController.getDefaultDataSubId());
+ int ddsPhoneId = mSubscriptionManagerService.getPhoneId(
+ mSubscriptionManagerService.getDefaultDataSubId());
if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) {
return true;
} else {
- if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
- if (mNetworkRequestList.isEmpty()) return false;
- for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
- phoneIdForRequest = phoneIdForRequest(networkRequest);
- if (phoneIdForRequest == phoneId) {
- return true;
- }
- }
- } else {
- if (mPrioritizedDcRequests.size() == 0) {
- return false;
- }
- for (DcRequest dcRequest : mPrioritizedDcRequests) {
- if (dcRequest != null) {
- phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
- if (phoneIdForRequest == phoneId) {
- return true;
- }
- }
+ if (mNetworkRequestList.isEmpty()) return false;
+ for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
+ if (phoneIdForRequest(networkRequest) == phoneId) {
+ return true;
}
}
}
diff --git a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
index b7cecbd..ac04627 100644
--- a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
+++ b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
@@ -37,6 +37,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -53,8 +54,7 @@
private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3;
private final @NonNull String mLogTag;
- // TODO: Change this to TelephonyNetworkAgent
- private final @NonNull NotifyQosSessionInterface mNetworkAgent;
+ private final @NonNull TelephonyNetworkAgent mNetworkAgent;
private final @NonNull Map<Integer, QosBearerSession> mQosBearerSessions;
private final @NonNull RcsStats mRcsStats;
@@ -80,12 +80,20 @@
/**
* Filter using the remote address.
*
- * @param address The local address.
+ * @param address The remote address.
* @param startPort Starting port.
* @param endPort Ending port.
* @return {@code true} if matches, {@code false} otherwise.
*/
boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort);
+
+ /**
+ * Filter using the protocol
+ *
+ * @param protocol ID
+ * @return {@code true} if matches, {@code false} otherwise.
+ */
+ boolean matchesProtocol(int protocol);
}
/**
@@ -94,8 +102,7 @@
* @param networkAgent The network agent to send events to.
* @param phone The phone instance.
*/
- public QosCallbackTracker(@NonNull NotifyQosSessionInterface networkAgent,
- @NonNull Phone phone) {
+ public QosCallbackTracker(@NonNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone) {
mQosBearerSessions = new HashMap<>();
mCallbacksToFilter = new HashMap<>();
mNetworkAgent = networkAgent;
@@ -103,40 +110,41 @@
mRcsStats = RcsStats.getInstance();
mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId();
- if (phone.isUsingNewDataStack()) {
- //TODO: Replace the NetworkAgent in the constructor with TelephonyNetworkAgent
- // after mPhone.isUsingNewDataStack() check is removed.
- ((TelephonyNetworkAgent) networkAgent).registerCallback(
- new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) {
- @Override
- public void onQosCallbackRegistered(int qosCallbackId,
- @NonNull QosFilter filter) {
- addFilter(qosCallbackId,
- new QosCallbackTracker.IFilter() {
- @Override
- public boolean matchesLocalAddress(
- @NonNull InetAddress address, int startPort,
- int endPort) {
- return filter.matchesLocalAddress(address, startPort,
- endPort);
- }
+ networkAgent.registerCallback(
+ new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) {
+ @Override
+ public void onQosCallbackRegistered(int qosCallbackId,
+ @NonNull QosFilter filter) {
+ addFilter(qosCallbackId,
+ new QosCallbackTracker.IFilter() {
+ @Override
+ public boolean matchesLocalAddress(
+ @NonNull InetAddress address, int startPort,
+ int endPort) {
+ return filter.matchesLocalAddress(address, startPort,
+ endPort);
+ }
- @Override
- public boolean matchesRemoteAddress(
- @NonNull InetAddress address, int startPort,
- int endPort) {
- return filter.matchesRemoteAddress(address, startPort,
- endPort);
- }
- });
- }
+ @Override
+ public boolean matchesRemoteAddress(
+ @NonNull InetAddress address, int startPort,
+ int endPort) {
+ return filter.matchesRemoteAddress(address, startPort,
+ endPort);
+ }
- @Override
- public void onQosCallbackUnregistered(int qosCallbackId) {
+ @Override
+ public boolean matchesProtocol(int protocol) {
+ return filter.matchesProtocol(protocol);
+ }
+ });
+ }
- }
- });
- }
+ @Override
+ public void onQosCallbackUnregistered(int qosCallbackId) {
+
+ }
+ });
}
/**
@@ -280,41 +288,76 @@
private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter,
final @NonNull IFilter filter) {
- if (sessionFilter.getLocalPortRange() == null) return false;
- for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) {
- return filter.matchesLocalAddress(qosAddress.getAddress(),
- sessionFilter.getLocalPortRange().getStart(),
- sessionFilter.getLocalPortRange().getEnd());
+ int portStart;
+ int portEnd;
+ if (sessionFilter.getLocalPortRange() == null) {
+ portStart = QosBearerFilter.QOS_MIN_PORT;
+ portEnd = QosBearerFilter.QOS_MAX_PORT;
+ } else if (sessionFilter.getLocalPortRange().isValid()) {
+ portStart = sessionFilter.getLocalPortRange().getStart();
+ portEnd = sessionFilter.getLocalPortRange().getEnd();
+ } else {
+ return false;
+ }
+ if (sessionFilter.getLocalAddresses().isEmpty()) {
+ InetAddress anyAddress;
+ try {
+ anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ return filter.matchesLocalAddress(anyAddress, portStart, portEnd);
+ } else {
+ for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) {
+ return filter.matchesLocalAddress(qosAddress.getAddress(), portStart, portEnd);
+ }
}
return false;
}
private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter,
final @NonNull IFilter filter) {
- if (sessionFilter.getRemotePortRange() == null) return false;
- for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) {
- return filter.matchesRemoteAddress(qosAddress.getAddress(),
- sessionFilter.getRemotePortRange().getStart(),
- sessionFilter.getRemotePortRange().getEnd());
+ int portStart;
+ int portEnd;
+ boolean result = false;
+ if (sessionFilter.getRemotePortRange() == null) {
+ portStart = QosBearerFilter.QOS_MIN_PORT;
+ portEnd = QosBearerFilter.QOS_MAX_PORT;
+ } else if (sessionFilter.getRemotePortRange().isValid()) {
+ portStart = sessionFilter.getRemotePortRange().getStart();
+ portEnd = sessionFilter.getRemotePortRange().getEnd();
+ } else {
+ return false;
}
- return false;
- }
-
- private boolean matchesByRemoteAndLocalAddress(@NonNull QosBearerFilter sessionFilter,
- final @NonNull IFilter filter) {
- if (sessionFilter.getLocalPortRange() == null
- || sessionFilter.getRemotePortRange() == null) return false;
- for (final LinkAddress remoteAddress : sessionFilter.getRemoteAddresses()) {
- for (final LinkAddress localAddress : sessionFilter.getLocalAddresses()) {
- return filter.matchesRemoteAddress(remoteAddress.getAddress(),
- sessionFilter.getRemotePortRange().getStart(),
- sessionFilter.getRemotePortRange().getEnd())
- && filter.matchesLocalAddress(localAddress.getAddress(),
- sessionFilter.getLocalPortRange().getStart(),
- sessionFilter.getLocalPortRange().getEnd());
+ if (sessionFilter.getRemoteAddresses().isEmpty()) {
+ InetAddress anyAddress;
+ try {
+ anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ result = filter.matchesRemoteAddress(anyAddress, portStart, portEnd);
+ } else {
+ for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) {
+ result = filter.matchesRemoteAddress(qosAddress.getAddress(), portStart, portEnd);
}
}
- return false;
+ return result;
+ }
+
+ private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter,
+ final @NonNull IFilter filter, boolean hasMatchedFilter) {
+ boolean result = false;
+ int protocol = sessionFilter.getProtocol();
+ if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP
+ || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) {
+ result = filter.matchesProtocol(protocol);
+ } else {
+ // FWK currently doesn't support filtering based on protocol ID ESP & AH. We will follow
+ // match results of other filters.
+ result = hasMatchedFilter;
+ }
+ return result;
}
private QosBearerFilter getFilterByPrecedence(
@@ -329,28 +372,36 @@
QosBearerFilter qosFilter = null;
for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
+ boolean unMatched = false;
+ boolean hasMatchedFilter = false;
if (!sessionFilter.getLocalAddresses().isEmpty()
- && !sessionFilter.getRemoteAddresses().isEmpty()
- && sessionFilter.getLocalPortRange() != null
- && sessionFilter.getLocalPortRange().isValid()
- && sessionFilter.getRemotePortRange() != null
- && sessionFilter.getRemotePortRange().isValid()) {
- if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) {
- qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ || sessionFilter.getLocalPortRange() != null) {
+ if (!matchesByLocalAddress(sessionFilter, filter)) {
+ unMatched = true;
+ } else {
+ hasMatchedFilter = true;
}
- } else if (!sessionFilter.getRemoteAddresses().isEmpty()
- && sessionFilter.getRemotePortRange() != null
- && sessionFilter.getRemotePortRange().isValid()) {
- if (matchesByRemoteAddress(sessionFilter, filter)) {
- qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
+ if (!sessionFilter.getRemoteAddresses().isEmpty()
+ || sessionFilter.getRemotePortRange() != null) {
+ if (!matchesByRemoteAddress(sessionFilter, filter)) {
+ unMatched = true;
+ } else {
+ hasMatchedFilter = true;
}
- } else if (!sessionFilter.getLocalAddresses().isEmpty()
- && sessionFilter.getLocalPortRange() != null
- && sessionFilter.getLocalPortRange().isValid()) {
- if (matchesByLocalAddress(sessionFilter, filter)) {
- qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
+
+ if (sessionFilter.getProtocol() != QosBearerFilter.QOS_PROTOCOL_UNSPECIFIED) {
+ if (!matchesByProtocol(sessionFilter, filter, hasMatchedFilter)) {
+ unMatched = true;
+ } else {
+ hasMatchedFilter = true;
}
}
+
+ if (!unMatched && hasMatchedFilter) {
+ qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
}
return qosFilter;
}
@@ -375,7 +426,7 @@
qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
remoteAddresses);
- mNetworkAgent.notifyQosSessionAvailable(
+ mNetworkAgent.sendQosSessionAvailable(
callbackId, session.getQosBearerSessionId(), epsBearerAttr);
} else {
NrQos qos = (NrQos) session.getQos();
@@ -386,7 +437,7 @@
qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
qos.getAveragingWindow(), remoteAddresses);
- mNetworkAgent.notifyQosSessionAvailable(
+ mNetworkAgent.sendQosSessionAvailable(
callbackId, session.getQosBearerSessionId(), nrQosAttr);
}
@@ -397,7 +448,7 @@
}
private void sendSessionLost(int callbackId, @NonNull QosBearerSession session) {
- mNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(),
+ mNetworkAgent.sendQosSessionLost(callbackId, session.getQosBearerSessionId(),
session.getQos() instanceof EpsQos
? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER);
log("sendSessionLost, callbackId=" + callbackId);
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
index 3b12562..5ed09e6 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
@@ -22,10 +22,10 @@
import android.net.KeepalivePacketData;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkScore;
import android.net.QosFilter;
-import android.net.QosSessionAttributes;
import android.net.Uri;
import android.os.Looper;
import android.util.ArraySet;
@@ -46,11 +46,13 @@
* for telephony to propagate network related information to the connectivity service. It always
* has an associated parent {@link DataNetwork}.
*/
-public class TelephonyNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface {
+public class TelephonyNetworkAgent extends NetworkAgent {
private final String mLogTag;
- private final Phone mPhone;
private final LocalLog mLocalLog = new LocalLog(128);
+ /** Max unregister network agent delay. */
+ private static final int NETWORK_AGENT_TEARDOWN_DELAY_MS = 5_000;
+
/** The parent data network. */
private final @NonNull DataNetwork mDataNetwork;
@@ -153,18 +155,25 @@
@NonNull NetworkAgentConfig config, @NonNull NetworkProvider provider,
@NonNull TelephonyNetworkAgentCallback callback) {
super(phone.getContext(), looper, "TelephonyNetworkAgent",
- dataNetwork.getNetworkCapabilities(), dataNetwork.getLinkProperties(), score,
+ // Connectivity service does not allow an agent created in suspended state.
+ // Always create the network agent with NOT_SUSPENDED and immediately update the
+ // suspended state afterwards.
+ new NetworkCapabilities.Builder(dataNetwork.getNetworkCapabilities())
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .build(),
+ dataNetwork.getLinkProperties(), score,
config, provider);
register();
mDataNetwork = dataNetwork;
mNetworkAgentConfig = config;
mTelephonyNetworkAgentCallbacks.add(callback);
- mPhone = phone;
mId = getNetwork().getNetId();
mLogTag = "TNA-" + mId;
log("TelephonyNetworkAgent created, nc="
- + dataNetwork.getNetworkCapabilities() + ", score=" + score);
+ + new NetworkCapabilities.Builder(dataNetwork.getNetworkCapabilities())
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .build() + ", score=" + score);
}
/**
@@ -289,46 +298,16 @@
}
/**
- * Sends the attributes of Qos Session back to the Application. This method is create for
- * Mockito to mock since
- * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is
- * {@code final} that can't be mocked.
- *
- * @param qosCallbackId the callback id that the session belongs to.
- * @param sessionId the unique session id across all Qos Sessions.
- * @param attributes the attributes of the Qos Session.
- */
- @Override
- public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
- @NonNull final QosSessionAttributes attributes) {
- super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
- }
-
- /**
- * Sends event that the Qos Session was lost. This method is create for Mockito to mock
- * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be
- * mocked..
- *
- * @param qosCallbackId the callback id that the session belongs to.
- * @param sessionId the unique session id across all Qos Sessions.
- * @param qosSessionType the session type {@code QosSession#QosSessionType}.
- */
- @Override
- public void notifyQosSessionLost(final int qosCallbackId,
- final int sessionId, final int qosSessionType) {
- super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
- }
-
- /**
* Abandon the network agent. This is used for telephony to re-create the network agent when
- * immutable capabilities got changed, where telephony calls {@link NetworkAgent#unregister()}
- * and then create another network agent with new capabilities. Abandon this network agent
- * allowing it ignore the subsequent {@link #onNetworkUnwanted()} invocation caused by
- * {@link NetworkAgent#unregister()}.
+ * immutable capabilities got changed, where telephony calls
+ * {@link NetworkAgent#unregisterAfterReplacement} and then create another network agent with
+ * new capabilities. Abandon this network agent allowing it ignore the subsequent
+ * {@link #onNetworkUnwanted()} invocation caused by
+ * {@link NetworkAgent#unregisterAfterReplacement}.
*/
public void abandon() {
mAbandoned = true;
- unregister();
+ unregisterAfterReplacement(NETWORK_AGENT_TEARDOWN_DELAY_MS);
}
/**
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index 85f0ae1..2cdf807 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -20,27 +20,15 @@
import android.net.NetworkFactory;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
-import android.os.AsyncResult;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.dataconnection.ApnContext;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
import com.android.internal.telephony.metrics.NetworkRequestsStats;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -72,19 +60,14 @@
public static final int EVENT_SUBSCRIPTION_CHANGED = 2;
private static final int EVENT_NETWORK_REQUEST = 3;
private static final int EVENT_NETWORK_RELEASE = 4;
- private static final int EVENT_DATA_HANDOVER_NEEDED = 5;
- private static final int EVENT_DATA_HANDOVER_COMPLETED = 6;
private final PhoneSwitcher mPhoneSwitcher;
- private final SubscriptionController mSubscriptionController;
private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE);
- // Key: network request. Value: the transport of DcTracker it applies to,
+ // Key: network request. Value: the transport of the network request applies to,
// AccessNetworkConstants.TRANSPORT_TYPE_INVALID if not applied.
private final Map<TelephonyNetworkRequest, Integer> mNetworkRequests = new HashMap<>();
- private final Map<Message, HandoverParams> mPendingHandovers = new HashMap<>();
-
private final Phone mPhone;
private AccessNetworksManager mAccessNetworksManager;
@@ -101,10 +84,9 @@
mPhone = phone;
mInternalHandler = new InternalHandler(looper);
- mSubscriptionController = SubscriptionController.getInstance();
mAccessNetworksManager = mPhone.getAccessNetworksManager();
- setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId()));
+ setCapabilityFilter(makeNetworkFilterByPhoneId(mPhone.getPhoneId()));
setScoreFilter(TELEPHONY_NETWORK_SCORE);
mPhoneSwitcher = PhoneSwitcher.getInstance();
@@ -112,10 +94,6 @@
mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH,
null);
- if (!phone.isUsingNewDataStack()) {
- mPhone.getTransportManager().registerForHandoverNeededEvent(mInternalHandler,
- EVENT_DATA_HANDOVER_NEEDED);
- }
mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
@@ -132,10 +110,8 @@
}
};
- private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController,
- int phoneId) {
- final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId);
- return makeNetworkFilter(subscriptionId);
+ private NetworkCapabilities makeNetworkFilterByPhoneId(int phoneId) {
+ return makeNetworkFilter(SubscriptionManager.getSubscriptionId(phoneId));
}
/**
@@ -201,93 +177,18 @@
onReleaseNetworkFor(msg);
break;
}
- case EVENT_DATA_HANDOVER_NEEDED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- HandoverParams handoverParams = (HandoverParams) ar.result;
- onDataHandoverNeeded(handoverParams.apnType, handoverParams.targetTransport,
- handoverParams);
- break;
- }
- case EVENT_DATA_HANDOVER_COMPLETED: {
- Bundle bundle = msg.getData();
- NetworkRequest nr = bundle.getParcelable(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
- boolean success = bundle.getBoolean(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
- int transport = bundle.getInt(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
- boolean fallback = bundle.getBoolean(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
- HandoverParams handoverParams = mPendingHandovers.remove(msg);
- if (handoverParams != null) {
- onDataHandoverSetupCompleted(nr, success, transport, fallback,
- handoverParams);
- } else {
- logl("Handover completed but cannot find handover entry!");
- }
- break;
- }
}
}
}
private int getTransportTypeFromNetworkRequest(TelephonyNetworkRequest networkRequest) {
- if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
- int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
- int capability = networkRequest.getApnTypeNetworkCapability();
- if (capability >= 0) {
- transport = mAccessNetworksManager
- .getPreferredTransportByNetworkCapability(capability);
- }
- return transport;
- } else {
- int apnType = ApnContext.getApnTypeFromNetworkRequest(
- networkRequest.getNativeNetworkRequest());
- return mAccessNetworksManager.getCurrentTransport(apnType);
+ int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+ int capability = networkRequest.getApnTypeNetworkCapability();
+ if (capability >= 0) {
+ transport = mAccessNetworksManager
+ .getPreferredTransportByNetworkCapability(capability);
}
- }
-
- /**
- * Request network
- *
- * @param networkRequest Network request from clients
- * @param requestType The request type
- * @param transport Transport type
- * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
- * handover is completed. For normal request, this should be null.
- */
- private void requestNetworkInternal(TelephonyNetworkRequest networkRequest,
- @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) {
- NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
- mSubscriptionId);
-
- if (mPhone.isUsingNewDataStack()) {
- mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
- } else {
- if (mPhone.getDcTracker(transport) != null) {
- mPhone.getDcTracker(transport).requestNetwork(
- networkRequest.getNativeNetworkRequest(), requestType,
- onHandoverCompleteMsg);
- }
- }
- }
-
- private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest) {
- mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
- }
-
- // TODO: Clean this up after old data stack removed.
- private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest,
- @ReleaseNetworkType int releaseType,
- int transport) {
- if (mPhone.isUsingNewDataStack()) {
- mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
- } else {
- if (mPhone.getDcTracker(transport) != null) {
- mPhone.getDcTracker(transport).releaseNetwork(
- networkRequest.getNativeNetworkRequest(), releaseType);
- }
- }
+ return transport;
}
private static int getAction(boolean wasActive, boolean isActive) {
@@ -317,15 +218,11 @@
? "Requesting" : "Releasing") + " network request " + networkRequest);
int transportType = getTransportTypeFromNetworkRequest(networkRequest);
if (action == ACTION_REQUEST) {
- requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
- getTransportTypeFromNetworkRequest(networkRequest), null);
+ NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
+ mSubscriptionId);
+ mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
} else if (action == ACTION_RELEASE) {
- if (mPhone.isUsingNewDataStack()) {
- releaseNetworkInternal(networkRequest);
- } else {
- releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_DETACH,
- getTransportTypeFromNetworkRequest(networkRequest));
- }
+ mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
}
mNetworkRequests.put(networkRequest,
@@ -336,8 +233,7 @@
// watch for phone->subId changes, reapply new filter and let
// that flow through to apply/revoke of requests
private void onSubIdChange() {
- final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId(
- mPhone.getPhoneId());
+ int newSubscriptionId = SubscriptionManager.getSubscriptionId(mPhone.getPhoneId());
if (mSubscriptionId != newSubscriptionId) {
if (DBG) logl("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId);
mSubscriptionId = newSubscriptionId;
@@ -365,8 +261,9 @@
logl("onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply);
if (shouldApply) {
- requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
- getTransportTypeFromNetworkRequest(networkRequest), null);
+ NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
+ mSubscriptionId);
+ mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
}
}
@@ -388,136 +285,10 @@
logl("onReleaseNetworkFor " + networkRequest + " applied " + applied);
if (applied) {
- if (mPhone.isUsingNewDataStack()) {
- releaseNetworkInternal(networkRequest);
- } else {
- // Most of the time, the network request only exists in one of the DcTracker, but in
- // the middle of handover, the network request temporarily exists in both
- // DcTrackers. If connectivity service releases the network request while handover
- // is ongoing, we need to remove network requests from both DcTrackers.
- // Note that this part will be refactored in T, where we won't even have DcTracker
- // at all.
- releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- }
+ mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
}
}
- private void onDataHandoverNeeded(@ApnType int apnType, int targetTransport,
- HandoverParams handoverParams) {
- log("onDataHandoverNeeded: apnType=" + ApnSetting.getApnTypeString(apnType)
- + ", target transport="
- + AccessNetworkConstants.transportTypeToString(targetTransport));
- if (mAccessNetworksManager.getCurrentTransport(apnType) == targetTransport) {
- log("APN type " + ApnSetting.getApnTypeString(apnType) + " is already on "
- + AccessNetworkConstants.transportTypeToString(targetTransport));
- return;
- }
-
- boolean handoverPending = false;
- for (Map.Entry<TelephonyNetworkRequest, Integer> entry : mNetworkRequests.entrySet()) {
- TelephonyNetworkRequest networkRequest = entry.getKey();
- int currentTransport = entry.getValue();
- boolean applied = currentTransport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
- if (ApnContext.getApnTypeFromNetworkRequest(
- networkRequest.getNativeNetworkRequest()) == apnType
- && applied
- && currentTransport != targetTransport) {
- DcTracker dcTracker = mPhone.getDcTracker(currentTransport);
- if (dcTracker != null) {
- DataConnection dc = dcTracker.getDataConnectionByApnType(
- ApnSetting.getApnTypeString(apnType));
- if (dc != null && (dc.isActive())) {
- Message onCompleteMsg = mInternalHandler.obtainMessage(
- EVENT_DATA_HANDOVER_COMPLETED);
- onCompleteMsg.getData().putParcelable(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST,
- networkRequest.getNativeNetworkRequest());
- mPendingHandovers.put(onCompleteMsg, handoverParams);
- requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER,
- targetTransport, onCompleteMsg);
- log("Requested handover " + ApnSetting.getApnTypeString(apnType)
- + " to "
- + AccessNetworkConstants.transportTypeToString(targetTransport)
- + ". " + networkRequest);
- handoverPending = true;
- } else {
- // Request is there, but no actual data connection. In this case, just move
- // the request to the new transport.
- log("The network request is on transport " + AccessNetworkConstants
- .transportTypeToString(currentTransport) + ", but no live data "
- + "connection. Just move the request to transport "
- + AccessNetworkConstants.transportTypeToString(targetTransport)
- + ", dc=" + dc);
- entry.setValue(targetTransport);
- releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
- currentTransport);
- requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
- targetTransport, null);
- }
- } else {
- log("DcTracker on " + AccessNetworkConstants.transportTypeToString(
- currentTransport) + " is not available.");
- }
- }
- }
-
- if (!handoverPending) {
- log("No handover request pending. Handover process is now completed");
- handoverParams.callback.onCompleted(true, false);
- }
- }
-
- private void onDataHandoverSetupCompleted(NetworkRequest request, boolean success,
- int targetTransport, boolean fallback,
- HandoverParams handoverParams) {
- log("onDataHandoverSetupCompleted: " + request + ", success=" + success
- + ", targetTransport="
- + AccessNetworkConstants.transportTypeToString(targetTransport)
- + ", fallback=" + fallback);
-
- TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(request, mPhone);
- // At this point, handover setup has been completed on the target transport.
- // If it succeeded, or it failed without falling back to the original transport,
- // we should release the request from the original transport.
- if (!fallback) {
- int originTransport = DataUtils.getSourceTransport(targetTransport);
- int releaseType = success
- ? DcTracker.RELEASE_TYPE_HANDOVER
- // If handover fails, we need to tear down the existing connection, so the
- // new data connection can be re-established on the new transport. If we leave
- // the existing data connection in current transport, then DCT and qualified
- // network service will be out of sync. Specifying release type to detach
- // the transport is moved to the other transport, but network request is still
- // there, connectivity service will not call unwanted to tear down the network.
- // We need explicitly tear down the data connection here so the new data
- // connection can be re-established on the other transport.
- : DcTracker.RELEASE_TYPE_DETACH;
- releaseNetworkInternal(networkRequest, releaseType, originTransport);
-
- // Before updating the network request with the target transport, make sure the request
- // is still there because it's possible that connectivity service has already released
- // the network while handover is ongoing. If connectivity service already released
- // the network request, we need to tear down the just-handovered data connection on the
- // target transport.
- if (mNetworkRequests.containsKey(networkRequest)) {
- // Update it with the target transport.
- mNetworkRequests.put(networkRequest, targetTransport);
- }
- } else {
- // If handover fails and requires to fallback, the context of target transport needs to
- // be released
- if (!success) {
- releaseNetworkInternal(networkRequest,
- DcTracker.RELEASE_TYPE_NORMAL, targetTransport);
- }
- }
-
- handoverParams.callback.onCompleted(success, fallback);
- }
-
protected void log(String s) {
Rlog.d(LOG_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index b55304a..b334b89 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -157,8 +157,7 @@
/**
* Data config manager for retrieving data config.
*/
- // TODO: Make this @NonNull after old data stack removed.
- private final @Nullable DataConfigManager mDataConfigManager;
+ private final @NonNull DataConfigManager mDataConfigManager;
/**
* The attached data network. Note that the data network could be in any state. {@code null}
@@ -204,12 +203,8 @@
// to satisfy it.
mState = REQUEST_STATE_UNSATISFIED;
mCreatedTimeMillis = SystemClock.elapsedRealtime();
- if (phone.isUsingNewDataStack()) {
- mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
- updatePriority();
- } else {
- mDataConfigManager = null;
- }
+ mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
+ updatePriority();
}
/**
@@ -401,8 +396,7 @@
* @return {@code true} if this network request can result in bringing up a metered network.
*/
public boolean isMeteredRequest() {
- // TODO: Remove null check after old data stack removed.
- return mDataConfigManager != null && mDataConfigManager.isAnyMeteredCapability(
+ return mDataConfigManager.isAnyMeteredCapability(
getCapabilities(), mPhone.getServiceState().getDataRoaming());
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java
deleted file mode 100644
index 827dbdd..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.telephony.Annotation;
-
-/**
- * Container of network configuration settings relevant for telephony module.
- *
- */
-public class ApnConfigType {
-
- private final int mType;
- private final int mPriority;
-
- public ApnConfigType(@Annotation.ApnType int type, int priority) {
- mType = type;
- mPriority = priority;
- }
-
- /**
- * Returns the apn type of this config type
- * @return Type of apn.
- */
- public int getType() {
- return mType;
- }
-
- /**
- * Returns the priority of this apn config type.
- * @return The priority of this apn.
- */
- public int getPriority() {
- return mPriority;
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
deleted file mode 100644
index 156ac92..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.PersistableBundle;
-import android.telephony.Annotation;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.data.ApnSetting;
-import android.util.ArrayMap;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Hard coded configuration of specific network types that the telephony module needs.
- * Formerly stored in network attributes within the resources file.
- */
-public class ApnConfigTypeRepository {
-
- private static final String TAG = ApnConfigTypeRepository.class.getSimpleName();
-
- private final Map<Integer, ApnConfigType> mConfigTypeMap;
-
- public ApnConfigTypeRepository(PersistableBundle carrierConfig) {
- mConfigTypeMap = new HashMap<>();
- setup(carrierConfig);
- }
-
- /**
- * Gets list of apn config types.
- * @return All apn config types.
- */
- public Collection<ApnConfigType> getTypes() {
- return mConfigTypeMap.values();
- }
-
- /**
- * Gets the apn config type by apn type.
- * @param type The ApnType to search for.
- * @return The config type matching the given apn type.
- */
- @Nullable
- public ApnConfigType getByType(@Annotation.ApnType int type) {
- return mConfigTypeMap.get(type);
- }
-
- private void setup(PersistableBundle carrierConfig) {
- addApns(getCarrierApnTypeMap(CarrierConfigManager.getDefaultConfig()));
- addApns(getCarrierApnTypeMap(carrierConfig));
- }
-
- private void addApns(Map<Integer, Integer> apnTypeMap) {
- add(ApnSetting.TYPE_DEFAULT, apnTypeMap);
- add(ApnSetting.TYPE_MMS, apnTypeMap);
- add(ApnSetting.TYPE_SUPL, apnTypeMap);
- add(ApnSetting.TYPE_DUN, apnTypeMap);
- add(ApnSetting.TYPE_HIPRI, apnTypeMap);
- add(ApnSetting.TYPE_FOTA, apnTypeMap);
- add(ApnSetting.TYPE_IMS, apnTypeMap);
- add(ApnSetting.TYPE_CBS, apnTypeMap);
- add(ApnSetting.TYPE_IA, apnTypeMap);
- add(ApnSetting.TYPE_EMERGENCY, apnTypeMap);
- add(ApnSetting.TYPE_MCX, apnTypeMap);
- add(ApnSetting.TYPE_XCAP, apnTypeMap);
- add(ApnSetting.TYPE_ENTERPRISE, apnTypeMap);
- }
-
- @NonNull
- private Map<Integer, Integer> getCarrierApnTypeMap(PersistableBundle carrierConfig) {
- if (carrierConfig == null) {
- Rlog.w(TAG, "carrier config is null");
- return new ArrayMap<>();
- }
-
- final String[] apnTypeConfig =
- carrierConfig.getStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY);
-
- final Map<Integer, Integer> apnTypeMap = new ArrayMap<>();
- if (apnTypeConfig != null) {
- for (final String entry : apnTypeConfig) {
- try {
- final String[] keyValue = entry.split(":");
- if (keyValue.length != 2) {
- Rlog.e(TAG, "Apn type entry must have exactly one ':'");
- } else if (keyValue[0].contains(",")) {
- //getApnTypesBitmaskFromString parses commas to a list, not valid here.
- Rlog.e(TAG, "Invalid apn type name, entry: " + entry);
- } else {
- int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(keyValue[0]);
- if (apnTypeBitmask > 0) {
- apnTypeMap.put(apnTypeBitmask, Integer.parseInt(keyValue[1]));
- } else {
- Rlog.e(TAG, "Invalid apn type name, entry: " + entry);
- }
- }
-
- } catch (Exception ex) {
- Rlog.e(TAG, "Exception on apn type entry: " + entry + "\n", ex);
- }
- }
- }
- return apnTypeMap;
- }
-
- private void add(@Annotation.ApnType int type, Map<Integer, Integer> apnTypeMap) {
- if (apnTypeMap.containsKey(type)) {
- mConfigTypeMap.put(type, new ApnConfigType(type, apnTypeMap.get(type)));
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
deleted file mode 100644
index 3f487cd..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ /dev/null
@@ -1,675 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.os.Message;
-import android.telephony.Annotation.ApnType;
-import android.telephony.data.ApnSetting;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.LocalLog;
-import android.util.SparseIntArray;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Maintain the Apn context
- */
-public class ApnContext {
-
- public final String LOG_TAG;
- private final static String SLOG_TAG = "ApnContext";
-
- protected static final boolean DBG = false;
-
- private final Phone mPhone;
-
- private final String mApnType;
-
- private DctConstants.State mState;
-
- private int mPriority;
-
- private ApnSetting mApnSetting;
-
- private DataConnection mDataConnection;
-
- private String mReason;
-
- /**
- * user/app requested connection on this APN
- */
- AtomicBoolean mDataEnabled;
-
- private final Object mRefCountLock = new Object();
-
- private final DcTracker mDcTracker;
-
-
- /**
- * Remember this as a change in this value to a more permissive state
- * should cause us to retry even permanent failures
- */
- private boolean mConcurrentVoiceAndDataAllowed;
-
- /**
- * used to track a single connection request so disconnects can get ignored if
- * obsolete.
- */
- private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
-
- /**
- * Retry manager that handles the APN retry and delays.
- */
- private final RetryManager mRetryManager;
-
- /**
- * ApnContext constructor
- * @param phone phone object
- * @param typeId APN type Id
- * @param logTag Tag for logging
- * @param tracker Data call tracker
- * @param priority Priority of APN type
- */
- public ApnContext(Phone phone, int typeId, String logTag, DcTracker tracker, int priority) {
- this(phone, ApnSetting.getApnTypeString(typeId), logTag, tracker, priority);
- }
-
- /**
- * ApnContext constructor
- * @param phone phone object
- * @param apnType APN type (e.g. default, supl, mms, etc...)
- * @param logTag Tag for logging
- * @param tracker Data call tracker
- * @param priority Priority of APN type
- */
- public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority) {
- mPhone = phone;
- mApnType = apnType;
- mState = DctConstants.State.IDLE;
- setReason(Phone.REASON_DATA_ENABLED);
- mDataEnabled = new AtomicBoolean(false);
- mPriority = priority;
- LOG_TAG = logTag;
- mDcTracker = tracker;
- mRetryManager = new RetryManager(phone, tracker.getDataThrottler(),
- ApnSetting.getApnTypesBitmaskFromString(apnType));
- }
-
-
-
- /**
- * Get the APN type
- * @return The APN type
- */
- public String getApnType() {
- return mApnType;
- }
-
- /**
- * Gets the APN type bitmask.
- * @return The APN type bitmask
- */
- public int getApnTypeBitmask() {
- return ApnSetting.getApnTypesBitmaskFromString(mApnType);
- }
-
- /**
- * Get the associated data connection
- * @return The data connection
- */
- public synchronized DataConnection getDataConnection() {
- return mDataConnection;
- }
-
- /**
- * This priority is taken into account when concurrent data connections are not allowed. The
- * APN with the HIGHER priority is given preference.
- * @return The priority of the APN type
- */
- public int getPriority() {
- return mPriority;
- }
-
- /**
- * Updates the priority of this context.
- * @param priority The priority of the APN type
- */
- public void setPriority(int priority) {
- mPriority = priority;
- }
-
- /**
- * Keeping for backwards compatibility and in case it's needed in the future
- * @return true
- */
- public boolean isDependencyMet() {
- return true;
- }
-
- /**
- * Set the associated data connection.
- * @param dc data connection
- */
- public synchronized void setDataConnection(DataConnection dc) {
- log("setDataConnectionAc: old=" + mDataConnection + ",new=" + dc + " this=" + this);
- mDataConnection = dc;
- }
-
- /**
- * Release data connection.
- * @param reason The reason of releasing data connection
- */
- public synchronized void releaseDataConnection(String reason) {
- if (mDataConnection != null) {
- mDataConnection.tearDown(this, reason, null);
- mDataConnection = null;
- }
- setState(DctConstants.State.IDLE);
- }
-
- /**
- * Get the current APN setting.
- * @return APN setting
- */
- public synchronized ApnSetting getApnSetting() {
- log("getApnSetting: apnSetting=" + mApnSetting);
- return mApnSetting;
- }
-
- /**
- * Set the APN setting.
- * @param apnSetting APN setting
- */
- public synchronized void setApnSetting(ApnSetting apnSetting) {
- log("setApnSetting: apnSetting=" + apnSetting);
- mApnSetting = apnSetting;
- }
-
- /**
- * Set the list of APN candidates which will be used for data call setup later.
- * @param waitingApns List of APN candidates
- */
- public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
- mRetryManager.setWaitingApns(waitingApns);
- }
-
- /**
- * Get the next available APN to try.
- * @return APN setting which will be used for data call setup.{@code null} if there is no
- * APN can be retried.
- */
- public @Nullable ApnSetting getNextApnSetting() {
- return mRetryManager.getNextApnSetting();
- }
-
- /**
- * Get the delay for trying the next APN setting if the current one failed.
- * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
- * delay.
- * @return The delay in milliseconds
- */
- public long getDelayForNextApn(boolean failFastEnabled) {
- return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason());
- }
-
- /**
- * Mark the current APN setting permanently failed, which means it will not be retried anymore.
- * @param apn APN setting
- */
- public void markApnPermanentFailed(ApnSetting apn) {
- mRetryManager.markApnPermanentFailed(apn);
- }
-
- /**
- * Get the list of waiting APNs.
- * @return the list of waiting APNs
- */
- public @NonNull ArrayList<ApnSetting> getWaitingApns() {
- return mRetryManager.getWaitingApns();
- }
-
- /**
- * Save the state indicating concurrent voice/data allowed.
- * @param allowed True if concurrent voice/data is allowed
- */
- public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
- mConcurrentVoiceAndDataAllowed = allowed;
- }
-
- /**
- * Get the state indicating concurrent voice/data allowed.
- * @return True if concurrent voice/data is allowed
- */
- public synchronized boolean isConcurrentVoiceAndDataAllowed() {
- return mConcurrentVoiceAndDataAllowed;
- }
-
- /**
- * Set the current data call state.
- * @param s Current data call state
- */
- public synchronized void setState(DctConstants.State s) {
- log("setState: " + s + ", previous state:" + mState);
-
- if (mState != s) {
- mStateLocalLog.log("State changed from " + mState + " to " + s);
- mState = s;
- }
-
- if (mState == DctConstants.State.FAILED) {
- // when teardown the connection and set to IDLE
- mRetryManager.getWaitingApns().clear();
- }
- }
-
- /**
- * Get the current data call state.
- * @return The current data call state
- */
- public synchronized DctConstants.State getState() {
- return mState;
- }
-
- /**
- * Check whether the data call is disconnected or not.
- * @return True if the data call is disconnected
- */
- public boolean isDisconnected() {
- DctConstants.State currentState = getState();
- return ((currentState == DctConstants.State.IDLE) ||
- currentState == DctConstants.State.FAILED);
- }
-
- /**
- * Set the reason for data call connection.
- * @param reason Reason for data call connection
- */
- public synchronized void setReason(String reason) {
- log("set reason as " + reason + ",current state " + mState);
- mReason = reason;
- }
-
- /**
- * Get the reason for data call connection.
- * @return The reason for data call connection
- */
- public synchronized String getReason() {
- return mReason;
- }
-
- /**
- * Check if ready for data call connection
- * @return True if ready, otherwise false.
- */
- public boolean isReady() {
- return mDataEnabled.get() && isDependencyMet();
- }
-
- /**
- * Check if the data call is in the state which allow connecting.
- * @return True if allowed, otherwise false.
- */
- public boolean isConnectable() {
- return isReady() && ((mState == DctConstants.State.IDLE)
- || (mState == DctConstants.State.RETRYING)
- || (mState == DctConstants.State.FAILED));
- }
-
- /**
- * Check if apn reason is fast retry reason which should apply shorter delay between apn re-try.
- * @return True if it is fast retry reason, otherwise false.
- */
- private boolean isFastRetryReason() {
- return Phone.REASON_NW_TYPE_CHANGED.equals(mReason) ||
- Phone.REASON_APN_CHANGED.equals(mReason);
- }
-
- /** Check if the data call is in connected or connecting state.
- * @return True if the data call is in connected or connecting state
- */
- public boolean isConnectedOrConnecting() {
- return isReady() && ((mState == DctConstants.State.CONNECTED)
- || (mState == DctConstants.State.CONNECTING)
- || (mState == DctConstants.State.RETRYING));
- }
-
- /**
- * Set data call enabled/disabled state.
- * @param enabled True if data call is enabled
- */
- public void setEnabled(boolean enabled) {
- log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
- mDataEnabled.set(enabled);
- }
-
- /**
- * Check if the data call is enabled or not.
- * @return True if enabled
- */
- public boolean isEnabled() {
- return mDataEnabled.get();
- }
-
- public boolean isProvisioningApn() {
- String provisioningApn = mPhone.getContext().getResources()
- .getString(R.string.mobile_provisioning_apn);
- if (!TextUtils.isEmpty(provisioningApn) &&
- (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
- return (mApnSetting.getApnName().equals(provisioningApn));
- } else {
- return false;
- }
- }
-
- private final ArraySet<NetworkRequest> mNetworkRequests = new ArraySet<>();
- private final LocalLog mStateLocalLog = new LocalLog(32);
-
- private static final LocalLog sLocalLog = new LocalLog(256);
-
- /** Add a line to the ApnContext local log. */
- public static void requestLog(ApnContext apnContext, String str) {
- if (apnContext != null) {
- String logString = "[ApnContext:" + apnContext.getApnType() + "] " + str;
- if (DBG) {
- Rlog.d(SLOG_TAG, logString);
- }
- synchronized (sLocalLog) {
- sLocalLog.log(logString);
- }
- }
- }
-
- /**
- * Request a network
- *
- * @param networkRequest Network request from clients
- * @param type The request type
- * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
- * handover is completed. For normal request, this should be null.
- */
- public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
- Message onHandoverCompleteMsg) {
- synchronized (mRefCountLock) {
- mNetworkRequests.add(networkRequest);
- requestLog(this, "requestNetwork for " + networkRequest + ", type="
- + DcTracker.requestTypeToString(type));
- mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
- onHandoverCompleteMsg);
- if (mDataConnection != null) {
- // New network request added. Should re-evaluate properties of
- // the data connection. For example, the score may change.
- mDataConnection.reevaluateDataConnectionProperties();
- }
- }
- }
-
- public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
- synchronized (mRefCountLock) {
- if (mNetworkRequests.contains(networkRequest)) {
- mNetworkRequests.remove(networkRequest);
- if (mDataConnection != null) {
- // New network request added. Should re-evaluate properties of
- // the data connection. For example, the score may change.
- mDataConnection.reevaluateDataConnectionProperties();
- }
- requestLog(this, "releaseNetwork left with " + mNetworkRequests.size()
- + " requests.");
- if (mNetworkRequests.size() == 0
- || type == DcTracker.RELEASE_TYPE_DETACH
- || type == DcTracker.RELEASE_TYPE_HANDOVER) {
- mDcTracker.disableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type);
- }
- }
- }
- }
-
- /**
- * @param excludeDun True if excluding requests that have DUN capability
- * @return True if the attached network requests contain restricted capability.
- */
- public boolean hasRestrictedRequests(boolean excludeDun) {
- synchronized (mRefCountLock) {
- for (NetworkRequest nr : mNetworkRequests) {
- if (excludeDun &&
- nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- continue;
- }
- if (!nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
-
- public void resetErrorCodeRetries() {
- requestLog(this, "resetErrorCodeRetries");
-
- String[] config = mPhone.getContext().getResources().getStringArray(
- com.android.internal.R.array.config_cell_retries_per_error_code);
- synchronized (mRetriesLeftPerErrorCode) {
- mRetriesLeftPerErrorCode.clear();
-
- for (String c : config) {
- String errorValue[] = c.split(",");
- if (errorValue != null && errorValue.length == 2) {
- int count = 0;
- int errorCode = 0;
- try {
- errorCode = Integer.parseInt(errorValue[0]);
- count = Integer.parseInt(errorValue[1]);
- } catch (NumberFormatException e) {
- log("Exception parsing config_retries_per_error_code: " + e);
- continue;
- }
- if (count > 0 && errorCode > 0) {
- mRetriesLeftPerErrorCode.put(errorCode, count);
- }
- } else {
- log("Exception parsing config_retries_per_error_code: " + c);
- }
- }
- }
- }
-
- public boolean restartOnError(int errorCode) {
- boolean result = false;
- int retriesLeft = 0;
- synchronized(mRetriesLeftPerErrorCode) {
- retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
- switch (retriesLeft) {
- case 0: {
- // not set, never restart modem
- break;
- }
- case 1: {
- resetErrorCodeRetries();
- result = true;
- break;
- }
- default: {
- mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
- result = false;
- }
- }
- }
- requestLog(this, "restartOnError(" + errorCode + ") found " + retriesLeft
- + " and returned " + result);
- return result;
- }
-
- public int incAndGetConnectionGeneration() {
- return mConnectionGeneration.incrementAndGet();
- }
-
- public int getConnectionGeneration() {
- return mConnectionGeneration.get();
- }
-
- long getRetryAfterDisconnectDelay() {
- return mRetryManager.getRetryAfterDisconnectDelay();
- }
-
- /**
- * Get APN type from the network request.
- *
- * @param nr The network request.
- * @return The APN type.
- */
- public static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
- // For now, ignore the bandwidth stuff
- if (nr.getTransportTypes().length > 0
- && !nr.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- return ApnSetting.TYPE_NONE;
- }
-
- // in the near term just do 1-1 matches.
- // TODO - actually try to match the set of capabilities
- int apnType = ApnSetting.TYPE_NONE;
- boolean error = false;
-
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- apnType = ApnSetting.TYPE_DEFAULT;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_MMS;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_SUPL;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_DUN;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_FOTA;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_IMS;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_CBS;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_IA;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_EMERGENCY;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_MCX;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_XCAP;
- }
- if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) {
- if (apnType != ApnSetting.TYPE_NONE) error = true;
- apnType = ApnSetting.TYPE_ENTERPRISE;
- }
- if (error) {
- // TODO: If this error condition is removed, the framework's handling of
- // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
- // say FOTA and INTERNET are marked as restricted. This is not how
- // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
- Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
- }
- if (apnType == ApnSetting.TYPE_NONE) {
- Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
- }
- return apnType;
- }
-
- public List<NetworkRequest> getNetworkRequests() {
- synchronized (mRefCountLock) {
- return new ArrayList<NetworkRequest>(mNetworkRequests);
- }
- }
-
- @Override
- public synchronized String toString() {
- // We don't print mDataConnection because its recursive.
- return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={"
- + mRetryManager.getWaitingApns() + " priority=" + mPriority + "}"
- + " mApnSetting={" + mApnSetting
- + "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + "}";
- }
-
- private void log(String s) {
- if (DBG) {
- Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
- }
- }
-
- public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
- synchronized (mRefCountLock) {
- pw.println(toString());
- if (mNetworkRequests.size() > 0) {
- pw.println("NetworkRequests:");
- pw.increaseIndent();
- for (NetworkRequest nr : mNetworkRequests) {
- pw.println(nr);
- }
- pw.decreaseIndent();
- }
- pw.println("Historical APN state:");
- pw.increaseIndent();
- mStateLocalLog.dump(fd, pw, args);
- pw.decreaseIndent();
- pw.println(mRetryManager);
- pw.println("--------------------------");
- }
- }
-
- /** Dumps the ApnContext local log. */
- public static void dumpLocalLog(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- printWriter.println("Local log:");
- synchronized (sLocalLog) {
- sLocalLog.dump(fd, printWriter, args);
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
deleted file mode 100644
index 3c2a6ef..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-
-import com.android.internal.telephony.Phone;
-import com.android.telephony.Rlog;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-/**
- * This class represents a apn setting for create PDP link
- */
-public class ApnSettingUtils {
-
- static final String LOG_TAG = "ApnSetting";
-
- private static final boolean DBG = false;
-
- /**
- * Check if this APN type is metered.
- *
- * @param apnType the APN type
- * @param phone the phone object
- * @return {@code true} if the APN type is metered, {@code false} otherwise.
- */
- public static boolean isMeteredApnType(@ApnType int apnType, Phone phone) {
- if (phone == null) {
- return true;
- }
-
- boolean isRoaming = phone.getServiceState().getDataRoaming();
- int subId = phone.getSubId();
-
- String carrierConfig;
- // First check if the device is roaming. If yes, use the roaming metered APN list.
- // Otherwise use the normal metered APN list.
- if (isRoaming) {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
- } else {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
- }
-
- if (DBG) {
- Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming);
- }
-
- CarrierConfigManager configManager = (CarrierConfigManager)
- phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) {
- Rlog.e(LOG_TAG, "Carrier config service is not available");
- return true;
- }
-
- PersistableBundle b = configManager.getConfigForSubId(subId);
- if (b == null) {
- Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
- return true;
- }
-
- String[] meteredApnTypes = b.getStringArray(carrierConfig);
- if (meteredApnTypes == null) {
- Rlog.e(LOG_TAG, carrierConfig + " is not available. " + "subId = " + subId);
- return true;
- }
-
- HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
- if (DBG) {
- Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
- + Arrays.toString(meteredApnSet.toArray()));
- }
-
- if (meteredApnSet.contains(ApnSetting.getApnTypeString(apnType))) {
- if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is metered.");
- return true;
- } else if (apnType == ApnSetting.TYPE_ALL) {
- // Assuming no configuration error, if at least one APN type is
- // metered, then this APN setting is metered.
- if (meteredApnSet.size() > 0) {
- if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
- return true;
- }
- }
-
- if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is not metered.");
- return false;
- }
-
- /**
- * Check if this APN setting is metered.
- *
- * @param apn APN setting
- * @param phone The phone object
- * @return True if this APN setting is metered, otherwise false.
- */
- public static boolean isMetered(ApnSetting apn, Phone phone) {
- if (phone == null || apn == null) {
- return true;
- }
-
- for (int apnType : apn.getApnTypes()) {
- // If one of the APN type is metered, then this APN setting is metered.
- if (isMeteredApnType(apnType, phone)) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
deleted file mode 100644
index 348908a..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ /dev/null
@@ -1,4088 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET;
-
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.net.ConnectivityManager;
-import android.net.InetAddresses;
-import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
-import android.net.NetworkProvider;
-import android.net.NetworkRequest;
-import android.net.ProxyInfo;
-import android.net.RouteInfo;
-import android.net.SocketKeepalive;
-import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnManager;
-import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
-import android.net.vcn.VcnNetworkPolicyResult;
-import android.os.AsyncResult;
-import android.os.HandlerExecutor;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserManager;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.Annotation.DataState;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.LinkCapacityEstimate;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataCallResponse.HandoverFailureMode;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.NetworkSliceInfo;
-import android.telephony.data.Qos;
-import android.telephony.data.QosBearerSession;
-import android.telephony.data.TrafficDescriptor;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Pair;
-import android.util.TimeUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.CarrierPrivilegesTracker;
-import com.android.internal.telephony.CarrierSignalAgent;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.RIL;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyStatsLog;
-import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.telephony.metrics.DataCallSessionStats;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-/**
- * {@hide}
- *
- * DataConnection StateMachine.
- *
- * This a class for representing a single data connection, with instances of this
- * class representing a connection via the cellular network. There may be multiple
- * data connections and all of them are managed by the <code>DataConnectionTracker</code>.
- *
- * NOTE: All DataConnection objects must be running on the same looper, which is the default
- * as the coordinator has members which are used without synchronization.
- */
-public class DataConnection extends StateMachine {
- private static final boolean DBG = true;
- private static final boolean VDBG = true;
-
- private static final String NETWORK_TYPE = "MOBILE";
-
- private static final String RAT_NAME_5G = "nr";
- private static final String RAT_NAME_EVDO = "evdo";
-
- /**
- * OSId for "Android", using UUID version 5 with namespace ISO OSI.
- * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
- */
- private static final UUID OS_ID = UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
-
- /**
- * The data connection is not being or been handovered. Note this is the state for the source
- * data connection, not destination data connection
- */
- private static final int HANDOVER_STATE_IDLE = 1;
-
- /**
- * The data connection is being handovered. Note this is the state for the source
- * data connection, not destination data connection.
- */
- private static final int HANDOVER_STATE_BEING_TRANSFERRED = 2;
-
- /**
- * The data connection is already handovered. Note this is the state for the source
- * data connection, not destination data connection.
- */
- private static final int HANDOVER_STATE_COMPLETED = 3;
-
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"HANDOVER_STATE_"}, value = {
- HANDOVER_STATE_IDLE,
- HANDOVER_STATE_BEING_TRANSFERRED,
- HANDOVER_STATE_COMPLETED})
- public @interface HandoverState {}
-
- // The data connection providing default Internet connection will have a higher score of 50.
- // Other connections will have a slightly lower score of 45. The intention is other connections
- // will not cause ConnectivityService to tear down default internet connection. For example,
- // to validate Internet connection on non-default data SIM, we'll set up a temporary Internet
- // connection on that data SIM. In this case, score of 45 is assigned so ConnectivityService
- // will not replace the default Internet connection with it.
- private static final int DEFAULT_INTERNET_CONNECTION_SCORE = 50;
- private static final int OTHER_CONNECTION_SCORE = 45;
-
- // The score we report to connectivity service
- private int mScore;
-
- // The subscription id associated with this data connection.
- private int mSubId;
-
- // The data connection controller
- private DcController mDcController;
-
- // The Tester for failing all bringup's
- private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
-
- // Whether or not the data connection should allocate its own pdu session id
- private boolean mDoAllocatePduSessionId;
-
- private static AtomicInteger mInstanceNumber = new AtomicInteger(0);
- private AsyncChannel mAc;
-
- // The DCT that's talking to us, we only support one!
- private DcTracker mDct = null;
-
- private String[] mPcscfAddr;
-
- private final String mTagSuffix;
-
- private final LocalLog mHandoverLocalLog = new LocalLog(64);
-
- private int[] mAdministratorUids = new int[0];
-
- // stats per data call
- private DataCallSessionStats mDataCallSessionStats;
-
- /**
- * Used internally for saving connecting parameters.
- */
- public static class ConnectionParams {
- int mTag;
- ApnContext mApnContext;
- int mProfileId;
- int mRilRat;
- Message mOnCompletedMsg;
- final int mConnectionGeneration;
- @RequestNetworkType
- final int mRequestType;
- final int mSubId;
- final boolean mIsPreferredApn;
-
- ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology,
- Message onCompletedMsg, int connectionGeneration,
- @RequestNetworkType int requestType, int subId,
- boolean isPreferredApn) {
- mApnContext = apnContext;
- mProfileId = profileId;
- mRilRat = rilRadioTechnology;
- mOnCompletedMsg = onCompletedMsg;
- mConnectionGeneration = connectionGeneration;
- mRequestType = requestType;
- mSubId = subId;
- mIsPreferredApn = isPreferredApn;
- }
-
- @Override
- public String toString() {
- return "{mTag=" + mTag + " mApnContext=" + mApnContext
- + " mProfileId=" + mProfileId
- + " mRat=" + mRilRat
- + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg)
- + " mRequestType=" + DcTracker.requestTypeToString(mRequestType)
- + " mSubId=" + mSubId
- + " mIsPreferredApn=" + mIsPreferredApn
- + "}";
- }
- }
-
- /**
- * Used internally for saving disconnecting parameters.
- */
- public static class DisconnectParams {
- int mTag;
- public ApnContext mApnContext;
- String mReason;
- @ReleaseNetworkType
- final int mReleaseType;
- Message mOnCompletedMsg;
-
- DisconnectParams(ApnContext apnContext, String reason, @ReleaseNetworkType int releaseType,
- Message onCompletedMsg) {
- mApnContext = apnContext;
- mReason = reason;
- mReleaseType = releaseType;
- mOnCompletedMsg = onCompletedMsg;
- }
-
- @Override
- public String toString() {
- return "{mTag=" + mTag + " mApnContext=" + mApnContext
- + " mReason=" + mReason
- + " mReleaseType=" + DcTracker.releaseTypeToString(mReleaseType)
- + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
- }
- }
-
- private volatile ApnSetting mApnSetting;
- private ConnectionParams mConnectionParams;
- private DisconnectParams mDisconnectParams;
- @DataFailureCause
- private int mDcFailCause;
-
- @HandoverFailureMode
- private int mHandoverFailureMode;
-
- private Phone mPhone;
- private DataServiceManager mDataServiceManager;
- private VcnManager mVcnManager;
- private final int mTransportType;
- private LinkProperties mLinkProperties = new LinkProperties();
- private int mPduSessionId;
- private long mCreateTime;
- private long mLastFailTime;
- @DataFailureCause
- private int mLastFailCause;
- private static final String NULL_IP = "0.0.0.0";
- private Object mUserData;
- private boolean mCongestedOverride;
- private boolean mUnmeteredOverride;
- private int mRilRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
- private int mDataRegState = Integer.MAX_VALUE;
- // Indicating data connection is suspended due to temporary reasons, for example, out of
- // service, concurrency voice/data not supported, etc.. Note this flag is only meaningful when
- // data is in active state. When data is in inactive, connecting, or disconnecting, this flag
- // is unmeaningful.
- private boolean mIsSuspended;
- private int mDownlinkBandwidth = 14;
- private int mUplinkBandwidth = 14;
- private Qos mDefaultQos = null;
- private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
- private NetworkSliceInfo mSliceInfo;
- private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
-
- /** The corresponding network agent for this data connection. */
- private DcNetworkAgent mNetworkAgent;
-
- /**
- * The network agent from handover source data connection. This is the potential network agent
- * that will be transferred here after handover completed.
- */
- private DcNetworkAgent mHandoverSourceNetworkAgent;
-
- private int mDisabledApnTypeBitMask = 0;
-
- int mTag;
-
- /** Data connection id assigned by the modem. This is unique across transports */
- public int mCid;
-
- @HandoverState
- private int mHandoverState = HANDOVER_STATE_IDLE;
- private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>();
- PendingIntent mReconnectIntent = null;
-
- /** Class used to track VCN-defined Network policies for this DcNetworkAgent. */
- private final VcnNetworkPolicyChangeListener mVcnPolicyChangeListener =
- new DataConnectionVcnNetworkPolicyChangeListener();
-
- // ***** Event codes for driving the state machine, package visible for Dcc
- static final int BASE = Protocol.BASE_DATA_CONNECTION;
- static final int EVENT_CONNECT = BASE + 0;
- static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
- static final int EVENT_DEACTIVATE_DONE = BASE + 3;
- static final int EVENT_DISCONNECT = BASE + 4;
- static final int EVENT_DISCONNECT_ALL = BASE + 6;
- static final int EVENT_DATA_STATE_CHANGED = BASE + 7;
- static final int EVENT_TEAR_DOWN_NOW = BASE + 8;
- static final int EVENT_LOST_CONNECTION = BASE + 9;
- static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11;
- static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12;
- static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13;
- static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14;
- static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15;
- static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16;
- static final int EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED = BASE + 17;
- static final int EVENT_KEEPALIVE_STATUS = BASE + 18;
- static final int EVENT_KEEPALIVE_STARTED = BASE + 19;
- static final int EVENT_KEEPALIVE_STOPPED = BASE + 20;
- static final int EVENT_KEEPALIVE_START_REQUEST = BASE + 21;
- static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22;
- static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23;
- static final int EVENT_RESET = BASE + 24;
- static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25;
- static final int EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES = BASE + 26;
- static final int EVENT_NR_STATE_CHANGED = BASE + 27;
- static final int EVENT_DATA_CONNECTION_METEREDNESS_CHANGED = BASE + 28;
- static final int EVENT_NR_FREQUENCY_CHANGED = BASE + 29;
- static final int EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED = BASE + 30;
- static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = BASE + 31;
- static final int EVENT_CSS_INDICATOR_CHANGED = BASE + 32;
- static final int EVENT_UPDATE_SUSPENDED_STATE = BASE + 33;
- static final int EVENT_START_HANDOVER = BASE + 34;
- static final int EVENT_CANCEL_HANDOVER = BASE + 35;
- static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36;
- static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37;
- static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38;
- static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39;
- private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1;
-
- private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
- static {
- sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
- sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
- "EVENT_SETUP_DATA_CONNECTION_DONE";
- sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
- sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
- sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
- sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED";
- sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW";
- sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION";
- sCmdToString[EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED - BASE] =
- "EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED";
- sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON";
- sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF";
- sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE";
- sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] =
- "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
- sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
- "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
- sCmdToString[EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED - BASE] =
- "EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED";
- sCmdToString[EVENT_KEEPALIVE_STATUS - BASE] = "EVENT_KEEPALIVE_STATUS";
- sCmdToString[EVENT_KEEPALIVE_STARTED - BASE] = "EVENT_KEEPALIVE_STARTED";
- sCmdToString[EVENT_KEEPALIVE_STOPPED - BASE] = "EVENT_KEEPALIVE_STOPPED";
- sCmdToString[EVENT_KEEPALIVE_START_REQUEST - BASE] = "EVENT_KEEPALIVE_START_REQUEST";
- sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST";
- sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED";
- sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET";
- sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] =
- "EVENT_REEVALUATE_RESTRICTED_STATE";
- sCmdToString[EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE] =
- "EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES";
- sCmdToString[EVENT_NR_STATE_CHANGED - BASE] = "EVENT_NR_STATE_CHANGED";
- sCmdToString[EVENT_DATA_CONNECTION_METEREDNESS_CHANGED - BASE] =
- "EVENT_DATA_CONNECTION_METEREDNESS_CHANGED";
- sCmdToString[EVENT_NR_FREQUENCY_CHANGED - BASE] = "EVENT_NR_FREQUENCY_CHANGED";
- sCmdToString[EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED - BASE] =
- "EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED";
- sCmdToString[EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE] =
- "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
- sCmdToString[EVENT_CSS_INDICATOR_CHANGED - BASE] = "EVENT_CSS_INDICATOR_CHANGED";
- sCmdToString[EVENT_UPDATE_SUSPENDED_STATE - BASE] = "EVENT_UPDATE_SUSPENDED_STATE";
- sCmdToString[EVENT_START_HANDOVER - BASE] = "EVENT_START_HANDOVER";
- sCmdToString[EVENT_CANCEL_HANDOVER - BASE] = "EVENT_CANCEL_HANDOVER";
- sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET";
- sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID";
- sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID";
- sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] =
- "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE";
- }
- // Convert cmd to string or null if unknown
- static String cmdToString(int cmd) {
- String value = null;
- cmd -= BASE;
- if ((cmd >= 0) && (cmd < sCmdToString.length)) {
- value = sCmdToString[cmd];
- }
- if (value == null) {
- value = "0x" + Integer.toHexString(cmd + BASE);
- }
- return value;
- }
-
- /**
- * Create the connection object
- *
- * @param phone the Phone
- * @param id the connection id
- * @return DataConnection that was created.
- */
- public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct,
- DataServiceManager dataServiceManager,
- DcTesterFailBringUpAll failBringUpAll,
- DcController dcc) {
- String transportType = (dataServiceManager.getTransportType()
- == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- ? "C" // Cellular
- : "I"; // IWLAN
- DataConnection dc = new DataConnection(phone, transportType + "-"
- + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager, failBringUpAll,
- dcc);
- dc.start();
- if (DBG) dc.log("Made " + dc.getName());
- return dc;
- }
-
- void dispose() {
- log("dispose: call quiteNow()");
- quitNow();
- }
-
- /* Getter functions */
-
- LinkProperties getLinkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- boolean isDisconnecting() {
- return getCurrentState() == mDisconnectingState
- || getCurrentState() == mDisconnectingErrorCreatingConnection;
- }
-
- @VisibleForTesting
- public boolean isActive() {
- return getCurrentState() == mActiveState;
- }
-
- @VisibleForTesting
- public boolean isInactive() {
- return getCurrentState() == mInactiveState;
- }
-
- boolean isActivating() {
- return getCurrentState() == mActivatingState;
- }
-
- boolean hasBeenTransferred() {
- return mHandoverState == HANDOVER_STATE_COMPLETED;
- }
-
- int getCid() {
- return mCid;
- }
-
- /**
- * @return DataConnection's ApnSetting.
- */
- public ApnSetting getApnSetting() {
- return mApnSetting;
- }
-
- /**
- * Update http proxy of link properties based on current apn setting
- */
- private void updateLinkPropertiesHttpProxy() {
- if (mApnSetting == null
- || TextUtils.isEmpty(mApnSetting.getProxyAddressAsString())) {
- return;
- }
- try {
- int port = mApnSetting.getProxyPort();
- if (port == -1) {
- port = 8080;
- }
- ProxyInfo proxy = ProxyInfo.buildDirectProxy(
- mApnSetting.getProxyAddressAsString(), port);
- mLinkProperties.setHttpProxy(proxy);
- } catch (NumberFormatException e) {
- loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
- + mApnSetting.getProxyPort() + "): " + e);
- }
- }
-
- public static class UpdateLinkPropertyResult {
- public SetupResult setupResult = SetupResult.SUCCESS;
- public LinkProperties oldLp;
- public LinkProperties newLp;
- public UpdateLinkPropertyResult(LinkProperties curLp) {
- oldLp = curLp;
- newLp = curLp;
- }
- }
-
- /**
- * Class returned by onSetupConnectionCompleted.
- */
- public enum SetupResult {
- SUCCESS,
- ERROR_RADIO_NOT_AVAILABLE,
- ERROR_INVALID_ARG,
- ERROR_STALE,
- ERROR_DATA_SERVICE_SPECIFIC_ERROR,
- ERROR_DUPLICATE_CID,
- ERROR_NO_DEFAULT_CONNECTION;
-
- public int mFailCause;
-
- SetupResult() {
- mFailCause = DataFailCause.getFailCause(0);
- }
-
- @Override
- public String toString() {
- return name() + " SetupResult.mFailCause=" + DataFailCause.toString(mFailCause);
- }
- }
-
- public boolean isIpv4Connected() {
- boolean ret = false;
- Collection <InetAddress> addresses = mLinkProperties.getAddresses();
-
- for (InetAddress addr: addresses) {
- if (addr instanceof java.net.Inet4Address) {
- java.net.Inet4Address i4addr = (java.net.Inet4Address) addr;
- if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress() &&
- !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) {
- ret = true;
- break;
- }
- }
- }
- return ret;
- }
-
- public boolean isIpv6Connected() {
- boolean ret = false;
- Collection <InetAddress> addresses = mLinkProperties.getAddresses();
-
- for (InetAddress addr: addresses) {
- if (addr instanceof java.net.Inet6Address) {
- java.net.Inet6Address i6addr = (java.net.Inet6Address) addr;
- if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() &&
- !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) {
- ret = true;
- break;
- }
- }
- }
- return ret;
- }
-
- public int getPduSessionId() {
- return mPduSessionId;
- }
-
- public NetworkSliceInfo getSliceInfo() {
- return mSliceInfo;
- }
-
- public List<TrafficDescriptor> getTrafficDescriptors() {
- return mTrafficDescriptors;
- }
-
- /**
- * Update DC fields based on a new DataCallResponse
- * @param response the response to use to update DC fields
- */
- public void updateResponseFields(DataCallResponse response) {
- updateQosParameters(response);
- updateSliceInfo(response);
- updateTrafficDescriptors(response);
- }
-
- public void updateQosParameters(final @Nullable DataCallResponse response) {
- if (response == null) {
- mDefaultQos = null;
- mQosBearerSessions.clear();
- return;
- }
-
- mDefaultQos = response.getDefaultQos();
- mQosBearerSessions = response.getQosBearerSessions();
-
- if (mNetworkAgent != null) {
- syncQosToNetworkAgent();
- }
- }
-
- private void syncQosToNetworkAgent() {
- final DcNetworkAgent networkAgent = mNetworkAgent;
- final List<QosBearerSession> qosBearerSessions = mQosBearerSessions;
- if (qosBearerSessions == null) {
- networkAgent.updateQosBearerSessions(new ArrayList<>());
- return;
- }
- networkAgent.updateQosBearerSessions(qosBearerSessions);
- }
-
- /**
- * Update the latest slice info on this data connection with
- * {@link DataCallResponse#getSliceInfo}.
- */
- public void updateSliceInfo(DataCallResponse response) {
- mSliceInfo = response.getSliceInfo();
- }
-
- /**
- * Update the latest traffic descriptor on this data connection with
- * {@link DataCallResponse#getTrafficDescriptors}.
- */
- public void updateTrafficDescriptors(DataCallResponse response) {
- mTrafficDescriptors = response.getTrafficDescriptors();
- mDcController.updateTrafficDescriptorsForCid(response.getId(),
- response.getTrafficDescriptors());
- }
-
- @VisibleForTesting
- public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
- UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
-
- if (newState == null) return result;
-
- result.newLp = new LinkProperties();
-
- // set link properties based on data call response
- result.setupResult = setLinkProperties(newState, result.newLp);
- if (result.setupResult != SetupResult.SUCCESS) {
- if (DBG) log("updateLinkProperty failed : " + result.setupResult);
- return result;
- }
- // copy HTTP proxy as it is not part DataCallResponse.
- result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
-
- checkSetMtu(mApnSetting, result.newLp);
-
- mLinkProperties = result.newLp;
-
- updateTcpBufferSizes(mRilRat);
-
- if (DBG && (! result.oldLp.equals(result.newLp))) {
- log("updateLinkProperty old LP=" + result.oldLp);
- log("updateLinkProperty new LP=" + result.newLp);
- }
-
- if (result.newLp.equals(result.oldLp) == false &&
- mNetworkAgent != null) {
- mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
- }
-
- return result;
- }
-
- /**
- * Sets the pdu session id of the data connection
- * @param pduSessionId pdu session id to set
- */
- @VisibleForTesting
- public void setPduSessionId(int pduSessionId) {
- if (mPduSessionId != pduSessionId) {
- logd("Changing pdu session id from: " + mPduSessionId + " to: " + pduSessionId + ", "
- + "Handover state: " + handoverStateToString(this.mHandoverState));
- mPduSessionId = pduSessionId;
- }
- }
-
- /**
- * Read the MTU value from link properties where it can be set from network. In case
- * not set by the network, set it again using the mtu szie value defined in the APN
- * database for the connected APN
- */
- private void checkSetMtu(ApnSetting apn, LinkProperties lp) {
- if (lp == null) return;
-
- if (apn == null || lp == null) return;
-
- if (lp.getMtu() != PhoneConstants.UNSET_MTU) {
- if (DBG) log("MTU set by call response to: " + lp.getMtu());
- return;
- }
-
- if (apn != null && apn.getMtuV4() != PhoneConstants.UNSET_MTU) {
- lp.setMtu(apn.getMtuV4());
- if (DBG) log("MTU set by APN to: " + apn.getMtuV4());
- return;
- }
-
- int mtu = mPhone.getContext().getResources().getInteger(
- com.android.internal.R.integer.config_mobile_mtu);
- if (mtu != PhoneConstants.UNSET_MTU) {
- lp.setMtu(mtu);
- if (DBG) log("MTU set by config resource to: " + mtu);
- }
- }
-
- //***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
- private DataConnection(Phone phone, String tagSuffix, int id,
- DcTracker dct, DataServiceManager dataServiceManager,
- DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
- super("DC-" + tagSuffix, dcc);
- mTagSuffix = tagSuffix;
- setLogRecSize(300);
- setLogOnlyTransitions(true);
- if (DBG) log("DataConnection created");
-
- mPhone = phone;
- mDct = dct;
- mDataServiceManager = dataServiceManager;
- mVcnManager = mPhone.getContext().getSystemService(VcnManager.class);
- mTransportType = dataServiceManager.getTransportType();
- mDcTesterFailBringUpAll = failBringUpAll;
- mDcController = dcc;
- mId = id;
- mCid = -1;
- mDataRegState = mPhone.getServiceState().getDataRegistrationState();
- mIsSuspended = false;
- mDataCallSessionStats = new DataCallSessionStats(mPhone);
- mDoAllocatePduSessionId = false;
-
- int networkType = getNetworkType();
- mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType);
- updateLinkBandwidthsFromCarrierConfig(mRilRat);
-
- addState(mDefaultState);
- addState(mInactiveState, mDefaultState);
- addState(mActivatingState, mDefaultState);
- addState(mActiveState, mDefaultState);
- addState(mDisconnectingState, mDefaultState);
- addState(mDisconnectingErrorCreatingConnection, mDefaultState);
- setInitialState(mInactiveState);
- }
-
- private @NetworkType int getNetworkType() {
- ServiceState ss = mPhone.getServiceState();
- int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-
- NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
- if (nri != null) {
- networkType = nri.getAccessNetworkTechnology();
- }
-
- return networkType;
- }
-
- /**
- * Get the source transport for handover. For example, handover from WWAN to WLAN, WWAN is the
- * source transport, and vice versa.
- */
- private @TransportType int getHandoverSourceTransport() {
- return mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
- ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
- : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
- }
-
- /**
- * API to generate the OsAppId for enterprise traffic category.
- * @return byte[] representing OsId + length of OsAppId + OsAppId
- */
- @VisibleForTesting
- public static byte[] getEnterpriseOsAppId() {
- byte[] osAppId = NetworkCapabilities.getCapabilityCarrierName(
- NetworkCapabilities.NET_CAPABILITY_ENTERPRISE).getBytes();
- // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
- ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
- bb.putLong(OS_ID.getMostSignificantBits());
- bb.putLong(OS_ID.getLeastSignificantBits());
- bb.put((byte) osAppId.length);
- bb.put(osAppId);
- if (VDBG) {
- Rlog.d("DataConnection", "getEnterpriseOsAppId: "
- + IccUtils.bytesToHexString(bb.array()));
- }
- return bb.array();
- }
-
- /**
- * Begin setting up a data connection, calls setupDataCall
- * and the ConnectionParams will be returned with the
- * EVENT_SETUP_DATA_CONNECTION_DONE
- *
- * @param cp is the connection parameters
- *
- * @return Fail cause if failed to setup data connection. {@link DataFailCause#NONE} if success.
- */
- private @DataFailureCause int connect(ConnectionParams cp) {
- log("connect: carrier='" + mApnSetting.getEntryName()
- + "' APN='" + mApnSetting.getApnName()
- + "' proxy='" + mApnSetting.getProxyAddressAsString()
- + "' port='" + mApnSetting.getProxyPort() + "'");
- ApnContext.requestLog(cp.mApnContext, "DataConnection.connect");
-
- // Check if we should fake an error.
- if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) {
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause)
- .setRetryDurationMillis(
- mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime)
- .setMtuV4(PhoneConstants.UNSET_MTU)
- .setMtuV6(PhoneConstants.UNSET_MTU)
- .build();
-
- Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
- AsyncResult.forMessage(msg, response, null);
- sendMessage(msg);
- if (DBG) {
- log("connect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp()
- + " send error response=" + response);
- }
- mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1;
- return DataFailCause.NONE;
- }
-
- mCreateTime = -1;
- mLastFailTime = -1;
- mLastFailCause = DataFailCause.NONE;
-
- Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
- msg.obj = cp;
-
- DataProfile dp = new DataProfile.Builder()
- .setApnSetting(mApnSetting)
- .setPreferred(cp.mIsPreferredApn)
- .build();
-
- // We need to use the actual modem roaming state instead of the framework roaming state
- // here. This flag is only passed down to ril_service for picking the correct protocol (for
- // old modem backward compatibility).
- boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
-
- // If the apn is NOT metered, we will allow data roaming regardless of the setting.
- boolean isUnmeteredApnType = !ApnSettingUtils.isMeteredApnType(
- cp.mApnContext.getApnTypeBitmask(), mPhone);
-
- // Set this flag to true if the user turns on data roaming. Or if we override the roaming
- // state in framework, we should set this flag to true as well so the modem will not reject
- // the data call setup (because the modem actually thinks the device is roaming).
- boolean allowRoaming = mPhone.getDataRoamingEnabled()
- || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()
- || isUnmeteredApnType));
-
- String dnn = null;
- byte[] osAppId = null;
- if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
- osAppId = getEnterpriseOsAppId();
- } else {
- dnn = mApnSetting.getApnName();
- }
- final TrafficDescriptor td = osAppId == null && dnn == null ? null
- : new TrafficDescriptor(dnn, osAppId);
- final boolean matchAllRuleAllowed = td == null || td.getOsAppId() == null;
-
- if (DBG) {
- log("allowRoaming=" + allowRoaming
- + ", mPhone.getDataRoamingEnabled()=" + mPhone.getDataRoamingEnabled()
- + ", isModemRoaming=" + isModemRoaming
- + ", mPhone.getServiceState().getDataRoaming()="
- + mPhone.getServiceState().getDataRoaming()
- + ", isUnmeteredApnType=" + isUnmeteredApnType
- + ", trafficDescriptor=" + td
- + ", matchAllRuleAllowed=" + matchAllRuleAllowed
- );
- }
-
- // Check if this data setup is a handover.
- LinkProperties linkProperties = null;
- int reason = DataService.REQUEST_REASON_NORMAL;
- if (cp.mRequestType == REQUEST_TYPE_HANDOVER) {
- // If this is a data setup for handover, we need to pass the link properties
- // of the existing data connection to the modem.
- DcTracker srcDcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
- if (srcDcTracker == null || cp.mApnContext == null) {
- loge("connect: Handover failed. dcTracker=" + srcDcTracker + ", apnContext="
- + cp.mApnContext);
- return DataFailCause.HANDOVER_FAILED;
- }
-
-
- // srcDc is the source data connection while the current instance is the target
- DataConnection srcDc =
- srcDcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType());
- if (srcDc == null) {
- loge("connect: Can't find data connection for handover.");
- return DataFailCause.HANDOVER_FAILED;
- }
-
- // Helpful for logging purposes
- DataServiceManager srcDsm = srcDc.mDataServiceManager;
- String srcDsmTag = (srcDsm == null ? "(null)" : srcDsm.getTag());
- logd("connect: REQUEST_TYPE_HANDOVER - Request handover from " + srcDc.getName()
- + ", targetDsm=" + mDataServiceManager.getTag()
- + ", sourceDsm=" + srcDsmTag);
-
-
- /* startHandover is called on the source data connection, and if successful,
- we ask the target data connection (which is the current instance) to call
- #setupDataCall with request type handover.
- */
- Consumer<Integer> onCompleted = (dataServiceCallbackResultCode) ->
- /* startHandover is called on the srcDc handler, but the callback needs to
- be called on the current (which is the targetDc) handler which is why we
- call sendRunnableMessage. */
- sendRunnableMessage(EVENT_START_HANDOVER_ON_TARGET,
- (inCorrectState) -> requestHandover(inCorrectState, srcDc,
- dataServiceCallbackResultCode,
- cp, msg, dp, isModemRoaming, allowRoaming));
- srcDc.startHandover(onCompleted);
- return DataFailCause.NONE;
- }
-
- // setup data call for REQUEST_TYPE_NORMAL
- mDoAllocatePduSessionId = mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
- allocatePduSessionId(psi -> {
- this.setPduSessionId(psi);
- mDataServiceManager.setupDataCall(
- ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
- dp,
- isModemRoaming,
- allowRoaming,
- reason,
- linkProperties,
- psi,
- null, //slice info is null since this is not a handover
- td,
- matchAllRuleAllowed,
- msg);
- TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
- dp.getProfileId(), dp.getApn(), dp.getProtocolType());
- });
- return DataFailCause.NONE;
- }
-
- private void allocatePduSessionId(Consumer<Integer> allocateCallback) {
- if (mDoAllocatePduSessionId) {
- Message msg = this.obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID);
- msg.obj = allocateCallback;
- mPhone.mCi.allocatePduSessionId(msg);
- } else {
- allocateCallback.accept(PDU_SESSION_ID_NOT_SET);
- }
- }
-
- private void onRquestHandoverFailed(ConnectionParams cp) {
- sendMessage(obtainMessage(EVENT_CANCEL_HANDOVER));
- notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
- }
-
- private void requestHandover(boolean inCorrectState, DataConnection srcDc,
- @DataServiceCallback.ResultCode int resultCode,
- ConnectionParams cp, Message msg, DataProfile dp, boolean isModemRoaming,
- boolean allowRoaming) {
-
- if (!inCorrectState) {
- logd("requestHandover: Not in correct state");
- if (isResultCodeSuccess(resultCode)) {
- if (srcDc != null) {
- logd("requestHandover: Not in correct state - Success result code");
- // We need to cancel the handover on source if we ended up in the wrong state.
- srcDc.cancelHandover();
- } else {
- logd("requestHandover: Not in correct state - Success result code - "
- + "srcdc = null");
- }
- }
- onRquestHandoverFailed(cp);
- return;
- } else if (!isResultCodeSuccess(resultCode)) {
- if (DBG) {
- logd("requestHandover: Non success result code from DataService, "
- + "setupDataCall will not be called, result code = "
- + DataServiceCallback.resultCodeToString(resultCode));
- }
- onRquestHandoverFailed(cp);
- return;
- }
-
- if (srcDc == null) {
- loge("requestHandover: Cannot find source data connection.");
- onRquestHandoverFailed(cp);
- return;
- }
-
- LinkProperties linkProperties;
- int reason;
-
- // Preserve the potential network agent from the source data connection. The ownership
- // is not transferred at this moment.
- mHandoverSourceNetworkAgent = srcDc.getNetworkAgent();
- if (mHandoverSourceNetworkAgent == null) {
- loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName());
- onRquestHandoverFailed(cp);
- return;
- }
-
- linkProperties = srcDc.getLinkProperties();
- if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) {
- loge("requestHandover: Can't find link properties of handover data connection. dc="
- + srcDc);
- onRquestHandoverFailed(cp);
- return;
- }
-
- mHandoverLocalLog.log("Handover started. Preserved the agent.");
- log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
-
- reason = DataService.REQUEST_REASON_HANDOVER;
-
- TrafficDescriptor td = dp.getApn() == null ? null
- : new TrafficDescriptor(dp.getApn(), null);
- boolean matchAllRuleAllowed = true;
-
- mDataServiceManager.setupDataCall(
- ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
- dp,
- isModemRoaming,
- allowRoaming,
- reason,
- linkProperties,
- srcDc.getPduSessionId(),
- srcDc.getSliceInfo(),
- td,
- matchAllRuleAllowed,
- msg);
- TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
- dp.getProfileId(), dp.getApn(), dp.getProtocolType());
- }
-
- /**
- * Called on the source data connection from the target data connection.
- */
- @VisibleForTesting
- public void startHandover(Consumer<Integer> onTargetDcComplete) {
- logd("startHandover: " + toStringSimple());
- // Set the handover state to being transferred on "this" data connection which is the src.
- setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
-
- Consumer<Integer> onSrcDcComplete =
- resultCode -> onHandoverStarted(resultCode, onTargetDcComplete);
- /*
- The flow here is:
- srcDc#startHandover -> dataService#startHandover -> (onHandoverStarted) ->
- onSrcDcComplete -> onTargetDcComplete
- */
- mDataServiceManager.startHandover(mCid,
- this.obtainMessage(EVENT_START_HANDOVER,
- onSrcDcComplete));
- }
-
- /**
- * Called on the source data connection when the async call to start handover is complete
- */
- private void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode,
- Consumer<Integer> onTargetDcComplete) {
- logd("onHandoverStarted: " + toStringSimple());
- if (!isResultCodeSuccess(resultCode)) {
- setHandoverState(HANDOVER_STATE_IDLE);
- }
- onTargetDcComplete.accept(resultCode);
- }
-
- private void cancelHandover() {
- if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) {
- logd("cancelHandover: handover state is " + handoverStateToString(mHandoverState)
- + ", expecting HANDOVER_STATE_BEING_TRANSFERRED");
- }
- mDataServiceManager.cancelHandover(mCid, this.obtainMessage(EVENT_CANCEL_HANDOVER));
- setHandoverState(HANDOVER_STATE_IDLE);
- }
-
- /**
- * Update NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED based on congested override
- * @param isCongested whether this DC should be set to congested or not
- */
- public void onCongestednessChanged(boolean isCongested) {
- sendMessage(obtainMessage(EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED, isCongested));
- }
-
- /**
- * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on metered override
- * @param isUnmetered whether this DC should be set to unmetered or not
- */
- public void onMeterednessChanged(boolean isUnmetered) {
- sendMessage(obtainMessage(EVENT_DATA_CONNECTION_METEREDNESS_CHANGED, isUnmetered));
- }
-
- /**
- * TearDown the data connection when the deactivation is complete a Message with
- * msg.what == EVENT_DEACTIVATE_DONE
- *
- * @param o is the object returned in the AsyncResult.obj.
- */
- private void tearDownData(Object o) {
- int discReason = DataService.REQUEST_REASON_NORMAL;
- ApnContext apnContext = null;
- if ((o != null) && (o instanceof DisconnectParams)) {
- DisconnectParams dp = (DisconnectParams) o;
- apnContext = dp.mApnContext;
- if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
- || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
- discReason = DataService.REQUEST_REASON_SHUTDOWN;
- } else if (dp.mReleaseType == DcTracker.RELEASE_TYPE_HANDOVER) {
- discReason = DataService.REQUEST_REASON_HANDOVER;
- }
- }
-
- String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
-
-
- //Needed to be final to work in a closure
- final int fDiscReason = discReason;
- releasePduSessionId(() -> {
- // This is run after release pdu session id is complete
- this.setPduSessionId(PDU_SESSION_ID_NOT_SET);
- mDataServiceManager.deactivateDataCall(mCid, fDiscReason,
- obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
- mDataCallSessionStats.setDeactivateDataCallReason(fDiscReason);
- });
- }
-
- private void releasePduSessionId(Runnable releaseCallback) {
- // If the transport is IWLAN, and there is a valid PDU session id, also the data connection
- // is not being handovered, we should release the pdu session id.
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
- && mHandoverState == HANDOVER_STATE_IDLE
- && this.getPduSessionId() != PDU_SESSION_ID_NOT_SET) {
- Message msg = this.obtainMessage(EVENT_RELEASE_PDU_SESSION_ID);
- msg.obj = releaseCallback;
- mPhone.mCi.releasePduSessionId(msg, this.getPduSessionId());
- } else {
- // Just go and run the callback since we either have no pdu session id to release
- // or we are in the middle of a handover
- releaseCallback.run();
- }
- }
-
- private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
- for (ConnectionParams cp : mApnContexts.values()) {
- ApnContext apnContext = cp.mApnContext;
- if (apnContext == alreadySent) continue;
- if (reason != null) apnContext.setReason(reason);
- Pair<ApnContext, Integer> pair = new Pair<>(apnContext, cp.mConnectionGeneration);
- Message msg = mDct.obtainMessage(event, cp.mRequestType,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, pair);
- AsyncResult.forMessage(msg);
- msg.sendToTarget();
- }
- }
-
- /**
- * Send the connectionCompletedMsg.
- *
- * @param cp is the ConnectionParams
- * @param cause and if no error the cause is DataFailCause.NONE
- * @param handoverFailureMode The action on handover failure
- * @param sendAll is true if all contexts are to be notified
- */
- private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause,
- @HandoverFailureMode int handoverFailureMode, boolean sendAll) {
- ApnContext alreadySent = null;
-
- if (cp != null && cp.mOnCompletedMsg != null) {
- // Get the completed message but only use it once
- Message connectionCompletedMsg = cp.mOnCompletedMsg;
- cp.mOnCompletedMsg = null;
- alreadySent = cp.mApnContext;
-
- long timeStamp = System.currentTimeMillis();
- connectionCompletedMsg.arg1 = cp.mRequestType;
- connectionCompletedMsg.arg2 = handoverFailureMode;
-
- if (cause == DataFailCause.NONE) {
- mCreateTime = timeStamp;
- AsyncResult.forMessage(connectionCompletedMsg);
- } else {
- mLastFailCause = cause;
- mLastFailTime = timeStamp;
-
- // Return message with a Throwable exception to signify an error.
- if (cause == DataFailCause.NONE) cause = DataFailCause.UNKNOWN;
- AsyncResult.forMessage(connectionCompletedMsg, cause,
- new Throwable(DataFailCause.toString(cause)));
- }
- if (DBG) {
- log("notifyConnectCompleted at " + timeStamp + " cause="
- + DataFailCause.toString(cause) + " connectionCompletedMsg="
- + msgToString(connectionCompletedMsg));
- }
-
- connectionCompletedMsg.sendToTarget();
- }
- if (sendAll) {
- log("Send to all. " + alreadySent + " " + DataFailCause.toString(cause));
- notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR,
- DataFailCause.toString(cause));
- }
- }
-
- /**
- * Send ar.userObj if its a message, which is should be back to originator.
- *
- * @param dp is the DisconnectParams.
- */
- private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
- if (VDBG) log("NotifyDisconnectCompleted");
-
- ApnContext alreadySent = null;
- String reason = null;
-
- if (dp != null && dp.mOnCompletedMsg != null) {
- // Get the completed message but only use it once
- Message msg = dp.mOnCompletedMsg;
- dp.mOnCompletedMsg = null;
- if (msg.obj instanceof ApnContext) {
- alreadySent = (ApnContext)msg.obj;
- }
- reason = dp.mReason;
- if (VDBG) {
- log(String.format("msg=%s msg.obj=%s", msg.toString(),
- ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
- }
- AsyncResult.forMessage(msg);
- msg.sendToTarget();
- }
- if (sendAll) {
- if (reason == null) {
- reason = DataFailCause.toString(DataFailCause.UNKNOWN);
- }
- notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason);
- }
- if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
- }
-
- private void sendRunnableMessage(int eventCode, @NonNull final Consumer<Boolean> r) {
- sendMessage(eventCode, r);
- }
-
- /*
- * **************************************************************************
- * Begin Members and methods owned by DataConnectionTracker but stored
- * in a DataConnection because there is one per connection.
- * **************************************************************************
- */
-
- /*
- * The id is owned by DataConnectionTracker.
- */
- private int mId;
-
- /**
- * Get the DataConnection ID
- */
- public int getDataConnectionId() {
- return mId;
- }
-
- /*
- * **************************************************************************
- * End members owned by DataConnectionTracker
- * **************************************************************************
- */
-
- /**
- * Clear all settings called when entering mInactiveState.
- */
- private synchronized void clearSettings() {
- if (DBG) log("clearSettings");
-
- mCreateTime = -1;
- mLastFailTime = -1;
- mLastFailCause = DataFailCause.NONE;
- mCid = -1;
-
- mPcscfAddr = new String[5];
-
- mLinkProperties = new LinkProperties();
- mApnContexts.clear();
- mApnSetting = null;
- mUnmeteredUseOnly = false;
- mMmsUseOnly = false;
- mEnterpriseUse = false;
- mRestrictedNetworkOverride = false;
- mDcFailCause = DataFailCause.NONE;
- mDisabledApnTypeBitMask = 0;
- mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- mCongestedOverride = false;
- mUnmeteredOverride = false;
- mDownlinkBandwidth = 14;
- mUplinkBandwidth = 14;
- mIsSuspended = false;
- mHandoverState = HANDOVER_STATE_IDLE;
- mHandoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN;
- mSliceInfo = null;
- mDefaultQos = null;
- mDoAllocatePduSessionId = false;
- mQosBearerSessions.clear();
- mTrafficDescriptors.clear();
- }
-
- /**
- * Process setup data completion result from data service
- *
- * @param resultCode The result code returned by data service
- * @param response Data call setup response from data service
- * @param cp The original connection params used for data call setup
- * @return Setup result
- */
- private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode,
- DataCallResponse response,
- ConnectionParams cp) {
- SetupResult result;
-
- log("onSetupConnectionCompleted: resultCode=" + resultCode + ", response=" + response);
- if (cp.mTag != mTag) {
- if (DBG) {
- log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
- }
- result = SetupResult.ERROR_STALE;
- } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) {
- result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
- result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
- } else if (resultCode == DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE) {
- result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
- result.mFailCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE;
- } else if (resultCode == DataServiceCallback.RESULT_ERROR_INVALID_ARG) {
- result = SetupResult.ERROR_INVALID_ARG;
- result.mFailCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER;
- } else if (response.getCause() != 0) {
- if (response.getCause() == DataFailCause.RADIO_NOT_AVAILABLE) {
- result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
- result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
- } else {
- result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
- result.mFailCause = DataFailCause.getFailCause(response.getCause());
- }
- } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
- && mDcController.getActiveDcByCid(response.getId()) != null) {
- if (!mDcController.getTrafficDescriptorsForCid(response.getId())
- .equals(response.getTrafficDescriptors())) {
- if (DBG) log("Updating traffic descriptors: " + response.getTrafficDescriptors());
- mDcController.getActiveDcByCid(response.getId()).updateTrafficDescriptors(response);
- mDct.obtainMessage(DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED).sendToTarget();
- }
- if (DBG) log("DataConnection already exists for cid: " + response.getId());
- result = SetupResult.ERROR_DUPLICATE_CID;
- result.mFailCause = DataFailCause.DUPLICATE_CID;
- } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
- && !mDcController.isDefaultDataActive()) {
- if (DBG) log("No default data connection currently active");
- mCid = response.getId();
- result = SetupResult.ERROR_NO_DEFAULT_CONNECTION;
- result.mFailCause = DataFailCause.NO_DEFAULT_DATA;
- } else {
- if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
- mCid = response.getId();
- setPduSessionId(response.getPduSessionId());
- updatePcscfAddr(response);
- updateResponseFields(response);
- result = updateLinkProperty(response).setupResult;
- }
-
- return result;
- }
-
- private static boolean isResultCodeSuccess(int resultCode) {
- return resultCode == DataServiceCallback.RESULT_SUCCESS
- || resultCode == DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
- }
-
- private boolean isDnsOk(String[] domainNameServers) {
- if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
- && !mPhone.isDnsCheckDisabled()) {
- // Work around a race condition where QMI does not fill in DNS:
- // Deactivate PDP and let DataConnectionTracker retry.
- // Do not apply the race condition workaround for MMS APN
- // if Proxy is an IP-address.
- // Otherwise, the default APN will not be restored anymore.
- if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
- log(String.format(
- "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
- mApnSetting.getApnTypeBitmask(), ApnSetting.TYPE_MMS_STRING,
- mApnSetting.getMmsProxyAddressAsString(),
- isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
- return false;
- }
- }
- return true;
- }
-
- /**
- * TCP buffer size config based on the ril technology. There are 6 parameters
- * read_min, read_default, read_max, write_min, write_default, write_max in the TCP buffer
- * config string and they are separated by a comma. The unit of these parameters is byte.
- */
- private static final String TCP_BUFFER_SIZES_GPRS = "4092,8760,48000,4096,8760,48000";
- private static final String TCP_BUFFER_SIZES_EDGE = "4093,26280,70800,4096,16384,70800";
- private static final String TCP_BUFFER_SIZES_UMTS = "58254,349525,1048576,58254,349525,1048576";
- private static final String TCP_BUFFER_SIZES_1XRTT = "16384,32768,131072,4096,16384,102400";
- private static final String TCP_BUFFER_SIZES_EVDO = "4094,87380,262144,4096,16384,262144";
- private static final String TCP_BUFFER_SIZES_EHRPD = "131072,262144,1048576,4096,16384,524288";
- private static final String TCP_BUFFER_SIZES_HSDPA = "61167,367002,1101005,8738,52429,262114";
- private static final String TCP_BUFFER_SIZES_HSPA = "40778,244668,734003,16777,100663,301990";
- private static final String TCP_BUFFER_SIZES_LTE =
- "524288,1048576,2097152,262144,524288,1048576";
- private static final String TCP_BUFFER_SIZES_HSPAP =
- "122334,734003,2202010,32040,192239,576717";
- private static final String TCP_BUFFER_SIZES_NR =
- "2097152,6291456,16777216,512000,2097152,8388608";
- private static final String TCP_BUFFER_SIZES_LTE_CA =
- "4096,6291456,12582912,4096,1048576,2097152";
-
- private void updateTcpBufferSizes(int rilRat) {
- String sizes = null;
- ServiceState ss = mPhone.getServiceState();
- if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE &&
- ss.isUsingCarrierAggregation()) {
- rilRat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
- }
- String ratName = ServiceState.rilRadioTechnologyToString(rilRat).toLowerCase(Locale.ROOT);
- // ServiceState gives slightly different names for EVDO tech ("evdo-rev.0" for ex)
- // - patch it up:
- if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
- rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A ||
- rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B) {
- ratName = RAT_NAME_EVDO;
- }
-
- // NR 5G Non-Standalone use LTE cell as the primary cell, the ril technology is LTE in this
- // case. We use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
- && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE ||
- rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected())
- && mPhone.getServiceStateTracker().getNrContextIds().contains(mCid)) {
- ratName = RAT_NAME_5G;
- }
-
- log("updateTcpBufferSizes: " + ratName);
-
- // in the form: "ratname:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
- String[] configOverride = mPhone.getContext().getResources().getStringArray(
- com.android.internal.R.array.config_mobile_tcp_buffers);
- for (int i = 0; i < configOverride.length; i++) {
- String[] split = configOverride[i].split(":");
- if (ratName.equals(split[0]) && split.length == 2) {
- sizes = split[1];
- break;
- }
- }
-
- if (sizes == null) {
- // no override - use telephony defaults
- // doing it this way allows device or carrier to just override the types they
- // care about and inherit the defaults for the others.
- switch (rilRat) {
- case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
- sizes = TCP_BUFFER_SIZES_GPRS;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
- sizes = TCP_BUFFER_SIZES_EDGE;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
- sizes = TCP_BUFFER_SIZES_UMTS;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
- sizes = TCP_BUFFER_SIZES_1XRTT;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
- sizes = TCP_BUFFER_SIZES_EVDO;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
- sizes = TCP_BUFFER_SIZES_EHRPD;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
- sizes = TCP_BUFFER_SIZES_HSDPA;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
- sizes = TCP_BUFFER_SIZES_HSPA;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
- // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
- if (RAT_NAME_5G.equals(ratName)) {
- sizes = TCP_BUFFER_SIZES_NR;
- } else {
- sizes = TCP_BUFFER_SIZES_LTE;
- }
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
- // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
- if (RAT_NAME_5G.equals(ratName)) {
- sizes = TCP_BUFFER_SIZES_NR;
- } else {
- sizes = TCP_BUFFER_SIZES_LTE_CA;
- }
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
- sizes = TCP_BUFFER_SIZES_HSPAP;
- break;
- case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
- sizes = TCP_BUFFER_SIZES_NR;
- break;
- default:
- // Leave empty - this will let ConnectivityService use the system default.
- break;
- }
- }
- mLinkProperties.setTcpBufferSizes(sizes);
- }
-
- private void updateLinkBandwidthsFromCarrierConfig(int rilRat) {
- String ratName = DataConfigManager.getDataConfigNetworkType(
- mPhone.getDisplayInfoController().getTelephonyDisplayInfo());
-
- if (DBG) log("updateLinkBandwidthsFromCarrierConfig: " + ratName);
-
- Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
- if (values == null) {
- values = new Pair<>(14, 14);
- }
- mDownlinkBandwidth = values.first;
- mUplinkBandwidth = values.second;
- }
-
-
- private void updateLinkBandwidthsFromModem(List<LinkCapacityEstimate> lceList) {
- if (DBG) log("updateLinkBandwidthsFromModem: lceList=" + lceList);
- boolean downlinkUpdated = false;
- boolean uplinkUpdated = false;
- LinkCapacityEstimate lce = lceList.get(0);
- // LCE status deprecated in IRadio 1.2, so only check for IRadio < 1.2
- if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)
- || mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
- if (lce.getDownlinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
- mDownlinkBandwidth = lce.getDownlinkCapacityKbps();
- downlinkUpdated = true;
- }
- if (lce.getUplinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
- mUplinkBandwidth = lce.getUplinkCapacityKbps();
- uplinkUpdated = true;
- }
- }
-
- if (!downlinkUpdated || !uplinkUpdated) {
- fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
- }
-
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
- }
- }
-
- private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps,
- int downlinkBandwidthKbps) {
- if (DBG) {
- log("updateLinkBandwidthsFromBandwidthEstimator, UL= "
- + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps);
- }
- boolean downlinkUpdated = false;
- boolean uplinkUpdated = false;
- if (downlinkBandwidthKbps > 0) {
- mDownlinkBandwidth = downlinkBandwidthKbps;
- downlinkUpdated = true;
- }
- if (uplinkBandwidthKbps > 0) {
- mUplinkBandwidth = uplinkBandwidthKbps;
- uplinkUpdated = true;
- }
-
- if (!downlinkUpdated || !uplinkUpdated) {
- fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
- }
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
- }
- }
-
- private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) {
- String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
- if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
- ratName = mPhone.getServiceState().getNrFrequencyRange()
- == ServiceState.FREQUENCY_RANGE_MMWAVE
- ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
- }
- Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
- if (values != null) {
- if (!downlinkUpdated) {
- mDownlinkBandwidth = values.first;
- }
- if (!uplinkUpdated) {
- mUplinkBandwidth = values.second;
- }
- mUplinkBandwidth = Math.min(mUplinkBandwidth, mDownlinkBandwidth);
- }
- }
-
- private boolean isBandwidthSourceKey(String source) {
- return source.equals(mPhone.getContext().getResources().getString(
- com.android.internal.R.string.config_bandwidthEstimateSource));
- }
-
- /**
- * Indicates if this data connection was established for unmetered use only. Note that this
- * flag should be populated when data becomes active. And if it is set to true, it can be set to
- * false later when we are reevaluating the data connection. But if it is set to false, it
- * can never become true later because setting it to true will cause this data connection
- * losing some immutable network capabilities, which can cause issues in connectivity service.
- */
- private boolean mUnmeteredUseOnly = false;
-
- /**
- * Indicates if this data connection was established for MMS use only. This is true only when
- * mobile data is disabled but the user allows sending and receiving MMS messages. If the data
- * enabled settings indicate that MMS data is allowed unconditionally, MMS can be sent when data
- * is disabled even if it is a metered APN type.
- */
- private boolean mMmsUseOnly = false;
-
- /**
- * Indicates if when this connection was established we had a restricted/privileged
- * NetworkRequest and needed it to overcome data-enabled limitations.
- *
- * This flag overrides the APN-based restriction capability, restricting the network
- * based on both having a NetworkRequest with restricted AND needing a restricted
- * bit to overcome user-disabled status. This allows us to handle the common case
- * of having both restricted requests and unrestricted requests for the same apn:
- * if conditions require a restricted network to overcome user-disabled then it must
- * be restricted, otherwise it is unrestricted (or restricted based on APN type).
- *
- * This supports a privileged app bringing up a network without general apps having access
- * to it when the network is otherwise unavailable (hipri). The first use case is
- * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic
- * other than from the privileged carrier-app.
- *
- * Note that the data connection cannot go from unrestricted to restricted because the
- * connectivity service does not support dynamically closing TCP connections at this point.
- */
- private boolean mRestrictedNetworkOverride = false;
-
- /**
- * Indicates if this data connection supports enterprise use. Note that this flag should be
- * populated when data becomes active. Once it is set, the value cannot be changed because
- * setting it will cause this data connection to lose immutable network capabilities, which can
- * cause issues in connectivity service.
- */
- private boolean mEnterpriseUse = false;
-
- /**
- * Check if this data connection should be restricted. We should call this when data connection
- * becomes active, or when we want to re-evaluate the conditions to decide if we need to
- * unstrict the data connection.
- *
- * @return True if this data connection needs to be restricted.
- */
- private boolean shouldRestrictNetwork() {
- // first, check if there is any network request that containing restricted capability
- // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request)
- boolean isAnyRestrictedRequest = false;
- for (ApnContext apnContext : mApnContexts.keySet()) {
- if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) {
- isAnyRestrictedRequest = true;
- break;
- }
- }
-
- // If all of the network requests are non-restricted, then we don't need to restrict
- // the network.
- if (!isAnyRestrictedRequest) {
- return false;
- }
-
- // If the network is unmetered, then we don't need to restrict the network because users
- // won't be charged anyway.
- if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
- return false;
- }
-
- // If the data is disabled, then we need to restrict the network so only privileged apps can
- // use the restricted network while data is disabled.
- if (!mPhone.getDataEnabledSettings().isDataEnabled()) {
- return true;
- }
-
- // If the device is roaming, and the user does not turn on data roaming, then we need to
- // restrict the network so only privileged apps can use it.
- if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
- return true;
- }
-
- // Otherwise we should not restrict the network so anyone who requests can use it.
- return false;
- }
-
- /**
- * @return True if this data connection should only be used for unmetered purposes.
- */
- private boolean isUnmeteredUseOnly() {
- // If this data connection is on IWLAN, then it's unmetered and can be used by everyone.
- // Should not be for unmetered used only.
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- return false;
- }
-
- // If data is enabled, this data connection can't be for unmetered used only because
- // everyone should be able to use it if:
- // 1. Device is not roaming, or
- // 2. Device is roaming and data roaming is turned on
- if (mPhone.getDataEnabledSettings().isDataEnabled()) {
- if (!mPhone.getServiceState().getDataRoaming() || mDct.getDataRoamingEnabled()) {
- return false;
- }
- }
-
- // The data connection can only be unmetered used only if all attached APN contexts
- // attached to this data connection are unmetered.
- for (ApnContext apnContext : mApnContexts.keySet()) {
- if (ApnSettingUtils.isMeteredApnType(apnContext.getApnTypeBitmask(), mPhone)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * @return True if this data connection should only be used for MMS purposes.
- */
- private boolean isMmsUseOnly() {
- // MMS use only if data is disabled, MMS is allowed unconditionally, and MMS is the only
- // APN type for this data connection.
- DataEnabledSettings des = mPhone.getDataEnabledSettings();
- boolean mmsAllowedUnconditionally = !des.isDataEnabled() && des.isMmsAlwaysAllowed();
- boolean mmsApnOnly = isApnContextAttached(ApnSetting.TYPE_MMS, true);
- return mmsAllowedUnconditionally && mmsApnOnly;
- }
-
- /**
- * Check if this data connection supports enterprise use. We call this when the data connection
- * becomes active or when we want to reevaluate the conditions to decide if we need to update
- * the network agent capabilities.
- *
- * @return True if this data connection supports enterprise use.
- */
- private boolean isEnterpriseUse() {
- return mApnContexts.keySet().stream().anyMatch(
- ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE);
- }
-
- /**
- * Get the network capabilities for this data connection.
- *
- * @return the {@link NetworkCapabilities} of this data connection.
- */
- public NetworkCapabilities getNetworkCapabilities() {
- final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- boolean unmeteredApns = false;
-
- if (mApnSetting != null && !mEnterpriseUse && !mMmsUseOnly) {
- final int[] types = ApnSetting.getApnTypesFromBitmask(
- mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask);
- for (int type : types) {
- if ((!mRestrictedNetworkOverride && mUnmeteredUseOnly)
- && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
- log("Dropped the metered " + ApnSetting.getApnTypeString(type)
- + " type for the unmetered data call.");
- continue;
- }
- switch (type) {
- case ApnSetting.TYPE_ALL: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- break;
- }
- case ApnSetting.TYPE_DEFAULT: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- break;
- }
- case ApnSetting.TYPE_MMS: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- break;
- }
- case ApnSetting.TYPE_SUPL: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
- break;
- }
- case ApnSetting.TYPE_DUN: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- break;
- }
- case ApnSetting.TYPE_FOTA: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
- break;
- }
- case ApnSetting.TYPE_IMS: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
- break;
- }
- case ApnSetting.TYPE_CBS: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
- break;
- }
- case ApnSetting.TYPE_IA: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- break;
- }
- case ApnSetting.TYPE_EMERGENCY: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
- break;
- }
- case ApnSetting.TYPE_MCX: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX);
- break;
- }
- case ApnSetting.TYPE_XCAP: {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
- break;
- }
- default:
- }
- }
-
- if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
- unmeteredApns = true;
- }
- }
-
- // Mark NOT_METERED in the following cases:
- // 1. All APNs in the APN settings are unmetered.
- // 2. The non-restricted data is intended for unmetered use only.
- if (unmeteredApns || (mUnmeteredUseOnly && !mRestrictedNetworkOverride)) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- } else {
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- }
-
- if (mEnterpriseUse) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
-
- if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- }
-
- if (mMmsUseOnly) {
- if (ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone)) {
- log("Adding unmetered capability for the unmetered MMS-only data connection");
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- }
- log("Adding MMS capability for the MMS-only data connection");
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- }
-
- if (mRestrictedNetworkOverride) {
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- // don't use dun on restriction-overriden networks.
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- }
-
- builder.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth);
- builder.setLinkUpstreamBandwidthKbps(mUplinkBandwidth);
-
- builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(mSubId).build());
- builder.setSubscriptionIds(Collections.singleton(mSubId));
-
- if (!mPhone.getServiceState().getDataRoaming()) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- }
-
- if (!mCongestedOverride) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
- }
-
- if (mUnmeteredOverride) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
- }
-
- if (!mIsSuspended) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
- }
-
- final int carrierServicePackageUid = getCarrierServicePackageUid();
-
- // TODO(b/205736323): Owner and Admin UIDs currently come from separate data sources. Unify
- // them, and remove ArrayUtils.contains() check.
- if (carrierServicePackageUid != Process.INVALID_UID
- && ArrayUtils.contains(mAdministratorUids, carrierServicePackageUid)) {
- builder.setOwnerUid(carrierServicePackageUid);
- builder.setAllowedUids(Collections.singleton(carrierServicePackageUid));
- }
- builder.setAdministratorUids(mAdministratorUids);
-
- // Always start with NOT_VCN_MANAGED, then remove if VcnManager indicates this is part of a
- // VCN.
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
- final VcnNetworkPolicyResult vcnPolicy = getVcnPolicy(builder.build());
- if (!vcnPolicy.getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)) {
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
- }
- if (!vcnPolicy.getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- }
-
- return builder.build();
- }
-
- // TODO(b/205736323): Once TelephonyManager#getCarrierServicePackageNameForLogicalSlot() is
- // plumbed to CarrierPrivilegesTracker's cache, query the cached UIDs.
- private int getFirstUidForPackage(String pkgName) {
- if (pkgName == null) {
- return Process.INVALID_UID;
- }
-
- List<UserInfo> users = mPhone.getContext().getSystemService(UserManager.class).getUsers();
- for (UserInfo user : users) {
- int userId = user.getUserHandle().getIdentifier();
- try {
- PackageManager pm = mPhone.getContext().getPackageManager();
-
- if (pm != null) {
- return pm.getPackageUidAsUser(pkgName, userId);
- }
- } catch (NameNotFoundException exception) {
- // Didn't find package. Try other users
- Rlog.i(
- "DataConnection",
- "Unable to find uid for package " + pkgName + " and user " + userId);
- }
- }
- return Process.INVALID_UID;
- }
-
- private int getCarrierServicePackageUid() {
- String pkgName =
- mPhone.getContext()
- .getSystemService(TelephonyManager.class)
- .getCarrierServicePackageNameForLogicalSlot(mPhone.getPhoneId());
-
- return getFirstUidForPackage(pkgName);
- }
-
- /**
- * Check if the this data network is VCN-managed.
- *
- * @param networkCapabilities The network capabilities of this data network.
- * @return The VCN's policy for this DataNetwork.
- */
- private VcnNetworkPolicyResult getVcnPolicy(NetworkCapabilities networkCapabilities) {
- return mVcnManager.applyVcnNetworkPolicy(networkCapabilities, getLinkProperties());
- }
-
- /** @return {@code true} if validation is required, {@code false} otherwise. */
- public boolean isValidationRequired() {
- final NetworkCapabilities nc = getNetworkCapabilities();
- return nc != null
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
- }
-
- /**
- * @return {@code True} if 464xlat should be skipped.
- */
- @VisibleForTesting
- public boolean shouldSkip464Xlat() {
- switch (mApnSetting.getSkip464Xlat()) {
- case Telephony.Carriers.SKIP_464XLAT_ENABLE:
- return true;
- case Telephony.Carriers.SKIP_464XLAT_DISABLE:
- return false;
- case Telephony.Carriers.SKIP_464XLAT_DEFAULT:
- default:
- break;
- }
-
- // As default, return true if ims and no internet
- final NetworkCapabilities nc = getNetworkCapabilities();
- return nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
- && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
-
- /**
- * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address.
- */
- @VisibleForTesting
- public static boolean isIpAddress(String address) {
- if (address == null) return false;
-
- // Accept IPv6 addresses (only) in square brackets for compatibility.
- if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) {
- address = address.substring(1, address.length() - 1);
- }
- return InetAddresses.isNumericAddress(address);
- }
-
- private SetupResult setLinkProperties(DataCallResponse response,
- LinkProperties linkProperties) {
- // Check if system property dns usable
- String propertyPrefix = "net." + response.getInterfaceName() + ".";
- String dnsServers[] = new String[2];
- dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
- dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
- boolean okToUseSystemPropertyDns = isDnsOk(dnsServers);
-
- SetupResult result;
-
- // Start with clean network properties and if we have
- // a failure we'll clear again at the bottom of this code.
- linkProperties.clear();
-
- if (response.getCause() == DataFailCause.NONE) {
- try {
- // set interface name
- linkProperties.setInterfaceName(response.getInterfaceName());
-
- // set link addresses
- if (response.getAddresses().size() > 0) {
- for (LinkAddress la : response.getAddresses()) {
- if (!la.getAddress().isAnyLocalAddress()) {
- if (DBG) {
- log("addr/pl=" + la.getAddress() + "/"
- + la.getPrefixLength());
- }
- linkProperties.addLinkAddress(la);
- }
- }
- } else {
- throw new UnknownHostException("no address for ifname="
- + response.getInterfaceName());
- }
-
- // set dns servers
- if (response.getDnsAddresses().size() > 0) {
- for (InetAddress dns : response.getDnsAddresses()) {
- if (!dns.isAnyLocalAddress()) {
- linkProperties.addDnsServer(dns);
- }
- }
- } else if (okToUseSystemPropertyDns) {
- for (String dnsAddr : dnsServers) {
- dnsAddr = dnsAddr.trim();
- if (dnsAddr.isEmpty()) continue;
- InetAddress ia;
- try {
- ia = InetAddresses.parseNumericAddress(dnsAddr);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric dns addr=" + dnsAddr);
- }
- if (!ia.isAnyLocalAddress()) {
- linkProperties.addDnsServer(ia);
- }
- }
- } else {
- throw new UnknownHostException("Empty dns response and no system default dns");
- }
-
- // set pcscf
- if (response.getPcscfAddresses().size() > 0) {
- for (InetAddress pcscf : response.getPcscfAddresses()) {
- linkProperties.addPcscfServer(pcscf);
- }
- }
-
- for (InetAddress gateway : response.getGatewayAddresses()) {
- int mtu = gateway instanceof java.net.Inet6Address ? response.getMtuV6()
- : response.getMtuV4();
- // Allow 0.0.0.0 or :: as a gateway;
- // this indicates a point-to-point interface.
- linkProperties.addRoute(new RouteInfo(null, gateway, null,
- RouteInfo.RTN_UNICAST, mtu));
- }
-
- // set interface MTU
- // this may clobber the setting read from the APN db, but that's ok
- // TODO: remove once LinkProperties#setMtu is deprecated
- linkProperties.setMtu(response.getMtu());
-
- result = SetupResult.SUCCESS;
- } catch (UnknownHostException e) {
- log("setLinkProperties: UnknownHostException " + e);
- result = SetupResult.ERROR_INVALID_ARG;
- }
- } else {
- result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
- }
-
- // An error occurred so clear properties
- if (result != SetupResult.SUCCESS) {
- if (DBG) {
- log("setLinkProperties: error clearing LinkProperties status="
- + response.getCause() + " result=" + result);
- }
- linkProperties.clear();
- }
-
- return result;
- }
-
- /**
- * Initialize connection, this will fail if the
- * apnSettings are not compatible.
- *
- * @param cp the Connection parameters
- * @return true if initialization was successful.
- */
- private boolean initConnection(ConnectionParams cp) {
- ApnContext apnContext = cp.mApnContext;
- if (mApnSetting == null) {
- // Only change apn setting if it isn't set, it will
- // only NOT be set only if we're in DcInactiveState.
- mApnSetting = apnContext.getApnSetting();
- }
- if (mApnSetting == null || (!mApnSetting.canHandleType(apnContext.getApnTypeBitmask())
- && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE)) {
- if (DBG) {
- log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
- + " dc=" + DataConnection.this);
- }
- return false;
- }
- mTag += 1;
- mConnectionParams = cp;
- mConnectionParams.mTag = mTag;
-
- // always update the ConnectionParams with the latest or the
- // connectionGeneration gets stale
- mApnContexts.put(apnContext, cp);
-
- if (DBG) {
- log("initConnection: "
- + " RefCount=" + mApnContexts.size()
- + " mApnList=" + mApnContexts
- + " mConnectionParams=" + mConnectionParams);
- }
- return true;
- }
-
- /**
- * The parent state for all other states.
- */
- private class DcDefaultState extends State {
- @Override
- public void enter() {
- if (DBG) log("DcDefaultState: enter");
-
- // Register for DRS or RAT change
- mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
- mTransportType, getHandler(),
- DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED, null);
-
- mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(),
- DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null);
- mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(),
- DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true);
- mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
- DataConnection.EVENT_NR_STATE_CHANGED, null);
- mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
- DataConnection.EVENT_NR_FREQUENCY_CHANGED, null);
- mPhone.getServiceStateTracker().registerForCssIndicatorChanged(getHandler(),
- DataConnection.EVENT_CSS_INDICATOR_CHANGED, null);
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
- mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged(getHandler(),
- DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE, null);
- }
-
- // Add ourselves to the list of data connections
- mDcController.addDc(DataConnection.this);
- }
- @Override
- public void exit() {
- if (DBG) log("DcDefaultState: exit");
-
- // Unregister for DRS or RAT change.
- mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
- mTransportType, getHandler());
-
- mPhone.getServiceStateTracker().unregisterForDataRoamingOn(getHandler());
- mPhone.getServiceStateTracker().unregisterForDataRoamingOff(getHandler());
- mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
- mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
- mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
- mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler());
- }
-
- // Remove ourselves from the DC lists
- mDcController.removeDc(DataConnection.this);
-
- if (mAc != null) {
- mAc.disconnected();
- mAc = null;
- }
- mApnContexts.clear();
- mReconnectIntent = null;
- mDct = null;
- mApnSetting = null;
- mPhone = null;
- mDataServiceManager = null;
- mLinkProperties = null;
- mLastFailCause = DataFailCause.NONE;
- mUserData = null;
- mDcController = null;
- mDcTesterFailBringUpAll = null;
- }
-
- @Override
- public boolean processMessage(Message msg) {
- boolean retVal = HANDLED;
-
- if (VDBG) {
- log("DcDefault msg=" + getWhatToString(msg.what)
- + " RefCount=" + mApnContexts.size());
- }
- switch (msg.what) {
- case EVENT_RESET:
- if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
- transitionTo(mInactiveState);
- break;
- case EVENT_CONNECT:
- if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
- ConnectionParams cp = (ConnectionParams) msg.obj;
- notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
- break;
-
- case EVENT_DISCONNECT:
- case EVENT_DISCONNECT_ALL:
- case EVENT_REEVALUATE_RESTRICTED_STATE:
- if (DBG) {
- log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what)
- + " RefCount=" + mApnContexts.size());
- }
- deferMessage(msg);
- break;
- case EVENT_TEAR_DOWN_NOW:
- if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
- mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
- null);
- mDataCallSessionStats.setDeactivateDataCallReason(
- DataService.REQUEST_REASON_NORMAL);
- break;
- case EVENT_LOST_CONNECTION:
- if (DBG) {
- String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
- + " tag=" + msg.arg1 + ":mTag=" + mTag;
- logAndAddLogRec(s);
- }
- break;
- case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
- AsyncResult ar = (AsyncResult)msg.obj;
- Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result;
- mDataRegState = drsRatPair.first;
- updateTcpBufferSizes(drsRatPair.second);
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
- updateLinkBandwidthsFromCarrierConfig(drsRatPair.second);
- }
- mRilRat = drsRatPair.second;
- if (DBG) {
- log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
- + " regState=" + ServiceState.rilServiceStateToString(mDataRegState)
- + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat));
- }
- mDataCallSessionStats.onDrsOrRatChanged(
- ServiceState.rilRadioTechnologyToNetworkType(mRilRat));
- break;
-
- case EVENT_START_HANDOVER: //calls startHandover()
- if (DBG) {
- log("DcDefaultState: EVENT_START_HANDOVER not expected.");
- }
- Consumer<Integer> r = (Consumer<Integer>) msg.obj;
- r.accept(DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- break;
- case EVENT_START_HANDOVER_ON_TARGET:
- if (DBG) {
- log("DcDefaultState: EVENT_START_HANDOVER not expected, but will "
- + "clean up, result code: "
- + DataServiceCallback.resultCodeToString(msg.arg1));
- }
- ((Consumer<Boolean>) msg.obj).accept(false /* is in correct state*/);
- break;
- case EVENT_CANCEL_HANDOVER:
- // We don't need to do anything in this case
- if (DBG) {
- log("DcDefaultState: EVENT_CANCEL_HANDOVER resultCode="
- + DataServiceCallback.resultCodeToString(msg.arg1));
- }
- break;
- case EVENT_RELEASE_PDU_SESSION_ID: {
- // We do the same thing in all state in order to preserve the existing workflow
- final AsyncResult asyncResult = (AsyncResult) msg.obj;
- if (asyncResult == null) {
- loge("EVENT_RELEASE_PDU_SESSION_ID: asyncResult is null!");
- } else {
- if (msg.obj != null) {
- if (DBG) logd("EVENT_RELEASE_PDU_SESSION_ID: id released");
- Runnable runnable = (Runnable) asyncResult.userObj;
- runnable.run();
- } else {
- loge("EVENT_RELEASE_PDU_SESSION_ID: no runnable set");
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_ALLOCATE_PDU_SESSION_ID: {
- // We do the same thing in all state in order to preserve the existing workflow
- final AsyncResult asyncResult = (AsyncResult) msg.obj;
- if (asyncResult == null) {
- loge("EVENT_ALLOCATE_PDU_SESSION_ID: asyncResult is null!");
- } else {
- Consumer<Integer> onAllocated = (Consumer<Integer>) asyncResult.userObj;
- if (asyncResult.exception != null) {
- loge("EVENT_ALLOCATE_PDU_SESSION_ID: exception",
- asyncResult.exception);
- onAllocated.accept(PDU_SESSION_ID_NOT_SET);
- } else if (asyncResult.result == null) {
- loge("EVENT_ALLOCATE_PDU_SESSION_ID: result null, no id");
- onAllocated.accept(PDU_SESSION_ID_NOT_SET);
- } else {
- int psi = (int) asyncResult.result;
- if (DBG) logd("EVENT_ALLOCATE_PDU_SESSION_ID: psi=" + psi);
- onAllocated.accept(psi);
- }
- }
- retVal = HANDLED;
- break;
- }
- default:
- if (DBG) {
- log("DcDefaultState: ignore msg.what=" + getWhatToString(msg.what));
- }
- break;
- }
-
- return retVal;
- }
- }
-
- private void updateSuspendState() {
- if (mNetworkAgent == null) {
- Rlog.d(getName(), "Setting suspend state without a NetworkAgent");
- }
-
- boolean newSuspendedState = false;
- // Data can only be (temporarily) suspended while data is in active state
- if (getCurrentState() == mActiveState) {
- // Never set suspended for emergency apn. Emergency data connection
- // can work while device is not in service.
- if (mApnSetting != null && mApnSetting.isEmergencyApn()) {
- newSuspendedState = false;
- // If we are not in service, change to suspended.
- } else if (mDataRegState != ServiceState.STATE_IN_SERVICE) {
- newSuspendedState = true;
- // Check voice/data concurrency.
- } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE;
- }
- }
-
- // Only notify when there is a change.
- if (mIsSuspended != newSuspendedState) {
- mIsSuspended = newSuspendedState;
-
- // If data connection is active, we need to notify the new data connection state
- // changed event reflecting the latest suspended state.
- if (isActive()) {
- notifyDataConnectionState();
- }
- }
- }
-
- private void notifyDataConnectionState() {
- // The receivers of this have no way to differentiate between default and enterprise
- // connections. Do not notify for enterprise.
- if (!isEnterpriseUse()) {
- mPhone.notifyDataConnection(getPreciseDataConnectionState());
- } else {
- log("notifyDataConnectionState: Skipping for enterprise; state=" + getState());
- }
- }
-
- private DcDefaultState mDefaultState = new DcDefaultState();
-
- private int getApnTypeBitmask() {
- return isEnterpriseUse() ? ApnSetting.TYPE_ENTERPRISE :
- mApnSetting != null ? mApnSetting.getApnTypeBitmask() : 0;
- }
-
- private boolean canHandleDefault() {
- return !isEnterpriseUse() && mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false;
- }
-
- /**
- * The state machine is inactive and expects a EVENT_CONNECT.
- */
- private class DcInactiveState extends State {
- // Inform all contexts we've failed connecting
- public void setEnterNotificationParams(ConnectionParams cp, @DataFailureCause int cause,
- @HandoverFailureMode int handoverFailureMode) {
- if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause");
- mConnectionParams = cp;
- mDisconnectParams = null;
- mDcFailCause = cause;
- mHandoverFailureMode = handoverFailureMode;
- }
-
- // Inform all contexts we've failed disconnected
- public void setEnterNotificationParams(DisconnectParams dp) {
- if (VDBG) log("DcInactiveState: setEnterNotificationParams dp");
- mConnectionParams = null;
- mDisconnectParams = dp;
- mDcFailCause = DataFailCause.NONE;
- }
-
- // Inform all contexts of the failure cause
- public void setEnterNotificationParams(@DataFailureCause int cause) {
- mConnectionParams = null;
- mDisconnectParams = null;
- mDcFailCause = cause;
- }
-
- @Override
- public void enter() {
- mTag += 1;
- if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
- TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
- TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE,
- mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
- mDataCallSessionStats.onDataCallDisconnected(mDcFailCause);
- if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) {
- // This is from source data connection to set itself's state
- setHandoverState(HANDOVER_STATE_COMPLETED);
- }
-
- // Check for dangling agent. Ideally the handover source agent should be null if
- // handover process is smooth. When it's not null, that means handover failed. The
- // agent was not successfully transferred to the new data connection. We should
- // gracefully notify connectivity service the network was disconnected.
- if (mHandoverSourceNetworkAgent != null) {
- DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection();
- if (sourceDc != null) {
- // If the source data connection still owns this agent, then just reset the
- // handover state back to idle because handover is already failed.
- mHandoverLocalLog.log(
- "Handover failed. Reset the source dc " + sourceDc.getName()
- + " state to idle");
- sourceDc.cancelHandover();
- } else {
- // The agent is now a dangling agent. No data connection owns this agent.
- // Gracefully notify connectivity service disconnected.
- mHandoverLocalLog.log(
- "Handover failed and dangling agent found.");
- mHandoverSourceNetworkAgent.acquireOwnership(
- DataConnection.this, mTransportType);
- log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent);
- mHandoverSourceNetworkAgent.unregister(DataConnection.this);
- mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this);
- }
- mHandoverSourceNetworkAgent = null;
- }
-
- if (mConnectionParams != null) {
- if (DBG) {
- log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
- + DataFailCause.toString(mDcFailCause));
- }
- notifyConnectCompleted(mConnectionParams, mDcFailCause, mHandoverFailureMode,
- true);
- }
- if (mDisconnectParams != null) {
- if (DBG) {
- log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause="
- + DataFailCause.toString(mDcFailCause));
- }
- notifyDisconnectCompleted(mDisconnectParams, true);
- }
- if (mDisconnectParams == null && mConnectionParams == null
- && mDcFailCause != DataFailCause.NONE) {
- if (DBG) {
- log("DcInactiveState: enter notifyAllDisconnectCompleted failCause="
- + DataFailCause.toString(mDcFailCause));
- }
- notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE,
- DataFailCause.toString(mDcFailCause));
- }
-
- // Remove ourselves from cid mapping, before clearSettings
- mDcController.removeActiveDcByCid(DataConnection.this);
-
- // For the first time entering here (idle state before setup), do not notify
- // disconnected state. Only notify data connection disconnected for data that is
- // actually moving from disconnecting to disconnected, or setup failed. In both cases,
- // APN setting will not be null.
- if (mApnSetting != null) {
- notifyDataConnectionState();
- }
- clearSettings();
- }
-
- @Override
- public void exit() {
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RESET:
- case EVENT_REEVALUATE_RESTRICTED_STATE:
- if (DBG) {
- log("DcInactiveState: msg.what=" + getWhatToString(msg.what)
- + ", ignore we're already done");
- }
- return HANDLED;
- case EVENT_CONNECT:
- if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
- ConnectionParams cp = (ConnectionParams) msg.obj;
-
- if (!initConnection(cp)) {
- log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
- notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
- transitionTo(mInactiveState);
- return HANDLED;
- }
-
- int cause = connect(cp);
- if (cause != DataFailCause.NONE) {
- log("DcInactiveState: msg.what=EVENT_CONNECT connect failed");
- notifyConnectCompleted(cp, cause,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
- transitionTo(mInactiveState);
- return HANDLED;
- }
-
- if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- mSubId = cp.mSubId;
- }
-
- transitionTo(mActivatingState);
- return HANDLED;
- case EVENT_DISCONNECT:
- if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
- notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
- return HANDLED;
- case EVENT_DISCONNECT_ALL:
- if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
- notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
- return HANDLED;
- default:
- if (VDBG) {
- log("DcInactiveState not handled msg.what=" + getWhatToString(msg.what));
- }
- return NOT_HANDLED;
- }
- }
- }
- private DcInactiveState mInactiveState = new DcInactiveState();
-
- /**
- * The state machine is activating a connection.
- */
- private class DcActivatingState extends State {
- @Override
- public void enter() {
- int apnTypeBitmask = getApnTypeBitmask();
- TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
- TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING,
- mPhone.getPhoneId(), mId, apnTypeBitmask, canHandleDefault());
- setHandoverState(HANDOVER_STATE_IDLE);
- // restricted evaluation depends on network requests from apnContext. The evaluation
- // should happen once entering connecting state rather than active state because it's
- // possible that restricted network request can be released during the connecting window
- // and if we wait for connection established, then we might mistakenly
- // consider it as un-restricted. ConnectivityService then will immediately
- // tear down the connection through networkAgent unwanted callback if all requests for
- // this connection are going away.
- mRestrictedNetworkOverride = shouldRestrictNetwork();
-
- CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker();
- if (carrierPrivTracker != null) {
- carrierPrivTracker.registerCarrierPrivilegesListener(
- getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null);
- }
- notifyDataConnectionState();
- mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
- }
- @Override
- public boolean processMessage(Message msg) {
- boolean retVal;
- AsyncResult ar;
- ConnectionParams cp;
-
- if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
- switch (msg.what) {
- case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
- case EVENT_CONNECT:
- // Activating can't process until we're done.
- deferMessage(msg);
- retVal = HANDLED;
- break;
-
- case EVENT_SETUP_DATA_CONNECTION_DONE:
- cp = (ConnectionParams) msg.obj;
-
- DataCallResponse dataCallResponse =
- msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
- SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp);
- if (result != SetupResult.ERROR_STALE) {
- if (mConnectionParams != cp) {
- loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
- + " != cp:" + cp);
- }
- }
- if (DBG) {
- log("DcActivatingState onSetupConnectionCompleted result=" + result
- + " dc=" + DataConnection.this);
- }
- ApnContext.requestLog(
- cp.mApnContext, "onSetupConnectionCompleted result=" + result);
- switch (result) {
- case SUCCESS:
- // All is well
- mDcFailCause = DataFailCause.NONE;
- transitionTo(mActiveState);
- break;
- case ERROR_RADIO_NOT_AVAILABLE:
- // Vendor ril rejected the command and didn't connect.
- // Transition to inactive but send notifications after
- // we've entered the mInactive state.
- mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
- transitionTo(mInactiveState);
- break;
- case ERROR_DUPLICATE_CID:
- // TODO (b/180988471): Properly handle the case when an existing cid is
- // returned by tearing down the network agent if enterprise changed.
- long retry = RetryManager.NO_SUGGESTED_RETRY_DELAY;
- if (cp.mApnContext != null) {
- retry = RetryManager.NO_RETRY;
- mDct.getDataThrottler().setRetryTime(
- cp.mApnContext.getApnTypeBitmask(),
- retry, DcTracker.REQUEST_TYPE_NORMAL);
- }
- String logStr = "DcActivatingState: "
- + DataFailCause.toString(result.mFailCause)
- + " retry=" + retry;
- if (DBG) log(logStr);
- ApnContext.requestLog(cp.mApnContext, logStr);
- mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
- transitionTo(mInactiveState);
- break;
- case ERROR_NO_DEFAULT_CONNECTION:
- // TODO (b/180988471): Properly handle the case when a default data
- // connection doesn't exist (tear down connection and retry).
- // Currently, this just tears down the connection without retry.
- if (DBG) log("DcActivatingState: NO_DEFAULT_DATA");
- case ERROR_INVALID_ARG:
- // The addresses given from the RIL are bad
- tearDownData(cp);
- transitionTo(mDisconnectingErrorCreatingConnection);
- break;
- case ERROR_DATA_SERVICE_SPECIFIC_ERROR:
-
- // Retrieve the suggested retry delay from the modem and save it.
- // If the modem want us to retry the current APN again, it will
- // suggest a positive delay value (in milliseconds). Otherwise we'll get
- // NO_SUGGESTED_RETRY_DELAY here.
-
- long delay = getSuggestedRetryDelay(dataCallResponse);
- long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
- if (delay == RetryManager.NO_RETRY) {
- retryTime = RetryManager.NO_RETRY;
- } else if (delay >= 0) {
- retryTime = SystemClock.elapsedRealtime() + delay;
- }
- int newRequestType = DcTracker.calculateNewRetryRequestType(
- mHandoverFailureMode, cp.mRequestType, mDcFailCause);
- mDct.getDataThrottler().setRetryTime(getApnTypeBitmask(),
- retryTime, newRequestType);
-
- String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
- + " delay=" + delay
- + " result=" + result
- + " result.isRadioRestartFailure="
- + DataFailCause.isRadioRestartFailure(mPhone.getContext(),
- result.mFailCause, mPhone.getSubId())
- + " isPermanentFailure=" +
- mDct.isPermanentFailure(result.mFailCause);
- if (DBG) log(str);
- ApnContext.requestLog(cp.mApnContext, str);
-
- // Save the cause. DcTracker.onDataSetupComplete will check this
- // failure cause and determine if we need to retry this APN later
- // or not.
- mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
- dataCallResponse != null
- ? dataCallResponse.getHandoverFailureMode()
- : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
- transitionTo(mInactiveState);
- break;
- case ERROR_STALE:
- loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
- + " tag:" + cp.mTag + " != mTag:" + mTag);
- break;
- default:
- throw new RuntimeException("Unknown SetupResult, should not happen");
- }
- retVal = HANDLED;
- mDataCallSessionStats
- .onSetupDataCallResponse(dataCallResponse,
- ServiceState.rilRadioTechnologyToNetworkType(cp.mRilRat),
- getApnTypeBitmask(), mApnSetting.getProtocol(),
- result.mFailCause);
- break;
- case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
- AsyncResult asyncResult = (AsyncResult) msg.obj;
- int[] administratorUids = (int[]) asyncResult.result;
- mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
- retVal = HANDLED;
- break;
- case EVENT_START_HANDOVER_ON_TARGET:
- //called after startHandover on target transport
- ((Consumer<Boolean>) msg.obj).accept(true /* is in correct state*/);
- retVal = HANDLED;
- break;
- case EVENT_CANCEL_HANDOVER:
- transitionTo(mInactiveState);
- retVal = HANDLED;
- break;
- default:
- if (VDBG) {
- log("DcActivatingState not handled msg.what=" +
- getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
- }
- retVal = NOT_HANDLED;
- break;
- }
- return retVal;
- }
- }
- private DcActivatingState mActivatingState = new DcActivatingState();
-
- /**
- * The state machine is connected, expecting an EVENT_DISCONNECT.
- */
- private class DcActiveState extends State {
-
- @Override public void enter() {
- if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
- TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
- TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE,
- mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
- // If we were retrying there maybe more than one, otherwise they'll only be one.
- notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE,
- Phone.REASON_CONNECTED);
-
- mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
- DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
- mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(),
- DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null);
-
- // If the EVENT_CONNECT set the current max retry restore it here
- // if it didn't then this is effectively a NOP.
- mDcController.addActiveDcByCid(DataConnection.this);
-
- updateTcpBufferSizes(mRilRat);
- updateLinkBandwidthsFromCarrierConfig(mRilRat);
-
- final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
- configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
- configBuilder.setLegacyTypeName(NETWORK_TYPE);
- int networkType = getNetworkType();
- configBuilder.setLegacySubType(networkType);
- configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType));
- configBuilder.setLegacyExtraInfo(mApnSetting.getApnName());
- final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent();
- if (carrierSignalAgent.hasRegisteredReceivers(TelephonyManager
- .ACTION_CARRIER_SIGNAL_REDIRECTED)) {
- // carrierSignal Receivers will place the carrier-specific provisioning notification
- configBuilder.setProvisioningNotificationEnabled(false);
- }
-
- final String subscriberId = mPhone.getSubscriberId();
- if (!TextUtils.isEmpty(subscriberId)) {
- configBuilder.setSubscriberId(subscriberId);
- }
-
- // set skip464xlat if it is not default otherwise
- if (shouldSkip464Xlat()) {
- configBuilder.setNat64DetectionEnabled(false);
- }
-
- mUnmeteredUseOnly = isUnmeteredUseOnly();
- mMmsUseOnly = isMmsUseOnly();
- mEnterpriseUse = isEnterpriseUse();
-
- if (DBG) {
- log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride
- + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly
- + ", mMmsUseOnly = " + mMmsUseOnly
- + ", mEnterpriseUse = " + mEnterpriseUse);
- }
-
- // Always register a VcnNetworkPolicyChangeListener, regardless of whether this is a
- // handover
- // or new Network.
- mVcnManager.addVcnNetworkPolicyChangeListener(
- new HandlerExecutor(getHandler()), mVcnPolicyChangeListener);
-
- if (mConnectionParams != null
- && mConnectionParams.mRequestType == REQUEST_TYPE_HANDOVER) {
- // If this is a data setup for handover, we need to reuse the existing network agent
- // instead of creating a new one. This should be transparent to connectivity
- // service.
- DcTracker dcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
- DataConnection dc = dcTracker.getDataConnectionByApnType(
- mConnectionParams.mApnContext.getApnType());
- // It's possible that the source data connection has been disconnected by the modem
- // already. If not, set its handover state to completed.
- if (dc != null) {
- // Transfer network agent from the original data connection as soon as the
- // new handover data connection is connected.
- dc.setHandoverState(HANDOVER_STATE_COMPLETED);
- }
-
- if (mHandoverSourceNetworkAgent != null) {
- String logStr = "Transfer network agent " + mHandoverSourceNetworkAgent.getTag()
- + " successfully.";
- log(logStr);
- mHandoverLocalLog.log(logStr);
- mNetworkAgent = mHandoverSourceNetworkAgent;
- mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType);
-
- // TODO: Should evaluate mDisabledApnTypeBitMask again after handover. We don't
- // do it now because connectivity service does not support dynamically removing
- // immutable capabilities.
-
- mNetworkAgent.updateLegacySubtype(DataConnection.this);
- // Update the capability after handover
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
- mHandoverSourceNetworkAgent = null;
- } else {
- String logStr = "Failed to get network agent from original data connection";
- loge(logStr);
- mHandoverLocalLog.log(logStr);
- return;
- }
- } else {
- mScore = calculateScore();
- final NetworkFactory factory = PhoneFactory.getNetworkFactory(
- mPhone.getPhoneId());
- final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
-
- mDisabledApnTypeBitMask |= getDisallowedApnTypes();
- updateLinkPropertiesHttpProxy();
- mNetworkAgent = new DcNetworkAgent(DataConnection.this, mPhone, mScore,
- configBuilder.build(), provider, mTransportType);
-
- VcnNetworkPolicyResult policyResult =
- mVcnManager.applyVcnNetworkPolicy(
- getNetworkCapabilities(), getLinkProperties());
- if (policyResult.isTeardownRequested()) {
- tearDownAll(
- Phone.REASON_VCN_REQUESTED_TEARDOWN,
- DcTracker.RELEASE_TYPE_DETACH,
- null /* onCompletedMsg */);
- } else {
- // All network agents start out in CONNECTING mode, but DcNetworkAgents are
- // created when the network is already connected. Hence, send the connected
- // notification immediately.
- mNetworkAgent.markConnected();
- }
-
- // The network agent is always created with NOT_SUSPENDED capability, but the
- // network might be already out of service (or voice call is ongoing) just right
- // before data connection is created. Connectivity service would not allow a network
- // created with suspended state, so we create a non-suspended network first, and
- // then immediately evaluate the suspended state.
- sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE));
- }
-
- // The qos parameters are set when the call is connected
- syncQosToNetworkAgent();
-
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.registerForNattKeepaliveStatus(
- getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
- mPhone.mCi.registerForLceInfo(
- getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
- }
- notifyDataConnectionState();
- TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
- mCid, getApnTypeBitmask(), RilDataCall.State.CONNECTED);
- }
-
- @Override
- public void exit() {
- if (DBG) log("DcActiveState: exit dc=" + this);
- mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
- mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
-
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
- mPhone.mCi.unregisterForLceInfo(getHandler());
- }
-
- // If we are still owning this agent, then we should inform connectivity service the
- // data connection is disconnected. There is one exception that we shouldn't unregister,
- // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will
- // be transferred to the new data connection on the other transport.
- if (mNetworkAgent != null) {
- syncQosToNetworkAgent();
- if (mHandoverState == HANDOVER_STATE_IDLE) {
- mNetworkAgent.unregister(DataConnection.this);
- }
- mNetworkAgent.releaseOwnership(DataConnection.this);
- }
- mNetworkAgent = null;
-
- TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
- mCid, getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
-
- mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener);
-
- CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker();
- if (carrierPrivTracker != null) {
- carrierPrivTracker.unregisterCarrierPrivilegesListener(getHandler());
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- boolean retVal;
-
- switch (msg.what) {
- case EVENT_CONNECT: {
- ConnectionParams cp = (ConnectionParams) msg.obj;
- // either add this new apn context to our set or
- // update the existing cp with the latest connection generation number
- mApnContexts.put(cp.mApnContext, cp);
- // TODO (b/118347948): evaluate if it's still needed after assigning
- // different scores to different Cellular network.
- mDisabledApnTypeBitMask &= ~cp.mApnContext.getApnTypeBitmask();
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- if (DBG) {
- log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
- }
- notifyConnectCompleted(cp, DataFailCause.NONE,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
- retVal = HANDLED;
- break;
- }
- case EVENT_DISCONNECT: {
- DisconnectParams dp = (DisconnectParams) msg.obj;
- if (DBG) {
- log("DcActiveState: EVENT_DISCONNECT dp=" + dp
- + " dc=" + DataConnection.this);
- }
- if (mApnContexts.containsKey(dp.mApnContext)) {
- if (DBG) {
- log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
- + mApnContexts.size());
- }
-
- if (mApnContexts.size() == 1) {
- mApnContexts.clear();
- mDisconnectParams = dp;
- mConnectionParams = null;
- dp.mTag = mTag;
- tearDownData(dp);
- transitionTo(mDisconnectingState);
- } else {
- mApnContexts.remove(dp.mApnContext);
- // TODO (b/118347948): evaluate if it's still needed after assigning
- // different scores to different Cellular network.
- mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask();
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- notifyDisconnectCompleted(dp, false);
- }
- } else {
- log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
- + " in this dc=" + DataConnection.this);
- notifyDisconnectCompleted(dp, false);
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_DISCONNECT_ALL: {
- if (DBG) {
- log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
- + " dc=" + DataConnection.this);
- }
- DisconnectParams dp = (DisconnectParams) msg.obj;
- mDisconnectParams = dp;
- mConnectionParams = null;
- dp.mTag = mTag;
- tearDownData(dp);
- transitionTo(mDisconnectingState);
- retVal = HANDLED;
- break;
- }
- case EVENT_LOST_CONNECTION: {
- if (DBG) {
- log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
- }
-
- mInactiveState.setEnterNotificationParams(DataFailCause.LOST_CONNECTION);
- transitionTo(mInactiveState);
- retVal = HANDLED;
- break;
- }
- case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result;
- mDataRegState = drsRatPair.first;
- updateTcpBufferSizes(drsRatPair.second);
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
- updateLinkBandwidthsFromCarrierConfig(drsRatPair.second);
- }
- mRilRat = drsRatPair.second;
- if (DBG) {
- log("DcActiveState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
- + " drs=" + mDataRegState
- + " mRilRat=" + mRilRat);
- }
- updateSuspendState();
- if (mNetworkAgent != null) {
- mNetworkAgent.updateLegacySubtype(DataConnection.this);
- // The new suspended state will be passed through connectivity service
- // through NET_CAPABILITY_NOT_SUSPENDED.
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
- }
- retVal = HANDLED;
- mDataCallSessionStats.onDrsOrRatChanged(
- ServiceState.rilRadioTechnologyToNetworkType(mRilRat));
- break;
- }
- case EVENT_NR_FREQUENCY_CHANGED:
- // fallthrough
- case EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED:
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
- updateLinkBandwidthsFromCarrierConfig(mRilRat);
- }
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED:
- boolean isUnmetered = (boolean) msg.obj;
- if (isUnmetered == mUnmeteredOverride) {
- retVal = HANDLED;
- break;
- }
- mUnmeteredOverride = isUnmetered;
- if (mNetworkAgent != null) {
- mNetworkAgent.updateLegacySubtype(DataConnection.this);
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- case EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED:
- boolean isCongested = (boolean) msg.obj;
- if (isCongested == mCongestedOverride) {
- retVal = HANDLED;
- break;
- }
- mCongestedOverride = isCongested;
- if (mNetworkAgent != null) {
- mNetworkAgent.updateLegacySubtype(DataConnection.this);
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- case EVENT_DATA_CONNECTION_ROAM_ON:
- case EVENT_DATA_CONNECTION_ROAM_OFF: {
- if (mNetworkAgent != null) {
- mNetworkAgent.updateLegacySubtype(DataConnection.this);
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
- case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED:
- case EVENT_CSS_INDICATOR_CHANGED:
- case EVENT_UPDATE_SUSPENDED_STATE: {
- updateSuspendState();
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_BW_REFRESH_RESPONSE: {
- AsyncResult ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
- } else {
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
- updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_KEEPALIVE_START_REQUEST: {
- KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
- int slotId = msg.arg1;
- int intervalMillis = msg.arg2 * 1000;
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.startNattKeepalive(
- DataConnection.this.mCid, pkt, intervalMillis,
- DataConnection.this.obtainMessage(
- EVENT_KEEPALIVE_STARTED, slotId, 0, null));
- } else {
- // We currently do not support NATT Keepalive requests using the
- // DataService API, so unless the request is WWAN (always bound via
- // the CommandsInterface), the request cannot be honored.
- //
- // TODO: b/72331356 to add support for Keepalive to the DataService
- // so that keepalive requests can be handled (if supported) by the
- // underlying transport.
- if (mNetworkAgent != null) {
- mNetworkAgent.sendSocketKeepaliveEvent(
- msg.arg1, SocketKeepalive.ERROR_INVALID_NETWORK);
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_KEEPALIVE_STOP_REQUEST: {
- int slotId = msg.arg1;
- int handle = mNetworkAgent.keepaliveTracker.getHandleForSlot(slotId);
- if (handle < 0) {
- loge("No slot found for stopSocketKeepalive! " + slotId);
- mNetworkAgent.sendSocketKeepaliveEvent(
- slotId, SocketKeepalive.ERROR_NO_SUCH_SLOT);
- retVal = HANDLED;
- break;
- } else {
- logd("Stopping keepalive with handle: " + handle);
- }
-
- mPhone.mCi.stopNattKeepalive(
- handle, DataConnection.this.obtainMessage(
- EVENT_KEEPALIVE_STOPPED, handle, slotId, null));
- retVal = HANDLED;
- break;
- }
- case EVENT_KEEPALIVE_STARTED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- final int slot = msg.arg1;
- if (ar.exception != null || ar.result == null) {
- loge("EVENT_KEEPALIVE_STARTED: error starting keepalive, e="
- + ar.exception);
- mNetworkAgent.sendSocketKeepaliveEvent(
- slot, SocketKeepalive.ERROR_HARDWARE_ERROR);
- } else {
- KeepaliveStatus ks = (KeepaliveStatus) ar.result;
- if (ks == null) {
- loge("Null KeepaliveStatus received!");
- } else {
- mNetworkAgent.keepaliveTracker.handleKeepaliveStarted(slot, ks);
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_KEEPALIVE_STATUS: {
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- loge("EVENT_KEEPALIVE_STATUS: error in keepalive, e=" + ar.exception);
- // We have no way to notify connectivity in this case.
- }
- if (ar.result != null) {
- KeepaliveStatus ks = (KeepaliveStatus) ar.result;
- mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(ks);
- }
-
- retVal = HANDLED;
- break;
- }
- case EVENT_KEEPALIVE_STOPPED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- final int handle = msg.arg1;
- final int slotId = msg.arg2;
-
- if (ar.exception != null) {
- loge("EVENT_KEEPALIVE_STOPPED: error stopping keepalive for handle="
- + handle + " e=" + ar.exception);
- mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(
- new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN));
- } else {
- log("Keepalive Stop Requested for handle=" + handle);
- mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(
- new KeepaliveStatus(
- handle, KeepaliveStatus.STATUS_INACTIVE));
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_LINK_CAPACITY_CHANGED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- loge("EVENT_LINK_CAPACITY_CHANGED e=" + ar.exception);
- } else {
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
- updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: {
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- loge("EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE e=" + ar.exception);
- } else {
- Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result;
- if (isBandwidthSourceKey(
- DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
- updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second);
- }
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_REEVALUATE_RESTRICTED_STATE: {
- // If the network was restricted, and now it does not need to be restricted
- // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability.
- if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) {
- if (DBG) {
- log("Data connection becomes not-restricted. dc=" + this);
- }
- // Note we only do this when network becomes non-restricted. When a
- // non-restricted becomes restricted (e.g. users disable data, or turn off
- // data roaming), DCT will explicitly tear down the networks (because
- // connectivity service does not support force-close TCP connections today).
- // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability
- // (see {@link NetworkCapabilities}) once we add it to the network, we can't
- // remove it through the entire life cycle of the connection.
- mRestrictedNetworkOverride = false;
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
-
- // If the data does need to be unmetered use only (e.g. users turn on data, or
- // device is not roaming anymore assuming data roaming is off), then we can
- // dynamically add those metered APN type capabilities back. (But not the
- // other way around because most of the APN-type capabilities are immutable
- // capabilities.)
- if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) {
- mUnmeteredUseOnly = false;
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
-
- mMmsUseOnly = isMmsUseOnly();
-
- retVal = HANDLED;
- break;
- }
- case EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES: {
- // Update other properties like link properties if needed in future.
- updateScore();
- retVal = HANDLED;
- break;
- }
- case EVENT_NR_STATE_CHANGED: {
- updateTcpBufferSizes(mRilRat);
- if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
- updateLinkBandwidthsFromCarrierConfig(mRilRat);
- }
- if (mNetworkAgent != null) {
- mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
- AsyncResult asyncResult = (AsyncResult) msg.obj;
- int[] administratorUids = (int[]) asyncResult.result;
- mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
-
- // Administrator UIDs changed, so update NetworkAgent with new
- // NetworkCapabilities
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(
- getNetworkCapabilities(), DataConnection.this);
- }
- retVal = HANDLED;
- break;
- case EVENT_START_HANDOVER: //calls startHandover()
- Consumer<Integer> r = (Consumer<Integer>) msg.obj;
- r.accept(msg.arg1);
- retVal = HANDLED;
- break;
-
- default:
- if (VDBG) {
- log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
- }
- retVal = NOT_HANDLED;
- break;
- }
- return retVal;
- }
- }
- private DcActiveState mActiveState = new DcActiveState();
-
- /**
- * The state machine is disconnecting.
- */
- private class DcDisconnectingState extends State {
- @Override
- public void enter() {
- TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
- TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING,
- mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
- notifyDataConnectionState();
- }
- @Override
- public boolean processMessage(Message msg) {
- boolean retVal;
-
- switch (msg.what) {
- case EVENT_CONNECT:
- if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
- + mApnContexts.size());
- deferMessage(msg);
- retVal = HANDLED;
- break;
-
- case EVENT_DEACTIVATE_DONE:
- DisconnectParams dp = (DisconnectParams) msg.obj;
-
- String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
- + mApnContexts.size();
-
- if (DBG) log(str);
- ApnContext.requestLog(dp.mApnContext, str);
-
- // Clear out existing qos sessions
- updateQosParameters(null);
-
- if (dp.mTag == mTag) {
- // Transition to inactive but send notifications after
- // we've entered the mInactive state.
- mInactiveState.setEnterNotificationParams(dp);
- transitionTo(mInactiveState);
- } else {
- if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
- + " dp.tag=" + dp.mTag + " mTag=" + mTag);
- }
- retVal = HANDLED;
- break;
-
- default:
- if (VDBG) {
- log("DcDisconnectingState not handled msg.what="
- + getWhatToString(msg.what));
- }
- retVal = NOT_HANDLED;
- break;
- }
- return retVal;
- }
- }
- private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
-
- /**
- * The state machine is disconnecting after an creating a connection.
- */
- private class DcDisconnectionErrorCreatingConnection extends State {
- @Override
- public void enter() {
- TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
- TelephonyStatsLog
- .MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION,
- mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
- notifyDataConnectionState();
- }
- @Override
- public boolean processMessage(Message msg) {
- boolean retVal;
-
- switch (msg.what) {
- case EVENT_DEACTIVATE_DONE:
- ConnectionParams cp = (ConnectionParams) msg.obj;
- if (cp.mTag == mTag) {
- String str = "DcDisconnectionErrorCreatingConnection" +
- " msg.what=EVENT_DEACTIVATE_DONE";
- if (DBG) log(str);
- ApnContext.requestLog(cp.mApnContext, str);
-
- // Transition to inactive but send notifications after
- // we've entered the mInactive state.
- mInactiveState.setEnterNotificationParams(cp,
- DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
- transitionTo(mInactiveState);
- } else {
- if (DBG) {
- log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
- + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
- }
- }
- retVal = HANDLED;
- break;
-
- default:
- if (VDBG) {
- log("DcDisconnectionErrorCreatingConnection not handled msg.what="
- + getWhatToString(msg.what));
- }
- retVal = NOT_HANDLED;
- break;
- }
- return retVal;
- }
- }
- private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
- new DcDisconnectionErrorCreatingConnection();
-
- /**
- * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
- * Used for cellular networks that use Access Point Names (APN) such
- * as GSM networks.
- *
- * @param apnContext is the Access Point Name to bring up a connection to
- * @param profileId for the connection
- * @param rilRadioTechnology Radio technology for the data connection
- * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
- * With AsyncResult.userObj set to the original msg.obj,
- * AsyncResult.result = FailCause and AsyncResult.exception = Exception().
- * @param connectionGeneration used to track a single connection request so disconnects can get
- * ignored if obsolete.
- * @param requestType Data request type
- * @param subId the subscription id associated with this data connection.
- * @param isApnPreferred whether or not the apn is preferred.
- */
- public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
- Message onCompletedMsg, int connectionGeneration,
- @RequestNetworkType int requestType, int subId, boolean isApnPreferred) {
- if (DBG) {
- log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
- }
-
- if (mApnSetting == null) {
- mApnSetting = apnContext.getApnSetting();
- }
-
- sendMessage(DataConnection.EVENT_CONNECT,
- new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
- connectionGeneration, requestType, subId, isApnPreferred));
- }
-
- /**
- * Tear down the connection through the apn on the network.
- *
- * @param apnContext APN context
- * @param reason reason to tear down
- * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
- * With AsyncResult.userObj set to the original msg.obj.
- */
- public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
- if (DBG) {
- log("tearDown: apnContext=" + apnContext + " reason=" + reason + " onCompletedMsg="
- + onCompletedMsg);
- }
- sendMessage(DataConnection.EVENT_DISCONNECT,
- new DisconnectParams(apnContext, reason, DcTracker.RELEASE_TYPE_DETACH,
- onCompletedMsg));
- }
-
- // ******* "public" interface
-
- /**
- * Used for testing purposes.
- */
- void tearDownNow() {
- if (DBG) log("tearDownNow()");
- sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
- }
-
- /**
- * Tear down the connection through the apn on the network. Ignores reference count and
- * and always tears down.
- *
- * @param releaseType Data release type
- * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
- * With AsyncResult.userObj set to the original msg.obj.
- */
- public void tearDownAll(String reason, @ReleaseNetworkType int releaseType,
- Message onCompletedMsg) {
- if (DBG) {
- log("tearDownAll: reason=" + reason + ", releaseType="
- + DcTracker.releaseTypeToString(releaseType));
- }
- sendMessage(DataConnection.EVENT_DISCONNECT_ALL,
- new DisconnectParams(null, reason, releaseType, onCompletedMsg));
- }
-
- /**
- * Reset the data connection to inactive state.
- */
- public void reset() {
- sendMessage(EVENT_RESET);
- if (DBG) log("reset");
- }
-
- /**
- * Re-evaluate the restricted state. If the restricted data connection does not need to be
- * restricted anymore, we need to dynamically change the network's capability.
- */
- void reevaluateRestrictedState() {
- sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE);
- if (DBG) log("reevaluate restricted state");
- }
-
- /**
- * Re-evaluate the data connection properties. For example, it will recalculate data connection
- * score and update through network agent it if changed.
- */
- void reevaluateDataConnectionProperties() {
- sendMessage(EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES);
- if (DBG) log("reevaluate data connection properties");
- }
-
- /**
- * @return The parameters used for initiating a data connection.
- */
- public ConnectionParams getConnectionParams() {
- return mConnectionParams;
- }
-
- /**
- * Update PCSCF addresses
- *
- * @param response
- */
- public void updatePcscfAddr(DataCallResponse response) {
- mPcscfAddr = response.getPcscfAddresses().stream()
- .map(InetAddress::getHostAddress).toArray(String[]::new);
- }
-
- /**
- * @return The list of PCSCF addresses
- */
- public String[] getPcscfAddresses() {
- return mPcscfAddr;
- }
-
- /**
- * Using the result of the SETUP_DATA_CALL determine the retry delay.
- *
- * @param response The response from setup data call
- * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested.
- * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds
- * to the next SETUP_DATA_CALL.
- */
- private long getSuggestedRetryDelay(DataCallResponse response) {
- /** According to ril.h
- * The value < 0 means no value is suggested
- * The value 0 means retry should be done ASAP.
- * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry.
- */
- if (response == null) {
- return RetryManager.NO_SUGGESTED_RETRY_DELAY;
- }
-
- long suggestedRetryTime = response.getRetryDurationMillis();
-
- // The value < 0 means no value is suggested
- if (suggestedRetryTime < 0) {
- if (DBG) log("No suggested retry delay.");
- return RetryManager.NO_SUGGESTED_RETRY_DELAY;
- } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)
- && suggestedRetryTime == Long.MAX_VALUE) {
- if (DBG) log("Network suggested not retrying.");
- return RetryManager.NO_RETRY;
- } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6)
- && suggestedRetryTime == Integer.MAX_VALUE) {
- if (DBG) log("Network suggested not retrying.");
- return RetryManager.NO_RETRY;
- }
-
- return suggestedRetryTime;
- }
-
- public List<ApnContext> getApnContexts() {
- return new ArrayList<>(mApnContexts.keySet());
- }
-
- /**
- * Return whether there is an ApnContext for the given type in this DataConnection.
- * @param type APN type to check
- * @param exclusive true if the given APN type should be the only APN type that exists
- * @return True if there is an ApnContext for the given type
- */
- private boolean isApnContextAttached(@ApnType int type, boolean exclusive) {
- boolean attached = mApnContexts.keySet().stream()
- .map(ApnContext::getApnTypeBitmask)
- .anyMatch(bitmask -> bitmask == type);
- if (exclusive) {
- attached &= mApnContexts.size() == 1;
- }
- return attached;
- }
-
- /** Get the network agent of the data connection */
- @Nullable
- DcNetworkAgent getNetworkAgent() {
- return mNetworkAgent;
- }
-
- void setHandoverState(@HandoverState int state) {
- if (mHandoverState != state) {
- String logStr = "State changed from " + handoverStateToString(mHandoverState)
- + " to " + handoverStateToString(state);
- mHandoverLocalLog.log(logStr);
- logd(logStr);
- mHandoverState = state;
- }
- }
-
- /** Sets the {@link DataCallSessionStats} mock for this data connection during unit testing. */
- @VisibleForTesting
- public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) {
- mDataCallSessionStats = dataCallSessionStats;
- }
-
- /**
- * @return the string for msg.what as our info.
- */
- @Override
- protected String getWhatToString(int what) {
- return cmdToString(what);
- }
-
- private static String msgToString(Message msg) {
- String retVal;
- if (msg == null) {
- retVal = "null";
- } else {
- StringBuilder b = new StringBuilder();
-
- b.append("{what=");
- b.append(cmdToString(msg.what));
-
- b.append(" when=");
- TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
-
- if (msg.arg1 != 0) {
- b.append(" arg1=");
- b.append(msg.arg1);
- }
-
- if (msg.arg2 != 0) {
- b.append(" arg2=");
- b.append(msg.arg2);
- }
-
- if (msg.obj != null) {
- b.append(" obj=");
- b.append(msg.obj);
- }
-
- b.append(" target=");
- b.append(msg.getTarget());
-
- b.append(" replyTo=");
- b.append(msg.replyTo);
-
- b.append("}");
-
- retVal = b.toString();
- }
- return retVal;
- }
-
- static void slog(String s) {
- Rlog.d("DC", s);
- }
-
- /**
- * Log with debug
- *
- * @param s is string log
- */
- @Override
- protected void log(String s) {
- Rlog.d(getName(), s);
- }
-
- /**
- * Log with debug attribute
- *
- * @param s is string log
- */
- @Override
- protected void logd(String s) {
- Rlog.d(getName(), s);
- }
-
- /**
- * Log with verbose attribute
- *
- * @param s is string log
- */
- @Override
- protected void logv(String s) {
- Rlog.v(getName(), s);
- }
-
- /**
- * Log with info attribute
- *
- * @param s is string log
- */
- @Override
- protected void logi(String s) {
- Rlog.i(getName(), s);
- }
-
- /**
- * Log with warning attribute
- *
- * @param s is string log
- */
- @Override
- protected void logw(String s) {
- Rlog.w(getName(), s);
- }
-
- /**
- * Log with error attribute
- *
- * @param s is string log
- */
- @Override
- protected void loge(String s) {
- Rlog.e(getName(), s);
- }
-
- /**
- * Log with error attribute
- *
- * @param s is string log
- * @param e is a Throwable which logs additional information.
- */
- @Override
- protected void loge(String s, Throwable e) {
- Rlog.e(getName(), s, e);
- }
-
- /** Doesn't print mApnList of ApnContext's which would be recursive */
- public synchronized String toStringSimple() {
- return getName() + ": State=" + getCurrentState().getName()
- + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
- + " mCid=" + mCid + " mCreateTime=" + mCreateTime
- + " mLastastFailTime=" + mLastFailTime
- + " mLastFailCause=" + DataFailCause.toString(mLastFailCause)
- + " mTag=" + mTag
- + " mLinkProperties=" + mLinkProperties
- + " linkCapabilities=" + getNetworkCapabilities()
- + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride;
- }
-
- @Override
- public String toString() {
- return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
- }
-
- /** Check if the device is connected to NR 5G Non-Standalone network. */
- private boolean isNRConnected() {
- return mPhone.getServiceState().getNrState()
- == NetworkRegistrationInfo.NR_STATE_CONNECTED;
- }
-
- /**
- * @return The disallowed APN types bitmask
- */
- private @ApnType int getDisallowedApnTypes() {
- CarrierConfigManager configManager = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- int apnTypesBitmask = 0;
- if (configManager != null) {
- PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
- if (bundle != null) {
- String key = (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- ? CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY
- : CarrierConfigManager.KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY;
- if (bundle.getStringArray(key) != null) {
- String disallowedApnTypesString =
- TextUtils.join(",", bundle.getStringArray(key));
- if (!TextUtils.isEmpty(disallowedApnTypesString)) {
- apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString(
- disallowedApnTypesString);
- }
- }
- }
- }
-
- return apnTypesBitmask;
- }
-
- private void dumpToLog() {
- dump(null, new PrintWriter(new StringWriter(0)) {
- @Override
- public void println(String s) {
- DataConnection.this.logd(s);
- }
-
- @Override
- public void flush() {
- }
- }, null);
- }
-
- /**
- * Re-calculate score and update through network agent if it changes.
- */
- private void updateScore() {
- int oldScore = mScore;
- mScore = calculateScore();
- if (oldScore != mScore && mNetworkAgent != null) {
- log("Updating score from " + oldScore + " to " + mScore);
- mNetworkAgent.sendNetworkScore(mScore, this);
- }
- }
-
- private int calculateScore() {
- int score = OTHER_CONNECTION_SCORE;
-
- // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have
- // specify a subId, this dataConnection is considered to be default Internet data
- // connection. In this case we assign a slightly higher score of 50. The intention is
- // it will not be replaced by other data connections accidentally in DSDS usecase.
- for (ApnContext apnContext : mApnContexts.keySet()) {
- for (NetworkRequest networkRequest : apnContext.getNetworkRequests()) {
- if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- && networkRequest.getNetworkSpecifier() == null) {
- score = DEFAULT_INTERNET_CONNECTION_SCORE;
- break;
- }
- }
- }
-
- return score;
- }
-
- private String handoverStateToString(@HandoverState int state) {
- switch (state) {
- case HANDOVER_STATE_IDLE: return "IDLE";
- case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED";
- case HANDOVER_STATE_COMPLETED: return "COMPLETED";
- default: return "UNKNOWN";
- }
- }
-
- private @DataState int getState() {
- if (isInactive()) {
- return TelephonyManager.DATA_DISCONNECTED;
- } else if (isActivating()) {
- return TelephonyManager.DATA_CONNECTING;
- } else if (isActive()) {
- // The data connection can only be suspended when it's in active state.
- if (mIsSuspended) {
- return TelephonyManager.DATA_SUSPENDED;
- }
- return TelephonyManager.DATA_CONNECTED;
- } else if (isDisconnecting()) {
- return TelephonyManager.DATA_DISCONNECTING;
- }
-
- return TelephonyManager.DATA_UNKNOWN;
- }
-
- /**
- * Get precise data connection state
- *
- * @return The {@link PreciseDataConnectionState}
- */
- public PreciseDataConnectionState getPreciseDataConnectionState() {
- return new PreciseDataConnectionState.Builder()
- .setTransportType(mTransportType)
- .setId(mCid)
- .setState(getState())
- .setApnSetting(mApnSetting)
- .setLinkProperties(mLinkProperties)
- .setNetworkType(getNetworkType())
- .setFailCause(mDcFailCause)
- .build();
- }
-
- /**
- * Dump the current state.
- *
- * @param fd
- * @param printWriter
- * @param args
- */
- @Override
- public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
- pw.print("DataConnection ");
- super.dump(fd, pw, args);
- pw.flush();
- pw.increaseIndent();
- pw.println("transport type="
- + AccessNetworkConstants.transportTypeToString(mTransportType));
- pw.println("mApnContexts.size=" + mApnContexts.size());
- pw.println("mApnContexts=" + mApnContexts);
- pw.println("mApnSetting=" + mApnSetting);
- pw.println("mTag=" + mTag);
- pw.println("mCid=" + mCid);
- pw.println("mConnectionParams=" + mConnectionParams);
- pw.println("mDisconnectParams=" + mDisconnectParams);
- pw.println("mDcFailCause=" + DataFailCause.toString(mDcFailCause));
- pw.println("mPhone=" + mPhone);
- pw.println("mSubId=" + mSubId);
- pw.println("mLinkProperties=" + mLinkProperties);
- pw.flush();
- pw.println("mDataRegState=" + mDataRegState);
- pw.println("mHandoverState=" + handoverStateToString(mHandoverState));
- pw.println("mRilRat=" + mRilRat);
- pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
- pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
- pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
- pw.println("mLastFailCause=" + DataFailCause.toString(mLastFailCause));
- pw.println("mUserData=" + mUserData);
- pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride);
- pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
- pw.println("mMmsUseOnly=" + mMmsUseOnly);
- pw.println("mEnterpriseUse=" + mEnterpriseUse);
- pw.println("mUnmeteredOverride=" + mUnmeteredOverride);
- pw.println("mCongestedOverride=" + mCongestedOverride);
- pw.println("mDownlinkBandwidth" + mDownlinkBandwidth);
- pw.println("mUplinkBandwidth=" + mUplinkBandwidth);
- pw.println("mDefaultQos=" + mDefaultQos);
- pw.println("mQosBearerSessions=" + mQosBearerSessions);
- pw.println("disallowedApnTypes="
- + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes()));
- pw.println("mInstanceNumber=" + mInstanceNumber);
- pw.println("mAc=" + mAc);
- pw.println("mScore=" + mScore);
- if (mNetworkAgent != null) {
- mNetworkAgent.dump(fd, pw, args);
- }
- pw.println("handover local log:");
- pw.increaseIndent();
- mHandoverLocalLog.dump(fd, pw, args);
- pw.decreaseIndent();
- pw.decreaseIndent();
- pw.println();
- pw.flush();
- }
-
- /**
- * Class used to track VCN-defined Network policies for this DataConnection.
- *
- * <p>MUST be registered with the associated DataConnection's Handler.
- */
- private class DataConnectionVcnNetworkPolicyChangeListener
- implements VcnNetworkPolicyChangeListener {
- @Override
- public void onPolicyChanged() {
- // Poll the current underlying Network policy from VcnManager and send to NetworkAgent.
- final NetworkCapabilities networkCapabilities = getNetworkCapabilities();
- VcnNetworkPolicyResult policyResult =
- mVcnManager.applyVcnNetworkPolicy(
- networkCapabilities, getLinkProperties());
- if (policyResult.isTeardownRequested()) {
- tearDownAll(
- Phone.REASON_VCN_REQUESTED_TEARDOWN,
- DcTracker.RELEASE_TYPE_DETACH,
- null /* onCompletedMsg */);
- }
-
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(networkCapabilities, DataConnection.this);
- }
- }
- }
-}
-
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
deleted file mode 100644
index 68f2ab3..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.HashSet;
-
-/**
- * The class to describe the reasons of allowing or disallowing to establish a data connection.
- */
-public class DataConnectionReasons {
- private HashSet<DataDisallowedReasonType> mDataDisallowedReasonSet = new HashSet<>();
- private DataAllowedReasonType mDataAllowedReason = DataAllowedReasonType.NONE;
-
- public DataConnectionReasons() {}
-
- void add(DataDisallowedReasonType reason) {
- // Adding a disallowed reason will clean up the allowed reason because they are
- // mutual exclusive.
- mDataAllowedReason = DataAllowedReasonType.NONE;
- mDataDisallowedReasonSet.add(reason);
- }
-
- void add(DataAllowedReasonType reason) {
- // Adding an allowed reason will clean up the disallowed reasons because they are
- // mutual exclusive.
- mDataDisallowedReasonSet.clear();
-
- // Only higher priority allowed reason can overwrite the old one. See
- // DataAllowedReasonType for the oder.
- if (reason.ordinal() > mDataAllowedReason.ordinal()) {
- mDataAllowedReason = reason;
- }
- }
-
- @Override
- public String toString() {
- StringBuilder reasonStr = new StringBuilder();
- if (mDataDisallowedReasonSet.size() > 0) {
- reasonStr.append("Data disallowed reasons:");
- for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
- reasonStr.append(" ").append(reason);
- }
- } else {
- reasonStr.append("Data allowed reason:");
- reasonStr.append(" ").append(mDataAllowedReason);
- }
- return reasonStr.toString();
- }
-
- void copyFrom(DataConnectionReasons reasons) {
- this.mDataDisallowedReasonSet = reasons.mDataDisallowedReasonSet;
- this.mDataAllowedReason = reasons.mDataAllowedReason;
- }
-
- boolean allowed() {
- return mDataDisallowedReasonSet.size() == 0;
- }
-
- /**
- * Check if it contains a certain disallowed reason.
- *
- * @param reason The disallowed reason to check.
- * @return {@code true} if the provided reason matches one of the disallowed reasons.
- */
- @VisibleForTesting
- public boolean contains(DataDisallowedReasonType reason) {
- return mDataDisallowedReasonSet.contains(reason);
- }
-
- /**
- * Check if only one disallowed reason prevent data connection.
- *
- * @param reason The given reason to check
- * @return True if the given reason is the only one that prevents data connection
- */
- public boolean containsOnly(DataDisallowedReasonType reason) {
- return mDataDisallowedReasonSet.size() == 1 && contains(reason);
- }
-
- boolean contains(DataAllowedReasonType reason) {
- return reason == mDataAllowedReason;
- }
-
- boolean containsHardDisallowedReasons() {
- for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
- if (reason.isHardReason()) {
- return true;
- }
- }
- return false;
- }
-
- // Disallowed reasons. There could be multiple reasons if data connection is not allowed.
- public enum DataDisallowedReasonType {
- // Soft failure reasons. Normally the reasons from users or policy settings.
-
- // Data is disabled by the user or policy.
- DATA_DISABLED(false),
- // Data roaming is disabled by the user.
- ROAMING_DISABLED(false),
- // Default data not selected.
- DEFAULT_DATA_UNSELECTED(false),
-
- // Belows are all hard failure reasons.
- NOT_ATTACHED(true),
- SIM_NOT_READY(true),
- INVALID_PHONE_STATE(true),
- CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
- PS_RESTRICTED(true),
- UNDESIRED_POWER_STATE(true),
- INTERNAL_DATA_DISABLED(true),
- RADIO_DISABLED_BY_CARRIER(true),
- // Not in the right state for data call setup.
- APN_NOT_CONNECTABLE(true),
- // Data is in connecting state. No need to send another setup request.
- DATA_IS_CONNECTING(true),
- // Data is being disconnected. Telephony will retry after disconnected.
- DATA_IS_DISCONNECTING(true),
- // Data is already connected. No need to setup data again.
- DATA_ALREADY_CONNECTED(true),
- // certain APNs are not allowed on IWLAN in legacy mode.
- ON_IWLAN(true),
- // certain APNs are only allowed when the device is camped on NR.
- NOT_ON_NR(true),
- // Data is not allowed while device is in emergency callback mode.
- IN_ECBM(true),
- // The given APN type's preferred transport has switched.
- ON_OTHER_TRANSPORT(true),
- // Underlying data service is not bound.
- DATA_SERVICE_NOT_READY(true),
- // Qualified networks service does not allow certain types of APN brought up on either
- // cellular or IWLAN.
- DISABLED_BY_QNS(true),
- // Data is throttled. The network explicitly requested device not to establish data
- // connection for a certain period.
- DATA_THROTTLED(true);
-
- private boolean mIsHardReason;
-
- boolean isHardReason() {
- return mIsHardReason;
- }
-
- DataDisallowedReasonType(boolean isHardReason) {
- mIsHardReason = isHardReason;
- }
- }
-
- // Data allowed reasons. There will be only one reason if data is allowed.
- enum DataAllowedReasonType {
- // Note that unlike disallowed reasons, we only have one allowed reason every time
- // when we check data is allowed or not. The order of these allowed reasons is very
- // important. The lower ones take precedence over the upper ones.
- NONE,
- NORMAL,
- UNMETERED_APN,
- RESTRICTED_REQUEST,
- EMERGENCY_APN,
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
deleted file mode 100644
index 305b4a8..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-
-import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE;
-import static android.telephony.PhoneStateListener.LISTEN_NONE;
-
-import android.annotation.IntDef;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.Handler;
-import android.os.RegistrantList;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.sysprop.TelephonyProperties;
-import android.telephony.Annotation.CallState;
-import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.util.LocalLog;
-import android.util.Pair;
-
-import com.android.internal.telephony.GlobalSettingsHelper;
-import com.android.internal.telephony.MultiSimSettingController;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.data.DataEnabledOverride;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * The class to hold different data enabled/disabled settings. Also it allows clients to register
- * for overall data enabled setting changed event.
- * @hide
- */
-public class DataEnabledSettings {
-
- private static final String LOG_TAG = "DataEnabledSettings";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"REASON_"},
- value = {
- REASON_REGISTERED,
- REASON_INTERNAL_DATA_ENABLED,
- REASON_USER_DATA_ENABLED,
- REASON_POLICY_DATA_ENABLED,
- REASON_DATA_ENABLED_BY_CARRIER,
- REASON_PROVISIONED_CHANGED,
- REASON_PROVISIONING_DATA_ENABLED_CHANGED,
- REASON_OVERRIDE_RULE_CHANGED,
- REASON_OVERRIDE_CONDITION_CHANGED,
- REASON_THERMAL_DATA_ENABLED
- })
- public @interface DataEnabledChangedReason {}
-
- public static final int REASON_REGISTERED = 0;
-
- public static final int REASON_INTERNAL_DATA_ENABLED = 1;
-
- public static final int REASON_USER_DATA_ENABLED = 2;
-
- public static final int REASON_POLICY_DATA_ENABLED = 3;
-
- public static final int REASON_DATA_ENABLED_BY_CARRIER = 4;
-
- public static final int REASON_PROVISIONED_CHANGED = 5;
-
- public static final int REASON_PROVISIONING_DATA_ENABLED_CHANGED = 6;
-
- public static final int REASON_OVERRIDE_RULE_CHANGED = 7;
-
- public static final int REASON_OVERRIDE_CONDITION_CHANGED = 8;
-
- public static final int REASON_THERMAL_DATA_ENABLED = 9;
-
- /**
- * responds to the setInternalDataEnabled call - used internally to turn off data.
- * For example during emergency calls
- */
- private boolean mInternalDataEnabled = true;
-
- /**
- * Flag indicating data allowed by network policy manager or not.
- */
- private boolean mPolicyDataEnabled = true;
-
- /**
- * Indicate if metered APNs are enabled by the carrier. set false to block all the metered APNs
- * from continuously sending requests, which causes undesired network load.
- */
- private boolean mCarrierDataEnabled = true;
-
- /**
- * Flag indicating data allowed by Thermal service or not.
- */
- private boolean mThermalDataEnabled = true;
-
- /**
- * Flag indicating whether data is allowed or not for the device. It can be disabled by
- * user, carrier, policy or thermal
- */
- private boolean mIsDataEnabled = false;
-
- private final Phone mPhone;
-
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
- private ContentResolver mResolver = null;
-
- private final RegistrantList mOverallDataEnabledChangedRegistrants = new RegistrantList();
-
- // TODO: Merge this with mOverallDataEnabledChangedRegistrants. In the future, notifying data
- // enabled changed with APN types bitmask
- private final RegistrantList mOverallDataEnabledOverrideChangedRegistrants =
- new RegistrantList();
-
- private final LocalLog mSettingChangeLocalLog = new LocalLog(32);
-
- private DataEnabledOverride mDataEnabledOverride;
-
- private TelephonyManager mTelephonyManager;
-
- // for msim, user data enabled setting depends on subId.
- private final SubscriptionManager.OnSubscriptionsChangedListener
- mOnSubscriptionsChangeListener =
- new SubscriptionManager.OnSubscriptionsChangedListener() {
- @Override
- public void onSubscriptionsChanged() {
- synchronized (this) {
- if (mSubId != mPhone.getSubId()) {
- log("onSubscriptionsChanged subId: " + mSubId + " to: "
- + mPhone.getSubId());
- mSubId = mPhone.getSubId();
- mDataEnabledOverride = getDataEnabledOverride();
- updatePhoneStateListener();
- updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
- mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
- }
- }
- }
- };
-
- private void updatePhoneStateListener() {
- mTelephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
- if (SubscriptionManager.isUsableSubscriptionId(mSubId)) {
- mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
- }
- mTelephonyManager.listen(mPhoneStateListener, LISTEN_CALL_STATE);
- }
-
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(@CallState int state, String phoneNumber) {
- updateDataEnabledAndNotify(REASON_OVERRIDE_CONDITION_CHANGED);
- }
- };
-
- @Override
- public String toString() {
- return "[mInternalDataEnabled=" + mInternalDataEnabled
- + ", isUserDataEnabled=" + isUserDataEnabled()
- + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
- + ", mPolicyDataEnabled=" + mPolicyDataEnabled
- + ", mCarrierDataEnabled=" + mCarrierDataEnabled
- + ", mIsDataEnabled=" + mIsDataEnabled
- + ", mThermalDataEnabled=" + mThermalDataEnabled
- + ", " + mDataEnabledOverride
- + "]";
- }
-
- public DataEnabledSettings(Phone phone) {
- mPhone = phone;
- mResolver = mPhone.getContext().getContentResolver();
- SubscriptionManager subscriptionManager = (SubscriptionManager) mPhone.getContext()
- .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
- mTelephonyManager = (TelephonyManager) mPhone.getContext()
- .getSystemService(Context.TELEPHONY_SERVICE);
- mDataEnabledOverride = getDataEnabledOverride();
- updateDataEnabled();
- }
-
- private DataEnabledOverride getDataEnabledOverride() {
- return new DataEnabledOverride(SubscriptionController.getInstance()
- .getDataEnabledOverrideRules(mPhone.getSubId()));
- }
-
- public synchronized void setInternalDataEnabled(boolean enabled) {
- if (mInternalDataEnabled != enabled) {
- localLog("InternalDataEnabled", enabled);
- mInternalDataEnabled = enabled;
- updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED);
- }
- }
- public synchronized boolean isInternalDataEnabled() {
- return mInternalDataEnabled;
- }
-
- private synchronized void setUserDataEnabled(boolean enabled) {
- // By default the change should propagate to the group.
- setUserDataEnabled(enabled, true);
- }
-
- /**
- * @param notifyMultiSimSettingController if setUserDataEnabled is already from propagating
- * from MultiSimSettingController, don't notify MultiSimSettingController again.
- * For example, if sub1 and sub2 are in the same group and user enables data for sub
- * 1, sub 2 will also be enabled but with propagateToGroup = false.
- */
- public synchronized void setUserDataEnabled(boolean enabled,
- boolean notifyMultiSimSettingController) {
- // Can't disable data for stand alone opportunistic subscription.
- if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return;
-
- boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(),
- Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0));
- if (changed) {
- localLog("UserDataEnabled", enabled);
- mPhone.notifyUserMobileDataStateChanged(enabled);
- updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
- if (notifyMultiSimSettingController) {
- MultiSimSettingController.getInstance().notifyUserDataEnabled(
- mPhone.getSubId(), enabled);
- }
- }
- }
-
- /**
- * Policy control of data connection with reason
- * @param reason the reason the data enable change is taking place
- * @param enabled True if enabling the data, otherwise disabling.
- */
- public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason,
- boolean enabled) {
- switch (reason) {
- case TelephonyManager.DATA_ENABLED_REASON_USER:
- setUserDataEnabled(enabled);
- break;
- case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
- setCarrierDataEnabled(enabled);
- break;
- case TelephonyManager.DATA_ENABLED_REASON_POLICY:
- setPolicyDataEnabled(enabled);
- break;
- case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
- setThermalDataEnabled(enabled);
- break;
- default:
- log("Invalid data enable reason " + reason);
- break;
- }
- }
-
- public synchronized boolean isUserDataEnabled() {
- // User data should always be true for opportunistic subscription.
- if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext())) return true;
-
- boolean defaultVal = TelephonyProperties.mobile_data().orElse(true);
-
- return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
- Settings.Global.MOBILE_DATA, mPhone.getSubId(), defaultVal);
- }
-
- /**
- * Set whether always allowing MMS data connection.
- *
- * @param alwaysAllow {@code true} if MMS data is always allowed.
- *
- * @return {@code false} if the setting is changed.
- */
- public synchronized boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
- localLog("setAlwaysAllowMmsData", alwaysAllow);
- mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow);
- boolean changed = SubscriptionController.getInstance()
- .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
- if (changed) {
- updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED);
- notifyDataEnabledOverrideChanged();
- }
-
- return changed;
- }
-
- /**
- * Set allowing mobile data during voice call. This is used for allowing data on the non-default
- * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
- * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
- * non-default data SIM during the life cycle of the voice call.
- *
- * @param allow {@code true} if allowing using data during voice call, {@code false} if
- * disallowed
- *
- * @return {@code true} if operation is successful. otherwise {@code false}.
- */
- public synchronized boolean setAllowDataDuringVoiceCall(boolean allow) {
- localLog("setAllowDataDuringVoiceCall", allow);
- if (allow == isDataAllowedInVoiceCall()) {
- return true;
- }
- mDataEnabledOverride.setDataAllowedInVoiceCall(allow);
-
- boolean changed = SubscriptionController.getInstance()
- .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
- if (changed) {
- updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED);
- notifyDataEnabledOverrideChanged();
- }
-
- return changed;
- }
-
- /**
- * Check if data is allowed during voice call.
- *
- * @return {@code true} if data is allowed during voice call.
- */
- public synchronized boolean isDataAllowedInVoiceCall() {
- return mDataEnabledOverride.isDataAllowedInVoiceCall();
- }
-
- public synchronized boolean isMmsAlwaysAllowed() {
- return mDataEnabledOverride.isMmsAlwaysAllowed();
- }
-
- private synchronized void setPolicyDataEnabled(boolean enabled) {
- if (mPolicyDataEnabled != enabled) {
- localLog("PolicyDataEnabled", enabled);
- mPolicyDataEnabled = enabled;
- updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED);
- }
- }
-
- public synchronized boolean isPolicyDataEnabled() {
- return mPolicyDataEnabled;
- }
-
- private synchronized void setCarrierDataEnabled(boolean enabled) {
- if (mCarrierDataEnabled != enabled) {
- localLog("CarrierDataEnabled", enabled);
- mCarrierDataEnabled = enabled;
- updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER);
- }
- }
-
- public synchronized boolean isCarrierDataEnabled() {
- return mCarrierDataEnabled;
- }
-
- private synchronized void setThermalDataEnabled(boolean enabled) {
- if (mThermalDataEnabled != enabled) {
- localLog("ThermalDataEnabled", enabled);
- mThermalDataEnabled = enabled;
- updateDataEnabledAndNotify(REASON_THERMAL_DATA_ENABLED);
- }
- }
-
- public synchronized boolean isThermalDataEnabled() {
- return mThermalDataEnabled;
- }
-
- public synchronized void updateProvisionedChanged() {
- updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED);
- }
-
- public synchronized void updateProvisioningDataEnabled() {
- updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED);
- }
-
- public synchronized boolean isDataEnabled() {
- return mIsDataEnabled;
- }
-
- /**
- * Check if data is enabled for a specific reason {@@TelephonyManager.DataEnabledReason}
- *
- * @return {@code true} if the overall data is enabled; {@code false} if not.
- */
- public synchronized boolean isDataEnabledForReason(
- @TelephonyManager.DataEnabledReason int reason) {
- switch (reason) {
- case TelephonyManager.DATA_ENABLED_REASON_USER:
- return isUserDataEnabled();
- case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
- return isCarrierDataEnabled();
- case TelephonyManager.DATA_ENABLED_REASON_POLICY:
- return isPolicyDataEnabled();
- case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
- return isThermalDataEnabled();
- default:
- return false;
- }
- }
-
- private synchronized void updateDataEnabledAndNotify(int reason) {
- boolean prevDataEnabled = mIsDataEnabled;
-
- updateDataEnabled();
-
- if (prevDataEnabled != mIsDataEnabled) {
- notifyDataEnabledChanged(!prevDataEnabled, reason);
- }
- }
-
- private synchronized void updateDataEnabled() {
- if (isProvisioning()) {
- mIsDataEnabled = isProvisioningDataEnabled();
- } else {
- mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride
- .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL))
- && mPolicyDataEnabled && mCarrierDataEnabled && mThermalDataEnabled;
- }
- }
-
- public boolean isProvisioning() {
- return Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0;
- }
- /**
- * In provisioning, we might want to have enable mobile data during provisioning. It depends
- * on value of Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED which is set by
- * setupwizard. It only matters if it's in provisioning stage.
- * @return whether we are enabling userData during provisioning stage.
- */
- public boolean isProvisioningDataEnabled() {
- final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata",
- "false");
- boolean retVal = "true".equalsIgnoreCase(prov_property);
-
- final int prov_mobile_data = Settings.Global.getInt(mResolver,
- Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
- retVal ? 1 : 0);
- retVal = prov_mobile_data != 0;
- log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property
- + ", " + prov_mobile_data + ")");
-
- return retVal;
- }
-
- public synchronized void setDataRoamingEnabled(boolean enabled) {
- // will trigger handleDataOnRoamingChange() through observer
- boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(),
- Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled);
-
- if (changed) {
- localLog("setDataRoamingEnabled", enabled);
- MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(),
- enabled);
- }
- }
-
- /**
- * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
- */
- public synchronized boolean getDataRoamingEnabled() {
- return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
- Settings.Global.DATA_ROAMING, mPhone.getSubId(), getDefaultDataRoamingEnabled());
- }
-
- /**
- * get default values for {@link Settings.Global#DATA_ROAMING}
- * return {@code true} if either
- * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or
- * system property ro.com.android.dataroaming is set to true. otherwise return {@code false}
- */
- public synchronized boolean getDefaultDataRoamingEnabled() {
- final CarrierConfigManager configMgr = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
- "ro.com.android.dataroaming", "false"));
- isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean(
- CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL);
- return isDataRoamingEnabled;
- }
-
- private void notifyDataEnabledChanged(boolean enabled, int reason) {
- mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
- mPhone.notifyDataEnabled(enabled, reason);
- }
-
- public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
- mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj);
- notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED);
- }
-
- public void unregisterForDataEnabledChanged(Handler h) {
- mOverallDataEnabledChangedRegistrants.remove(h);
- }
-
- private void notifyDataEnabledOverrideChanged() {
- mOverallDataEnabledOverrideChangedRegistrants.notifyRegistrants();
- }
-
- /**
- * Register for data enabled override changed event.
- *
- * @param h The handler
- * @param what The event
- */
- public void registerForDataEnabledOverrideChanged(Handler h, int what) {
- mOverallDataEnabledOverrideChangedRegistrants.addUnique(h, what, null);
- notifyDataEnabledOverrideChanged();
- }
-
- /**
- * Unregistered for data enabled override changed event.
- *
- * @param h The handler
- */
- public void unregisterForDataEnabledOverrideChanged(Handler h) {
- mOverallDataEnabledOverrideChangedRegistrants.remove(h);
- }
-
- private static boolean isStandAloneOpportunistic(int subId, Context context) {
- SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo(
- subId, context.getOpPackageName(), context.getAttributionTag());
- return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null;
- }
-
- public synchronized boolean isDataEnabled(int apnType) {
- if (isProvisioning()) {
- return isProvisioningDataEnabled();
- } else {
- boolean userDataEnabled = isUserDataEnabled();
- // Check if we should temporarily enable data in certain conditions.
- boolean isDataEnabledOverridden = mDataEnabledOverride
- .shouldOverrideDataEnabledSettings(mPhone, apnType);
-
- return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled
- && mThermalDataEnabled && (userDataEnabled || isDataEnabledOverridden));
- }
- }
-
- private void log(String s) {
- Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
- }
-
- private void localLog(String name, boolean value) {
- mSettingChangeLocalLog.log(name + " change to " + value);
- }
-
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" DataEnabledSettings=");
- mSettingChangeLocalLog.dump(fd, pw, args);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
deleted file mode 100644
index d006004..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ /dev/null
@@ -1,978 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.LinkProperties;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RegistrantList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.permission.LegacyPermissionManager;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.AnomalyReporter;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.IDataService;
-import android.telephony.data.IDataServiceCallback;
-import android.telephony.data.NetworkSliceInfo;
-import android.telephony.data.TrafficDescriptor;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConfigurationManager;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Data service manager manages handling data requests and responses on data services (e.g.
- * Cellular data service, IWLAN data service).
- */
-public class DataServiceManager extends Handler {
- private static final boolean DBG = true;
-
- static final String DATA_CALL_RESPONSE = "data_call_response";
-
- private static final int EVENT_BIND_DATA_SERVICE = 1;
-
- private static final int EVENT_WATCHDOG_TIMEOUT = 2;
-
- private static final long REQUEST_UNRESPONDED_TIMEOUT = 10 * MINUTE_IN_MILLIS; // 10 mins
-
- private static final long CHANGE_PERMISSION_TIMEOUT_MS = 15 * SECOND_IN_MILLIS; // 15 secs
-
- private final Phone mPhone;
-
- private final String mTag;
-
- private final CarrierConfigManager mCarrierConfigManager;
- private final AppOpsManager mAppOps;
- private final LegacyPermissionManager mPermissionManager;
-
- private final int mTransportType;
-
- private boolean mBound;
-
- private IDataService mIDataService;
-
- private DataServiceManagerDeathRecipient mDeathRecipient;
-
- private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
-
- private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
-
- private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
-
- private final RegistrantList mApnUnthrottledRegistrants = new RegistrantList();
-
- private String mTargetBindingPackageName;
-
- private CellularDataServiceConnection mServiceConnection;
-
- private final UUID mAnomalyUUID = UUID.fromString("fc1956de-c080-45de-8431-a1faab687110");
- private String mLastBoundPackageName;
-
- /**
- * Helpful for logging
- * @return the tag name
- *
- * @hide
- */
- public String getTag() {
- return mTag;
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
- && mPhone.getPhoneId() == intent.getIntExtra(
- CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
- // We should wait for carrier config changed event because the target binding
- // package name can come from the carrier config. Note that we still get this event
- // even when SIM is absent.
- if (DBG) log("Carrier config changed. Try to bind data service.");
- sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
- }
- }
- };
-
- private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- // TODO: try to rebind the service.
- String message = "Data service " + mLastBoundPackageName + " for transport type "
- + AccessNetworkConstants.transportTypeToString(mTransportType) + " died.";
- loge(message);
- AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId());
- }
- }
-
- private void grantPermissionsToService(String packageName) {
- final String[] pkgToGrant = {packageName};
- CountDownLatch latch = new CountDownLatch(1);
- try {
- mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
- pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run,
- isSuccess -> {
- if (isSuccess) {
- latch.countDown();
- } else {
- loge("Failed to grant permissions to service.");
- }
- });
- TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
- mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS,
- UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
- mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION,
- UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
- } catch (RuntimeException e) {
- loge("Binder to package manager died, permission grant for DataService failed.");
- throw e;
- }
- }
-
- /**
- * Loop through all DataServices installed on the system and revoke permissions from any that
- * are not currently the WWAN or WLAN data service.
- */
- private void revokePermissionsFromUnusedDataServices() {
- // Except the current data services from having their permissions removed.
- Set<String> dataServices = getAllDataServicePackageNames();
- for (int transportType : mPhone.getAccessNetworksManager().getAvailableTransports()) {
- dataServices.remove(getDataServicePackageName(transportType));
- }
-
- CountDownLatch latch = new CountDownLatch(1);
- try {
- String[] dataServicesArray = new String[dataServices.size()];
- dataServices.toArray(dataServicesArray);
- mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
- dataServicesArray, UserHandle.of(UserHandle.myUserId()), Runnable::run,
- isSuccess -> {
- if (isSuccess) {
- latch.countDown();
- } else {
- loge("Failed to revoke permissions from data services.");
- }
- });
- TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
- for (String pkg : dataServices) {
- mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, UserHandle.myUserId(),
- pkg, AppOpsManager.MODE_ERRORED);
- mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION, UserHandle.myUserId(),
- pkg, AppOpsManager.MODE_ERRORED);
- }
- } catch (RuntimeException e) {
- loge("Binder to package manager died; failed to revoke DataService permissions.");
- throw e;
- }
- }
-
- private final class CellularDataServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DBG) log("onServiceConnected");
- mIDataService = IDataService.Stub.asInterface(service);
- mDeathRecipient = new DataServiceManagerDeathRecipient();
- mBound = true;
- mLastBoundPackageName = getDataServicePackageName();
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
-
- try {
- service.linkToDeath(mDeathRecipient, 0);
- mIDataService.createDataServiceProvider(mPhone.getPhoneId());
- mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
- new CellularDataServiceCallback("dataCallListChanged"));
- mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(),
- new CellularDataServiceCallback("unthrottleApn"));
- } catch (RemoteException e) {
- loge("Remote exception. " + e);
- return;
- }
- mServiceBindingChangedRegistrants.notifyResult(true);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DBG) log("onServiceDisconnected");
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
- mIDataService = null;
- mBound = false;
- mServiceBindingChangedRegistrants.notifyResult(false);
- mTargetBindingPackageName = null;
- }
- }
-
- private final class CellularDataServiceCallback extends IDataServiceCallback.Stub {
-
- private final String mTag;
-
- CellularDataServiceCallback(String tag) {
- mTag = tag;
- }
-
- public String getTag() {
- return mTag;
- }
-
- @Override
- public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode,
- DataCallResponse response) {
- if (DBG) {
- log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
- + response);
- }
- removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
- Message msg = mMessageMap.remove(asBinder());
- if (msg != null) {
- msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
- sendCompleteMessage(msg, resultCode);
- } else {
- loge("Unable to find the message for setup call response.");
- }
- }
-
- @Override
- public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
- if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
- removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) {
- if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) {
- if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onRequestDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
- List<DataCallResponse> dataCallList) {
- if (DBG) log("onRequestDataCallListComplete. resultCode = " + resultCode);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
- mDataCallListChangedRegistrants.notifyRegistrants(
- new AsyncResult(null, dataCallList, null));
- }
-
- @Override
- public void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode) {
- if (DBG) log("onHandoverStarted. resultCode = " + resultCode);
- removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onHandoverCancelled(@DataServiceCallback.ResultCode int resultCode) {
- if (DBG) log("onHandoverCancelled. resultCode = " + resultCode);
- removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
- Message msg = mMessageMap.remove(asBinder());
- sendCompleteMessage(msg, resultCode);
- }
-
- @Override
- public void onApnUnthrottled(String apn) {
- if (apn != null) {
- mApnUnthrottledRegistrants.notifyRegistrants(
- new AsyncResult(null, apn, null));
- } else {
- loge("onApnUnthrottled: apn is null");
- }
- }
-
- @Override
- public void onDataProfileUnthrottled(DataProfile dataProfile) {
- if (dataProfile != null) {
- mApnUnthrottledRegistrants.notifyRegistrants(
- new AsyncResult(null, dataProfile, null));
- } else {
- loge("onDataProfileUnthrottled: dataProfile is null");
- }
- }
- }
-
- /**
- * Constructor
- *
- * @param phone The phone object
- * @param transportType The transport type
- * @param tagSuffix Logging tag suffix
- */
- public DataServiceManager(Phone phone, @TransportType int transportType, String tagSuffix) {
- mPhone = phone;
- mTag = "DSM" + tagSuffix;
- mTransportType = transportType;
- mBound = false;
- mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- // NOTE: Do NOT use AppGlobals to retrieve the permission manager; AppGlobals
- // caches the service instance, but we need to explicitly request a new service
- // so it can be mocked out for tests
- mPermissionManager = (LegacyPermissionManager) phone.getContext().getSystemService(
- Context.LEGACY_PERMISSION_SERVICE);
- mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- try {
- Context contextAsUser = phone.getContext().createPackageContextAsUser(
- phone.getContext().getPackageName(), 0, UserHandle.ALL);
- contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
- null /* broadcastPermission */, null);
- } catch (PackageManager.NameNotFoundException e) {
- loge("Package name not found: " + e.getMessage());
- }
-
- PhoneConfigurationManager.registerForMultiSimConfigChange(
- this, EVENT_BIND_DATA_SERVICE, null);
-
- sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
- }
-
- /**
- * Handle message events
- *
- * @param msg The message to handle
- */
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BIND_DATA_SERVICE:
- rebindDataService();
- break;
- case EVENT_WATCHDOG_TIMEOUT:
- handleRequestUnresponded((CellularDataServiceCallback) msg.obj);
- break;
- default:
- loge("Unhandled event " + msg.what);
- }
- }
-
- private void handleRequestUnresponded(CellularDataServiceCallback callback) {
- String message = "Request " + callback.getTag() + " unresponded on transport "
- + AccessNetworkConstants.transportTypeToString(mTransportType) + " in "
- + REQUEST_UNRESPONDED_TIMEOUT / 1000 + " seconds.";
- log(message);
- // Using fixed UUID to avoid duplicate bugreport notification
- AnomalyReporter.reportAnomaly(
- UUID.fromString("f5d5cbe6-9bd6-4009-b764-42b1b649b1de"),
- message, mPhone.getCarrierId());
- }
-
- private void unbindDataService() {
- // Start by cleaning up all packages that *shouldn't* have permissions.
- revokePermissionsFromUnusedDataServices();
- if (mIDataService != null && mIDataService.asBinder().isBinderAlive()) {
- log("unbinding service");
- // Remove the network availability updater and then unbind the service.
- try {
- mIDataService.removeDataServiceProvider(mPhone.getPhoneId());
- } catch (RemoteException e) {
- loge("Cannot remove data service provider. " + e);
- }
- }
-
- if (mServiceConnection != null) {
- mPhone.getContext().unbindService(mServiceConnection);
- }
- mIDataService = null;
- mServiceConnection = null;
- mTargetBindingPackageName = null;
- mBound = false;
- }
-
- private void bindDataService(String packageName) {
- if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
- loge("can't bindDataService with invalid phone or phoneId.");
- return;
- }
-
- if (TextUtils.isEmpty(packageName)) {
- loge("Can't find the binding package");
- return;
- }
-
- Intent intent = null;
- String className = getDataServiceClassName();
- if (TextUtils.isEmpty(className)) {
- intent = new Intent(DataService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
- } else {
- ComponentName cm = new ComponentName(packageName, className);
- intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
- }
-
- // Then pre-emptively grant the permissions to the package we will bind.
- grantPermissionsToService(packageName);
-
- try {
- mServiceConnection = new CellularDataServiceConnection();
- if (!mPhone.getContext().bindService(
- intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
- loge("Cannot bind to the data service.");
- return;
- }
- mTargetBindingPackageName = packageName;
- } catch (Exception e) {
- loge("Cannot bind to the data service. Exception: " + e);
- }
- }
-
- private void rebindDataService() {
- String packageName = getDataServicePackageName();
- // Do nothing if no need to rebind.
- if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
- && TextUtils.equals(packageName, mTargetBindingPackageName)) {
- if (DBG) log("Service " + packageName + " already bound or being bound.");
- return;
- }
-
- unbindDataService();
- bindDataService(packageName);
- }
-
- @NonNull
- private Set<String> getAllDataServicePackageNames() {
- // Cowardly using the public PackageManager interface here.
- // Note: This matches only packages that were installed on the system image. If we ever
- // expand the permissions model to allow CarrierPrivileged packages, then this will need
- // to be updated.
- List<ResolveInfo> dataPackages =
- mPhone.getContext().getPackageManager().queryIntentServices(
- new Intent(DataService.SERVICE_INTERFACE),
- PackageManager.MATCH_SYSTEM_ONLY);
- HashSet<String> packageNames = new HashSet<>();
- for (ResolveInfo info : dataPackages) {
- if (info.serviceInfo == null) continue;
- packageNames.add(info.serviceInfo.packageName);
- }
- return packageNames;
- }
-
- /**
- * Get the data service package name for our current transport type.
- *
- * @return package name of the data service package for the the current transportType.
- */
- public String getDataServicePackageName() {
- return getDataServicePackageName(mTransportType);
- }
-
- /**
- * Get the data service package by transport type.
- *
- * When we bind to a DataService package, we need to revoke permissions from stale
- * packages; we need to exclude data packages for all transport types, so we need to
- * to be able to query by transport type.
- *
- * @param transportType The transport type
- * @return package name of the data service package for the specified transportType.
- */
- private String getDataServicePackageName(@TransportType int transportType) {
- String packageName;
- int resourceId;
- String carrierConfig;
-
- switch (transportType) {
- case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
- resourceId = com.android.internal.R.string.config_wwan_data_service_package;
- carrierConfig = CarrierConfigManager
- .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
- break;
- case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
- resourceId = com.android.internal.R.string.config_wlan_data_service_package;
- carrierConfig = CarrierConfigManager
- .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
- break;
- default:
- throw new IllegalStateException("Transport type not WWAN or WLAN. type="
- + AccessNetworkConstants.transportTypeToString(mTransportType));
- }
-
- // Read package name from resource overlay
- packageName = mPhone.getContext().getResources().getString(resourceId);
-
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
- // If carrier config overrides it, use the one from carrier config
- packageName = b.getString(carrierConfig, packageName);
- }
-
- return packageName;
- }
-
- /**
- * Get the data service class name for our current transport type.
- *
- * @return class name of the data service package for the the current transportType.
- */
- private String getDataServiceClassName() {
- return getDataServiceClassName(mTransportType);
- }
-
-
- /**
- * Get the data service class by transport type.
- *
- * @param transportType either WWAN or WLAN
- * @return class name of the data service package for the specified transportType.
- */
- private String getDataServiceClassName(int transportType) {
- String className;
- int resourceId;
- String carrierConfig;
- switch (transportType) {
- case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
- resourceId = com.android.internal.R.string.config_wwan_data_service_class;
- carrierConfig = CarrierConfigManager
- .KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
- break;
- case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
- resourceId = com.android.internal.R.string.config_wlan_data_service_class;
- carrierConfig = CarrierConfigManager
- .KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
- break;
- default:
- throw new IllegalStateException("Transport type not WWAN or WLAN. type="
- + transportType);
- }
-
- // Read package name from resource overlay
- className = mPhone.getContext().getResources().getString(resourceId);
-
- PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
- if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
- // If carrier config overrides it, use the one from carrier config
- className = b.getString(carrierConfig, className);
- }
-
- return className;
- }
-
- private void sendCompleteMessage(Message msg, @DataServiceCallback.ResultCode int code) {
- if (msg != null) {
- msg.arg1 = code;
- msg.sendToTarget();
- }
- }
-
- /**
- * Setup a data connection. The data service provider must implement this method to support
- * establishing a packet data connection. When completed or error, the service must invoke
- * the provided callback to notify the platform.
- *
- * @param accessNetworkType Access network type that the data call will be established on.
- * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
- * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
- * @param isRoaming True if the device is data roaming.
- * @param allowRoaming True if data roaming is allowed by the user.
- * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or
- * {@link DataService#REQUEST_REASON_HANDOVER}.
- * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this
- * is the link properties of the existing data connection, otherwise null.
- * @param pduSessionId The pdu session id to be used for this data call. A value of -1 means
- * no pdu session id was attached to this call.
- * Reference: 3GPP TS 24.007 Section 11.2.3.1b
- * @param sliceInfo The slice that represents S-NSSAI.
- * Reference: 3GPP TS 24.501
- * @param trafficDescriptor The traffic descriptor for this data call, used for URSP matching.
- * Reference: 3GPP TS TS 24.526 Section 5.2
- * @param matchAllRuleAllowed True if using the default match-all URSP rule for this request is
- * allowed.
- * @param onCompleteMessage The result message for this request. Null if the client does not
- * care about the result.
- */
- public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
- @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,
- boolean matchAllRuleAllowed, Message onCompleteMessage) {
- if (DBG) log("setupDataCall");
- if (!mBound) {
- loge("setupDataCall: Data service not bound.");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- CellularDataServiceCallback callback = new CellularDataServiceCallback("setupDataCall");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
- REQUEST_UNRESPONDED_TIMEOUT);
- mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
- isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
- trafficDescriptor, matchAllRuleAllowed, callback);
- } catch (RemoteException e) {
- loge("setupDataCall: Cannot invoke setupDataCall on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Deactivate a data connection. The data service provider must implement this method to
- * support data connection tear down. When completed or error, the service must invoke the
- * provided callback to notify the platform.
- *
- * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile,
- * boolean, boolean, int, LinkProperties, Message)}
- * @param reason The reason for data deactivation. Must be
- * {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN}
- * or {@link DataService#REQUEST_REASON_HANDOVER}.
- * @param onCompleteMessage The result message for this request. Null if the client does not
- * care about the result.
- */
- public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
- if (DBG) log("deactivateDataCall");
- if (!mBound) {
- loge("Data service not bound.");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- CellularDataServiceCallback callback =
- new CellularDataServiceCallback("deactivateDataCall");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
- REQUEST_UNRESPONDED_TIMEOUT);
- mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
- } catch (RemoteException e) {
- loge("Cannot invoke deactivateDataCall on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Indicates that a handover has begun. This is called on the source transport.
- *
- * Any resources being transferred cannot be released while a
- * handover is underway.
- *
- * If a handover was unsuccessful, then the framework calls DataServiceManager#cancelHandover.
- * The target transport retains ownership over any of the resources being transferred.
- *
- * If a handover was successful, the framework calls DataServiceManager#deactivateDataCall with
- * reason HANDOVER. The target transport now owns the transferred resources and is
- * responsible for releasing them.
- *
- * @param cid The identifier of the data call which is provided in DataCallResponse
- * @param onCompleteMessage The result callback for this request.
- */
- public void startHandover(int cid, @NonNull Message onCompleteMessage) {
- CellularDataServiceCallback callback =
- setupCallbackHelper("startHandover", onCompleteMessage);
- if (callback == null) {
- loge("startHandover: callback == null");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- try {
- sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
- REQUEST_UNRESPONDED_TIMEOUT);
- mIDataService.startHandover(mPhone.getPhoneId(), cid, callback);
- } catch (RemoteException e) {
- loge("Cannot invoke startHandover on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Indicates that a handover was cancelled after a call to DataServiceManager#startHandover.
- * This is called on the source transport.
- *
- * Since the handover was unsuccessful, the source transport retains ownership over any of
- * the resources being transferred and is still responsible for releasing them.
- *
- * @param cid The identifier of the data call which is provided in DataCallResponse
- * @param onCompleteMessage The result callback for this request.
- */
- public void cancelHandover(int cid, @NonNull Message onCompleteMessage) {
- CellularDataServiceCallback callback =
- setupCallbackHelper("cancelHandover", onCompleteMessage);
- if (callback == null) {
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- try {
- sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
- REQUEST_UNRESPONDED_TIMEOUT);
- mIDataService.cancelHandover(mPhone.getPhoneId(), cid, callback);
- } catch (RemoteException e) {
- loge("Cannot invoke cancelHandover on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- @Nullable
- private CellularDataServiceCallback setupCallbackHelper(
- @NonNull final String operationName, @NonNull final Message onCompleteMessage) {
- if (DBG) log(operationName);
- if (!mBound) {
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return null;
- }
-
- CellularDataServiceCallback callback =
- new CellularDataServiceCallback(operationName);
- if (onCompleteMessage != null) {
- if (DBG) log(operationName + ": onCompleteMessage set");
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- } else {
- if (DBG) log(operationName + ": onCompleteMessage not set");
- }
- return callback;
- }
-
- /**
- * Set an APN to initial attach network.
- *
- * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
- * @param isRoaming True if the device is data roaming.
- * @param onCompleteMessage The result message for this request. Null if the client does not
- * care about the result.
- */
- public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
- Message onCompleteMessage) {
- if (DBG) log("setInitialAttachApn");
- if (!mBound) {
- loge("Data service not bound.");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- CellularDataServiceCallback callback =
- new CellularDataServiceCallback("setInitialAttachApn");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
- callback);
- } catch (RemoteException e) {
- loge("Cannot invoke setInitialAttachApn on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Send current carrier's data profiles to the data service for data call setup. This is
- * only for CDMA carrier that can change the profile through OTA. The data service should
- * always uses the latest data profile sent by the framework.
- *
- * @param dps A list of data profiles.
- * @param isRoaming True if the device is data roaming.
- * @param onCompleteMessage The result message for this request. Null if the client does not
- * care about the result.
- */
- public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
- Message onCompleteMessage) {
- if (DBG) log("setDataProfile");
- if (!mBound) {
- loge("Data service not bound.");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- CellularDataServiceCallback callback = new CellularDataServiceCallback("setDataProfile");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
- } catch (RemoteException e) {
- loge("Cannot invoke setDataProfile on data service.");
- mMessageMap.remove(callback.asBinder());
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Get the active data call list.
- *
- * @param onCompleteMessage The result message for this request. Null if the client does not
- * care about the result.
- */
- public void requestDataCallList(Message onCompleteMessage) {
- if (DBG) log("requestDataCallList");
- if (!mBound) {
- loge("Data service not bound.");
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- return;
- }
-
- CellularDataServiceCallback callback =
- new CellularDataServiceCallback("requestDataCallList");
- if (onCompleteMessage != null) {
- mMessageMap.put(callback.asBinder(), onCompleteMessage);
- }
- try {
- mIDataService.requestDataCallList(mPhone.getPhoneId(), callback);
- } catch (RemoteException e) {
- loge("Cannot invoke requestDataCallList on data service.");
- if (callback != null) {
- mMessageMap.remove(callback.asBinder());
- }
- sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
- }
- }
-
- /**
- * Register for data call list changed event.
- *
- * @param h The target to post the event message to.
- * @param what The event.
- */
- public void registerForDataCallListChanged(Handler h, int what) {
- if (h != null) {
- mDataCallListChangedRegistrants.addUnique(h, what, null);
- }
- }
-
- /**
- * Unregister for data call list changed event.
- *
- * @param h The handler
- */
- public void unregisterForDataCallListChanged(Handler h) {
- if (h != null) {
- mDataCallListChangedRegistrants.remove(h);
- }
- }
-
- /**
- * Register apn unthrottled event
- *
- * @param h The target to post the event message to.
- * @param what The event.
- */
- public void registerForApnUnthrottled(Handler h, int what) {
- if (h != null) {
- mApnUnthrottledRegistrants.addUnique(h, what, null);
- }
- }
-
- /**
- * Unregister for apn unthrottled event
- *
- * @param h The handler
- */
- public void unregisterForApnUnthrottled(Handler h) {
- if (h != null) {
- mApnUnthrottledRegistrants.remove(h);
- }
- }
-
- /**
- * Register for data service binding status changed event.
- *
- * @param h The target to post the event message to.
- * @param what The event.
- * @param obj The user object.
- */
- public void registerForServiceBindingChanged(Handler h, int what, Object obj) {
- if (h != null) {
- mServiceBindingChangedRegistrants.addUnique(h, what, obj);
- }
-
- }
-
- /**
- * Unregister for data service binding status changed event.
- *
- * @param h The handler
- */
- public void unregisterForServiceBindingChanged(Handler h) {
- if (h != null) {
- mServiceBindingChangedRegistrants.remove(h);
- }
- }
-
- /**
- * Get the transport type. Must be a {@link TransportType}.
- *
- * @return
- */
- @TransportType
- public int getTransportType() {
- return mTransportType;
- }
-
- private void log(String s) {
- Rlog.d(mTag, s);
- }
-
- private void loge(String s) {
- Rlog.e(mTag, s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
deleted file mode 100644
index 4a465d2..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.ElapsedRealtimeLong;
-import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.ThrottleStatus;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.telephony.Rlog;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Data throttler tracks the throttling status of the data network and notifies registrants when
- * there are changes. The throttler is per phone and per transport type.
- */
-public class DataThrottler extends Handler {
- private static final String TAG = DataThrottler.class.getSimpleName();
-
- private static final int EVENT_SET_RETRY_TIME = 1;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 2;
- private static final int EVENT_RESET = 3;
- private static final int EVENT_AIRPLANE_MODE_CHANGED = 4;
- private static final int EVENT_TRACING_AREA_CODE_CHANGED = 5;
-
- private final Phone mPhone;
- private final int mSlotIndex;
- private final @AccessNetworkConstants.TransportType int mTransportType;
- private boolean mResetWhenAreaCodeChanged = false;
-
- /**
- * Callbacks that report the apn throttle status.
- */
- private final List<DataThrottler.Callback> mCallbacks = new ArrayList<>();
-
- /**
- * Keeps track of detailed information of the throttle status that is meant to be
- * reported to other components.
- */
- private final Map<Integer, ThrottleStatus> mThrottleStatus = new ConcurrentHashMap<>();
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
- if (intent.getBooleanExtra(
- CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
- // Ignore the rebroadcast one to prevent multiple carrier config changed
- // event during boot up.
- return;
- }
- int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
- }
- }
- }
- }
- };
-
- public DataThrottler(Phone phone, int transportType) {
- super(null, false);
- mPhone = phone;
- mSlotIndex = phone.getPhoneId();
- mTransportType = transportType;
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
-
- mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this,
- EVENT_AIRPLANE_MODE_CHANGED, null);
- mPhone.getServiceStateTracker().registerForAreaCodeChanged(this,
- EVENT_TRACING_AREA_CODE_CHANGED, null);
- }
-
- @Override
- public void handleMessage(Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_SET_RETRY_TIME:
- int apnTypes = msg.arg1;
- int newRequestType = msg.arg2;
- long retryElapsedTime = (long) msg.obj;
- setRetryTimeInternal(apnTypes, retryElapsedTime, newRequestType);
- break;
- case EVENT_CARRIER_CONFIG_CHANGED:
- onCarrierConfigChanged();
- break;
- case EVENT_RESET:
- resetInternal();
- break;
- case EVENT_AIRPLANE_MODE_CHANGED:
- ar = (AsyncResult) msg.obj;
- if (!(Boolean) ar.result) {
- resetInternal();
- }
- break;
- case EVENT_TRACING_AREA_CODE_CHANGED:
- if (mResetWhenAreaCodeChanged) {
- resetInternal();
- }
- break;
- }
- }
-
- @NonNull
- private PersistableBundle getCarrierConfig() {
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- // If an invalid subId is used, this bundle will contain default values.
- PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
- if (config != null) {
- return config;
- }
- }
- // Return static default defined in CarrierConfigManager.
- return CarrierConfigManager.getDefaultConfig();
- }
-
- private void onCarrierConfigChanged() {
- PersistableBundle config = getCarrierConfig();
- mResetWhenAreaCodeChanged = config.getBoolean(
- CarrierConfigManager.KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
- }
-
- /**
- * Set the retry time and handover failure mode for the give APN types.
- *
- * @param apnTypes APN types
- * @param retryElapsedTime The elapsed time that data connection for APN types should not be
- * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
- * {@link RetryManager#NO_RETRY} indicates retry should never happen.
- */
- public void setRetryTime(@ApnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime,
- @DcTracker.RequestNetworkType int newRequestType) {
- sendMessage(obtainMessage(EVENT_SET_RETRY_TIME, apnTypes, newRequestType,
- retryElapsedTime));
- }
-
- /**
- * Set the retry time and handover failure mode for the give APN types. This is running on the
- * handler thread.
- *
- * @param apnTypes APN types
- * @param retryElapsedTime The elapsed time that data connection for APN types should not be
- * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
- * {@link RetryManager#NO_RETRY} indicates retry should never happen.
- */
- private void setRetryTimeInternal(@ApnType int apnTypes, @ElapsedRealtimeLong
- long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType) {
- if (retryElapsedTime < 0) {
- retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
- }
-
- List<ThrottleStatus> changedStatuses = new ArrayList<>();
- while (apnTypes != 0) {
- int apnType;
- // Due to an API mistake of ApnSetting.TYPE_DEFAULT (which combines default and hipri
- // bit), we need to do special handling here.
- if ((apnTypes & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
- apnType = ApnSetting.TYPE_DEFAULT;
- apnTypes &= ~ApnSetting.TYPE_DEFAULT;
- } else {
- //Extract the least significant bit.
- apnType = apnTypes & -apnTypes;
- //Remove the least significant bit.
- apnTypes &= apnTypes - 1;
- }
-
- //Update the apn throttle status
- ThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType);
-
- ThrottleStatus oldStatus = mThrottleStatus.get(apnType);
-
- //Check to see if there is a change that needs to be applied
- if (!newStatus.equals(oldStatus)) {
- //Mark as changed status
- changedStatuses.add(newStatus);
-
- //Put the new status in the temp space
- mThrottleStatus.put(apnType, newStatus);
- }
- }
-
- if (changedStatuses.size() > 0) {
- sendThrottleStatusChanged(changedStatuses);
- }
- }
-
- /**
- * Get the earliest retry time for the given APN type. The time is the system's elapse time.
- *
- * Note the DataThrottler is running phone process's main thread, which is most of the telephony
- * components running on. Calling this method from other threads might run into race conditions.
- *
- * @param apnType APN type
- * @return The earliest retry time for APN type. The time is the system's elapse time.
- * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN
- * type, {@link RetryManager#NO_RETRY} indicates retry should never happen.
- */
- @ElapsedRealtimeLong
- public long getRetryTime(@ApnType int apnType) {
- ThrottleStatus status = mThrottleStatus.get(apnType);
- if (status != null) {
- if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) {
- return RetryManager.NO_SUGGESTED_RETRY_DELAY;
- } else {
- return status.getThrottleExpiryTimeMillis();
- }
- }
- return RetryManager.NO_SUGGESTED_RETRY_DELAY;
- }
-
- /**
- * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
- */
- public void reset() {
- sendEmptyMessage(EVENT_RESET);
- }
-
- /**
- * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
- */
- private void resetInternal() {
- final List<Integer> apnTypes = new ArrayList<>();
- for (ThrottleStatus throttleStatus : mThrottleStatus.values()) {
- apnTypes.add(throttleStatus.getApnType());
- }
-
- for (int apnType : apnTypes) {
- setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY,
- DcTracker.REQUEST_TYPE_NORMAL);
- }
- }
-
- private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime,
- @DcTracker.RequestNetworkType int newRequestType) {
- ThrottleStatus.Builder builder = new ThrottleStatus.Builder();
-
- if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) {
- builder
- .setNoThrottle()
- .setRetryType(getRetryType(newRequestType));
- } else if (retryElapsedTime == RetryManager.NO_RETRY) {
- builder
- .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
- .setRetryType(ThrottleStatus.RETRY_TYPE_NONE);
- } else {
- builder
- .setThrottleExpiryTimeMillis(retryElapsedTime)
- .setRetryType(getRetryType(newRequestType));
- }
- return builder
- .setSlotIndex(mSlotIndex)
- .setTransportType(mTransportType)
- .setApnType(apnType)
- .build();
- }
-
- private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) {
- if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) {
- return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
- }
-
- if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
- return ThrottleStatus.RETRY_TYPE_HANDOVER;
- }
-
- loge("createStatus: Unknown requestType=" + newRequestType);
- return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
- }
-
- private void sendThrottleStatusChanged(List<ThrottleStatus> statuses) {
- synchronized (mCallbacks) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onThrottleStatusChanged(statuses);
- }
- }
- }
-
- private static void loge(String s) {
- Rlog.e(TAG, s);
- }
-
- /**
- * Reports changes to apn throttle statuses.
- *
- * Note: All statuses are sent when first registered.
- *
- * @param callback status changes callback
- */
- public void registerForThrottleStatusChanges(DataThrottler.Callback callback) {
- synchronized (mCallbacks) {
- //Only add if it's not there already
- if (!mCallbacks.contains(callback)) {
- //Report everything the first time
- List<ThrottleStatus> throttleStatuses =
- new ArrayList<>(mThrottleStatus.values());
- callback.onThrottleStatusChanged(throttleStatuses);
- mCallbacks.add(callback);
- }
- }
- }
-
- /**
- * Unregister the callback
- * @param callback the callback to unregister
- */
- public void unregisterForThrottleStatusChanges(DataThrottler.Callback callback) {
- synchronized (mCallbacks) {
- mCallbacks.remove(callback);
- }
- }
-
- /**
- * Callback for when throttle statuses change
- */
- public interface Callback {
- /**
- * Called whenever the throttle status of an APN has changed.
- *
- * Note: Called with all statuses when first registered.
- *
- * @param throttleStatuses the status changes
- */
- void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
deleted file mode 100644
index c34157e..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.hardware.radio.V1_4.DataConnActiveStatus;
-import android.net.LinkAddress;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.TrafficDescriptor;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.net.module.util.LinkPropertiesUtils;
-import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
-import com.android.net.module.util.NetUtils;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Data Connection Controller which is a package visible class and controls
- * multiple data connections. For instance listening for unsolicited messages
- * and then demultiplexing them to the appropriate DC.
- */
-public class DcController extends Handler {
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private final Phone mPhone;
- private final DcTracker mDct;
- private final String mTag;
- private final DataServiceManager mDataServiceManager;
- private final DcTesterDeactivateAll mDcTesterDeactivateAll;
-
- // package as its used by Testing code
- // @GuardedBy("mDcListAll")
- final ArrayList<DataConnection> mDcListAll = new ArrayList<>();
- // @GuardedBy("mDcListAll")
- private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>();
- // @GuardedBy("mTrafficDescriptorsByCid")
- private final HashMap<Integer, List<TrafficDescriptor>> mTrafficDescriptorsByCid =
- new HashMap<>();
-
- /**
- * Aggregated physical link status from all data connections. This reflects the device's RRC
- * connection state.
- * If {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
- * then This reflects "internet data connection" instead of RRC state.
- */
- private @DataCallResponse.LinkStatus int mPhysicalLinkStatus =
- DataCallResponse.LINK_STATUS_UNKNOWN;
-
- private RegistrantList mPhysicalLinkStatusChangedRegistrants = new RegistrantList();
-
- /**
- * Constructor.
- *
- * @param name to be used for the Controller
- * @param phone the phone associated with Dcc and Dct
- * @param dct the DataConnectionTracker associated with Dcc
- * @param dataServiceManager the data service manager that manages data services
- * @param looper looper for this handler
- */
- private DcController(String name, Phone phone, DcTracker dct,
- DataServiceManager dataServiceManager, Looper looper) {
- super(looper);
- mPhone = phone;
- mDct = dct;
- mTag = name;
- mDataServiceManager = dataServiceManager;
-
- mDcTesterDeactivateAll = (TelephonyUtils.IS_DEBUGGABLE)
- ? new DcTesterDeactivateAll(mPhone, DcController.this, this)
- : null;
- mDataServiceManager.registerForDataCallListChanged(this,
- DataConnection.EVENT_DATA_STATE_CHANGED);
- }
-
- public static DcController makeDcc(Phone phone, DcTracker dct,
- DataServiceManager dataServiceManager, Looper looper,
- String tagSuffix) {
- return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, looper);
- }
-
- void addDc(DataConnection dc) {
- synchronized (mDcListAll) {
- mDcListAll.add(dc);
- }
- }
-
- void removeDc(DataConnection dc) {
- synchronized (mDcListAll) {
- mDcListActiveByCid.remove(dc.mCid);
- mDcListAll.remove(dc);
- }
- synchronized (mTrafficDescriptorsByCid) {
- mTrafficDescriptorsByCid.remove(dc.mCid);
- }
- }
-
- public void addActiveDcByCid(DataConnection dc) {
- if (DBG && dc.mCid < 0) {
- log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
- }
- synchronized (mDcListAll) {
- mDcListActiveByCid.put(dc.mCid, dc);
- }
- updateTrafficDescriptorsForCid(dc.mCid, dc.getTrafficDescriptors());
- }
-
- DataConnection getActiveDcByCid(int cid) {
- synchronized (mDcListAll) {
- return mDcListActiveByCid.get(cid);
- }
- }
-
- void removeActiveDcByCid(DataConnection dc) {
- synchronized (mDcListAll) {
- DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
- if (DBG && removedDc == null) {
- log("removeActiveDcByCid removedDc=null dc=" + dc);
- }
- }
- synchronized (mTrafficDescriptorsByCid) {
- mTrafficDescriptorsByCid.remove(dc.mCid);
- }
- }
-
- boolean isDefaultDataActive() {
- synchronized (mDcListAll) {
- return mDcListActiveByCid.values().stream()
- .anyMatch(dc -> dc.getApnContexts().stream()
- .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
- }
- }
-
- List<TrafficDescriptor> getTrafficDescriptorsForCid(int cid) {
- synchronized (mTrafficDescriptorsByCid) {
- return mTrafficDescriptorsByCid.get(cid);
- }
- }
-
- void updateTrafficDescriptorsForCid(int cid, List<TrafficDescriptor> tds) {
- synchronized (mTrafficDescriptorsByCid) {
- mTrafficDescriptorsByCid.put(cid, tds);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- AsyncResult ar;
-
- switch (msg.what) {
- case DataConnection.EVENT_DATA_STATE_CHANGED:
- ar = (AsyncResult) msg.obj;
- if (ar.exception == null) {
- onDataStateChanged((ArrayList<DataCallResponse>) ar.result);
- } else {
- log("EVENT_DATA_STATE_CHANGED: exception; likely radio not available, ignore");
- }
- break;
- default:
- loge("Unexpected event " + msg);
- break;
- }
- }
-
- /**
- * Process the new list of "known" Data Calls
- * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
- */
- private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
- final HashMap<Integer, DataConnection> dcListActiveByCid;
- synchronized (mDcListAll) {
- dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
- }
-
- if (DBG) {
- log("onDataStateChanged: dcsList=" + dcsList
- + " dcListActiveByCid=" + dcListActiveByCid);
- }
-
- // Create hashmap of cid to DataCallResponse
- HashMap<Integer, DataCallResponse> dataCallResponseListByCid = new HashMap<>();
- for (DataCallResponse dcs : dcsList) {
- dataCallResponseListByCid.put(dcs.getId(), dcs);
- }
-
- // Add a DC that is active but not in the dcsList to the list of DC's to retry
- ArrayList<DataConnection> dcsToRetry = new ArrayList<>();
- for (DataConnection dc : dcListActiveByCid.values()) {
- DataCallResponse response = dataCallResponseListByCid.get(dc.mCid);
- if (response == null) {
- if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
- dcsToRetry.add(dc);
- } else {
- List<TrafficDescriptor> oldTds = getTrafficDescriptorsForCid(dc.mCid);
- List<TrafficDescriptor> newTds = response.getTrafficDescriptors();
- if (!oldTds.equals(newTds)) {
- if (DBG) {
- log("onDataStateChanged: add to retry due to TD changed dc=" + dc
- + ", oldTds=" + oldTds + ", newTds=" + newTds);
- }
- updateTrafficDescriptorsForCid(dc.mCid, newTds);
- dcsToRetry.add(dc);
- }
- }
- }
- if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
-
- // Find which connections have changed state and send a notification or cleanup
- // and any that are in active need to be retried.
- ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
-
- boolean isAnyDataCallDormant = false;
- boolean isAnyDataCallActive = false;
- boolean isInternetDataCallActive = false;
-
- for (DataCallResponse newState : dcsList) {
-
- DataConnection dc = dcListActiveByCid.get(newState.getId());
- if (dc == null) {
- // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
- loge("onDataStateChanged: no associated DC yet, ignore");
- continue;
- }
-
- List<ApnContext> apnContexts = dc.getApnContexts();
- if (apnContexts.size() == 0) {
- if (DBG) loge("onDataStateChanged: no connected apns, ignore");
- } else {
- // Determine if the connection/apnContext should be cleaned up
- // or just a notification should be sent out.
- if (DBG) {
- log("onDataStateChanged: Found ConnId=" + newState.getId()
- + " newState=" + newState.toString());
- }
- if (apnContexts.stream().anyMatch(
- i -> ApnSetting.TYPE_DEFAULT_STRING.equals(i.getApnType()))
- && newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
- isInternetDataCallActive = true;
- }
- if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) {
- if (mDct.isCleanupRequired.get()) {
- apnsToCleanup.addAll(apnContexts);
- mDct.isCleanupRequired.set(false);
- } else {
- int failCause = DataFailCause.getFailCause(newState.getCause());
- if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause,
- mPhone.getSubId())) {
- if (DBG) {
- log("onDataStateChanged: X restart radio, failCause="
- + failCause);
- }
- mDct.sendRestartRadio();
- } else if (mDct.isPermanentFailure(failCause)) {
- if (DBG) {
- log("onDataStateChanged: inactive, add to cleanup list. "
- + "failCause=" + failCause);
- }
- apnsToCleanup.addAll(apnContexts);
- } else {
- if (DBG) {
- log("onDataStateChanged: inactive, add to retry list. "
- + "failCause=" + failCause);
- }
- dcsToRetry.add(dc);
- }
- }
- } else {
- // Update the pdu session id
- dc.setPduSessionId(newState.getPduSessionId());
-
- dc.updatePcscfAddr(newState);
-
- // Its active so update the DataConnections link properties
- UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
- dc.updateResponseFields(newState);
- if (result.oldLp.equals(result.newLp)) {
- if (DBG) log("onDataStateChanged: no change");
- } else {
- if (LinkPropertiesUtils.isIdenticalInterfaceName(
- result.oldLp, result.newLp)) {
- if (!LinkPropertiesUtils.isIdenticalDnses(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalRoutes(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalHttpProxy(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalAddresses(
- result.oldLp, result.newLp)) {
- // If the same address type was removed and
- // added we need to cleanup
- CompareOrUpdateResult<Integer, LinkAddress> car
- = new CompareOrUpdateResult(
- result.oldLp != null ?
- result.oldLp.getLinkAddresses() : null,
- result.newLp != null ?
- result.newLp.getLinkAddresses() : null,
- (la) -> Objects.hash(((LinkAddress)la).getAddress(),
- ((LinkAddress)la).getPrefixLength(),
- ((LinkAddress)la).getScope()));
- if (DBG) {
- log("onDataStateChanged: oldLp=" + result.oldLp
- + " newLp=" + result.newLp + " car=" + car);
- }
- boolean needToClean = false;
- for (LinkAddress added : car.added) {
- for (LinkAddress removed : car.removed) {
- if (NetUtils.addressTypeMatches(
- removed.getAddress(),
- added.getAddress())) {
- needToClean = true;
- break;
- }
- }
- }
- if (needToClean) {
- if (DBG) {
- log("onDataStateChanged: addr change,"
- + " cleanup apns=" + apnContexts
- + " oldLp=" + result.oldLp
- + " newLp=" + result.newLp);
- }
- apnsToCleanup.addAll(apnContexts);
- }
- } else {
- if (DBG) {
- log("onDataStateChanged: no changes");
- }
- }
- } else {
- apnsToCleanup.addAll(apnContexts);
- if (DBG) {
- log("onDataStateChanged: interface change, cleanup apns="
- + apnContexts);
- }
- }
- }
- }
- }
-
- if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
- isAnyDataCallActive = true;
- }
- if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) {
- isAnyDataCallDormant = true;
- }
- }
-
- if (mDataServiceManager.getTransportType()
- == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- boolean isPhysicalLinkStatusFocusingOnInternetData =
- mDct.getLteEndcUsingUserDataForIdleDetection();
- int physicalLinkStatus =
- (isPhysicalLinkStatusFocusingOnInternetData
- ? isInternetDataCallActive : isAnyDataCallActive)
- ? DataCallResponse.LINK_STATUS_ACTIVE
- : DataCallResponse.LINK_STATUS_DORMANT;
- if (mPhysicalLinkStatus != physicalLinkStatus) {
- mPhysicalLinkStatus = physicalLinkStatus;
- mPhysicalLinkStatusChangedRegistrants.notifyResult(mPhysicalLinkStatus);
- }
- if (isAnyDataCallDormant && !isAnyDataCallActive) {
- // There is no way to indicate link activity per APN right now. So
- // Link Activity will be considered dormant only when all data calls
- // are dormant.
- // If a single data call is in dormant state and none of the data
- // calls are active broadcast overall link status as dormant.
- if (DBG) {
- log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll");
- }
- mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
- } else {
- if (DBG) {
- log("onDataStateChanged: Data Activity updated to NONE. "
- + "isAnyDataCallActive = " + isAnyDataCallActive
- + " isAnyDataCallDormant = " + isAnyDataCallDormant);
- }
- if (isAnyDataCallActive) {
- mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
- }
- }
- }
-
- if (DBG) {
- log("onDataStateChanged: dcsToRetry=" + dcsToRetry
- + " apnsToCleanup=" + apnsToCleanup);
- }
-
- // Cleanup connections that have changed
- for (ApnContext apnContext : apnsToCleanup) {
- mDct.cleanUpConnection(apnContext);
- }
-
- // Retry connections that have disappeared
- for (DataConnection dc : dcsToRetry) {
- if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
- dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
- }
-
- if (VDBG) log("onDataStateChanged: X");
- }
-
- /**
- * Register for physical link status (i.e. RRC state) changed event.
- * if {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
- * then physical link status is focusing on "internet data connection" instead of RRC state.
- * @param h The handler
- * @param what The event
- */
- @VisibleForTesting
- public void registerForPhysicalLinkStatusChanged(Handler h, int what) {
- mPhysicalLinkStatusChangedRegistrants.addUnique(h, what, null);
- }
-
- /**
- * Unregister from physical link status (i.e. RRC state) changed event.
- *
- * @param h The previously registered handler
- */
- void unregisterForPhysicalLinkStatusChanged(Handler h) {
- mPhysicalLinkStatusChangedRegistrants.remove(h);
- }
-
- private void log(String s) {
- Rlog.d(mTag, s);
- }
-
- private void loge(String s) {
- Rlog.e(mTag, s);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- synchronized (mDcListAll) {
- sb.append("mDcListAll=").append(mDcListAll)
- .append(" mDcListActiveByCid=").append(mDcListActiveByCid);
- }
- synchronized (mTrafficDescriptorsByCid) {
- sb.append("mTrafficDescriptorsByCid=").append(mTrafficDescriptorsByCid);
- }
- return sb.toString();
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" mPhone=" + mPhone);
- synchronized (mDcListAll) {
- pw.println(" mDcListAll=" + mDcListAll);
- pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);
- }
- synchronized (mTrafficDescriptorsByCid) {
- pw.println(" mTrafficDescriptorsByCid=" + mTrafficDescriptorsByCid);
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
deleted file mode 100644
index 3cdd209..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.Intent;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.DataFailCause;
-
-import com.android.telephony.Rlog;
-
-/**
- * A package visible class for supporting testing failing bringUp commands. This
- * saves the parameters from a action_fail_bringup intent. See
- * {@link DataConnection#doOnConnect} and {@see DcTesterFailBringUpAll} for more info.
- */
-public class DcFailBringUp {
- private static final String LOG_TAG = "DcFailBringUp";
- private static final boolean DBG = true;
-
- static final String INTENT_BASE = DataConnection.class.getPackage().getName();
-
- static final String ACTION_FAIL_BRINGUP = "action_fail_bringup";
-
- // counter with its --ei option name and default value
- static final String COUNTER = "counter";
- static final int DEFAULT_COUNTER = 2;
- int mCounter;
-
- // failCause with its --ei option name and default value
- static final String FAIL_CAUSE = "fail_cause";
- static final int DEFAULT_FAIL_CAUSE = DataFailCause.ERROR_UNSPECIFIED;
- @DataFailureCause
- int mFailCause;
-
- // suggestedRetryTime with its --ei option name and default value
- static final String SUGGESTED_RETRY_TIME = "suggested_retry_time";
- static final long DEFAULT_SUGGESTED_RETRY_TIME = -1;
- long mSuggestedRetryTime;
-
- // Get the Extra Intent parameters
- void saveParameters(Intent intent, String s) {
- if (DBG) log(s + ".saveParameters: action=" + intent.getAction());
- mCounter = intent.getIntExtra(COUNTER, DEFAULT_COUNTER);
- mFailCause = DataFailCause.getFailCause(
- intent.getIntExtra(FAIL_CAUSE, DEFAULT_FAIL_CAUSE));
- mSuggestedRetryTime =
- intent.getLongExtra(SUGGESTED_RETRY_TIME, DEFAULT_SUGGESTED_RETRY_TIME);
- if (DBG) {
- log(s + ".saveParameters: " + this);
- }
- }
-
- public void saveParameters(int counter, @DataFailureCause int failCause,
- long suggestedRetryTime) {
- mCounter = counter;
- mFailCause = DataFailCause.getFailCause(failCause);
- mSuggestedRetryTime = suggestedRetryTime;
- }
-
- @Override
- public String toString() {
- return "{mCounter=" + mCounter +
- " mFailCause=" + mFailCause +
- " mSuggestedRetryTime=" + mSuggestedRetryTime + "}";
-
- }
-
- private static void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
deleted file mode 100644
index c6def34..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.KeepalivePacketData;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketData;
-import android.net.NetworkAgent;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkCapabilities;
-import android.net.NetworkProvider;
-import android.net.QosFilter;
-import android.net.QosSessionAttributes;
-import android.net.SocketKeepalive;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.AnomalyReporter;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
-import android.telephony.data.QosBearerSession;
-import android.util.LocalLog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.SlidingWindowEventCounter;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.data.NotifyQosSessionInterface;
-import com.android.internal.telephony.data.QosCallbackTracker;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class represents a network agent which is communication channel between
- * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is
- * created when data connection enters {@link DataConnection.DcActiveState} until it exits that
- * state.
- *
- * Note that in IWLAN handover scenario, this agent could be transferred to the new
- * {@link DataConnection} so for a short window of time this object might be accessed by two
- * different {@link DataConnection}. Thus each method in this class needs to be synchronized.
- */
-public class DcNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface {
- private final String mTag;
-
- private final int mId;
-
- private final Phone mPhone;
-
- private final Handler mHandler;
-
- private int mTransportType;
-
- private NetworkCapabilities mNetworkCapabilities;
-
- public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
-
- private final QosCallbackTracker mQosCallbackTracker;
-
- private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor();
-
- private DataConnection mDataConnection;
-
- private final LocalLog mNetCapsLocalLog = new LocalLog(32);
-
- // For interface duplicate detection. Key is the net id, value is the interface name in string.
- private static Map<Integer, String> sInterfaceNames = new ConcurrentHashMap<>();
-
- private static final long NETWORK_UNWANTED_ANOMALY_WINDOW_MS = TimeUnit.MINUTES.toMillis(5);
- private static final int NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES = 12;
-
- private static final int EVENT_UNWANTED_TIMEOUT = 1;
-
- @VisibleForTesting
- public DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config,
- NetworkProvider networkProvider, int transportType) {
- super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent",
- dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config,
- networkProvider);
- register();
- mId = getNetwork().getNetId();
- mTag = "DcNetworkAgent" + "-" + mId;
- mPhone = phone;
- mHandler = new Handler(dc.getHandler().getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == EVENT_UNWANTED_TIMEOUT) {
- loge("onNetworkUnwanted timed out. Perform silent de-register.");
- logd("Unregister from connectivity service. " + sInterfaceNames.get(mId)
- + " removed.");
- sInterfaceNames.remove(mId);
- DcNetworkAgent.this.unregister();
- }
- }
- };
- mNetworkCapabilities = dc.getNetworkCapabilities();
- mTransportType = transportType;
- mDataConnection = dc;
- if (dc.getLinkProperties() != null) {
- checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName());
- logd("created for data connection " + dc.getName() + ", "
- + dc.getLinkProperties().getInterfaceName());
- } else {
- loge("The connection does not have a valid link properties.");
- }
- mQosCallbackTracker = new QosCallbackTracker(this, mPhone);
- }
-
- private @NetworkType int getNetworkType() {
- ServiceState ss = mPhone.getServiceState();
- int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-
- NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
- if (nri != null) {
- networkType = nri.getAccessNetworkTechnology();
- }
-
- return networkType;
- }
-
- private void checkDuplicateInterface(int netId, @Nullable String interfaceName) {
- for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) {
- if (Objects.equals(interfaceName, entry.getValue())) {
- String message = "Duplicate interface " + interfaceName
- + " is detected. DcNetworkAgent-" + entry.getKey()
- + " already used this interface name.";
- loge(message);
- // Using fixed UUID to avoid duplicate bugreport notification
- AnomalyReporter.reportAnomaly(
- UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
- message, mPhone.getCarrierId());
- return;
- }
- }
- sInterfaceNames.put(netId, interfaceName);
- }
-
- /**
- * @return The tag
- */
- String getTag() {
- return mTag;
- }
-
- /**
- * Set the data connection that owns this network agent.
- *
- * @param dc Data connection owning this network agent.
- * @param transportType Transport that this data connection is on.
- */
- public synchronized void acquireOwnership(@NonNull DataConnection dc,
- @TransportType int transportType) {
- mDataConnection = dc;
- mTransportType = transportType;
- logd(dc.getName() + " acquired the ownership of this agent.");
- }
-
- /**
- * Release the ownership of network agent.
- */
- public synchronized void releaseOwnership(DataConnection dc) {
- if (mDataConnection == null) {
- loge("releaseOwnership called on no-owner DcNetworkAgent!");
- return;
- } else if (mDataConnection != dc) {
- loge("releaseOwnership: This agent belongs to "
- + mDataConnection.getName() + ", ignored the request from " + dc.getName());
- return;
- }
- logd("Data connection " + mDataConnection.getName() + " released the ownership.");
- mDataConnection = null;
- }
-
- /**
- * @return The data connection that owns this agent
- */
- public synchronized DataConnection getDataConnection() {
- return mDataConnection;
- }
-
- private static final SlidingWindowEventCounter sNetworkUnwantedCounter =
- new SlidingWindowEventCounter(NETWORK_UNWANTED_ANOMALY_WINDOW_MS,
- NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES);
-
- @Override
- public synchronized void onNetworkUnwanted() {
- mHandler.sendEmptyMessageDelayed(EVENT_UNWANTED_TIMEOUT, TimeUnit.SECONDS.toMillis(30));
- trackNetworkUnwanted();
- if (mDataConnection == null) {
- loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!");
- return;
- }
-
- logd("onNetworkUnwanted called. Now tear down the data connection "
- + mDataConnection.getName());
- mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE,
- DcTracker.RELEASE_TYPE_DETACH, null);
- }
-
- /**
- * There have been several bugs where a RECONNECT loop kicks off where a DataConnection
- * connects to the Network, ConnectivityService indicates that the Network is unwanted,
- * and then the DataConnection reconnects. By the time we get the bug report it's too late
- * because there have already been hundreds of RECONNECTS. This is meant to capture the issue
- * when it first starts.
- *
- * The unwanted counter is configured to only take an anomaly report in extreme cases.
- * This is to avoid having the anomaly message show up on several devices.
- *
- * This is directly related to b/175845538. But, there have been several other occurrences of
- * this issue.
- */
- private void trackNetworkUnwanted() {
- if (sNetworkUnwantedCounter.addOccurrence()) {
- AnomalyReporter.reportAnomaly(
- UUID.fromString("3f578b5c-64e9-11eb-ae93-0242ac130002"),
- "Network Unwanted called 12 times in 5 minutes.", mPhone.getCarrierId());
- }
- }
-
- @Override
- public synchronized void onBandwidthUpdateRequested() {
- if (mDataConnection == null) {
- loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!");
- return;
- }
-
- if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE // active LCE service
- && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.pullLceData(mDataConnection.obtainMessage(
- DataConnection.EVENT_BW_REFRESH_RESPONSE));
- }
- }
-
- @Override
- public synchronized void onValidationStatus(int status, Uri redirectUri) {
- if (mDataConnection == null) {
- loge("onValidationStatus called on no-owner DcNetworkAgent!");
- return;
- }
-
- logd("validation status: " + status + " with redirection URL: " + redirectUri);
- DcTracker dct = mPhone.getDcTracker(mTransportType);
- if (dct != null) {
- Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- status, mDataConnection.getCid(), redirectUri.toString());
- msg.sendToTarget();
- }
- }
-
- private synchronized boolean isOwned(DataConnection dc, String reason) {
- if (mDataConnection == null) {
- loge(reason + " called on no-owner DcNetworkAgent!");
- return false;
- } else if (mDataConnection != dc) {
- loge(reason + ": This agent belongs to "
- + mDataConnection.getName() + ", ignored the request from " + dc.getName());
- return false;
- }
- return true;
- }
-
- /**
- * Update the legacy sub type (i.e. data network type).
- *
- * @param dc The data connection that invokes this method.
- */
- public synchronized void updateLegacySubtype(DataConnection dc) {
- if (!isOwned(dc, "updateLegacySubtype")) return;
-
- int networkType = getNetworkType();
- setLegacySubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
- }
-
- /**
- * Set the network capabilities.
- *
- * @param networkCapabilities The network capabilities.
- * @param dc The data connection that invokes this method.
- */
- public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities,
- DataConnection dc) {
- if (!isOwned(dc, "sendNetworkCapabilities")) return;
-
- if (!networkCapabilities.equals(mNetworkCapabilities)) {
- String logStr = "Changed from " + mNetworkCapabilities + " to "
- + networkCapabilities + ", Data RAT="
- + mPhone.getServiceState().getRilDataRadioTechnology()
- + ", dc=" + mDataConnection.getName();
- logd(logStr);
- mNetCapsLocalLog.log(logStr);
- if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- // only log metrics for DataConnection with NET_CAPABILITY_INTERNET
- if (mNetworkCapabilities == null
- || networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
- != mNetworkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) {
- TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent(
- mPhone.getPhoneId(), networkCapabilities);
- }
- }
- mNetworkCapabilities = networkCapabilities;
- }
- sendNetworkCapabilities(networkCapabilities);
- }
-
- /**
- * Set the link properties
- *
- * @param linkProperties The link properties
- * @param dc The data connection that invokes this method.
- */
- public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties,
- DataConnection dc) {
- if (!isOwned(dc, "sendLinkProperties")) return;
-
- sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName());
- sendLinkProperties(linkProperties);
- }
-
- /**
- * Set the network score.
- *
- * @param score The network score.
- * @param dc The data connection that invokes this method.
- */
- public synchronized void sendNetworkScore(int score, DataConnection dc) {
- if (!isOwned(dc, "sendNetworkScore")) return;
- sendNetworkScore(score);
- }
-
- /**
- * Unregister the network agent from connectivity service.
- *
- * @param dc The data connection that invokes this method.
- */
- public synchronized void unregister(DataConnection dc) {
- if (!isOwned(dc, "unregister")) return;
-
- mHandler.removeMessages(EVENT_UNWANTED_TIMEOUT);
- logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed.");
- sInterfaceNames.remove(mId);
- super.unregister();
- }
-
- @Override
- public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval,
- @NonNull KeepalivePacketData packet) {
- if (mDataConnection == null) {
- loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!");
- return;
- }
-
- if (packet instanceof NattKeepalivePacketData) {
- mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST,
- slot, (int) interval.getSeconds(), packet).sendToTarget();
- } else {
- sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED);
- }
- }
-
- @Override
- public synchronized void onStopSocketKeepalive(int slot) {
- if (mDataConnection == null) {
- loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!");
- return;
- }
-
- mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot)
- .sendToTarget();
- }
-
- @Override
- public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
- mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId,
- new QosCallbackTracker.IFilter() {
- @Override
- public boolean matchesLocalAddress(
- InetAddress address, int startPort, int endPort) {
- return filter.matchesLocalAddress(address, startPort, endPort);
- }
-
- @Override
- public boolean matchesRemoteAddress(
- InetAddress address, int startPort, int endPort) {
- return filter.matchesRemoteAddress(address, startPort, endPort);
- }
- }));
- }
-
- @Override
- public void onQosCallbackUnregistered(final int qosCallbackId) {
- mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId));
- }
-
- void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) {
- mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions));
- }
-
- @Override
- public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
- @NonNull final QosSessionAttributes attributes) {
- super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
- }
-
- @Override
- public void notifyQosSessionLost(final int qosCallbackId,
- final int sessionId, final int qosSessionType) {
- super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
- }
-
- @Override
- public String toString() {
- return "DcNetworkAgent-"
- + mId
- + " mDataConnection="
- + ((mDataConnection != null) ? mDataConnection.getName() : null)
- + " mTransportType="
- + AccessNetworkConstants.transportTypeToString(mTransportType)
- + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null)
- + " mNetworkCapabilities=" + mNetworkCapabilities;
- }
-
- /**
- * Dump the state of transport manager
- *
- * @param fd File descriptor
- * @param printWriter Print writer
- * @param args Arguments
- */
- public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
- pw.println(toString());
- pw.increaseIndent();
- pw.println("Net caps logs:");
- mNetCapsLocalLog.dump(fd, pw, args);
- pw.decreaseIndent();
- }
-
- /**
- * Log with debug level
- *
- * @param s is string log
- */
- private void logd(String s) {
- Rlog.d(mTag, s);
- }
-
- /**
- * Log with error level
- *
- * @param s is string log
- */
- private void loge(String s) {
- Rlog.e(mTag, s);
- }
-
- class DcKeepaliveTracker {
- private class KeepaliveRecord {
- public int slotId;
- public int currentStatus;
-
- KeepaliveRecord(int slotId, int status) {
- this.slotId = slotId;
- this.currentStatus = status;
- }
- }
-
- private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray();
-
- int getHandleForSlot(int slotId) {
- for (int i = 0; i < mKeepalives.size(); i++) {
- KeepaliveRecord kr = mKeepalives.valueAt(i);
- if (kr.slotId == slotId) return mKeepalives.keyAt(i);
- }
- return -1;
- }
-
- int keepaliveStatusErrorToPacketKeepaliveError(int error) {
- switch(error) {
- case KeepaliveStatus.ERROR_NONE:
- return SocketKeepalive.SUCCESS;
- case KeepaliveStatus.ERROR_UNSUPPORTED:
- return SocketKeepalive.ERROR_UNSUPPORTED;
- case KeepaliveStatus.ERROR_NO_RESOURCES:
- return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
- case KeepaliveStatus.ERROR_UNKNOWN:
- default:
- return SocketKeepalive.ERROR_HARDWARE_ERROR;
- }
- }
-
- void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
- switch (ks.statusCode) {
- case KeepaliveStatus.STATUS_INACTIVE:
- DcNetworkAgent.this.sendSocketKeepaliveEvent(slot,
- keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
- break;
- case KeepaliveStatus.STATUS_ACTIVE:
- DcNetworkAgent.this.sendSocketKeepaliveEvent(
- slot, SocketKeepalive.SUCCESS);
- // fall through to add record
- case KeepaliveStatus.STATUS_PENDING:
- logd("Adding keepalive handle="
- + ks.sessionHandle + " slot = " + slot);
- mKeepalives.put(ks.sessionHandle,
- new KeepaliveRecord(
- slot, ks.statusCode));
- break;
- default:
- logd("Invalid KeepaliveStatus Code: " + ks.statusCode);
- break;
- }
- }
-
- void handleKeepaliveStatus(KeepaliveStatus ks) {
- final KeepaliveRecord kr;
- kr = mKeepalives.get(ks.sessionHandle);
-
- if (kr == null) {
- // If there is no slot for the session handle, we received an event
- // for a different data connection. This is not an error because the
- // keepalive session events are broadcast to all listeners.
- loge("Discarding keepalive event for different data connection:" + ks);
- return;
- }
- // Switch on the current state, to see what we do with the status update
- switch (kr.currentStatus) {
- case KeepaliveStatus.STATUS_INACTIVE:
- logd("Inactive Keepalive received status!");
- DcNetworkAgent.this.sendSocketKeepaliveEvent(
- kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR);
- break;
- case KeepaliveStatus.STATUS_PENDING:
- switch (ks.statusCode) {
- case KeepaliveStatus.STATUS_INACTIVE:
- DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId,
- keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
- kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
- mKeepalives.remove(ks.sessionHandle);
- break;
- case KeepaliveStatus.STATUS_ACTIVE:
- logd("Pending Keepalive received active status!");
- kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
- DcNetworkAgent.this.sendSocketKeepaliveEvent(
- kr.slotId, SocketKeepalive.SUCCESS);
- break;
- case KeepaliveStatus.STATUS_PENDING:
- loge("Invalid unsolicied Keepalive Pending Status!");
- break;
- default:
- loge("Invalid Keepalive Status received, " + ks.statusCode);
- }
- break;
- case KeepaliveStatus.STATUS_ACTIVE:
- switch (ks.statusCode) {
- case KeepaliveStatus.STATUS_INACTIVE:
- logd("Keepalive received stopped status!");
- DcNetworkAgent.this.sendSocketKeepaliveEvent(
- kr.slotId, SocketKeepalive.SUCCESS);
-
- kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
- mKeepalives.remove(ks.sessionHandle);
- break;
- case KeepaliveStatus.STATUS_PENDING:
- case KeepaliveStatus.STATUS_ACTIVE:
- loge("Active Keepalive received invalid status!");
- break;
- default:
- loge("Invalid Keepalive Status received, " + ks.statusCode);
- }
- break;
- default:
- loge("Invalid Keepalive Status received, " + kr.currentStatus);
- }
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
deleted file mode 100644
index da775e8..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.NetworkRequest;
-import android.net.NetworkSpecifier;
-import android.net.TelephonyNetworkSpecifier;
-import android.telephony.Annotation.ApnType;
-
-import com.android.telephony.Rlog;
-
-/**
- * Wraps cellular network requests to configured apn types.
- */
-public class DcRequest implements Comparable<DcRequest> {
- private static final String LOG_TAG = "DcRequest";
-
- @NonNull
- public final NetworkRequest networkRequest;
- public final int priority;
- public final @ApnType int apnType;
-
- private DcRequest(@NonNull final NetworkRequest nr, @ApnType final int type,
- int apnPriority) {
- networkRequest = nr;
- priority = apnPriority;
- apnType = type;
- }
-
- /**
- * Create a DcRequest based off of the network request. If the network request is not cellular,
- * then null is returned and a warning is generated.
- * @param networkRequest sets the type of dc request
- * @param apnConfigTypeRepository apn config types to match on the network request
- * @return corresponding DcRequest
- *
- */
- @Nullable
- public static DcRequest create(@NonNull final NetworkRequest networkRequest,
- @NonNull final ApnConfigTypeRepository apnConfigTypeRepository) {
- final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
- final ApnConfigType apnConfigType = apnConfigTypeRepository.getByType(apnType);
- if (apnConfigType == null) {
- Rlog.d(LOG_TAG, "Non cellular request ignored: " + networkRequest.toString());
- checkForAnomalousNetworkRequest(networkRequest);
- return null;
- } else {
- Rlog.d(LOG_TAG, "Cellular request confirmed: " + networkRequest.toString());
- return new DcRequest(networkRequest, apnType, apnConfigType.getPriority());
- }
- }
-
- private static void checkForAnomalousNetworkRequest(NetworkRequest networkRequest) {
- NetworkSpecifier specifier = networkRequest.getNetworkSpecifier();
- if (specifier != null) {
- if (specifier instanceof TelephonyNetworkSpecifier) {
- reportAnomalousNetworkRequest(networkRequest);
- }
- }
- }
-
- private static void reportAnomalousNetworkRequest(NetworkRequest networkRequest) {
- //TODO: Report anomaly if this happens
- Rlog.w(LOG_TAG, "A TelephonyNetworkSpecifier for a non-cellular request is invalid: "
- + networkRequest.toString());
-
- }
-
- public String toString() {
- return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType;
- }
-
- public int hashCode() {
- return networkRequest.hashCode();
- }
-
- public boolean equals(Object o) {
- if (o instanceof DcRequest) {
- return networkRequest.equals(((DcRequest)o).networkRequest);
- }
- return false;
- }
-
- public int compareTo(DcRequest o) {
- return o.priority - priority;
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
deleted file mode 100644
index 11a0ae6..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-/**
- * To bring down all DC's send the following intent:
- *
- * adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all
- */
-public class DcTesterDeactivateAll {
- private static final String LOG_TAG = "DcTesterDeacativateAll";
- private static final boolean DBG = true;
-
- private Phone mPhone;
- private DcController mDcc;
-
- public static String sActionDcTesterDeactivateAll =
- "com.android.internal.telephony.dataconnection.action_deactivate_all";
-
-
- // The static intent receiver one for all instances and we assume this
- // is running on the same thread as Dcc.
- protected BroadcastReceiver sIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) log("sIntentReceiver.onReceive: action=" + action);
- if (action.equals(sActionDcTesterDeactivateAll)
- || action.equals(mPhone.getActionDetached())) {
- log("Send DEACTIVATE to all Dcc's");
- if (mDcc != null) {
- for (DataConnection dc : mDcc.mDcListAll) {
- dc.tearDownNow();
- }
- } else {
- if (DBG) log("onReceive: mDcc is null, ignoring");
- }
- } else {
- if (DBG) log("onReceive: unknown action=" + action);
- }
- }
- };
-
- DcTesterDeactivateAll(Phone phone, DcController dcc, Handler handler) {
- mPhone = phone;
- mDcc = dcc;
-
- if (TelephonyUtils.IS_DEBUGGABLE) {
- IntentFilter filter = new IntentFilter();
-
- filter.addAction(sActionDcTesterDeactivateAll);
- log("register for intent action=" + sActionDcTesterDeactivateAll);
-
- filter.addAction(mPhone.getActionDetached());
- log("register for intent action=" + mPhone.getActionDetached());
-
- phone.getContext().registerReceiver(sIntentReceiver, filter, null, handler,
- Context.RECEIVER_EXPORTED);
- }
- }
-
- void dispose() {
- if (TelephonyUtils.IS_DEBUGGABLE) {
- mPhone.getContext().unregisterReceiver(sIntentReceiver);
- }
- }
-
- private static void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
deleted file mode 100644
index 788da29..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.telephony.DataFailCause;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-/**
- * A package level call that causes all DataConnection bringUp calls to fail a specific
- * number of times. Here is an example that sets counter to 2 and cause to -3 for all instances:
- * adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup \
- * --ei counter 2 --ei fail_cause -3
- *
- * Also you can add a suggested retry time if desired:
- * --ei suggested_retry_time 5000
- *
- * The fail_cause is one of {@link DataFailCause}
- */
-public class DcTesterFailBringUpAll {
- private static final String LOG_TAG = "DcTesterFailBrinupAll";
- private static final boolean DBG = true;
-
- private Phone mPhone;
-
- private String mActionFailBringUp = DcFailBringUp.INTENT_BASE + "."
- + DcFailBringUp.ACTION_FAIL_BRINGUP;
-
- // The saved FailBringUp data from the intent
- private DcFailBringUp mFailBringUp = new DcFailBringUp();
-
- // The static intent receiver one for all instances.
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) log("sIntentReceiver.onReceive: action=" + action);
- if (action.equals(mActionFailBringUp)) {
- mFailBringUp.saveParameters(intent, "sFailBringUp");
- } else if (action.equals(mPhone.getActionDetached())) {
- // Counter is MAX, bringUp/retry will always fail
- log("simulate detaching");
- mFailBringUp.saveParameters(Integer.MAX_VALUE,
- DataFailCause.LOST_CONNECTION,
- DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME);
- } else if (action.equals(mPhone.getActionAttached())) {
- // Counter is 0 next bringUp/retry will succeed
- log("simulate attaching");
- mFailBringUp.saveParameters(0, DataFailCause.NONE,
- DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME);
- } else {
- if (DBG) log("onReceive: unknown action=" + action);
- }
- }
- };
-
- DcTesterFailBringUpAll(Phone phone, Handler handler) {
- mPhone = phone;
- if (TelephonyUtils.IS_DEBUGGABLE) {
- IntentFilter filter = new IntentFilter();
-
- filter.addAction(mActionFailBringUp);
- log("register for intent action=" + mActionFailBringUp);
-
- filter.addAction(mPhone.getActionDetached());
- log("register for intent action=" + mPhone.getActionDetached());
-
- filter.addAction(mPhone.getActionAttached());
- log("register for intent action=" + mPhone.getActionAttached());
-
- phone.getContext().registerReceiver(mIntentReceiver, filter, null, handler,
- Context.RECEIVER_EXPORTED);
- }
- }
-
- void dispose() {
- if (TelephonyUtils.IS_DEBUGGABLE) {
- mPhone.getContext().unregisterReceiver(mIntentReceiver);
- }
- }
-
- public DcFailBringUp getDcFailBringUp() {
- return mFailBringUp;
- }
-
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
deleted file mode 100755
index 28f69dc..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ /dev/null
@@ -1,5665 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_DO_FALLBACK;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL;
-
-import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
-import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkCapabilities;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkRequest;
-import android.net.TrafficStats;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RegistrantList;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.CellLocation;
-import android.telephony.DataFailCause;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.ServiceState.RilRadioTechnology;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyDisplayInfo;
-import android.telephony.TelephonyFrameworkInitializer;
-import android.telephony.TelephonyManager;
-import android.telephony.TelephonyManager.SimState;
-import android.telephony.cdma.CdmaCellLocation;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataCallResponse.HandoverFailureMode;
-import android.telephony.data.DataProfile;
-import android.telephony.data.ThrottleStatus;
-import android.telephony.gsm.GsmCellLocation;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.WindowManager;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.SettingsObserver;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
-import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
-import com.android.internal.telephony.metrics.DataStallRecoveryStats;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.telephony.util.NotificationChannelController;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.internal.util.AsyncChannel;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-/**
- * {@hide}
- */
-public class DcTracker extends Handler {
- protected static final boolean DBG = true;
- private static final boolean VDBG = false; // STOPSHIP if true
- private static final boolean VDBG_STALL = false; // STOPSHIP if true
- private static final boolean RADIO_TESTS = false;
- private static final String NOTIFICATION_TAG = DcTracker.class.getSimpleName();
-
- @IntDef(value = {
- REQUEST_TYPE_NORMAL,
- REQUEST_TYPE_HANDOVER,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RequestNetworkType {}
-
- /**
- * Normal request for {@link #requestNetwork(NetworkRequest, int, Message)}. For request
- * network, this adds the request to the {@link ApnContext}. If there were no network request
- * attached to the {@link ApnContext} earlier, this request setups a data connection.
- */
- public static final int REQUEST_TYPE_NORMAL = 1;
-
- /**
- * Handover request for {@link #requestNetwork(NetworkRequest, int, Message)} or
- * {@link #releaseNetwork(NetworkRequest, int)}. For request network, this
- * initiates the handover data setup process. The existing data connection will be seamlessly
- * handover to the new network. For release network, this performs a data connection softly
- * clean up at the underlying layer (versus normal data release).
- */
- public static final int REQUEST_TYPE_HANDOVER = 2;
-
- @IntDef(value = {
- RELEASE_TYPE_NORMAL,
- RELEASE_TYPE_DETACH,
- RELEASE_TYPE_HANDOVER,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ReleaseNetworkType {}
-
- /**
- * For release network, this is just removing the network request from the {@link ApnContext}.
- * Note this does not tear down the physical data connection. Normally the data connection is
- * torn down by connectivity service directly calling {@link NetworkAgent#unwanted()}.
- */
- public static final int RELEASE_TYPE_NORMAL = 1;
-
- /**
- * Detach request for {@link #releaseNetwork(NetworkRequest, int)} only. This
- * forces the APN context detach from the data connection. If this {@link ApnContext} is the
- * last one attached to the data connection, the data connection will be torn down, otherwise
- * the data connection remains active.
- */
- public static final int RELEASE_TYPE_DETACH = 2;
-
- /**
- * Handover request for {@link #releaseNetwork(NetworkRequest, int)}. For release
- * network, this performs a data connection softly clean up at the underlying layer (versus
- * normal data release).
- */
- public static final int RELEASE_TYPE_HANDOVER = 3;
-
- /** The extras for handover completion message */
- public static final String DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST = "extra_network_request";
- public static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type";
- public static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success";
- /**
- * The flag indicates whether after handover failure, the data connection should remain on the
- * original transport.
- */
- public static final String DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK =
- "extra_handover_failure_fallback";
-
- private final String mLogTag;
-
- public AtomicBoolean isCleanupRequired = new AtomicBoolean(false);
-
- private final TelephonyManager mTelephonyManager;
-
- private final AlarmManager mAlarmManager;
-
- /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
- private int mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
- // All data enabling/disabling related settings
- private final DataEnabledSettings mDataEnabledSettings;
-
- /**
- * After detecting a potential connection problem, this is the max number
- * of subsequent polls before attempting recovery.
- */
- // 1 sec. default polling interval when screen is on.
- private static final int POLL_NETSTAT_MILLIS = 1000;
- // 10 min. default polling interval when screen is off.
- private static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
- // Default sent packets without ack which triggers initial recovery steps
- private static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
-
- // Default for the data stall alarm while non-aggressive stall detection
- private static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
- // Default for the data stall alarm for aggressive stall detection
- private static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;
-
- private static final boolean DATA_STALL_SUSPECTED = true;
- protected static final boolean DATA_STALL_NOT_SUSPECTED = false;
-
- private static final String INTENT_DATA_STALL_ALARM =
- "com.android.internal.telephony.data-stall";
- // Tag for tracking stale alarms
- private static final String INTENT_DATA_STALL_ALARM_EXTRA_TAG = "data_stall_alarm_extra_tag";
- private static final String INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE =
- "data_stall_alarm_extra_transport_type";
-
- // Unique id for no data notification on setup data permanently failed.
- private static final int NO_DATA_NOTIFICATION = 1001;
-
- /** The higher index has higher priority. */
- private static final DctConstants.State[] DATA_CONNECTION_STATE_PRIORITIES = {
- DctConstants.State.IDLE,
- DctConstants.State.DISCONNECTING,
- DctConstants.State.CONNECTING,
- DctConstants.State.CONNECTED,
- };
-
- private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
- private DcController mDcc;
-
- /** kept in sync with mApnContexts
- * Higher numbers are higher priority and sorted so highest priority is first */
- private ArrayList<ApnContext> mPrioritySortedApnContexts = new ArrayList<>();
-
- /** all APN settings applicable to the current carrier */
- private ArrayList<ApnSetting> mAllApnSettings = new ArrayList<>();
-
- /** preferred apn */
- private ApnSetting mPreferredApn = null;
-
- /** Is packet service restricted by network */
- private boolean mIsPsRestricted = false;
-
- /** emergency apn Setting*/
- private ApnSetting mEmergencyApn = null;
-
- /* Once disposed dont handle any messages */
- private boolean mIsDisposed = false;
-
- private ContentResolver mResolver;
-
- /* Set to true with CMD_ENABLE_MOBILE_PROVISIONING */
- private boolean mIsProvisioning = false;
-
- /* The Url passed as object parameter in CMD_ENABLE_MOBILE_PROVISIONING */
- private String mProvisioningUrl = null;
-
- /* Indicating data service is bound or not */
- private boolean mDataServiceBound = false;
-
- /* Intent to hide/show the sign-in error notification for provisioning */
- private static final String INTENT_PROVISION = "com.android.internal.telephony.PROVISION";
-
- /**
- * Extra containing the phone ID for INTENT_PROVISION
- * Must be kept consistent with NetworkNotificationManager#setProvNotificationVisible.
- * TODO: refactor the deprecated API to prevent hardcoding values.
- */
- private static final String EXTRA_PROVISION_PHONE_ID = "provision.phone.id";
-
- /* Intent for the provisioning apn alarm */
- private static final String INTENT_PROVISIONING_APN_ALARM =
- "com.android.internal.telephony.provisioning_apn_alarm";
-
- /* Tag for tracking stale alarms */
- private static final String PROVISIONING_APN_ALARM_TAG_EXTRA = "provisioning.apn.alarm.tag";
-
- /* Debug property for overriding the PROVISIONING_APN_ALARM_DELAY_IN_MS */
- private static final String DEBUG_PROV_APN_ALARM = "persist.debug.prov_apn_alarm";
-
- /* Default for the provisioning apn alarm timeout */
- private static final int PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 15;
-
- /* The provision apn alarm intent used to disable the provisioning apn */
- private PendingIntent mProvisioningApnAlarmIntent = null;
-
- /* Used to track stale provisioning apn alarms */
- private int mProvisioningApnAlarmTag = (int) SystemClock.elapsedRealtime();
-
- private AsyncChannel mReplyAc = new AsyncChannel();
-
- private final LocalLog mDataRoamingLeakageLog = new LocalLog(32);
- private final LocalLog mApnSettingsInitializationLog = new LocalLog(32);
-
- /* 5G connection reevaluation watchdog alarm constants */
- private long mWatchdogTimeMs = 1000 * 60 * 60;
- private boolean mWatchdog = false;
-
- /* Default for whether 5G frequencies are considered unmetered */
- private boolean mNrNsaAllUnmetered = false;
- private boolean mNrNsaMmwaveUnmetered = false;
- private boolean mNrNsaSub6Unmetered = false;
- private boolean mNrSaAllUnmetered = false;
- private boolean mNrSaMmwaveUnmetered = false;
- private boolean mNrSaSub6Unmetered = false;
- private boolean mNrNsaRoamingUnmetered = false;
-
- // it effect the PhysicalLinkStatusChanged
- private boolean mLteEndcUsingUserDataForRrcDetection = false;
-
- /* List of SubscriptionPlans, updated when initialized and when plans are changed. */
- private List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
- /* List of network types an unmetered override applies to, set by onSubscriptionOverride
- * and cleared when the device is rebooted or the override expires. */
- private List<Integer> mUnmeteredNetworkTypes = null;
- /* List of network types a congested override applies to, set by onSubscriptionOverride
- * and cleared when the device is rebooted or the override expires. */
- private List<Integer> mCongestedNetworkTypes = null;
- /* Whether an unmetered override is currently active. */
- private boolean mUnmeteredOverride = false;
- /* Whether a congested override is currently active. */
- private boolean mCongestedOverride = false;
-
- @SimState
- private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
-
- private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
-
- if (action.equals(Intent.ACTION_SCREEN_ON)) {
- // TODO: Evaluate hooking this up with DeviceStateMonitor
- if (DBG) log("screen on");
- mIsScreenOn = true;
- stopNetStatPoll();
- startNetStatPoll();
- restartDataStallAlarm();
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- if (DBG) log("screen off");
- mIsScreenOn = false;
- stopNetStatPoll();
- startNetStatPoll();
- restartDataStallAlarm();
- } else if (action.equals(INTENT_DATA_STALL_ALARM)) {
- onActionIntentDataStallAlarm(intent);
- } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) {
- if (DBG) log("Provisioning apn alarm");
- onActionIntentProvisioningApnAlarm(intent);
- } else if (action.equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
- || action.equals(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)) {
- if (mPhone.getPhoneId() == intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
- int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
- TelephonyManager.SIM_STATE_UNKNOWN);
- sendMessage(obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED, simState, 0));
- }
- } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
- if (intent.getBooleanExtra(
- CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
- // Ignore the rebroadcast one to prevent multiple carrier config changed
- // event during boot up.
- return;
- }
- int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
- }
- }
- } else {
- if (DBG) log("onReceive: Unknown action=" + action);
- }
- }
- };
-
- private final Runnable mPollNetStat = new Runnable() {
- @Override
- public void run() {
- updateDataActivity();
-
- if (mIsScreenOn) {
- mNetStatPollPeriod = Settings.Global.getInt(mResolver,
- Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
- } else {
- mNetStatPollPeriod = Settings.Global.getInt(mResolver,
- Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
- POLL_NETSTAT_SCREEN_OFF_MILLIS);
- }
-
- if (mNetStatPollEnabled) {
- mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
- }
- }
- };
-
- private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
- @Override
- public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
- for (ThrottleStatus status : throttleStatuses) {
- if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) {
- setupDataOnConnectableApn(mApnContextsByType.get(status.getApnType()),
- Phone.REASON_DATA_UNTHROTTLED,
- RetryFailures.ALWAYS);
- }
- }
- }
- }
-
- private NetworkPolicyManager mNetworkPolicyManager;
- private final NetworkPolicyManager.SubscriptionCallback mSubscriptionCallback =
- new NetworkPolicyManager.SubscriptionCallback() {
- @Override
- public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- int[] networkTypes) {
- if (mPhone == null || mPhone.getSubId() != subId) return;
-
- List<Integer> tempList = new ArrayList<>();
- for (int networkType : networkTypes) {
- tempList.add(networkType);
- }
-
- log("Subscription override: overrideMask=" + overrideMask
- + ", overrideValue=" + overrideValue + ", networkTypes=" + tempList);
-
- if (overrideMask == SUBSCRIPTION_OVERRIDE_UNMETERED) {
- mUnmeteredNetworkTypes = tempList;
- mUnmeteredOverride = overrideValue != 0;
- reevaluateUnmeteredConnections();
- } else if (overrideMask == SUBSCRIPTION_OVERRIDE_CONGESTED) {
- mCongestedNetworkTypes = tempList;
- mCongestedOverride = overrideValue != 0;
- reevaluateCongestedConnections();
- }
- }
-
- @Override
- public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) {
- if (mPhone == null || mPhone.getSubId() != subId) return;
-
- mSubscriptionPlans = Arrays.asList(plans);
- if (DBG) log("SubscriptionPlans changed: " + mSubscriptionPlans);
- reevaluateUnmeteredConnections();
- }
- };
-
- private final SettingsObserver mSettingsObserver;
-
- private void registerSettingsObserver() {
- mSettingsObserver.unobserve();
- String simSuffix = "";
- if (TelephonyManager.getDefault().getSimCount() > 1) {
- simSuffix = Integer.toString(mPhone.getSubId());
- }
-
- mSettingsObserver.observe(
- Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix),
- DctConstants.EVENT_ROAMING_SETTING_CHANGE);
- mSettingsObserver.observe(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
- }
-
- /**
- * Maintain the sum of transmit and receive packets.
- *
- * The packet counts are initialized and reset to -1 and
- * remain -1 until they can be updated.
- */
- public static class TxRxSum {
- public long txPkts;
- public long rxPkts;
-
- public TxRxSum() {
- reset();
- }
-
- public TxRxSum(long txPkts, long rxPkts) {
- this.txPkts = txPkts;
- this.rxPkts = rxPkts;
- }
-
- public TxRxSum(TxRxSum sum) {
- txPkts = sum.txPkts;
- rxPkts = sum.rxPkts;
- }
-
- public void reset() {
- txPkts = -1;
- rxPkts = -1;
- }
-
- @Override
- public String toString() {
- return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
- }
-
- /**
- * Get total Tx/Rx packet count from TrafficStats
- */
- public void updateTotalTxRxSum() {
- this.txPkts = TrafficStats.getMobileTxPackets();
- this.rxPkts = TrafficStats.getMobileRxPackets();
- }
- }
-
- private void onDataReconnect(ApnContext apnContextforRetry, int subId,
- @RequestNetworkType int requestType) {
- int phoneSubId = mPhone.getSubId();
- String apnType = apnContextforRetry.getApnType();
- String reason = apnContextforRetry.getReason();
-
- if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != phoneSubId)) {
- log("onDataReconnect: invalid subId");
- return;
- }
-
- ApnContext apnContext = mApnContexts.get(apnType);
-
- if (DBG) {
- log("onDataReconnect: mState=" + mState + " reason=" + reason + " apnType=" + apnType
- + " apnContext=" + apnContext);
- }
-
- if ((apnContext != null) && (apnContext.isEnabled())) {
- apnContext.setReason(reason);
- DctConstants.State apnContextState = apnContext.getState();
- if (DBG) {
- log("onDataReconnect: apnContext state=" + apnContextState);
- }
- if ((apnContextState == DctConstants.State.FAILED)
- || (apnContextState == DctConstants.State.IDLE)) {
- if (DBG) {
- log("onDataReconnect: state is FAILED|IDLE, disassociate");
- }
- apnContext.releaseDataConnection("");
- } else {
- if (DBG) log("onDataReconnect: keep associated");
- }
- // TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
- sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, requestType,
- 0, apnContext));
- }
- }
-
- private void onActionIntentDataStallAlarm(Intent intent) {
- if (VDBG_STALL) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
-
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != mPhone.getSubId())) {
- return;
- }
-
- int transportType = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, 0);
- if (transportType != mTransportType) {
- return;
- }
-
- Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM,
- intent.getAction());
- msg.arg1 = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, 0);
- sendMessage(msg);
- }
-
- private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
-
- // member variables
- protected final Phone mPhone;
- private DctConstants.Activity mActivity = DctConstants.Activity.NONE;
- private DctConstants.State mState = DctConstants.State.IDLE;
- private final Handler mDataConnectionTracker;
-
- private long mTxPkts;
- private long mRxPkts;
- private int mNetStatPollPeriod;
- private boolean mNetStatPollEnabled = false;
-
- private TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
- // Used to track stale data stall alarms.
- private int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
- // The current data stall alarm intent
- private PendingIntent mDataStallAlarmIntent = null;
- // Number of packets sent since the last received packet
- private long mSentSinceLastRecv;
- // Controls when a simple recovery attempt it to be tried
- private int mNoRecvPollCount = 0;
- // Reference counter for enabling fail fast
- private static int sEnableFailFastRefCounter = 0;
- // True if data stall detection is enabled
- private volatile boolean mDataStallNoRxEnabled = true;
-
- protected volatile boolean mFailFast = false;
-
- // True when in voice call
- protected boolean mInVoiceCall = false;
-
- /** Intent sent when the reconnect alarm fires. */
- private PendingIntent mReconnectIntent = null;
-
- // When false we will not auto attach and manually attaching is required.
- protected boolean mAutoAttachOnCreationConfig = false;
- private AtomicBoolean mAutoAttachEnabled = new AtomicBoolean(false);
-
- // State of screen
- // (TODO: Reconsider tying directly to screen, maybe this is
- // really a lower power mode")
- private boolean mIsScreenOn = true;
-
- /** Allows the generation of unique Id's for DataConnection objects */
- private AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
-
- /** The data connections. */
- private HashMap<Integer, DataConnection> mDataConnections =
- new HashMap<Integer, DataConnection>();
-
- /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
- private HashMap<String, Integer> mApnToDataConnectionId = new HashMap<String, Integer>();
-
- /** Phone.APN_TYPE_* ===> ApnContext */
- protected ConcurrentHashMap<String, ApnContext> mApnContexts =
- new ConcurrentHashMap<String, ApnContext>();
-
- private SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
-
- private ArrayList<DataProfile> mLastDataProfileList = new ArrayList<>();
-
- /** RAT name ===> (downstream, upstream) bandwidth values from carrier config. */
- private ConcurrentHashMap<String, Pair<Integer, Integer>> mBandwidths =
- new ConcurrentHashMap<>();
-
- private boolean mConfigReady = false;
-
- /**
- * Handles changes to the APN db.
- */
- private class ApnChangeObserver extends ContentObserver {
- public ApnChangeObserver () {
- super(mDataConnectionTracker);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
- }
- }
-
- //***** Instance Variables
-
- private boolean mReregisterOnReconnectFailure = false;
-
-
- //***** Constants
-
- private static final int PROVISIONING_SPINNER_TIMEOUT_MILLIS = 120 * 1000;
-
- static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID =
- Uri.parse("content://telephony/carriers/preferapn_no_update/subId/");
- static final String APN_ID = "apn_id";
-
- private boolean mCanSetPreferApn = false;
-
- private AtomicBoolean mAttached = new AtomicBoolean(false);
-
- /** Watches for changes to the APN db. */
- private ApnChangeObserver mApnObserver;
-
- private BroadcastReceiver mProvisionBroadcastReceiver;
- private ProgressDialog mProvisioningSpinner;
-
- private final DataServiceManager mDataServiceManager;
-
- @AccessNetworkConstants.TransportType
- private final int mTransportType;
-
- private DataStallRecoveryHandler mDsRecoveryHandler;
- private HandlerThread mHandlerThread;
-
- private final DataThrottler mDataThrottler;
-
- private final ThrottleStatusChangedCallback mThrottleStatusCallback;
-
- /**
- * Request network completion message map. Key is the APN type, value is the list of completion
- * messages to be sent. Using a list because there might be multiple network requests for
- * the same APN type.
- */
- private final Map<Integer, List<Message>> mHandoverCompletionMsgs = new HashMap<>();
-
- //***** Constructor
- public DcTracker(Phone phone, @TransportType int transportType) {
- super();
- mPhone = phone;
- if (DBG) log("DCT.constructor");
- mTelephonyManager = TelephonyManager.from(phone.getContext())
- .createForSubscriptionId(phone.getSubId());
- // The 'C' in tag indicates cellular, and 'I' indicates IWLAN. This is to distinguish
- // between two DcTrackers, one for each.
- String tagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- ? "C" : "I");
- tagSuffix += "-" + mPhone.getPhoneId();
- mLogTag = "DCT" + tagSuffix;
-
- mTransportType = transportType;
- mDataServiceManager = new DataServiceManager(phone, transportType, tagSuffix);
- mDataThrottler = new DataThrottler(mPhone, transportType);
-
- mResolver = mPhone.getContext().getContentResolver();
- mAlarmManager =
- (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-
- mDsRecoveryHandler = new DataStallRecoveryHandler();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(INTENT_DATA_STALL_ALARM);
- filter.addAction(INTENT_PROVISIONING_APN_ALARM);
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
- filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
-
- mDataEnabledSettings = mPhone.getDataEnabledSettings();
-
- mDataEnabledSettings.registerForDataEnabledChanged(this,
- DctConstants.EVENT_DATA_ENABLED_CHANGED, null);
- mDataEnabledSettings.registerForDataEnabledOverrideChanged(this,
- DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED);
-
- mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
-
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
- mAutoAttachEnabled.set(sp.getBoolean(Phone.DATA_DISABLED_ON_BOOT_KEY, false));
-
- mNetworkPolicyManager = (NetworkPolicyManager) mPhone.getContext()
- .getSystemService(Context.NETWORK_POLICY_SERVICE);
- mNetworkPolicyManager.registerSubscriptionCallback(mSubscriptionCallback);
-
- mHandlerThread = new HandlerThread("DcHandlerThread");
- mHandlerThread.start();
- Handler dcHandler = new Handler(mHandlerThread.getLooper());
- mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler.getLooper(),
- tagSuffix);
- mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
-
- mDataConnectionTracker = this;
- registerForAllEvents();
- mApnObserver = new ApnChangeObserver();
- phone.getContext().getContentResolver().registerContentObserver(
- Telephony.Carriers.CONTENT_URI, true, mApnObserver);
-
- initApnContexts();
-
- addDefaultApnSettingsAsNeeded();
-
- mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
- registerSettingsObserver();
-
- mThrottleStatusCallback = new ThrottleStatusChangedCallback();
- mDataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
- }
-
- @VisibleForTesting
- public DcTracker() {
- mLogTag = "DCT";
- mTelephonyManager = null;
- mAlarmManager = null;
- mPhone = null;
- mDataConnectionTracker = null;
- mSettingsObserver = new SettingsObserver(null, this);
- mDataEnabledSettings = null;
- mTransportType = 0;
- mDataServiceManager = null;
- mDataThrottler = null;
- mThrottleStatusCallback = null;
- }
-
- public void registerServiceStateTrackerEvents() {
- mPhone.getServiceStateTracker().registerForDataConnectionAttached(mTransportType, this,
- DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
- mPhone.getServiceStateTracker().registerForDataConnectionDetached(mTransportType, this,
- DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
- mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
- DctConstants.EVENT_ROAMING_ON, null);
- mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
- DctConstants.EVENT_ROAMING_OFF, null, true);
- mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
- DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
- mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
- DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
- mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(mTransportType, this,
- DctConstants.EVENT_DATA_RAT_CHANGED, null);
- }
-
- public void unregisterServiceStateTrackerEvents() {
- mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(mTransportType, this);
- mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(mTransportType, this);
- mPhone.getServiceStateTracker().unregisterForDataRoamingOn(this);
- mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
- mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
- mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
- mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(mTransportType, this);
- mPhone.getServiceStateTracker().unregisterForAirplaneModeChanged(this);
- }
-
- private void registerForAllEvents() {
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
- mPhone.mCi.registerForOffOrNotAvailable(this,
- DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
- mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
- }
-
- // Note, this is fragile - the Phone is now presenting a merged picture
- // of PS (volte) & CS and by diving into its internals you're just seeing
- // the CS data. This works well for the purposes this is currently used for
- // but that may not always be the case. Should probably be redesigned to
- // accurately reflect what we're really interested in (registerForCSVoiceCallEnded).
- mPhone.getCallTracker().registerForVoiceCallEnded(this,
- DctConstants.EVENT_VOICE_CALL_ENDED, null);
- mPhone.getCallTracker().registerForVoiceCallStarted(this,
- DctConstants.EVENT_VOICE_CALL_STARTED, null);
- mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(this,
- DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED, null);
- registerServiceStateTrackerEvents();
- mDataServiceManager.registerForServiceBindingChanged(this,
- DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
- mDataServiceManager.registerForApnUnthrottled(this, DctConstants.EVENT_APN_UNTHROTTLED);
- }
-
- public void dispose() {
- if (DBG) log("DCT.dispose");
-
- if (mProvisionBroadcastReceiver != null) {
- mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
- mProvisionBroadcastReceiver = null;
- }
- if (mProvisioningSpinner != null) {
- mProvisioningSpinner.dismiss();
- mProvisioningSpinner = null;
- }
-
- cleanUpAllConnectionsInternal(true, null);
-
- mIsDisposed = true;
- mPhone.getContext().unregisterReceiver(mIntentReceiver);
- mSettingsObserver.unobserve();
-
- mNetworkPolicyManager.unregisterSubscriptionCallback(mSubscriptionCallback);
- mDcTesterFailBringUpAll.dispose();
-
- mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
- mApnContexts.clear();
- mApnContextsByType.clear();
- mPrioritySortedApnContexts.clear();
- unregisterForAllEvents();
-
- destroyDataConnections();
- }
-
- /**
- * Stop the internal handler thread
- *
- * TESTING ONLY
- */
- @VisibleForTesting
- public void stopHandlerThread() {
- mHandlerThread.quit();
- }
-
- private void unregisterForAllEvents() {
- //Unregister for all events
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.unregisterForAvailable(this);
- mPhone.mCi.unregisterForOffOrNotAvailable(this);
- mPhone.mCi.unregisterForPcoData(this);
- }
-
- mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
- mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
- mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this);
- unregisterServiceStateTrackerEvents();
- mDataServiceManager.unregisterForServiceBindingChanged(this);
- mDataEnabledSettings.unregisterForDataEnabledChanged(this);
- mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this);
- mDataServiceManager.unregisterForApnUnthrottled(this);
- }
-
- /**
- * Reevaluate existing data connections when conditions change.
- *
- * For example, handle reverting restricted networks back to unrestricted. If we're changing
- * user data to enabled and this makes data truly enabled (not disabled by other factors) we
- * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data
- * connection. This allows non-privilege apps to use the network.
- *
- * Or when we brought up a unmetered data connection while data is off, we only limit this
- * data connection for unmetered use only. When data is turned back on, we need to tear that
- * down so a full capable data connection can be re-established.
- */
- private void reevaluateDataConnections() {
- for (DataConnection dataConnection : mDataConnections.values()) {
- dataConnection.reevaluateRestrictedState();
- }
- }
-
- public long getSubId() {
- return mPhone.getSubId();
- }
-
- public DctConstants.Activity getActivity() {
- return mActivity;
- }
-
- private void setActivity(DctConstants.Activity activity) {
- log("setActivity = " + activity);
- mActivity = activity;
- mPhone.notifyDataActivity();
- }
-
- /**
- * Request a network
- *
- * @param networkRequest Network request from clients
- * @param type The request type
- * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
- * handover is completed. For normal request, this should be null.
- */
- public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
- Message onHandoverCompleteMsg) {
- if (type != REQUEST_TYPE_HANDOVER && onHandoverCompleteMsg != null) {
- throw new RuntimeException("request network with normal type request type but passing "
- + "handover complete message.");
- }
- final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsByType.get(apnType);
- if (apnContext != null) {
- apnContext.requestNetwork(networkRequest, type, onHandoverCompleteMsg);
- }
- }
-
- public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
- final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsByType.get(apnType);
- if (apnContext != null) {
- apnContext.releaseNetwork(networkRequest, type);
- }
- }
-
- // Turn telephony radio on or off.
- private void setRadio(boolean on) {
- final ITelephony phone = ITelephony.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getTelephonyServiceRegisterer()
- .get());
- try {
- phone.setRadio(on);
- } catch (Exception e) {
- // Ignore.
- }
- }
-
- // Class to handle Intent dispatched with user selects the "Sign-in to network"
- // notification.
- private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver {
- private final String mNetworkOperator;
- // Mobile provisioning URL. Valid while provisioning notification is up.
- // Set prior to notification being posted as URL contains ICCID which
- // disappears when radio is off (which is the case when notification is up).
- private final String mProvisionUrl;
-
- public ProvisionNotificationBroadcastReceiver(String provisionUrl, String networkOperator) {
- mNetworkOperator = networkOperator;
- mProvisionUrl = provisionUrl;
- }
-
- private void setEnableFailFastMobileData(int enabled) {
- sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled, 0));
- }
-
- private void enableMobileProvisioning() {
- final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
- Bundle bundle = new Bundle(1);
- bundle.putString(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl);
- msg.setData(bundle);
- sendMessage(msg);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PROVISION_PHONE_ID,
- SubscriptionManager.INVALID_PHONE_INDEX)) {
- return;
- }
- // Turning back on the radio can take time on the order of a minute, so show user a
- // spinner so they know something is going on.
- log("onReceive : ProvisionNotificationBroadcastReceiver");
- mProvisioningSpinner = new ProgressDialog(context);
- mProvisioningSpinner.setTitle(mNetworkOperator);
- mProvisioningSpinner.setMessage(
- // TODO: Don't borrow "Connecting..." i18n string; give Telephony a version.
- context.getText(com.android.internal.R.string.media_route_status_connecting));
- mProvisioningSpinner.setIndeterminate(true);
- mProvisioningSpinner.setCancelable(true);
- // Allow non-Activity Service Context to create a View.
- mProvisioningSpinner.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- mProvisioningSpinner.show();
- // After timeout, hide spinner so user can at least use their device.
- // TODO: Indicate to user that it is taking an unusually long time to connect?
- sendMessageDelayed(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
- mProvisioningSpinner), PROVISIONING_SPINNER_TIMEOUT_MILLIS);
- // This code is almost identical to the old
- // ConnectivityService.handleMobileProvisioningAction code.
- setRadio(true);
- setEnableFailFastMobileData(DctConstants.ENABLED);
- enableMobileProvisioning();
- }
- }
-
- @Override
- protected void finalize() {
- if(DBG && mPhone != null) log("finalize");
- }
-
- private void initApnContexts() {
- PersistableBundle carrierConfig;
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());
- } else {
- carrierConfig = null;
- }
- initApnContexts(carrierConfig);
- }
-
- //Blows away any existing apncontexts that may exist, only use in ctor.
- private void initApnContexts(PersistableBundle carrierConfig) {
- if (!mTelephonyManager.isDataCapable()) {
- log("initApnContexts: isDataCapable == false. No Apn Contexts loaded");
- return;
- }
-
- log("initApnContexts: E");
- // Load device network attributes from resources
- final Collection<ApnConfigType> types =
- new ApnConfigTypeRepository(carrierConfig).getTypes();
-
- for (ApnConfigType apnConfigType : types) {
- ApnContext apnContext = new ApnContext(mPhone, apnConfigType.getType(), mLogTag, this,
- apnConfigType.getPriority());
- int bitmask = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
- mPrioritySortedApnContexts.add(apnContext);
- mApnContexts.put(apnContext.getApnType(), apnContext);
- mApnContextsByType.put(bitmask, apnContext);
- // Notify listeners that all data is disconnected when DCT is initialized.
- // Once connections are established, DC will then notify that data is connected.
- // This is to prevent the case where the phone process crashed but we don't notify
- // listeners that data was disconnected, so they may be stuck in a connected state.
- mPhone.notifyDataConnection(new PreciseDataConnectionState.Builder()
- .setTransportType(mTransportType)
- .setState(TelephonyManager.DATA_DISCONNECTED)
- .setApnSetting(new ApnSetting.Builder()
- .setApnTypeBitmask(bitmask).buildWithoutCheck())
- .setNetworkType(getDataRat())
- .build());
- log("initApnContexts: apnContext=" + ApnSetting.getApnTypeString(
- apnConfigType.getType()));
- }
- mPrioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority());
- logSortedApnContexts();
- }
-
- private void sortApnContextByPriority() {
- if (!mTelephonyManager.isDataCapable()) {
- log("sortApnContextByPriority: isDataCapable == false. No Apn Contexts loaded");
- return;
- }
-
- PersistableBundle carrierConfig;
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());
- } else {
- carrierConfig = null;
- }
-
- log("sortApnContextByPriority: E");
- // Load device network attributes from resources
- final Collection<ApnConfigType> types =
- new ApnConfigTypeRepository(carrierConfig).getTypes();
- for (ApnConfigType apnConfigType : types) {
- if (mApnContextsByType.contains(apnConfigType.getType())) {
- ApnContext apnContext = mApnContextsByType.get(apnConfigType.getType());
- apnContext.setPriority(apnConfigType.getPriority());
- }
- }
-
- //Doing sorted in a different list to keep thread safety
- ArrayList<ApnContext> prioritySortedApnContexts =
- new ArrayList<>(mPrioritySortedApnContexts);
- prioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority());
- mPrioritySortedApnContexts = prioritySortedApnContexts;
- logSortedApnContexts();
- }
-
- public LinkProperties getLinkProperties(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext != null) {
- DataConnection dataConnection = apnContext.getDataConnection();
- if (dataConnection != null) {
- if (DBG) log("return link properties for " + apnType);
- return dataConnection.getLinkProperties();
- }
- }
- if (DBG) log("return new LinkProperties");
- return new LinkProperties();
- }
-
- public NetworkCapabilities getNetworkCapabilities(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext!=null) {
- DataConnection dataConnection = apnContext.getDataConnection();
- if (dataConnection != null) {
- if (DBG) {
- log("get active pdp is not null, return NetworkCapabilities for " + apnType);
- }
- return dataConnection.getNetworkCapabilities();
- }
- }
- if (DBG) log("return new NetworkCapabilities");
- return new NetworkCapabilities();
- }
-
- // Return all active apn types
- public String[] getActiveApnTypes() {
- if (DBG) log("get all active apn types");
- ArrayList<String> result = new ArrayList<String>();
-
- for (ApnContext apnContext : mApnContexts.values()) {
- if (mAttached.get() && apnContext.isReady()) {
- result.add(apnContext.getApnType());
- }
- }
-
- return result.toArray(new String[0]);
- }
-
- /**
- * Get ApnTypes with connected data connections. This is different than getActiveApnTypes()
- * which returns apn types that with active apn contexts.
- * @return apn types
- */
- public String[] getConnectedApnTypes() {
- return mApnContexts.values().stream()
- .filter(ac -> ac.getState() == DctConstants.State.CONNECTED)
- .map(ApnContext::getApnType)
- .toArray(String[]::new);
- }
-
- @VisibleForTesting
- public Collection<ApnContext> getApnContexts() {
- return mPrioritySortedApnContexts;
- }
-
- /** Return active ApnSetting of a specific apnType */
- public ApnSetting getActiveApnSetting(String apnType) {
- if (VDBG) log("get active ApnSetting for type:" + apnType);
- ApnContext apnContext = mApnContexts.get(apnType);
- return (apnContext != null) ? apnContext.getApnSetting() : null;
- }
-
- // Return active apn of specific apn type
- public String getActiveApnString(String apnType) {
- if (VDBG) log( "get active apn string for type:" + apnType);
- ApnSetting setting = getActiveApnSetting(apnType);
- return (setting != null) ? setting.getApnName() : null;
- }
-
- /**
- * Returns {@link DctConstants.State} based on the state of the {@link DataConnection} that
- * contains a {@link ApnSetting} that supported the given apn type {@code anpType}.
- *
- * <p>
- * Assumes there is less than one {@link ApnSetting} can support the given apn type.
- */
- // TODO: for enterprise this always returns IDLE, which is ok for now since it is never called
- // for enterprise
- public DctConstants.State getState(String apnType) {
- DctConstants.State state = DctConstants.State.IDLE;
- final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
- for (DataConnection dc : mDataConnections.values()) {
- ApnSetting apnSetting = dc.getApnSetting();
- if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) {
- if (dc.isActive()) {
- state = getBetterConnectionState(state, DctConstants.State.CONNECTED);
- } else if (dc.isActivating()) {
- state = getBetterConnectionState(state, DctConstants.State.CONNECTING);
- } else if (dc.isInactive()) {
- state = getBetterConnectionState(state, DctConstants.State.IDLE);
- } else if (dc.isDisconnecting()) {
- state = getBetterConnectionState(state, DctConstants.State.DISCONNECTING);
- }
- }
- }
- return state;
- }
-
- /**
- * Return a better connection state between {@code stateA} and {@code stateB}. Check
- * {@link #DATA_CONNECTION_STATE_PRIORITIES} for the details.
- * @return the better connection state between {@code stateA} and {@code stateB}.
- */
- private static DctConstants.State getBetterConnectionState(
- DctConstants.State stateA, DctConstants.State stateB) {
- int idxA = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateA);
- int idxB = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateB);
- return idxA >= idxB ? stateA : stateB;
- }
-
- // Return if apn type is a provisioning apn.
- private boolean isProvisioningApn(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext != null) {
- return apnContext.isProvisioningApn();
- }
- return false;
- }
-
- //****** Called from ServiceStateTracker
- /**
- * Invoked when ServiceStateTracker observes a transition from GPRS
- * attach to detach.
- */
- private void onDataConnectionDetached() {
- /*
- * We presently believe it is unnecessary to tear down the PDP context
- * when GPRS detaches, but we should stop the network polling.
- */
- if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
- stopNetStatPoll();
- stopDataStallAlarm();
- mAttached.set(false);
- }
-
- private void onDataConnectionAttached() {
- if (DBG) log("onDataConnectionAttached");
- mAttached.set(true);
- if (isAnyDataConnected()) {
- if (DBG) log("onDataConnectionAttached: start polling notify attached");
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- }
- if (mAutoAttachOnCreationConfig) {
- mAutoAttachEnabled.set(true);
- }
- setupDataOnAllConnectableApns(Phone.REASON_DATA_ATTACHED, RetryFailures.ALWAYS);
- }
-
- /**
- * Check if it is allowed to make a data connection (without checking APN context specific
- * conditions).
- *
- * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
- * param. It's okay to pass null here and no reasons will be
- * provided.
- * @return True if data connection is allowed, otherwise false.
- */
- public boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
- return isDataAllowed(null, REQUEST_TYPE_NORMAL, dataConnectionReasons);
- }
-
- /**
- * Check if it is allowed to make a data connection for a given APN type.
- *
- * @param apnContext APN context. If passing null, then will only check general but not APN
- * specific conditions (e.g. APN state, metered/unmetered APN).
- * @param requestType Setup data request type.
- * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
- * param. It's okay to pass null here and no reasons will be
- * provided.
- * @return True if data connection is allowed, otherwise false.
- */
- public boolean isDataAllowed(ApnContext apnContext, @RequestNetworkType int requestType,
- DataConnectionReasons dataConnectionReasons) {
- // Step 1: Get all environment conditions.
- // Step 2: Special handling for emergency APN.
- // Step 3. Build disallowed reasons.
- // Step 4: Determine if data should be allowed in some special conditions.
-
- DataConnectionReasons reasons = new DataConnectionReasons();
-
- int requestApnType = 0;
- if (apnContext != null) {
- requestApnType = apnContext.getApnTypeBitmask();
- }
-
- // Step 1: Get all environment conditions.
- final boolean internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
- boolean attachedState = mAttached.get();
- boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
- boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
- // TODO: Remove this hack added by ag/641832.
- int dataRat = getDataRat();
- if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
- desiredPowerState = true;
- radioStateFromCarrier = true;
- }
-
- boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(
- SubscriptionManager.getDefaultDataSubscriptionId());
-
- boolean isMeteredApnType = apnContext == null
- || ApnSettingUtils.isMeteredApnType(requestApnType, mPhone);
-
- PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
- // Note this is explicitly not using mPhone.getState. See b/19090488.
- // mPhone.getState reports the merge of CS and PS (volte) voice call state
- // but we only care about CS calls here for data/voice concurrency issues.
- // Calling getCallTracker currently gives you just the CS side where the
- // ImsCallTracker is held internally where applicable.
- // This should be redesigned to ask explicitly what we want:
- // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
- if (mPhone.getCallTracker() != null) {
- phoneState = mPhone.getCallTracker().getState();
- }
-
- // Step 2: Special handling for emergency APN.
- if (apnContext != null
- && requestApnType == ApnSetting.TYPE_EMERGENCY
- && apnContext.isConnectable()) {
- // If this is an emergency APN, as long as the APN is connectable, we
- // should allow it.
- if (dataConnectionReasons != null) {
- dataConnectionReasons.add(DataAllowedReasonType.EMERGENCY_APN);
- }
- // Bail out without further checks.
- return true;
- }
-
- // Step 3. Build disallowed reasons.
- if (apnContext != null && !apnContext.isConnectable()) {
- DctConstants.State state = apnContext.getState();
- if (state == DctConstants.State.CONNECTED) {
- reasons.add(DataDisallowedReasonType.DATA_ALREADY_CONNECTED);
- } else if (state == DctConstants.State.DISCONNECTING) {
- reasons.add(DataDisallowedReasonType.DATA_IS_DISCONNECTING);
- } else if (state == DctConstants.State.CONNECTING) {
- reasons.add(DataDisallowedReasonType.DATA_IS_CONNECTING);
- } else {
- reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
- }
- }
-
- // In legacy mode, if RAT is IWLAN then don't allow default/IA PDP at all.
- // Rest of APN types can be evaluated for remaining conditions.
- if ((apnContext != null && requestApnType == ApnSetting.TYPE_DEFAULT
- || requestApnType == ApnSetting.TYPE_ENTERPRISE
- || requestApnType == ApnSetting.TYPE_IA)
- && mPhone.getAccessNetworksManager().isInLegacyMode()
- && dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
- reasons.add(DataDisallowedReasonType.ON_IWLAN);
- }
-
- // If device is not on NR, don't allow enterprise
- if (apnContext != null && requestApnType == ApnSetting.TYPE_ENTERPRISE
- && dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_NR) {
- reasons.add(DataDisallowedReasonType.NOT_ON_NR);
- }
-
- if (shouldRestrictDataForEcbm() || mPhone.isInEmergencyCall()) {
- reasons.add(DataDisallowedReasonType.IN_ECBM);
- }
-
- if (!attachedState && !shouldAutoAttach() && requestType != REQUEST_TYPE_HANDOVER) {
- reasons.add(DataDisallowedReasonType.NOT_ATTACHED);
- }
- if (mPhone.getSubId() == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- reasons.add(DataDisallowedReasonType.SIM_NOT_READY);
- }
- if (phoneState != PhoneConstants.State.IDLE
- && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- reasons.add(DataDisallowedReasonType.INVALID_PHONE_STATE);
- reasons.add(DataDisallowedReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
- }
- if (!internalDataEnabled) {
- reasons.add(DataDisallowedReasonType.INTERNAL_DATA_DISABLED);
- }
- if (!defaultDataSelected) {
- reasons.add(DataDisallowedReasonType.DEFAULT_DATA_UNSELECTED);
- }
- if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) {
- reasons.add(DataDisallowedReasonType.ROAMING_DISABLED);
- }
- if (mIsPsRestricted) {
- reasons.add(DataDisallowedReasonType.PS_RESTRICTED);
- }
- if (!desiredPowerState) {
- reasons.add(DataDisallowedReasonType.UNDESIRED_POWER_STATE);
- }
- if (!radioStateFromCarrier) {
- reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
- }
- if (!mDataServiceBound) {
- reasons.add(DataDisallowedReasonType.DATA_SERVICE_NOT_READY);
- }
-
- if (apnContext != null) {
- if (mPhone.getAccessNetworksManager().getPreferredTransport(
- apnContext.getApnTypeBitmask())
- == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
- // If QNS explicitly specified this APN type is not allowed on either cellular or
- // IWLAN, we should not allow data setup.
- reasons.add(DataDisallowedReasonType.DISABLED_BY_QNS);
- } else if (mTransportType != mPhone.getAccessNetworksManager().getPreferredTransport(
- apnContext.getApnTypeBitmask())) {
- // If the latest preference has already switched to other transport, we should not
- // allow data setup.
- reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
- }
-
- // If the transport has been already switched to the other transport, we should not
- // allow the data setup. The only exception is the handover case, where we setup
- // handover data connection before switching the transport.
- if (mTransportType != mPhone.getAccessNetworksManager().getCurrentTransport(
- apnContext.getApnTypeBitmask()) && requestType != REQUEST_TYPE_HANDOVER) {
- reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
- }
-
- // Check if the device is under data throttling.
- long retryTime = mDataThrottler.getRetryTime(apnContext.getApnTypeBitmask());
- if (retryTime > SystemClock.elapsedRealtime()) {
- reasons.add(DataDisallowedReasonType.DATA_THROTTLED);
- }
- }
-
- boolean isDataEnabled = apnContext == null ? mDataEnabledSettings.isDataEnabled()
- : mDataEnabledSettings.isDataEnabled(requestApnType);
-
- if (!isDataEnabled) {
- reasons.add(DataDisallowedReasonType.DATA_DISABLED);
- }
-
- // If there are hard disallowed reasons, we should not allow data connection no matter what.
- if (reasons.containsHardDisallowedReasons()) {
- if (dataConnectionReasons != null) {
- dataConnectionReasons.copyFrom(reasons);
- }
- return false;
- }
-
- // Step 4: Determine if data should be allowed in some special conditions.
-
- // At this point, if data is not allowed, it must be because of the soft reasons. We
- // should start to check some special conditions that data will be allowed.
- if (!reasons.allowed()) {
- // Check if the transport is WLAN ie wifi (for AP-assisted mode devices)
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- reasons.add(DataAllowedReasonType.UNMETERED_APN);
- // Or if the data is on cellular, and the APN type is determined unmetered by the
- // configuration.
- } else if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
- && !isMeteredApnType && requestApnType != ApnSetting.TYPE_DEFAULT
- && requestApnType != ApnSetting.TYPE_ENTERPRISE) {
- reasons.add(DataAllowedReasonType.UNMETERED_APN);
- }
-
- // If the request is restricted and there are only soft disallowed reasons (e.g. data
- // disabled, data roaming disabled) existing, we should allow the data. ENTERPRISE is
- // an exception and should not be treated as restricted for this purpose; it should be
- // treated same as DEFAULT.
- if (apnContext != null
- && apnContext.hasRestrictedRequests(true)
- && !apnContext.getApnType().equals(ApnSetting.TYPE_ENTERPRISE_STRING)
- && !reasons.allowed()) {
- reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
- }
- } else {
- // If there is no disallowed reasons, then we should allow the data request with
- // normal reason.
- reasons.add(DataAllowedReasonType.NORMAL);
- }
-
- if (dataConnectionReasons != null) {
- dataConnectionReasons.copyFrom(reasons);
- }
-
- return reasons.allowed();
- }
-
- // arg for setupDataOnAllConnectableApns
- protected enum RetryFailures {
- // retry failed networks always (the old default)
- ALWAYS,
- // retry only when a substantial change has occurred. Either:
- // 1) we were restricted by voice/data concurrency and aren't anymore
- // 2) our apn list has change
- ONLY_ON_CHANGE
- };
-
- protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
- if (VDBG) log("setupDataOnAllConnectableApns: " + reason);
-
- if (DBG && !VDBG) {
- StringBuilder sb = new StringBuilder(120);
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- sb.append(apnContext.getApnType());
- sb.append(":[state=");
- sb.append(apnContext.getState());
- sb.append(",enabled=");
- sb.append(apnContext.isEnabled());
- sb.append("] ");
- }
- log("setupDataOnAllConnectableApns: " + reason + " " + sb);
- }
-
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- setupDataOnConnectableApn(apnContext, reason, retryFailures);
- }
- }
-
- protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
- RetryFailures retryFailures) {
- if (VDBG) log("setupDataOnAllConnectableApns: apnContext " + apnContext);
-
- if (apnContext.getState() == DctConstants.State.FAILED
- || apnContext.getState() == DctConstants.State.RETRYING) {
- if (retryFailures == RetryFailures.ALWAYS) {
- apnContext.releaseDataConnection(reason);
- } else if (!apnContext.isConcurrentVoiceAndDataAllowed()
- && mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
- apnContext.releaseDataConnection(reason);
- }
- }
- if (apnContext.isConnectable()) {
- log("isConnectable() call trySetupData");
- apnContext.setReason(reason);
- trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
- }
- }
-
- private boolean shouldRestrictDataForEcbm() {
- boolean isInEcm = mPhone.isInEcm();
- boolean isInImsEcm = mPhone.getImsPhone() != null && mPhone.getImsPhone().isInImsEcm();
- log("shouldRestrictDataForEcbm: isInEcm=" + isInEcm + " isInImsEcm=" + isInImsEcm);
- return isInEcm && !isInImsEcm;
- }
-
- private boolean isHandoverPending(@ApnType int apnType) {
- List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
- return messageList != null && messageList.size() > 0;
- }
-
- private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType,
- @Nullable Message onHandoverCompleteMsg) {
- if (onHandoverCompleteMsg != null) {
- addHandoverCompleteMsg(onHandoverCompleteMsg, apnContext.getApnTypeBitmask());
- }
-
- if (mPhone.getSimulatedRadioControl() != null) {
- // Assume data is connected on the simulator
- log("trySetupData: X We're on the simulator; assuming connected retValue=true");
- return;
- }
-
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons);
- String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
- + apnContext.getReason() + ", requestType=" + requestTypeToString(requestType)
- + ". " + dataConnectionReasons.toString();
- if (dataConnectionReasons.contains(DataDisallowedReasonType.DISABLED_BY_QNS)
- || dataConnectionReasons.contains(DataDisallowedReasonType.ON_OTHER_TRANSPORT)) {
- logStr += ", current transport=" + AccessNetworkConstants.transportTypeToString(
- mPhone.getAccessNetworksManager().getCurrentTransport(
- apnContext.getApnTypeBitmask()));
- logStr += ", preferred transport=" + AccessNetworkConstants.transportTypeToString(
- mPhone.getAccessNetworksManager().getPreferredTransport(
- apnContext.getApnTypeBitmask()));
- }
- if (DBG) log(logStr);
- ApnContext.requestLog(apnContext, logStr);
- if (!isDataAllowed) {
- StringBuilder str = new StringBuilder();
-
- str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
- + ", mState=" + apnContext.getState() + ", apnEnabled="
- + apnContext.isEnabled() + ", mDependencyMet="
- + apnContext.isDependencyMet() + "] ");
-
- if (!mDataEnabledSettings.isDataEnabled()) {
- str.append("isDataEnabled() = false. " + mDataEnabledSettings);
- }
-
- // Check if it fails because of the existing data is still disconnecting.
- if (dataConnectionReasons.contains(DataDisallowedReasonType.DATA_IS_DISCONNECTING)
- && isHandoverPending(apnContext.getApnTypeBitmask())) {
- // Normally we don't retry when isDataAllow() returns false, because that's consider
- // pre-condition not met, for example, data not enabled by the user, or airplane
- // mode is on. If we retry in those cases, there will be significant power impact.
- // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover
- // case only.
- log("Data is disconnecting. Will retry handover later.");
- return;
- }
-
- // If this is a data retry, we should set the APN state to FAILED so it won't stay
- // in RETRYING forever.
- if (apnContext.getState() == DctConstants.State.RETRYING) {
- apnContext.setState(DctConstants.State.FAILED);
- str.append(" Stop retrying.");
- }
-
- if (DBG) log(str.toString());
- ApnContext.requestLog(apnContext, str.toString());
- if (requestType == REQUEST_TYPE_HANDOVER) {
- // If fails due to latest preference already changed back to source transport, then
- // just fallback (will not attempt handover anymore, and will not tear down the
- // data connection on source transport.
- boolean fallback = dataConnectionReasons.contains(
- DataDisallowedReasonType.ON_OTHER_TRANSPORT);
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, fallback);
- }
- return;
- }
-
- if (apnContext.getState() == DctConstants.State.FAILED) {
- String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
- apnContext.setState(DctConstants.State.IDLE);
- }
- int radioTech = getDataRat();
- if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState()
- .getState() == ServiceState.STATE_IN_SERVICE) {
- radioTech = getVoiceRat();
- }
- log("service state=" + mPhone.getServiceState());
- apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
- .isConcurrentVoiceAndDataAllowed());
- if (apnContext.getState() == DctConstants.State.IDLE) {
- ArrayList<ApnSetting> waitingApns =
- buildWaitingApns(apnContext.getApnType(), radioTech);
- if (waitingApns.isEmpty()) {
- String str = "trySetupData: X No APN found retValue=false";
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
- if (requestType == REQUEST_TYPE_HANDOVER) {
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
- false);
- }
- return;
- } else {
- apnContext.setWaitingApns(waitingApns);
- if (DBG) {
- log("trySetupData: Create from mAllApnSettings : "
- + apnListToString(mAllApnSettings));
- }
- }
- }
-
- if (!setupData(apnContext, radioTech, requestType)
- && requestType == REQUEST_TYPE_HANDOVER) {
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
- }
- }
-
- /**
- * Clean up all data connections. Note this is just detach the APN context from the data
- * connection. After all APN contexts are detached from the data connection, the data
- * connection will be torn down.
- *
- * @param reason Reason for the clean up.
- */
- public void cleanUpAllConnections(String reason) {
- log("cleanUpAllConnections");
- Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
- msg.obj = reason;
- sendMessage(msg);
- }
-
- /**
- * Clean up all data connections by detaching the APN contexts from the data connections, which
- * eventually tearing down all data connections after all APN contexts are detached from the
- * data connections.
- *
- * @param detach {@code true} if detaching APN context from the underlying data connection (when
- * no other APN context is attached to the data connection, the data connection will be torn
- * down.) {@code false} to only reset the data connection's state machine.
- *
- * @param reason reason for the clean up.
- * @return boolean - true if we did cleanup any connections, false if they
- * were already all disconnected.
- */
- private boolean cleanUpAllConnectionsInternal(boolean detach, String reason) {
- if (DBG) log("cleanUpAllConnectionsInternal: detach=" + detach + " reason=" + reason);
- boolean didDisconnect = false;
- boolean disableMeteredOnly = false;
-
- // reasons that only metered apn will be torn down
- if (!TextUtils.isEmpty(reason)) {
- disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
- reason.equals(Phone.REASON_ROAMING_ON) ||
- reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
- }
-
- for (ApnContext apnContext : mApnContexts.values()) {
- // Exclude the IMS APN from single data connection case.
- if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
- && apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
- continue;
- }
-
- if (shouldCleanUpConnection(apnContext, disableMeteredOnly,
- reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION))) {
- // TODO - only do cleanup if not disconnected
- if (apnContext.isDisconnected() == false) didDisconnect = true;
- apnContext.setReason(reason);
- cleanUpConnectionInternal(detach, RELEASE_TYPE_DETACH, apnContext);
- } else if (DBG) {
- log("cleanUpAllConnectionsInternal: APN type " + apnContext.getApnType()
- + " shouldn't be cleaned up.");
- }
- }
-
- stopNetStatPoll();
- stopDataStallAlarm();
-
- // TODO: Do we need mRequestedApnType?
- mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
- if (areAllDataDisconnected()) {
- notifyAllDataDisconnected();
- }
-
- return didDisconnect;
- }
-
- boolean shouldCleanUpConnection(ApnContext apnContext, boolean disableMeteredOnly,
- boolean singlePdn) {
- if (apnContext == null) return false;
-
- // If APN setting is not null and the reason is single PDN arbitration, clean up connection.
- ApnSetting apnSetting = apnContext.getApnSetting();
- if (apnSetting != null && singlePdn) return true;
-
- // If meteredOnly is false, clean up all connections.
- if (!disableMeteredOnly) return true;
-
- // If meteredOnly is true, and apnSetting is null or it's un-metered, no need to clean up.
- if (apnSetting == null || !ApnSettingUtils.isMetered(apnSetting, mPhone)) return false;
-
- boolean isRoaming = mPhone.getServiceState().getDataRoaming();
- boolean isDataRoamingDisabled = !getDataRoamingEnabled();
- boolean isDataDisabled = !mDataEnabledSettings.isDataEnabled(
- apnSetting.getApnTypeBitmask());
-
- // Should clean up if its data is disabled, or data roaming is disabled while roaming.
- return isDataDisabled || (isRoaming && isDataRoamingDisabled);
- }
-
- /**
- * Detach the APN context from the associated data connection. This data connection might be
- * torn down if no other APN context is attached to it.
- *
- * @param apnContext The APN context to be detached
- */
- void cleanUpConnection(ApnContext apnContext) {
- if (DBG) log("cleanUpConnection: apnContext=" + apnContext);
- Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION);
- msg.arg2 = 0;
- msg.obj = apnContext;
- sendMessage(msg);
- }
-
- /**
- * Detach the APN context from the associated data connection. This data connection will be
- * torn down if no other APN context is attached to it.
- *
- * @param detach {@code true} if detaching APN context from the underlying data connection (when
- * no other APN context is attached to the data connection, the data connection will be torn
- * down.) {@code false} to only reset the data connection's state machine.
- * @param releaseType Data release type.
- * @param apnContext The APN context to be detached.
- */
- private void cleanUpConnectionInternal(boolean detach, @ReleaseNetworkType int releaseType,
- ApnContext apnContext) {
- if (apnContext == null) {
- if (DBG) log("cleanUpConnectionInternal: apn context is null");
- return;
- }
-
- DataConnection dataConnection = apnContext.getDataConnection();
- String str = "cleanUpConnectionInternal: detach=" + detach + " reason="
- + apnContext.getReason();
- if (VDBG) log(str + " apnContext=" + apnContext);
- ApnContext.requestLog(apnContext, str);
- if (detach) {
- if (apnContext.isDisconnected()) {
- // The request is detach and but ApnContext is not connected.
- // If apnContext is not enabled anymore, break the linkage to the data connection.
- apnContext.releaseDataConnection("");
- } else {
- // Connection is still there. Try to clean up.
- if (dataConnection != null) {
- if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
- boolean disconnectAll = false;
- if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
- && ServiceState.isCdma(getDataRat())) {
- if (DBG) {
- log("cleanUpConnectionInternal: disconnectAll DUN connection");
- }
- // For CDMA DUN, we need to tear it down immediately. A new data
- // connection will be reestablished with correct profile id.
- disconnectAll = true;
- }
- final int generation = apnContext.getConnectionGeneration();
- str = "cleanUpConnectionInternal: tearing down"
- + (disconnectAll ? " all" : "") + " using gen#" + generation;
- if (DBG) log(str + "apnContext=" + apnContext);
- ApnContext.requestLog(apnContext, str);
- Pair<ApnContext, Integer> pair = new Pair<>(apnContext, generation);
- Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);
-
- if (disconnectAll || releaseType == RELEASE_TYPE_HANDOVER) {
- dataConnection.tearDownAll(apnContext.getReason(), releaseType, msg);
- } else {
- dataConnection.tearDown(apnContext, apnContext.getReason(), msg);
- }
-
- apnContext.setState(DctConstants.State.DISCONNECTING);
- }
- } else {
- // apn is connected but no reference to the data connection.
- // Should not be happen, but reset the state in case.
- apnContext.setState(DctConstants.State.IDLE);
- ApnContext.requestLog(
- apnContext, "cleanUpConnectionInternal: connected, bug no dc");
- }
- }
- } else {
- // force clean up the data connection.
- if (dataConnection != null) dataConnection.reset();
- apnContext.setState(DctConstants.State.IDLE);
- apnContext.setDataConnection(null);
- }
-
- // If there is any outstanding handover request, we need to respond it.
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
-
- // Make sure reconnection alarm is cleaned up if there is no ApnContext
- // associated to the connection.
- if (dataConnection != null) {
- cancelReconnect(apnContext);
- }
- str = "cleanUpConnectionInternal: X detach=" + detach + " reason="
- + apnContext.getReason();
- if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
- }
-
- private Cursor getPreferredApnCursor(int subId) {
- Cursor cursor = null;
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- cursor = mPhone.getContext().getContentResolver().query(
- Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID,
- String.valueOf(subId)), null, null, null,
- Telephony.Carriers.DEFAULT_SORT_ORDER);
- }
- return cursor;
- }
-
- private ApnSetting getPreferredApnFromDB() {
- ApnSetting preferredApn = null;
- Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
- if (cursor != null) {
- if (cursor.getCount() > 0) {
- cursor.moveToFirst();
- preferredApn = ApnSetting.makeApnSetting(cursor);
- }
- cursor.close();
- }
- if (VDBG) log("getPreferredApnFromDB: preferredApn=" + preferredApn);
- return preferredApn;
- }
-
- private void setDefaultPreferredApnIfNeeded() {
- ApnSetting defaultPreferredApn = null;
- PersistableBundle bundle = getCarrierConfig();
- String defaultPreferredApnName = bundle.getString(CarrierConfigManager
- .KEY_DEFAULT_PREFERRED_APN_NAME_STRING);
-
- if (TextUtils.isEmpty(defaultPreferredApnName) || getPreferredApnFromDB() != null) {
- return;
- }
-
- String selection = Telephony.Carriers.APN + " = \"" + defaultPreferredApnName + "\" AND "
- + Telephony.Carriers.EDITED_STATUS + " = " + Telephony.Carriers.UNEDITED;
- Cursor cursor = mPhone.getContext().getContentResolver().query(
- Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
- "filtered/subId/" + mPhone.getSubId()),
- null, selection, null, Telephony.Carriers._ID);
-
- if (cursor != null) {
- if (cursor.getCount() > 0) {
- if (cursor.moveToFirst()) {
- defaultPreferredApn = ApnSetting.makeApnSetting(cursor);
- }
- }
- cursor.close();
- }
-
- if (defaultPreferredApn != null
- && defaultPreferredApn.canHandleType(mRequestedApnType)) {
- log("setDefaultPreferredApnIfNeeded: For APN type "
- + ApnSetting.getApnTypeString(mRequestedApnType)
- + " found default apnSetting "
- + defaultPreferredApn);
-
- setPreferredApn(defaultPreferredApn.getId(), true);
- }
-
- return;
- }
-
- /**
- * Check if preferred apn is allowed to edit by user.
- * @return {@code true} if it is allowed to edit.
- */
- @VisibleForTesting
- public boolean isPreferredApnUserEdited() {
- boolean isUserEdited = false;
- Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
- if (cursor != null) {
- if (cursor.getCount() > 0) {
- if (cursor.moveToFirst()) {
- isUserEdited = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.EDITED_STATUS))
- == Telephony.Carriers.USER_EDITED;
- }
- }
- cursor.close();
- }
- if (VDBG) log("isPreferredApnUserEdited: isUserEdited=" + isUserEdited);
- return isUserEdited;
- }
-
- /**
- * Fetch the DUN apns
- * @return a list of DUN ApnSetting objects
- */
- @VisibleForTesting
- public @NonNull ArrayList<ApnSetting> fetchDunApns() {
- if (mPhone.getServiceState().getRoaming() && !isPreferredApnUserEdited()
- && getCarrierConfig().getBoolean(CarrierConfigManager
- .KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL)) {
- if (VDBG) log("fetchDunApns: Dun apn is not used in roaming network");
- return new ArrayList<ApnSetting>(0);
- }
-
- int bearer = getDataRat();
- ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
- ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
-
- if (dunCandidates.isEmpty()) {
- if (!ArrayUtils.isEmpty(mAllApnSettings)) {
- for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(ApnSetting.TYPE_DUN)) {
- dunCandidates.add(apn);
- }
- }
- if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates);
- }
- }
-
- int preferredApnSetId = getPreferredApnSetId();
- ApnSetting preferredApn = getPreferredApnFromDB();
- for (ApnSetting dunSetting : dunCandidates) {
- if (dunSetting.canSupportNetworkType(
- ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
- if (preferredApnSetId == dunSetting.getApnSetId()) {
- if (preferredApn != null && preferredApn.equals(dunSetting)) {
- // If there is a preferred APN can handled DUN type, prepend it to list to
- // use it preferred.
- retDunSettings.add(0, dunSetting);
- } else {
- retDunSettings.add(dunSetting);
- }
- }
- }
- }
-
- if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
- return retDunSettings;
- }
-
- private int getPreferredApnSetId() {
- // preferapnset uri returns all APNs for the current carrier which have an apn_set_id
- // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id,
- // the query will return null)
- Cursor c = mPhone.getContext().getContentResolver()
- .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
- "preferapnset/subId/" + mPhone.getSubId()),
- new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null);
- if (c == null) {
- loge("getPreferredApnSetId: cursor is null");
- return Telephony.Carriers.NO_APN_SET_ID;
- }
-
- int setId;
- if (c.getCount() < 1) {
- loge("getPreferredApnSetId: no APNs found");
- setId = Telephony.Carriers.NO_APN_SET_ID;
- } else {
- c.moveToFirst();
- setId = c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
- }
-
- if (!c.isClosed()) {
- c.close();
- }
- return setId;
- }
-
- public boolean hasMatchedTetherApnSetting() {
- ArrayList<ApnSetting> matches = fetchDunApns();
- log("hasMatchedTetherApnSetting: APNs=" + matches);
- return matches.size() > 0;
- }
-
- /**
- * @return the {@link DataConnection} with the given context id {@code cid}.
- */
- public DataConnection getDataConnectionByContextId(int cid) {
- return mDcc.getActiveDcByCid(cid);
- }
-
- /**
- * @return the {@link DataConnection} with the given APN context. Null if no data connection
- * is found.
- */
- public @Nullable DataConnection getDataConnectionByApnType(String apnType) {
- // TODO: Clean up all APN type in string usage
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext != null) {
- return apnContext.getDataConnection();
- }
- return null;
- }
-
- /**
- * Check if the data fail cause is a permanent failure (i.e. Frameworks will not retry data
- * setup).
- *
- * @param dcFailCause The data fail cause
- * @return {@code true} if the data fail cause is a permanent failure.
- */
- @VisibleForTesting
- public boolean isPermanentFailure(@DataFailureCause int dcFailCause) {
- return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause,
- mPhone.getSubId())
- && (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST));
- }
-
- private DataConnection findFreeDataConnection() {
- for (DataConnection dataConnection : mDataConnections.values()) {
- boolean inUse = false;
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.getDataConnection() == dataConnection) {
- inUse = true;
- break;
- }
- }
- if (!inUse) {
- if (DBG) {
- log("findFreeDataConnection: found free DataConnection=" + dataConnection);
- }
- return dataConnection;
- }
- }
- log("findFreeDataConnection: NO free DataConnection");
- return null;
- }
-
- /**
- * Setup a data connection based on given APN type.
- *
- * @param apnContext APN context
- * @param radioTech RAT of the data connection
- * @param requestType Data request type
- * @return True if successful, otherwise false.
- */
- private boolean setupData(ApnContext apnContext, int radioTech,
- @RequestNetworkType int requestType) {
- if (DBG) {
- log("setupData: apnContext=" + apnContext + ", requestType="
- + requestTypeToString(requestType));
- }
- ApnContext.requestLog(
- apnContext, "setupData. requestType=" + requestTypeToString(requestType));
- ApnSetting apnSetting;
- DataConnection dataConnection = null;
-
- apnSetting = apnContext.getNextApnSetting();
-
- if (apnSetting == null) {
- if (DBG) log("setupData: return for no apn found!");
- return false;
- }
-
- // profile id is only meaningful when the profile is persistent on the modem.
- int profileId = DATA_PROFILE_INVALID;
- if (apnSetting.isPersistent()) {
- profileId = apnSetting.getProfileId();
- if (profileId == DATA_PROFILE_DEFAULT) {
- profileId = getApnProfileID(apnContext.getApnType());
- }
- }
-
- // On CDMA, if we're explicitly asking for DUN, we need have
- // a dun-profiled connection so we can't share an existing one
- // On GSM/LTE we can share existing apn connections provided they support
- // this type.
- // If asking for ENTERPRISE, there are no compatible data connections, so skip this check
- if ((apnContext.getApnTypeBitmask() != ApnSetting.TYPE_DUN
- || ServiceState.isGsm(getDataRat()))
- && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE) {
- dataConnection = checkForCompatibleDataConnection(apnContext, apnSetting);
- if (dataConnection != null) {
- // Get the apn setting used by the data connection
- ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting();
- if (dataConnectionApnSetting != null) {
- // Setting is good, so use it.
- apnSetting = dataConnectionApnSetting;
- }
- }
- }
- if (dataConnection == null) {
- if (isOnlySingleDcAllowed(radioTech)) {
- if (isHigherPriorityApnContextActive(apnContext)) {
- if (DBG) {
- log("setupData: Higher priority ApnContext active. Ignoring call");
- }
- return false;
- }
-
- // Should not start cleanUp if the setupData is for IMS APN
- // or retry of same APN(State==RETRYING).
- if (!apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)
- && (apnContext.getState() != DctConstants.State.RETRYING)) {
- // Only lower priority calls left. Disconnect them all in this single PDP case
- // so that we can bring up the requested higher priority call (once we receive
- // response for deactivate request for the calls we are about to disconnect
- if (cleanUpAllConnectionsInternal(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
- // If any call actually requested to be disconnected, means we can't
- // bring up this connection yet as we need to wait for those data calls
- // to be disconnected.
- if (DBG) log("setupData: Some calls are disconnecting first."
- + " Wait and retry");
- return false;
- }
- }
-
- // No other calls are active, so proceed
- if (DBG) log("setupData: Single pdp. Continue setting up data call.");
- }
-
- dataConnection = findFreeDataConnection();
-
- if (dataConnection == null) {
- dataConnection = createDataConnection();
- }
-
- if (dataConnection == null) {
- if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
- return false;
- }
- }
- final int generation = apnContext.incAndGetConnectionGeneration();
- if (DBG) {
- log("setupData: dc=" + dataConnection + " apnSetting=" + apnSetting + " gen#="
- + generation);
- }
-
- apnContext.setDataConnection(dataConnection);
- apnContext.setApnSetting(apnSetting);
- apnContext.setState(DctConstants.State.CONNECTING);
-
- Message msg = obtainMessage();
- msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
- msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
-
- ApnSetting preferredApn = getPreferredApn();
- boolean isPreferredApn = apnSetting.equals(preferredApn);
- dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation, requestType,
- mPhone.getSubId(), isPreferredApn);
-
- if (DBG) {
- if (isPreferredApn) {
- log("setupData: initing! isPreferredApn=" + isPreferredApn
- + ", apnSetting={" + apnSetting.toString() + "}");
- } else {
- String preferredApnStr = preferredApn == null ? "null" : preferredApn.toString();
- log("setupData: initing! isPreferredApn=" + isPreferredApn
- + ", apnSetting={" + apnSetting + "}"
- + ", preferredApn={" + preferredApnStr + "}");
- }
- }
- return true;
- }
-
- // Get the allowed APN types for initial attach. The order in the returned list represent
- // the order of APN types that should be used for initial attach.
- private @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() {
- PersistableBundle bundle = getCarrierConfig();
- if (bundle != null) {
- String[] apnTypesArray = bundle.getStringArray(
- CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY);
- if (apnTypesArray != null) {
- return Arrays.stream(apnTypesArray)
- .map(ApnSetting::getApnTypesBitmaskFromString)
- .collect(Collectors.toList());
- }
- }
-
- return Collections.emptyList();
- }
-
- protected void setInitialAttachApn() {
- ApnSetting apnSetting = null;
- int preferredApnSetId = getPreferredApnSetId();
- ArrayList<ApnSetting> allApnSettings = new ArrayList<>();
- if (mPreferredApn != null) {
- // Put the preferred apn at the beginning of the list. It's okay to have a duplicate
- // when later on mAllApnSettings get added. That would not change the selection result.
- allApnSettings.add(mPreferredApn);
- }
- allApnSettings.addAll(mAllApnSettings);
-
- // Get the allowed APN types for initial attach. Note that if none of the APNs has the
- // allowed APN types, then the initial attach will not be performed.
- List<Integer> allowedApnTypes = getAllowedInitialAttachApnTypes();
- for (int allowedApnType : allowedApnTypes) {
- apnSetting = allApnSettings.stream()
- .filter(apn -> apn.canHandleType(allowedApnType))
- .filter(apn -> (apn.getApnSetId() == preferredApnSetId
- || apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID))
- .findFirst()
- .orElse(null);
- if (apnSetting != null) break;
- }
-
- if (DBG) {
- log("setInitialAttachApn: Allowed APN types=" + allowedApnTypes.stream()
- .map(ApnSetting::getApnTypeString)
- .collect(Collectors.joining(",")));
- }
-
- if (apnSetting == null) {
- if (DBG) log("setInitialAttachApn: X There in no available apn.");
- } else {
- if (DBG) log("setInitialAttachApn: X selected APN=" + apnSetting);
- mDataServiceManager.setInitialAttachApn(new DataProfile.Builder()
- .setApnSetting(apnSetting)
- .setPreferred(apnSetting.equals(getPreferredApn()))
- .build(),
- mPhone.getServiceState().getDataRoamingFromRegistration(), null);
- }
- }
-
- /**
- * Handles changes to the APN database.
- */
- private void onApnChanged() {
- if (mPhone instanceof GsmCdmaPhone) {
- // The "current" may no longer be valid. MMS depends on this to send properly. TBD
- ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
- }
-
- // TODO: It'd be nice to only do this if the changed entrie(s)
- // match the current operator.
- if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
- mDataThrottler.reset();
- setDefaultPreferredApnIfNeeded();
- createAllApnList();
- setDataProfilesAsNeeded();
- setInitialAttachApn();
- cleanUpConnectionsOnUpdatedApns(isAnyDataConnected(), Phone.REASON_APN_CHANGED);
-
- // FIXME: See bug 17426028 maybe no conditional is needed.
- if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
- setupDataOnAllConnectableApns(Phone.REASON_APN_CHANGED, RetryFailures.ALWAYS);
- }
- }
-
- /**
- * "Active" here means ApnContext isEnabled() and not in FAILED state
- * @param apnContext to compare with
- * @return true if higher priority active apn found
- */
- private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
- if (apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
- return false;
- }
-
- for (ApnContext otherContext : mPrioritySortedApnContexts) {
- if (otherContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
- continue;
- }
- if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
- if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Reports if we support multiple connections or not.
- * This is a combination of factors, based on carrier and RAT.
- * @param rilRadioTech the RIL Radio Tech currently in use
- * @return true if only single DataConnection is allowed
- */
- private boolean isOnlySingleDcAllowed(int rilRadioTech) {
- int networkType = ServiceState.rilRadioTechnologyToNetworkType(rilRadioTech);
- // Default single dc rats with no knowledge of carrier
- int[] singleDcRats = null;
- // get the carrier specific value, if it exists, from CarrierConfigManager.
- // generally configManager and bundle should not be null, but if they are it should be okay
- // to leave singleDcRats null as well
- CarrierConfigManager configManager = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
- if (bundle != null) {
- singleDcRats = bundle.getIntArray(
- CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY);
- }
- }
- boolean onlySingleDcAllowed = false;
- if (TelephonyUtils.IS_DEBUGGABLE
- && SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
- onlySingleDcAllowed = true;
- }
- if (singleDcRats != null) {
- for (int i = 0; i < singleDcRats.length && !onlySingleDcAllowed; i++) {
- if (networkType == singleDcRats[i]) {
- onlySingleDcAllowed = true;
- }
- }
- }
-
- if (DBG) {
- log("isOnlySingleDcAllowed(" + TelephonyManager.getNetworkTypeName(networkType) + "): "
- + onlySingleDcAllowed);
- }
- return onlySingleDcAllowed;
- }
-
- void sendRestartRadio() {
- if (DBG)log("sendRestartRadio:");
- Message msg = obtainMessage(DctConstants.EVENT_RESTART_RADIO);
- sendMessage(msg);
- }
-
- private void restartRadio() {
- if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
- cleanUpAllConnectionsInternal(true, Phone.REASON_RADIO_TURNED_OFF);
- mPhone.getServiceStateTracker().powerOffRadioSafely();
- /* Note: no need to call setRadioPower(true). Assuming the desired
- * radio power state is still ON (as tracked by ServiceStateTracker),
- * ServiceStateTracker will call setRadioPower when it receives the
- * RADIO_STATE_CHANGED notification for the power off. And if the
- * desired power state has changed in the interim, we don't want to
- * override it with an unconditional power on.
- */
- }
-
- /**
- * Return true if data connection need to be setup after disconnected due to
- * reason.
- *
- * @param apnContext APN context
- * @return true if try setup data connection is need for this reason
- */
- private boolean retryAfterDisconnected(ApnContext apnContext) {
- boolean retry = true;
- String reason = apnContext.getReason();
-
- if (Phone.REASON_RADIO_TURNED_OFF.equals(reason) || (isOnlySingleDcAllowed(getDataRat())
- && isHigherPriorityApnContextActive(apnContext))) {
- retry = false;
- }
- return retry;
- }
-
- protected void startReconnect(long delay, ApnContext apnContext,
- @RequestNetworkType int requestType) {
- apnContext.setState(DctConstants.State.RETRYING);
- Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getSubId(), requestType, apnContext);
- cancelReconnect(apnContext);
-
- // Wait a bit before trying the next APN, so that
- // we're not tying up the RIL command channel
- sendMessageDelayed(msg, delay);
-
- if (DBG) {
- log("startReconnect: delay=" + delay + ", apn="
- + apnContext + ", reason=" + apnContext.getReason()
- + ", subId=" + mPhone.getSubId() + ", request type="
- + requestTypeToString(requestType));
- }
- }
-
- /**
- * Cancels the alarm associated with apnContext.
- *
- * @param apnContext on which the alarm should be stopped.
- */
- protected void cancelReconnect(ApnContext apnContext) {
- if (apnContext == null) return;
-
- if (DBG) {
- log("cancelReconnect: apn=" + apnContext);
- }
- removeMessages(DctConstants.EVENT_DATA_RECONNECT, apnContext);
- }
-
- /**
- * Read configuration. Note this must be called after carrier config is ready.
- */
- private void readConfiguration() {
- log("readConfiguration");
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- // Auto attach is for cellular only.
- mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
- .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
- }
-
- mAutoAttachEnabled.set(false);
- setDefaultPreferredApnIfNeeded();
- read5GConfiguration();
- registerSettingsObserver();
- SubscriptionPlan[] plans = mNetworkPolicyManager.getSubscriptionPlans(
- mPhone.getSubId(), mPhone.getContext().getOpPackageName());
- mSubscriptionPlans = plans == null ? Collections.emptyList() : Arrays.asList(plans);
- if (DBG) log("SubscriptionPlans initialized: " + mSubscriptionPlans);
- reevaluateUnmeteredConnections();
- mConfigReady = true;
- }
-
- /**
- * @return {@code true} if carrier config has been applied.
- */
- private boolean isCarrierConfigApplied() {
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
- if (b != null) {
- return CarrierConfigManager.isConfigForIdentifiedCarrier(b);
- }
- }
- return false;
- }
-
- private void onCarrierConfigChanged() {
- if (DBG) log("onCarrierConfigChanged");
-
- if (!isCarrierConfigApplied()) {
- log("onCarrierConfigChanged: Carrier config is not ready yet.");
- return;
- }
-
- readConfiguration();
-
- if (mSimState == TelephonyManager.SIM_STATE_LOADED) {
- setDefaultDataRoamingEnabled();
- createAllApnList();
- setDataProfilesAsNeeded();
- setInitialAttachApn();
- sortApnContextByPriority();
- cleanUpConnectionsOnUpdatedApns(true, Phone.REASON_CARRIER_CHANGE);
- setupDataOnAllConnectableApns(Phone.REASON_CARRIER_CHANGE, RetryFailures.ALWAYS);
- } else {
- log("onCarrierConfigChanged: SIM is not loaded yet.");
- }
- }
-
- private void onSimAbsent() {
- if (DBG) log("onSimAbsent");
-
- mConfigReady = false;
- cleanUpAllConnectionsInternal(true, Phone.REASON_SIM_NOT_READY);
- mAllApnSettings.clear();
- mAutoAttachOnCreationConfig = false;
- // Clear auto attach as modem is expected to do a new attach once SIM is ready
- mAutoAttachEnabled.set(false);
- // In no-sim case, we should still send the emergency APN to the modem, if there is any.
- createAllApnList();
- setDataProfilesAsNeeded();
- }
-
- private void onSimStateUpdated(@SimState int simState) {
- mSimState = simState;
-
- if (DBG) {
- log("onSimStateUpdated: state=" + SubscriptionInfoUpdater.simStateString(mSimState));
- }
-
- if (mSimState == TelephonyManager.SIM_STATE_ABSENT) {
- onSimAbsent();
- } else if (mSimState == TelephonyManager.SIM_STATE_LOADED) {
- mDataThrottler.reset();
- if (mConfigReady) {
- createAllApnList();
- setDataProfilesAsNeeded();
- setInitialAttachApn();
- setupDataOnAllConnectableApns(Phone.REASON_SIM_LOADED, RetryFailures.ALWAYS);
- } else {
- log("onSimStateUpdated: config not ready yet.");
- }
- }
- }
-
- private void onApnUnthrottled(String apn) {
- if (apn != null) {
- ApnSetting apnSetting = mAllApnSettings.stream()
- .filter(as -> apn.equals(as.getApnName()))
- .findFirst()
- .orElse(null);
- if (apnSetting != null) {
- @ApnType int apnTypes = apnSetting.getApnTypeBitmask();
- mDataThrottler.setRetryTime(apnTypes, RetryManager.NO_SUGGESTED_RETRY_DELAY,
- REQUEST_TYPE_NORMAL);
- } else {
- loge("EVENT_APN_UNTHROTTLED: Invalid APN passed: " + apn);
- }
- } else {
- loge("EVENT_APN_UNTHROTTLED: apn is null");
- }
- }
-
- private void onTrafficDescriptorsUpdated() {
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
- && apnContext.getApnSetting().getPermanentFailed()) {
- setupDataOnConnectableApn(
- apnContext, Phone.REASON_TRAFFIC_DESCRIPTORS_UPDATED, RetryFailures.ALWAYS);
- }
- }
- }
-
- private DataConnection checkForCompatibleDataConnection(ApnContext apnContext,
- ApnSetting nextApn) {
- int apnType = apnContext.getApnTypeBitmask();
- ArrayList<ApnSetting> dunSettings = null;
-
- if (ApnSetting.TYPE_DUN == apnType) {
- dunSettings = fetchDunApns();
- }
- if (DBG) {
- log("checkForCompatibleDataConnection: apnContext=" + apnContext);
- }
-
- DataConnection potentialDc = null;
- for (DataConnection curDc : mDataConnections.values()) {
- if (curDc != null) {
- ApnSetting apnSetting = curDc.getApnSetting();
- log("apnSetting: " + apnSetting);
- if (dunSettings != null && dunSettings.size() > 0) {
- for (ApnSetting dunSetting : dunSettings) {
- //This ignore network type as a check which is ok because that's checked
- //when calculating dun candidates.
- if (areCompatible(dunSetting, apnSetting)) {
- if (curDc.isActive()) {
- if (DBG) {
- log("checkForCompatibleDataConnection:"
- + " found dun conn=" + curDc);
- }
- return curDc;
- } else if (curDc.isActivating()) {
- potentialDc = curDc;
- }
- }
- }
- } else if (isApnSettingCompatible(curDc, apnType)) {
- if (curDc.isActive()) {
- if (DBG) {
- log("checkForCompatibleDataConnection:"
- + " found canHandle conn=" + curDc);
- }
- return curDc;
- } else if (curDc.isActivating()
- || (apnSetting != null && apnSetting.equals(nextApn))) {
- potentialDc = curDc;
- }
- }
- }
- }
-
- if (DBG) {
- log("checkForCompatibleDataConnection: potential dc=" + potentialDc);
- }
- return potentialDc;
- }
-
- private boolean isApnSettingCompatible(DataConnection dc, int apnType) {
- ApnSetting apnSetting = dc.getApnSetting();
- if (apnSetting == null) return false;
-
- // Nothing can be compatible with type ENTERPRISE
- for (ApnContext apnContext : dc.getApnContexts()) {
- if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
- return false;
- }
- }
-
- return apnSetting.canHandleType(apnType);
- }
-
- private void addHandoverCompleteMsg(Message onCompleteMsg,
- @ApnType int apnType) {
- if (onCompleteMsg != null) {
- List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
- if (messageList == null) messageList = new ArrayList<>();
- messageList.add(onCompleteMsg);
- mHandoverCompletionMsgs.put(apnType, messageList);
- }
- }
-
- private void sendHandoverCompleteMessages(@ApnType int apnType, boolean success,
- boolean fallbackOnFailedHandover) {
- List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
- if (messageList != null) {
- for (Message msg : messageList) {
- sendHandoverCompleteMsg(msg, success, mTransportType, fallbackOnFailedHandover);
- }
- messageList.clear();
- }
- }
-
- private void sendHandoverCompleteMsg(Message message, boolean success,
- @TransportType int transport, boolean doFallbackOnFailedHandover) {
- if (message == null) return;
-
- Bundle b = message.getData();
- b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
- b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
- b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover);
- message.sendToTarget();
- }
-
- private static boolean shouldFallbackOnFailedHandover(
- @HandoverFailureMode int handoverFailureMode,
- @RequestNetworkType int requestType,
- @DataFailureCause int cause) {
- if (requestType != REQUEST_TYPE_HANDOVER) {
- //The fallback is only relevant if the request is a handover
- return false;
- } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_DO_FALLBACK) {
- return true;
- } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_LEGACY) {
- return cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED;
- } else {
- return false;
- }
- }
-
- /**
- * Calculates the new request type that will be used the next time a data connection retries
- * after a failed data call attempt.
- */
- @RequestNetworkType
- public static int calculateNewRetryRequestType(@HandoverFailureMode int handoverFailureMode,
- @RequestNetworkType int requestType,
- @DataFailureCause int cause) {
- boolean fallbackOnFailedHandover =
- shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause);
- if (requestType != REQUEST_TYPE_HANDOVER) {
- //The fallback is only relevant if the request is a handover
- return requestType;
- }
-
- if (fallbackOnFailedHandover) {
- // Since fallback is happening, the request type is really "NONE".
- return REQUEST_TYPE_NORMAL;
- }
-
- if (handoverFailureMode == HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL) {
- return REQUEST_TYPE_NORMAL;
- }
-
- return REQUEST_TYPE_HANDOVER;
- }
-
- public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType,
- Message onHandoverCompleteMsg) {
- sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType,
- onHandoverCompleteMsg));
- }
-
- private void onEnableApn(@ApnType int apnType, @RequestNetworkType int requestType,
- Message onHandoverCompleteMsg) {
- ApnContext apnContext = mApnContextsByType.get(apnType);
- if (apnContext == null) {
- loge("onEnableApn(" + apnType + "): NO ApnContext");
- if (onHandoverCompleteMsg != null) {
- sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
- }
- return;
- }
-
- String str = "onEnableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
- + ", request type=" + requestTypeToString(requestType);
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
-
- if (!apnContext.isDependencyMet()) {
- apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
- apnContext.setEnabled(true);
- str = "onEnableApn: dependency is not met.";
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
- if (onHandoverCompleteMsg != null) {
- sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
- }
- return;
- }
-
- if (apnContext.isReady()) {
- DctConstants.State state = apnContext.getState();
- switch(state) {
- case CONNECTING:
- if (onHandoverCompleteMsg != null) {
- if (DBG) {
- log("onEnableApn: already in CONNECTING state. Handover request "
- + "will be responded after connected.");
- }
- addHandoverCompleteMsg(onHandoverCompleteMsg, apnType);
- } else {
- if (DBG) log("onEnableApn: in CONNECTING state. Exit now.");
- }
- return;
- case CONNECTED:
- if (onHandoverCompleteMsg != null) {
- sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType,
- false);
- if (DBG) {
- log("onEnableApn: already in CONNECTED state. Consider as handover "
- + "succeeded");
- }
- } else {
- if (DBG) log("onEnableApn: APN in CONNECTED state. Exit now.");
- }
- return;
- case IDLE:
- case FAILED:
- case RETRYING:
- // We're "READY" but not active so disconnect (cleanup = true) and
- // connect (trySetup = true) to be sure we retry the connection.
- apnContext.setReason(Phone.REASON_DATA_ENABLED);
- break;
- }
- } else {
- if (apnContext.isEnabled()) {
- apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
- } else {
- apnContext.setReason(Phone.REASON_DATA_ENABLED);
- }
- if (apnContext.getState() == DctConstants.State.FAILED) {
- apnContext.setState(DctConstants.State.IDLE);
- }
- }
- apnContext.setEnabled(true);
- apnContext.resetErrorCodeRetries();
-
- if (mConfigReady || apnContext.getApnTypeBitmask() == ApnSetting.TYPE_EMERGENCY) {
- trySetupData(apnContext, requestType, onHandoverCompleteMsg);
- } else {
- log("onEnableApn: config not ready yet.");
- }
- }
-
- public void disableApn(@ApnType int apnType, @ReleaseNetworkType int releaseType) {
- sendMessage(obtainMessage(DctConstants.EVENT_DISABLE_APN, apnType, releaseType));
- }
-
- private void onDisableApn(@ApnType int apnType,
- @ReleaseNetworkType int releaseType) {
- ApnContext apnContext = mApnContextsByType.get(apnType);
- if (apnContext == null) {
- loge("disableApn(" + apnType + "): NO ApnContext");
- return;
- }
-
- boolean cleanup = false;
- String str = "onDisableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
- + ", release type=" + releaseTypeToString(releaseType);
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
-
- if (apnContext.isReady()) {
- cleanup = (releaseType == RELEASE_TYPE_DETACH
- || releaseType == RELEASE_TYPE_HANDOVER);
- if (apnContext.isDependencyMet()) {
- apnContext.setReason(Phone.REASON_DATA_DISABLED_INTERNAL);
- // If ConnectivityService has disabled this network, stop trying to bring
- // it up, but do not tear it down - ConnectivityService will do that
- // directly by talking with the DataConnection.
- //
- // This doesn't apply to DUN. When the user disable tethering, we would like to
- // detach the APN context from the data connection so the data connection can be
- // torn down if no other APN context attached to it.
- if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
- || apnContext.getState() != DctConstants.State.CONNECTED) {
- str = "Clean up the connection. Apn type = " + apnContext.getApnType()
- + ", state = " + apnContext.getState();
- if (DBG) log(str);
- ApnContext.requestLog(apnContext, str);
- cleanup = true;
- }
- } else {
- apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
- }
- }
-
- apnContext.setEnabled(false);
- if (cleanup) {
- cleanUpConnectionInternal(true, releaseType, apnContext);
- }
-
- if (isOnlySingleDcAllowed(getDataRat()) && !isHigherPriorityApnContextActive(apnContext)) {
- if (DBG) log("disableApn:isOnlySingleDcAllowed true & higher priority APN disabled");
- // If the highest priority APN is disabled and only single
- // data call is allowed, try to setup data call on other connectable APN.
- setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION,
- RetryFailures.ALWAYS);
- }
- }
-
- /**
- * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value for user modification only
- */
- public void setDataRoamingEnabledByUser(boolean enabled) {
- mDataEnabledSettings.setDataRoamingEnabled(enabled);
- setDataRoamingFromUserAction(true);
- if (DBG) {
- log("setDataRoamingEnabledByUser: set phoneSubId=" + mPhone.getSubId()
- + " isRoaming=" + enabled);
- }
- }
-
- /**
- * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
- */
- public boolean getDataRoamingEnabled() {
- boolean isDataRoamingEnabled = mDataEnabledSettings.getDataRoamingEnabled();
-
- if (VDBG) {
- log("getDataRoamingEnabled: phoneSubId=" + mPhone.getSubId()
- + " isDataRoamingEnabled=" + isDataRoamingEnabled);
- }
- return isDataRoamingEnabled;
- }
-
- /**
- * Set default value for {@link android.provider.Settings.Global#DATA_ROAMING}
- * if the setting is not from user actions. default value is based on carrier config and system
- * properties.
- */
- private void setDefaultDataRoamingEnabled() {
- // For single SIM phones, this is a per phone property.
- String setting = Settings.Global.DATA_ROAMING;
- boolean useCarrierSpecificDefault = false;
- if (mTelephonyManager.getSimCount() != 1) {
- setting = setting + mPhone.getSubId();
- try {
- Settings.Global.getInt(mResolver, setting);
- } catch (SettingNotFoundException ex) {
- // For msim, update to carrier default if uninitialized.
- useCarrierSpecificDefault = true;
- }
- } else if (!isDataRoamingFromUserAction()) {
- // for single sim device, update to carrier default if user action is not set
- useCarrierSpecificDefault = true;
- }
- log("setDefaultDataRoamingEnabled: useCarrierSpecificDefault "
- + useCarrierSpecificDefault);
- if (useCarrierSpecificDefault) {
- boolean defaultVal = mDataEnabledSettings.getDefaultDataRoamingEnabled();
- mDataEnabledSettings.setDataRoamingEnabled(defaultVal);
- }
- }
-
- private boolean isDataRoamingFromUserAction() {
- final SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(mPhone.getContext());
- // since we don't want to unset user preference from system update, pass true as the default
- // value if shared pref does not exist and set shared pref to false explicitly from factory
- // reset.
- if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
- sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
- }
- return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
- }
-
- private void setDataRoamingFromUserAction(boolean isUserAction) {
- final SharedPreferences.Editor sp = PreferenceManager
- .getDefaultSharedPreferences(mPhone.getContext()).edit();
- sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, isUserAction).commit();
- }
-
- // When the data roaming status changes from roaming to non-roaming.
- private void onDataRoamingOff() {
- if (DBG) log("onDataRoamingOff");
-
- reevaluateDataConnections();
-
- if (!getDataRoamingEnabled()) {
- // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn
- // attach and send the data profile again as the modem should have both roaming and
- // non-roaming protocol in place. Modem should choose the right protocol based on the
- // roaming condition.
- setDataProfilesAsNeeded();
- setInitialAttachApn();
-
- // If the user did not enable data roaming, now when we transit from roaming to
- // non-roaming, we should try to reestablish the data connection.
-
- setupDataOnAllConnectableApns(Phone.REASON_ROAMING_OFF, RetryFailures.ALWAYS);
- }
- }
-
- // This method is called
- // 1. When the data roaming status changes from non-roaming to roaming.
- // 2. When allowed data roaming settings is changed by the user.
- private void onDataRoamingOnOrSettingsChanged(int messageType) {
- if (DBG) log("onDataRoamingOnOrSettingsChanged");
- // Used to differentiate data roaming turned on vs settings changed.
- boolean settingChanged = (messageType == DctConstants.EVENT_ROAMING_SETTING_CHANGE);
-
- // Check if the device is actually data roaming
- if (!mPhone.getServiceState().getDataRoaming()) {
- if (DBG) log("device is not roaming. ignored the request.");
- return;
- }
-
- checkDataRoamingStatus(settingChanged);
-
- if (getDataRoamingEnabled()) {
- // If the restricted data was brought up when data roaming is disabled, and now users
- // enable data roaming, we need to re-evaluate the conditions and possibly change the
- // network's capability.
- if (settingChanged) {
- reevaluateDataConnections();
- }
-
- if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
-
- setupDataOnAllConnectableApns(Phone.REASON_ROAMING_ON, RetryFailures.ALWAYS);
- } else {
- // If the user does not turn on data roaming, when we transit from non-roaming to
- // roaming, we need to tear down the data connection otherwise the user might be
- // charged for data roaming usage.
- if (DBG) log("onDataRoamingOnOrSettingsChanged: Tear down data connection on roaming.");
- cleanUpAllConnectionsInternal(true, Phone.REASON_ROAMING_ON);
- }
- }
-
- // We want to track possible roaming data leakage. Which is, if roaming setting
- // is disabled, yet we still setup a roaming data connection or have a connected ApnContext
- // switched to roaming. When this happens, we log it in a local log.
- private void checkDataRoamingStatus(boolean settingChanged) {
- if (!settingChanged && !getDataRoamingEnabled()
- && mPhone.getServiceState().getDataRoaming()) {
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.getState() == DctConstants.State.CONNECTED) {
- mDataRoamingLeakageLog.log("PossibleRoamingLeakage "
- + " connection params: " + (apnContext.getDataConnection() != null
- ? apnContext.getDataConnection().getConnectionParams() : ""));
- }
- }
- }
- }
-
- private void onRadioAvailable() {
- if (DBG) log("onRadioAvailable");
- if (!areAllDataDisconnected()) {
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, null);
- }
- }
-
- private void onRadioOffOrNotAvailable() {
- // Make sure our reconnect delay starts at the initial value
- // next time the radio comes on
-
- mReregisterOnReconnectFailure = false;
-
- // Clear auto attach as modem is expected to do a new attach
- mAutoAttachEnabled.set(false);
-
- if (mPhone.getSimulatedRadioControl() != null) {
- // Assume data is connected on the simulator
- // FIXME this can be improved
- log("We're on the simulator; assuming radio off is meaningless");
- } else {
- if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
- cleanUpAllConnectionsInternal(false, Phone.REASON_RADIO_TURNED_OFF);
- }
- }
-
- private void completeConnection(ApnContext apnContext, @RequestNetworkType int type) {
-
- if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext);
-
- if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) {
- if (DBG) {
- log("completeConnection: MOBILE_PROVISIONING_ACTION url="
- + mProvisioningUrl);
- }
- Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
- Intent.CATEGORY_APP_BROWSER);
- newIntent.setData(Uri.parse(mProvisioningUrl));
- newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mPhone.getContext().startActivity(newIntent);
- } catch (ActivityNotFoundException e) {
- loge("completeConnection: startActivityAsUser failed" + e);
- }
- }
- mIsProvisioning = false;
- mProvisioningUrl = null;
- if (mProvisioningSpinner != null) {
- sendMessage(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
- mProvisioningSpinner));
- }
-
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-
- PersistableBundle b = getCarrierConfig();
- if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
- && b.getBoolean(CarrierConfigManager
- .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
- NotificationManager notificationManager = (NotificationManager)
- mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(Integer.toString(mPhone.getSubId()),
- NO_DATA_NOTIFICATION);
- }
- }
-
- /**
- * A SETUP (aka bringUp) has completed, possibly with an error. If
- * there is an error this method will call {@link #onDataSetupCompleteError}.
- */
- protected void onDataSetupComplete(ApnContext apnContext, boolean success,
- @DataFailureCause int cause, @RequestNetworkType int requestType,
- @HandoverFailureMode int handoverFailureMode) {
- boolean fallbackOnFailedHandover = shouldFallbackOnFailedHandover(
- handoverFailureMode, requestType, cause);
-
- if (success && (handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN
- && handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)) {
- Log.wtf(mLogTag, "bad failure mode: "
- + DataCallResponse.failureModeToString(handoverFailureMode));
- } else if (handoverFailureMode
- != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER
- && cause != DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE) {
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), success,
- fallbackOnFailedHandover);
- }
-
- if (success) {
- DataConnection dataConnection = apnContext.getDataConnection();
-
- if (RADIO_TESTS) {
- // Note: To change radio.test.onDSC.null.dcac from command line you need to
- // adb root and adb remount and from the command line you can only change the
- // value to 1 once. To change it a second time you can reboot or execute
- // adb shell stop and then adb shell start. The command line to set the value is:
- // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
- ContentResolver cr = mPhone.getContext().getContentResolver();
- String radioTestProperty = "radio.test.onDSC.null.dcac";
- if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
- log("onDataSetupComplete: " + radioTestProperty +
- " is true, set dcac to null and reset property to false");
- dataConnection = null;
- Settings.System.putInt(cr, radioTestProperty, 0);
- log("onDataSetupComplete: " + radioTestProperty + "=" +
- Settings.System.getInt(mPhone.getContext().getContentResolver(),
- radioTestProperty, -1));
- }
- }
- if (dataConnection == null) {
- log("onDataSetupComplete: no connection to DC, handle as error");
- onDataSetupCompleteError(apnContext, requestType, false);
- } else {
- ApnSetting apn = apnContext.getApnSetting();
- if (DBG) {
- log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
- : apn.getApnName()));
- }
-
- // everything is setup
- if (TextUtils.equals(apnContext.getApnType(), ApnSetting.TYPE_DEFAULT_STRING)
- && mCanSetPreferApn && mPreferredApn == null) {
- if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
- mPreferredApn = apn;
- if (mPreferredApn != null) {
- setPreferredApn(mPreferredApn.getId());
- }
- }
-
- // A connection is setup
- apnContext.setState(DctConstants.State.CONNECTED);
-
- checkDataRoamingStatus(false);
-
- boolean isProvApn = apnContext.isProvisioningApn();
- final ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- if (mProvisionBroadcastReceiver != null) {
- mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
- mProvisionBroadcastReceiver = null;
- }
-
- if ((!isProvApn) || mIsProvisioning) {
- if (mIsProvisioning) {
- // Hide any notification that was showing previously
- hideProvisioningNotification();
- }
-
- // Complete the connection normally notifying the world we're connected.
- // We do this if this isn't a special provisioning apn or if we've been
- // told its time to provision.
- completeConnection(apnContext, requestType);
- } else {
- // This is a provisioning APN that we're reporting as connected. Later
- // when the user desires to upgrade this to a "default" connection,
- // mIsProvisioning == true, we'll go through the code path above.
- // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING
- // is sent to the DCT.
- if (DBG) {
- log("onDataSetupComplete: successful, BUT send connected to prov apn as"
- + " mIsProvisioning:" + mIsProvisioning + " == false"
- + " && (isProvisioningApn:" + isProvApn + " == true");
- }
-
- // While radio is up, grab provisioning URL. The URL contains ICCID which
- // disappears when radio is off.
- mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
- mPhone.getMobileProvisioningUrl(),
- mTelephonyManager.getNetworkOperatorName());
- mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
- new IntentFilter(INTENT_PROVISION));
-
- // Put up user notification that sign-in is required.
- showProvisioningNotification();
-
- // Turn off radio to save battery and avoid wasting carrier resources.
- // The network isn't usable and network validation will just fail anyhow.
- setRadio(false);
- }
- if (DBG) {
- log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType());
- }
- if (TelephonyUtils.IS_DEBUGGABLE) {
- // adb shell setprop persist.radio.test.pco [pco_val]
- String radioTestProperty = "persist.radio.test.pco";
- int pcoVal = SystemProperties.getInt(radioTestProperty, -1);
- if (pcoVal != -1) {
- log("PCO testing: read pco value from persist.radio.test.pco " + pcoVal);
- final byte[] value = new byte[1];
- value[0] = (byte) pcoVal;
- final Intent intent =
- new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_DEFAULT);
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
- ApnSetting.PROTOCOL_IPV4V6);
- intent.putExtra(TelephonyManager.EXTRA_PCO_ID, 0xFF00);
- intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, value);
- mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
- }
- }
- }
- } else {
- if (DBG) {
- ApnSetting apn = apnContext.getApnSetting();
- log("onDataSetupComplete: error apn=" + apn.getApnName() + ", cause="
- + DataFailCause.toString(cause) + ", requestType="
- + requestTypeToString(requestType));
- }
- if (DataFailCause.isEventLoggable(cause)) {
- // Log this failure to the Event Logs.
- int cid = getCellLocationId();
- EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
- cause, cid, mTelephonyManager.getNetworkType());
- }
- ApnSetting apn = apnContext.getApnSetting();
-
- // Compose broadcast intent send to the specific carrier signaling receivers
- Intent intent = new Intent(TelephonyManager
- .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
- intent.putExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, cause);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
- ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType()));
- mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
-
- if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), cause, mPhone.getSubId())
- || apnContext.restartOnError(cause)) {
- if (DBG) log("Modem restarted.");
- sendRestartRadio();
- }
-
- // If the data call failure cause is a permanent failure, we mark the APN as permanent
- // failed.
- if (isPermanentFailure(cause)) {
- log("cause=" + DataFailCause.toString(cause)
- + ", mark apn as permanent failed. apn = " + apn);
- apnContext.markApnPermanentFailed(apn);
-
- PersistableBundle b = getCarrierConfig();
- if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
- && b.getBoolean(CarrierConfigManager
- .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
- NotificationManager notificationManager = (NotificationManager)
- mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
-
- CharSequence title = mPhone.getContext().getText(
- com.android.internal.R.string.RestrictedOnDataTitle);
- CharSequence details = mPhone.getContext().getText(
- com.android.internal.R.string.RestrictedStateContent);
-
- Notification notification = new Notification.Builder(mPhone.getContext(),
- NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
- .setTicker(title)
- .setColor(mPhone.getContext().getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setStyle(new Notification.BigTextStyle().bigText(details))
- .setContentText(details)
- .build();
- notificationManager.notify(Integer.toString(mPhone.getSubId()),
- NO_DATA_NOTIFICATION, notification);
- }
- }
-
- int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType,
- cause);
- onDataSetupCompleteError(apnContext, newRequestType, fallbackOnFailedHandover);
- }
- }
-
-
-
- /**
- * Error has occurred during the SETUP {aka bringUP} request and the DCT
- * should either try the next waiting APN or start over from the
- * beginning if the list is empty. Between each SETUP request there will
- * be a delay defined by {@link ApnContext#getDelayForNextApn(boolean)}.
- */
- protected void onDataSetupCompleteError(ApnContext apnContext,
- @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) {
- long delay = apnContext.getDelayForNextApn(mFailFast);
- // Check if we need to retry or not.
- if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallbackOnFailedHandover) {
- if (DBG) {
- log("onDataSetupCompleteError: APN type=" + apnContext.getApnType()
- + ". Request type=" + requestTypeToString(requestType) + ", Retry in "
- + delay + "ms.");
- }
- startReconnect(delay, apnContext, requestType);
- } else {
- // If we are not going to retry any APN, set this APN context to failed state.
- // This would be the final state of a data connection.
- apnContext.setState(DctConstants.State.FAILED);
- apnContext.setDataConnection(null);
- log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
- + ", requestType=" + requestTypeToString(requestType));
- //send request network complete messages as needed
- sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
- fallbackOnFailedHandover);
- }
- }
-
- /**
- * Called when EVENT_NETWORK_STATUS_CHANGED is received.
- *
- * @param status One of {@code NetworkAgent.VALID_NETWORK} or
- * {@code NetworkAgent.INVALID_NETWORK}.
- * @param cid context id {@code cid}
- * @param redirectUrl If the Internet probe was redirected, this
- * is the destination it was redirected to, otherwise {@code null}
- */
- private void onNetworkStatusChanged(int status, int cid, String redirectUrl) {
- if (!TextUtils.isEmpty(redirectUrl)) {
- Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED);
- intent.putExtra(TelephonyManager.EXTRA_REDIRECTION_URL, redirectUrl);
- mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
- log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
- } else {
- final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID;
- final DataConnection dc = getDataConnectionByContextId(cid);
- if (!mDsRecoveryHandler.isRecoveryOnBadNetworkEnabled()) {
- if (DBG) log("Skip data stall recovery on network status change with in threshold");
- return;
- }
- if (mTransportType != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- if (DBG) log("Skip data stall recovery on non WWAN");
- return;
- }
- if (dc != null && dc.isValidationRequired()) {
- mDsRecoveryHandler.processNetworkStatusChanged(isValid);
- }
- }
- }
-
- /**
- * Called when EVENT_DISCONNECT_DONE is received.
- */
- private void onDisconnectDone(ApnContext apnContext) {
- if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
- apnContext.setState(DctConstants.State.IDLE);
- // If all data connection are gone, check whether Airplane mode request was pending.
- if (areAllDataDisconnected()
- && mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
- if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
- // Radio will be turned off. No need to retry data setup
- apnContext.setApnSetting(null);
- apnContext.setDataConnection(null);
-
- // Need to notify disconnect as well, in the case of switching Airplane mode.
- // Otherwise, it would cause 30s delayed to turn on Airplane mode.
- notifyAllDataDisconnected();
- return;
- }
- // If APN is still enabled, try to bring it back up automatically
- if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
- // Wait a bit before trying the next APN, so that
- // we're not tying up the RIL command channel.
- // This also helps in any external dependency to turn off the context.
- if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
-
- // See if there are still handover request pending that we need to retry handover
- // after previous data gets disconnected.
- if (isHandoverPending(apnContext.getApnTypeBitmask())) {
- if (DBG) log("Handover request pending. Retry handover immediately.");
- startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER);
- } else {
- long delay = apnContext.getRetryAfterDisconnectDelay();
- if (delay > 0) {
- // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
- // the waiting APN list, which will also reset/reconfigure the retry manager.
- startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL);
- }
- }
- } else {
- boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_restartRadioAfterProvisioning);
-
- if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) {
- log("onDisconnectDone: restartRadio after provisioning");
- restartRadio();
- }
- apnContext.setApnSetting(null);
- apnContext.setDataConnection(null);
- if (isOnlySingleDcAllowed(getDataRat())) {
- if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn");
- setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION,
- RetryFailures.ALWAYS);
- } else {
- if(DBG) log("onDisconnectDone: not retrying");
- }
- }
-
- if (areAllDataDisconnected()) {
- apnContext.setConcurrentVoiceAndDataAllowed(
- mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed());
- notifyAllDataDisconnected();
- }
-
- }
-
- private void onVoiceCallStarted() {
- if (DBG) log("onVoiceCallStarted");
- mInVoiceCall = true;
- if (isAnyDataConnected()
- && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- if (DBG) log("onVoiceCallStarted stop polling");
- stopNetStatPoll();
- stopDataStallAlarm();
- }
- }
-
- protected void onVoiceCallEnded() {
- if (DBG) log("onVoiceCallEnded");
- mInVoiceCall = false;
- if (isAnyDataConnected()) {
- if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- } else {
- // clean slate after call end.
- resetPollStats();
- }
- }
- // reset reconnect timer
- setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS);
- }
- /**
- * @return {@code true} if there is any data in connected state.
- */
- @VisibleForTesting
- public boolean isAnyDataConnected() {
- for (DataConnection dc : mDataConnections.values()) {
- if (dc.isActive()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return {@code true} if all data connections are in disconnected state.
- */
- public boolean areAllDataDisconnected() {
- for (DataConnection dc : mDataConnections.values()) {
- if (!dc.isInactive()) {
- if (DBG) log("areAllDataDisconnected false due to DC: " + dc.getName());
- return false;
- }
- }
- return true;
- }
-
- protected void setDataProfilesAsNeeded() {
- if (DBG) log("setDataProfilesAsNeeded");
-
- ArrayList<DataProfile> dataProfileList = new ArrayList<>();
-
- int preferredApnSetId = getPreferredApnSetId();
- for (ApnSetting apn : mAllApnSettings) {
- if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
- || preferredApnSetId == apn.getApnSetId()) {
- DataProfile dp = new DataProfile.Builder()
- .setApnSetting(apn)
- .setPreferred(apn.equals(getPreferredApn()))
- .build();
- if (!dataProfileList.contains(dp)) {
- dataProfileList.add(dp);
- }
- } else {
- if (VDBG) {
- log("setDataProfilesAsNeeded: APN set id " + apn.getApnSetId()
- + " does not match the preferred set id " + preferredApnSetId);
- }
- }
- }
-
- // Check if the data profiles we are sending are same as we did last time. We don't want to
- // send the redundant profiles to the modem. Also if there the list is empty, we don't
- // send it to the modem.
- if (!dataProfileList.isEmpty()
- && (dataProfileList.size() != mLastDataProfileList.size()
- || !mLastDataProfileList.containsAll(dataProfileList))) {
- mDataServiceManager.setDataProfile(dataProfileList,
- mPhone.getServiceState().getDataRoamingFromRegistration(), null);
- }
- }
-
- /**
- * Based on the sim operator numeric, create a list for all possible
- * Data Connections and setup the preferredApn.
- */
- protected void createAllApnList() {
- mAllApnSettings.clear();
- String operator = mPhone.getOperatorNumeric();
-
- // ORDER BY Telephony.Carriers._ID ("_id")
- Cursor cursor = mPhone.getContext().getContentResolver().query(
- Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, "filtered/subId/"
- + mPhone.getSubId()), null, null, null, Telephony.Carriers._ID);
-
- if (cursor != null) {
- while (cursor.moveToNext()) {
- ApnSetting apn = ApnSetting.makeApnSetting(cursor);
- if (apn == null) {
- continue;
- }
- mAllApnSettings.add(apn);
- }
- cursor.close();
- } else {
- if (DBG) log("createAllApnList: cursor is null");
- mApnSettingsInitializationLog.log("cursor is null for carrier, operator: "
- + operator);
- }
-
- dedupeApnSettings();
-
- if (mAllApnSettings.isEmpty()) {
- log("createAllApnList: No APN found for carrier, operator: " + operator);
- mApnSettingsInitializationLog.log("no APN found for carrier, operator: "
- + operator);
- mPreferredApn = null;
- } else {
- mPreferredApn = getPreferredApn();
- if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
- }
-
- addDefaultApnSettingsAsNeeded();
- if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
- }
-
- private void dedupeApnSettings() {
- ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
-
- // coalesce APNs if they are similar enough to prevent
- // us from bringing up two data calls with the same interface
- int i = 0;
- while (i < mAllApnSettings.size() - 1) {
- ApnSetting first = mAllApnSettings.get(i);
- ApnSetting second = null;
- int j = i + 1;
- while (j < mAllApnSettings.size()) {
- second = mAllApnSettings.get(j);
- if (first.similar(second)) {
- ApnSetting newApn = mergeApns(first, second);
- mAllApnSettings.set(i, newApn);
- first = newApn;
- mAllApnSettings.remove(j);
- } else {
- j++;
- }
- }
- i++;
- }
- }
-
- private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
- int id = dest.getId();
- if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
- id = src.getId();
- }
- final int resultApnType = src.getApnTypeBitmask() | dest.getApnTypeBitmask();
- Uri mmsc = (dest.getMmsc() == null ? src.getMmsc() : dest.getMmsc());
- String mmsProxy = TextUtils.isEmpty(dest.getMmsProxyAddressAsString())
- ? src.getMmsProxyAddressAsString() : dest.getMmsProxyAddressAsString();
- int mmsPort = dest.getMmsProxyPort() == -1 ? src.getMmsProxyPort() : dest.getMmsProxyPort();
- String proxy = TextUtils.isEmpty(dest.getProxyAddressAsString())
- ? src.getProxyAddressAsString() : dest.getProxyAddressAsString();
- int port = dest.getProxyPort() == -1 ? src.getProxyPort() : dest.getProxyPort();
- int protocol = src.getProtocol() == ApnSetting.PROTOCOL_IPV4V6 ? src.getProtocol()
- : dest.getProtocol();
- int roamingProtocol = src.getRoamingProtocol() == ApnSetting.PROTOCOL_IPV4V6
- ? src.getRoamingProtocol() : dest.getRoamingProtocol();
- int networkTypeBitmask = (dest.getNetworkTypeBitmask() == 0
- || src.getNetworkTypeBitmask() == 0)
- ? 0 : (dest.getNetworkTypeBitmask() | src.getNetworkTypeBitmask());
- return new ApnSetting.Builder()
- .setId(id)
- .setOperatorNumeric(dest.getOperatorNumeric())
- .setEntryName(dest.getEntryName())
- .setApnName(dest.getApnName())
- .setProxyAddress(proxy)
- .setProxyPort(port)
- .setMmsc(mmsc)
- .setMmsProxyAddress(mmsProxy)
- .setMmsProxyPort(mmsPort)
- .setUser(dest.getUser())
- .setPassword(dest.getPassword())
- .setAuthType(dest.getAuthType())
- .setApnTypeBitmask(resultApnType)
- .setProtocol(protocol)
- .setRoamingProtocol(roamingProtocol)
- .setCarrierEnabled(dest.isEnabled())
- .setNetworkTypeBitmask(networkTypeBitmask)
- .setProfileId(dest.getProfileId())
- .setModemCognitive(dest.isPersistent() || src.isPersistent())
- .setMaxConns(dest.getMaxConns())
- .setWaitTime(dest.getWaitTime())
- .setMaxConnsTime(dest.getMaxConnsTime())
- .setMtuV4(dest.getMtuV4())
- .setMtuV6(dest.getMtuV6())
- .setMvnoType(dest.getMvnoType())
- .setMvnoMatchData(dest.getMvnoMatchData())
- .setApnSetId(dest.getApnSetId())
- .setCarrierId(dest.getCarrierId())
- .setSkip464Xlat(dest.getSkip464Xlat())
- .build();
- }
-
- private DataConnection createDataConnection() {
- if (DBG) log("createDataConnection E");
-
- int id = mUniqueIdGenerator.getAndIncrement();
- DataConnection dataConnection = DataConnection.makeDataConnection(mPhone, id, this,
- mDataServiceManager, mDcTesterFailBringUpAll, mDcc);
- mDataConnections.put(id, dataConnection);
- if (DBG) log("createDataConnection() X id=" + id + " dc=" + dataConnection);
- return dataConnection;
- }
-
- private void destroyDataConnections() {
- if(mDataConnections != null) {
- if (DBG) log("destroyDataConnections: clear mDataConnectionList");
- mDataConnections.clear();
- } else {
- if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
- }
- }
-
- /**
- * Build a list of APNs to be used to create PDP's.
- *
- * @param requestedApnType
- * @return waitingApns list to be used to create PDP
- * error when waitingApns.isEmpty()
- */
- private @NonNull ArrayList<ApnSetting> buildWaitingApns(String requestedApnType,
- int radioTech) {
- if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
- ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
-
- int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
- if (requestedApnTypeBitmask == ApnSetting.TYPE_ENTERPRISE) {
- requestedApnTypeBitmask = ApnSetting.TYPE_DEFAULT;
- }
- if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) {
- ArrayList<ApnSetting> dunApns = fetchDunApns();
- if (dunApns.size() > 0) {
- for (ApnSetting dun : dunApns) {
- apnList.add(dun);
- if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
- }
- return apnList;
- }
- }
-
- String operator = mPhone.getOperatorNumeric();
-
- // This is a workaround for a bug (7305641) where we don't failover to other
- // suitable APNs if our preferred APN fails. On prepaid ATT sims we need to
- // failover to a provisioning APN, but once we've used their default data
- // connection we are locked to it for life. This change allows ATT devices
- // to say they don't want to use preferred at all.
- boolean usePreferred = true;
- try {
- usePreferred = !mPhone.getContext().getResources().getBoolean(com.android
- .internal.R.bool.config_dontPreferApn);
- } catch (Resources.NotFoundException e) {
- if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
- usePreferred = true;
- }
- if (usePreferred) {
- mPreferredApn = getPreferredApn();
- }
- if (DBG) {
- log("buildWaitingApns: usePreferred=" + usePreferred
- + " canSetPreferApn=" + mCanSetPreferApn
- + " mPreferredApn=" + mPreferredApn
- + " operator=" + operator + " radioTech=" + radioTech);
- }
-
- if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
- mPreferredApn.canHandleType(requestedApnTypeBitmask)) {
- if (DBG) {
- log("buildWaitingApns: Preferred APN:" + operator + ":"
- + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn);
- }
-
- if (TextUtils.equals(mPreferredApn.getOperatorNumeric(), operator)
- || mPreferredApn.getCarrierId() == mPhone.getCarrierId()) {
- if (mPreferredApn.canSupportNetworkType(
- ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
- // Create a new instance of ApnSetting for ENTERPRISE because each
- // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
- // APN as DEFAULT but is a separate DataConnection
- if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
- == ApnSetting.TYPE_ENTERPRISE) {
- apnList.add(ApnSetting.makeApnSetting(mPreferredApn));
- } else {
- apnList.add(mPreferredApn);
- }
- if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
- return apnList;
- }
- }
- if (DBG) log("buildWaitingApns: no preferred APN");
- setPreferredApn(-1);
- mPreferredApn = null;
- }
-
- if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
- int preferredApnSetId = getPreferredApnSetId();
- for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(requestedApnTypeBitmask)) {
- if (apn.canSupportNetworkType(
- ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
- if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
- || preferredApnSetId == apn.getApnSetId()) {
- if (VDBG) log("buildWaitingApns: adding apn=" + apn);
- // Create a new instance of ApnSetting for ENTERPRISE because each
- // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
- // APN as DEFAULT but is a separate DataConnection
- if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
- == ApnSetting.TYPE_ENTERPRISE) {
- apnList.add(ApnSetting.makeApnSetting(apn));
- } else {
- apnList.add(apn);
- }
- } else {
- log("buildWaitingApns: APN set id " + apn.getApnSetId()
- + " does not match the preferred set id " + preferredApnSetId);
- }
- } else {
- if (DBG) {
- log("buildWaitingApns: networkTypeBitmask:"
- + apn.getNetworkTypeBitmask()
- + " does not include radioTech:"
- + ServiceState.rilRadioTechnologyToString(radioTech));
- }
- }
- } else if (VDBG) {
- log("buildWaitingApns: couldn't handle requested ApnType="
- + requestedApnType);
- }
- }
-
- if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
- return apnList;
- }
-
- private String apnListToString (ArrayList<ApnSetting> apns) {
- StringBuilder result = new StringBuilder();
- for (int i = 0, size = apns.size(); i < size; i++) {
- result.append('[')
- .append(apns.get(i).toString())
- .append(']');
- }
- return result.toString();
- }
-
- private void setPreferredApn(int pos) {
- setPreferredApn(pos, false);
- }
-
- private void setPreferredApn(int pos, boolean force) {
- if (!force && !mCanSetPreferApn) {
- log("setPreferredApn: X !canSEtPreferApn");
- return;
- }
-
- String subId = Long.toString(mPhone.getSubId());
- Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
- log("setPreferredApn: delete");
- ContentResolver resolver = mPhone.getContext().getContentResolver();
- resolver.delete(uri, null, null);
-
- if (pos >= 0) {
- log("setPreferredApn: insert");
- ContentValues values = new ContentValues();
- values.put(APN_ID, pos);
- resolver.insert(uri, values);
- }
- }
-
- @Nullable
- ApnSetting getPreferredApn() {
- //Only call this method from main thread
- if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
- log("getPreferredApn: mAllApnSettings is empty");
- return null;
- }
-
- String subId = Long.toString(mPhone.getSubId());
- Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
- Cursor cursor = mPhone.getContext().getContentResolver().query(
- uri, new String[] { "_id", "name", "apn" },
- null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
-
- if (cursor != null) {
- mCanSetPreferApn = true;
- } else {
- mCanSetPreferApn = false;
- }
-
- if (VDBG) {
- log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
- + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
- }
-
- if (mCanSetPreferApn && cursor.getCount() > 0) {
- int pos;
- cursor.moveToFirst();
- pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
- for(ApnSetting p : mAllApnSettings) {
- if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
- log("getPreferredApn: For APN type "
- + ApnSetting.getApnTypeString(mRequestedApnType)
- + " found apnSetting " + p);
- cursor.close();
- return p;
- }
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- log("getPreferredApn: X not found");
- return null;
- }
-
- @Override
- public void handleMessage (Message msg) {
- if (VDBG) log("handleMessage msg=" + msg);
-
- AsyncResult ar;
- Pair<ApnContext, Integer> pair;
- ApnContext apnContext;
- int generation;
- int requestType;
- int handoverFailureMode;
- switch (msg.what) {
- case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
- onDataConnectionDetached();
- break;
-
- case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
- onDataConnectionAttached();
- break;
-
- case DctConstants.EVENT_DO_RECOVERY:
- mDsRecoveryHandler.doRecovery();
- break;
-
- case DctConstants.EVENT_APN_CHANGED:
- onApnChanged();
- break;
-
- case DctConstants.EVENT_PS_RESTRICT_ENABLED:
- /**
- * We don't need to explicitly to tear down the PDP context
- * when PS restricted is enabled. The base band will deactive
- * PDP context and notify us with PDP_CONTEXT_CHANGED.
- * But we should stop the network polling and prevent reset PDP.
- */
- if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
- stopNetStatPoll();
- stopDataStallAlarm();
- mIsPsRestricted = true;
- break;
-
- case DctConstants.EVENT_PS_RESTRICT_DISABLED:
- /**
- * When PS restrict is removed, we need setup PDP connection if
- * PDP connection is down.
- */
- if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
- mIsPsRestricted = false;
- if (isAnyDataConnected()) {
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- } else {
- // TODO: Should all PDN states be checked to fail?
- if (mState == DctConstants.State.FAILED) {
- cleanUpAllConnectionsInternal(false, Phone.REASON_PS_RESTRICT_ENABLED);
- mReregisterOnReconnectFailure = false;
- }
- apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
- if (apnContext != null) {
- apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
- trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
- } else {
- loge("**** Default ApnContext not found ****");
- if (TelephonyUtils.IS_DEBUGGABLE) {
- throw new RuntimeException("Default ApnContext not found");
- }
- }
- }
- break;
-
- case DctConstants.EVENT_TRY_SETUP_DATA:
- apnContext = (ApnContext) msg.obj;
- requestType = msg.arg1;
- trySetupData(apnContext, requestType, null);
- break;
- case DctConstants.EVENT_CLEAN_UP_CONNECTION:
- if (DBG) log("EVENT_CLEAN_UP_CONNECTION");
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj);
- break;
- case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS:
- if ((msg.obj != null) && (msg.obj instanceof String == false)) {
- msg.obj = null;
- }
- cleanUpAllConnectionsInternal(true, (String) msg.obj);
- break;
-
- case DctConstants.EVENT_DATA_RAT_CHANGED:
- if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
- // unknown rat is an exception for data rat change. It's only received when out
- // of service and is not applicable for apn bearer bitmask. We should bypass the
- // check of waiting apn list and keep the data connection on, and no need to
- // setup a new one.
- break;
- }
- cleanUpConnectionsOnUpdatedApns(false, Phone.REASON_NW_TYPE_CHANGED);
- //May new Network allow setupData, so try it here
- setupDataOnAllConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
- RetryFailures.ONLY_ON_CHANGE);
- break;
-
- case DctConstants.CMD_CLEAR_PROVISIONING_SPINNER:
- // Check message sender intended to clear the current spinner.
- if (mProvisioningSpinner == msg.obj) {
- mProvisioningSpinner.dismiss();
- mProvisioningSpinner = null;
- }
- break;
-
- case DctConstants.EVENT_ENABLE_APN:
- onEnableApn(msg.arg1, msg.arg2, (Message) msg.obj);
- break;
-
- case DctConstants.EVENT_DISABLE_APN:
- onDisableApn(msg.arg1, msg.arg2);
- break;
-
- case DctConstants.EVENT_DATA_STALL_ALARM:
- onDataStallAlarm(msg.arg1);
- break;
-
- case DctConstants.EVENT_ROAMING_OFF:
- onDataRoamingOff();
- break;
-
- case DctConstants.EVENT_ROAMING_ON:
- case DctConstants.EVENT_ROAMING_SETTING_CHANGE:
- onDataRoamingOnOrSettingsChanged(msg.what);
- break;
-
- case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
- // Update sharedPreference to false when exits new device provisioning, indicating
- // no users modifications on the settings for new devices. Thus carrier specific
- // default roaming settings can be applied for new devices till user modification.
- final SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(mPhone.getContext());
- if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
- sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
- }
- break;
-
- case DctConstants.EVENT_NETWORK_STATUS_CHANGED:
- int status = msg.arg1;
- int cid = msg.arg2;
- String url = (String) msg.obj;
- onNetworkStatusChanged(status, cid, url);
- break;
-
- case DctConstants.EVENT_RADIO_AVAILABLE:
- onRadioAvailable();
- break;
-
- case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
- onRadioOffOrNotAvailable();
- break;
-
- case DctConstants.EVENT_DATA_SETUP_COMPLETE:
- ar = (AsyncResult) msg.obj;
- pair = (Pair<ApnContext, Integer>) ar.userObj;
- apnContext = pair.first;
- generation = pair.second;
- requestType = msg.arg1;
- handoverFailureMode = msg.arg2;
- if (apnContext.getConnectionGeneration() == generation) {
- boolean success = true;
- int cause = DataFailCause.UNKNOWN;
- if (ar.exception != null) {
- success = false;
- cause = (int) ar.result;
- }
- onDataSetupComplete(apnContext, success, cause, requestType,
- handoverFailureMode);
- } else {
- loge("EVENT_DATA_SETUP_COMPLETE: Dropped the event because generation "
- + "did not match.");
- }
- break;
-
- case DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR:
- ar = (AsyncResult) msg.obj;
- pair = (Pair<ApnContext, Integer>) ar.userObj;
- apnContext = pair.first;
- generation = pair.second;
- handoverFailureMode = msg.arg2;
- if (apnContext.getConnectionGeneration() == generation) {
- onDataSetupCompleteError(apnContext, handoverFailureMode, false);
- } else {
- loge("EVENT_DATA_SETUP_COMPLETE_ERROR: Dropped the event because generation "
- + "did not match.");
- }
- break;
-
- case DctConstants.EVENT_DISCONNECT_DONE:
- log("EVENT_DISCONNECT_DONE msg=" + msg);
- ar = (AsyncResult) msg.obj;
- pair = (Pair<ApnContext, Integer>) ar.userObj;
- apnContext = pair.first;
- generation = pair.second;
- if (apnContext.getConnectionGeneration() == generation) {
- onDisconnectDone(apnContext);
- } else {
- loge("EVENT_DISCONNECT_DONE: Dropped the event because generation "
- + "did not match.");
- }
- break;
-
- case DctConstants.EVENT_VOICE_CALL_STARTED:
- onVoiceCallStarted();
- break;
-
- case DctConstants.EVENT_VOICE_CALL_ENDED:
- onVoiceCallEnded();
- break;
- case DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: {
- sEnableFailFastRefCounter += (msg.arg1 == DctConstants.ENABLED) ? 1 : -1;
- if (DBG) {
- log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
- + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
- }
- if (sEnableFailFastRefCounter < 0) {
- final String s = "CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
- + "sEnableFailFastRefCounter:" + sEnableFailFastRefCounter + " < 0";
- loge(s);
- sEnableFailFastRefCounter = 0;
- }
- final boolean enabled = sEnableFailFastRefCounter > 0;
- if (DBG) {
- log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: enabled=" + enabled
- + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
- }
- if (mFailFast != enabled) {
- mFailFast = enabled;
-
- mDataStallNoRxEnabled = !enabled;
- if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
- && isAnyDataConnected()
- && (!mInVoiceCall ||
- mPhone.getServiceStateTracker()
- .isConcurrentVoiceAndDataAllowed())) {
- if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: start data stall");
- stopDataStallAlarm();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- } else {
- if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: stop data stall");
- stopDataStallAlarm();
- }
- }
-
- break;
- }
- case DctConstants.CMD_ENABLE_MOBILE_PROVISIONING: {
- Bundle bundle = msg.getData();
- if (bundle != null) {
- try {
- mProvisioningUrl = (String)bundle.get(DctConstants.PROVISIONING_URL_KEY);
- } catch(ClassCastException e) {
- loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url not a string" + e);
- mProvisioningUrl = null;
- }
- }
- if (TextUtils.isEmpty(mProvisioningUrl)) {
- loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url is empty, ignoring");
- mIsProvisioning = false;
- mProvisioningUrl = null;
- } else {
- loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioningUrl=" + mProvisioningUrl);
- mIsProvisioning = true;
- startProvisioningApnAlarm();
- }
- break;
- }
- case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
- if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
- ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
- if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
- if (mProvisioningApnAlarmTag == msg.arg1) {
- if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
- mIsProvisioning = false;
- mProvisioningUrl = null;
- stopProvisioningApnAlarm();
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnCtx);
- } else {
- if (DBG) {
- log("EVENT_PROVISIONING_APN_ALARM: ignore stale tag,"
- + " mProvisioningApnAlarmTag:" + mProvisioningApnAlarmTag
- + " != arg1:" + msg.arg1);
- }
- }
- } else {
- if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Not connected ignore");
- }
- break;
- }
- case DctConstants.CMD_IS_PROVISIONING_APN: {
- if (DBG) log("CMD_IS_PROVISIONING_APN");
- boolean isProvApn;
- try {
- String apnType = null;
- Bundle bundle = msg.getData();
- if (bundle != null) {
- apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
- }
- if (TextUtils.isEmpty(apnType)) {
- loge("CMD_IS_PROVISIONING_APN: apnType is empty");
- isProvApn = false;
- } else {
- isProvApn = isProvisioningApn(apnType);
- }
- } catch (ClassCastException e) {
- loge("CMD_IS_PROVISIONING_APN: NO provisioning url ignoring");
- isProvApn = false;
- }
- if (DBG) log("CMD_IS_PROVISIONING_APN: ret=" + isProvApn);
- mReplyAc.replyToMessage(msg, DctConstants.CMD_IS_PROVISIONING_APN,
- isProvApn ? DctConstants.ENABLED : DctConstants.DISABLED);
- break;
- }
- case DctConstants.EVENT_RESTART_RADIO: {
- restartRadio();
- break;
- }
- case DctConstants.CMD_NET_STAT_POLL: {
- if (msg.arg1 == DctConstants.ENABLED) {
- handleStartNetStatPoll((DctConstants.Activity)msg.obj);
- } else if (msg.arg1 == DctConstants.DISABLED) {
- handleStopNetStatPoll((DctConstants.Activity)msg.obj);
- }
- break;
- }
- case DctConstants.EVENT_PCO_DATA_RECEIVED: {
- handlePcoData((AsyncResult)msg.obj);
- break;
- }
- case DctConstants.EVENT_DATA_RECONNECT:
- if (DBG) {
- log("EVENT_DATA_RECONNECT: subId=" + msg.arg1 + ", type="
- + requestTypeToString(msg.arg2));
- }
- onDataReconnect((ApnContext) msg.obj, msg.arg1, msg.arg2);
- break;
- case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
- onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
- break;
- case DctConstants.EVENT_DATA_ENABLED_CHANGED:
- ar = (AsyncResult) msg.obj;
- if (ar.result instanceof Pair) {
- Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
- boolean enabled = p.first;
- int reason = p.second;
- onDataEnabledChanged(enabled, reason);
- }
- break;
- case DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED:
- onDataEnabledOverrideRulesChanged();
- break;
- case DctConstants.EVENT_NR_TIMER_WATCHDOG:
- mWatchdog = false;
- reevaluateUnmeteredConnections();
- break;
- case DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED:
- reevaluateCongestedConnections();
- reevaluateUnmeteredConnections();
- break;
- case DctConstants.EVENT_CARRIER_CONFIG_CHANGED:
- onCarrierConfigChanged();
- break;
- case DctConstants.EVENT_SIM_STATE_UPDATED:
- int simState = msg.arg1;
- onSimStateUpdated(simState);
- break;
- case DctConstants.EVENT_APN_UNTHROTTLED:
- ar = (AsyncResult) msg.obj;
- String apn = (String) ar.result;
- onApnUnthrottled(apn);
- break;
- case DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED:
- onTrafficDescriptorsUpdated();
- break;
- default:
- Rlog.e("DcTracker", "Unhandled event=" + msg);
- break;
-
- }
- }
-
- private int getApnProfileID(String apnType) {
- if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
- return RILConstants.DATA_PROFILE_IMS;
- } else if (TextUtils.equals(apnType, ApnSetting.TYPE_FOTA_STRING)) {
- return RILConstants.DATA_PROFILE_FOTA;
- } else if (TextUtils.equals(apnType, ApnSetting.TYPE_CBS_STRING)) {
- return RILConstants.DATA_PROFILE_CBS;
- } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IA_STRING)) {
- return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
- } else if (TextUtils.equals(apnType, ApnSetting.TYPE_DUN_STRING)) {
- return RILConstants.DATA_PROFILE_TETHERED;
- } else {
- return RILConstants.DATA_PROFILE_DEFAULT;
- }
- }
-
- private int getCellLocationId() {
- int cid = -1;
- CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation();
-
- if (loc != null) {
- if (loc instanceof GsmCellLocation) {
- cid = ((GsmCellLocation)loc).getCid();
- } else if (loc instanceof CdmaCellLocation) {
- cid = ((CdmaCellLocation)loc).getBaseStationId();
- }
- }
- return cid;
- }
-
- /**
- * Update link bandwidth estimate default values from carrier config.
- * @param bandwidths String array of "RAT:upstream,downstream" for each RAT
- * @param useLte For NR NSA, whether to use LTE value for upstream or not
- */
- private void updateLinkBandwidths(String[] bandwidths, boolean useLte) {
- ConcurrentHashMap<String, Pair<Integer, Integer>> temp = new ConcurrentHashMap<>();
- for (String config : bandwidths) {
- int downstream = 14;
- int upstream = 14;
- String[] kv = config.split(":");
- if (kv.length == 2) {
- String[] split = kv[1].split(",");
- if (split.length == 2) {
- try {
- downstream = Integer.parseInt(split[0]);
- upstream = Integer.parseInt(split[1]);
- } catch (NumberFormatException ignored) {
- }
- }
- temp.put(kv[0], new Pair<>(downstream, upstream));
- }
- }
- if (useLte) {
- Pair<Integer, Integer> ltePair =
- temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_LTE);
- if (ltePair != null) {
- if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA)) {
- temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA, new Pair<>(
- temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA).first,
- ltePair.second));
- }
- if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE)) {
- temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE, new Pair<>(
- temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE)
- .first, ltePair.second));
- }
- }
- }
- mBandwidths = temp;
- for (DataConnection dc : mDataConnections.values()) {
- dc.sendMessage(DataConnection.EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED);
- }
- }
-
- /**
- * Return the link upstream/downstream values from CarrierConfig for the given RAT name.
- * @param ratName RAT name from ServiceState#rilRadioTechnologyToString.
- * @return pair of downstream/upstream values (kbps), or null if the config is not defined.
- */
- public Pair<Integer, Integer> getLinkBandwidthsFromCarrierConfig(String ratName) {
- return mBandwidths.get(ratName);
- }
-
- @VisibleForTesting
- public boolean shouldAutoAttach() {
- if (mAutoAttachEnabled.get()) return true;
-
- PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
- ServiceState serviceState = mPhone.getServiceState();
-
- if (phoneSwitcher == null || serviceState == null) return false;
-
- // If voice is also not in service, don't auto attach.
- if (serviceState.getState() != ServiceState.STATE_IN_SERVICE) return false;
-
- // If voice is on LTE or NR, don't auto attach as for LTE / NR data would be attached.
- if (serviceState.getVoiceNetworkType() == NETWORK_TYPE_LTE
- || serviceState.getVoiceNetworkType() == NETWORK_TYPE_NR) return false;
-
- // If phone is non default phone, modem may have detached from data for optimization.
- // If phone is in voice call, for DSDS case DDS switch may be limited so we do try our
- // best to setup data connection and allow auto-attach.
- return (mPhone.getPhoneId() != phoneSwitcher.getPreferredDataPhoneId()
- || mPhone.getState() != PhoneConstants.State.IDLE);
- }
-
- private void notifyAllDataDisconnected() {
- sEnableFailFastRefCounter = 0;
- mFailFast = false;
- log("notify all data disconnected");
- mAllDataDisconnectedRegistrants.notifyRegistrants();
- }
-
- public void registerForAllDataDisconnected(Handler h, int what) {
- mAllDataDisconnectedRegistrants.addUnique(h, what, null);
-
- if (areAllDataDisconnected()) {
- notifyAllDataDisconnected();
- }
- }
-
- public void unregisterForAllDataDisconnected(Handler h) {
- mAllDataDisconnectedRegistrants.remove(h);
- }
-
- private void onDataEnabledChanged(boolean enable,
- @DataEnabledChangedReason int enabledChangedReason) {
- if (DBG) {
- log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
- + enabledChangedReason);
- }
-
- if (enable) {
- reevaluateDataConnections();
- setupDataOnAllConnectableApns(Phone.REASON_DATA_ENABLED, RetryFailures.ALWAYS);
- } else {
- String cleanupReason;
- switch (enabledChangedReason) {
- case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED:
- cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL;
- break;
- case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER:
- cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN;
- break;
- case DataEnabledSettings.REASON_USER_DATA_ENABLED:
- case DataEnabledSettings.REASON_POLICY_DATA_ENABLED:
- case DataEnabledSettings.REASON_PROVISIONED_CHANGED:
- case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED:
- default:
- cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED;
- break;
-
- }
- cleanUpAllConnectionsInternal(true, cleanupReason);
- }
- }
-
- private void reevaluateCongestedConnections() {
- log("reevaluateCongestedConnections");
- int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
- // congested override and either network is specified or unknown and all networks specified
- boolean isCongested = mCongestedOverride && (mCongestedNetworkTypes.contains(rat)
- || mCongestedNetworkTypes.containsAll(Arrays.stream(
- TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet())));
- for (DataConnection dataConnection : mDataConnections.values()) {
- dataConnection.onCongestednessChanged(isCongested);
- }
- }
-
- private void reevaluateUnmeteredConnections() {
- log("reevaluateUnmeteredConnections");
- int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
- if (isNrUnmetered() && (!mPhone.getServiceState().getRoaming() || mNrNsaRoamingUnmetered)) {
- setDataConnectionUnmetered(true);
- if (!mWatchdog) {
- startWatchdogAlarm();
- }
- } else {
- stopWatchdogAlarm();
- setDataConnectionUnmetered(isNetworkTypeUnmetered(rat));
- }
- }
-
- private void setDataConnectionUnmetered(boolean isUnmetered) {
- if (!isUnmetered || isTempNotMeteredSupportedByCarrier()) {
- for (DataConnection dataConnection : mDataConnections.values()) {
- dataConnection.onMeterednessChanged(isUnmetered);
- }
- }
- }
-
- private boolean isNetworkTypeUnmetered(@NetworkType int networkType) {
- boolean isUnmetered;
- if (mUnmeteredNetworkTypes == null || !mUnmeteredOverride) {
- // check SubscriptionPlans if override is not defined
- isUnmetered = isNetworkTypeUnmeteredViaSubscriptionPlan(networkType);
- log("isNetworkTypeUnmeteredViaSubscriptionPlan: networkType=" + networkType
- + ", isUnmetered=" + isUnmetered);
- return isUnmetered;
- }
- // unmetered override and either network is specified or unknown and all networks specified
- isUnmetered = mUnmeteredNetworkTypes.contains(networkType)
- || mUnmeteredNetworkTypes.containsAll(Arrays.stream(
- TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet()));
- if (DBG) {
- log("isNetworkTypeUnmetered: networkType=" + networkType
- + ", isUnmetered=" + isUnmetered);
- }
- return isUnmetered;
- }
-
- private boolean isNetworkTypeUnmeteredViaSubscriptionPlan(@NetworkType int networkType) {
- if (mSubscriptionPlans.isEmpty()) {
- // safe return false if unable to get subscription plans or plans don't exist
- return false;
- }
-
- boolean isGeneralUnmetered = true;
- Set<Integer> allNetworkTypes = Arrays.stream(TelephonyManager.getAllNetworkTypes())
- .boxed().collect(Collectors.toSet());
- for (SubscriptionPlan plan : mSubscriptionPlans) {
- // check plan is general (applies to all network types) or specific
- if (Arrays.stream(plan.getNetworkTypes()).boxed().collect(Collectors.toSet())
- .containsAll(allNetworkTypes)) {
- if (!isPlanUnmetered(plan)) {
- // metered takes precedence over unmetered for safety
- isGeneralUnmetered = false;
- }
- } else {
- // check plan applies to given network type
- if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
- for (int planNetworkType : plan.getNetworkTypes()) {
- if (planNetworkType == networkType) {
- return isPlanUnmetered(plan);
- }
- }
- }
- }
- }
- return isGeneralUnmetered;
- }
-
- private boolean isPlanUnmetered(SubscriptionPlan plan) {
- return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED;
- }
-
- private boolean isNrUnmetered() {
- int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
- int override = mPhone.getDisplayInfoController().getTelephonyDisplayInfo()
- .getOverrideNetworkType();
-
- if (isNetworkTypeUnmetered(NETWORK_TYPE_NR)) {
- if (mNrNsaMmwaveUnmetered) {
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
- if (DBG) log("NR unmetered for mmwave only");
- return true;
- }
- return false;
- } else if (mNrNsaSub6Unmetered) {
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
- if (DBG) log("NR unmetered for sub6 only");
- return true;
- }
- return false;
- }
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
- || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
- || rat == NETWORK_TYPE_NR) {
- if (DBG) log("NR unmetered for all frequencies");
- return true;
- }
- return false;
- }
-
- if (mNrNsaAllUnmetered) {
- if (mNrNsaMmwaveUnmetered) {
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
- if (DBG) log("NR NSA unmetered for mmwave only via carrier configs");
- return true;
- }
- return false;
- } else if (mNrNsaSub6Unmetered) {
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
- if (DBG) log("NR NSA unmetered for sub6 only via carrier configs");
- return true;
- }
- return false;
- }
- if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
- || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
- if (DBG) log("NR NSA unmetered for all frequencies via carrier configs");
- return true;
- }
- return false;
- }
-
- if (mNrSaAllUnmetered) {
- // TODO: add logic for mNrSaMmwaveUnmetered and mNrSaSub6Unmetered once it's defined
- // in TelephonyDisplayInfo
- if (rat == NETWORK_TYPE_NR) {
- if (DBG) log("NR SA unmetered for all frequencies via carrier configs");
- return true;
- }
- return false;
- }
-
- return false;
- }
-
- private boolean isTempNotMeteredSupportedByCarrier() {
- CarrierConfigManager configManager =
- mPhone.getContext().getSystemService(CarrierConfigManager.class);
- if (configManager != null) {
- PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
- if (bundle != null) {
- return bundle.getBoolean(
- CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL);
- }
- }
-
- return false;
- }
-
- protected void log(String s) {
- Rlog.d(mLogTag, s);
- }
-
- private void loge(String s) {
- Rlog.e(mLogTag, s);
- }
-
- private void logSortedApnContexts() {
- if (VDBG) {
- log("initApnContexts: X mApnContexts=" + mApnContexts);
-
- StringBuilder sb = new StringBuilder();
- sb.append("sorted apncontexts -> [");
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- sb.append(apnContext);
- sb.append(", ");
-
- log("sorted list");
- }
- sb.append("]");
- log(sb.toString());
- }
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("DcTracker:");
- pw.println(" RADIO_TESTS=" + RADIO_TESTS);
- pw.println(" mDataEnabledSettings=" + mDataEnabledSettings);
- pw.println(" isDataAllowed=" + isDataAllowed(null));
- pw.flush();
- pw.println(" mRequestedApnType=" + mRequestedApnType);
- pw.println(" mPhone=" + mPhone.getPhoneName());
- pw.println(" mConfigReady=" + mConfigReady);
- pw.println(" mSimState=" + SubscriptionInfoUpdater.simStateString(mSimState));
- pw.println(" mActivity=" + mActivity);
- pw.println(" mState=" + mState);
- pw.println(" mTxPkts=" + mTxPkts);
- pw.println(" mRxPkts=" + mRxPkts);
- pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
- pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
- pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
- pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
- pw.println(" mDataStallNoRxEnabled=" + mDataStallNoRxEnabled);
- pw.println(" mEmergencyApn=" + mEmergencyApn);
- pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
- pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
- pw.println(" mResolver=" + mResolver);
- pw.println(" mReconnectIntent=" + mReconnectIntent);
- pw.println(" mAutoAttachEnabled=" + mAutoAttachEnabled.get());
- pw.println(" mIsScreenOn=" + mIsScreenOn);
- pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
- pw.println(" mDataServiceBound=" + mDataServiceBound);
- pw.println(" mDataRoamingLeakageLog= ");
- mDataRoamingLeakageLog.dump(fd, pw, args);
- pw.println(" mApnSettingsInitializationLog= ");
- mApnSettingsInitializationLog.dump(fd, pw, args);
- pw.flush();
- pw.println(" ***************************************");
- DcController dcc = mDcc;
- if (dcc != null) {
- if (mDataServiceBound) {
- dcc.dump(fd, pw, args);
- } else {
- pw.println(" Can't dump mDcc because data service is not bound.");
- }
- } else {
- pw.println(" mDcc=null");
- }
- pw.println(" ***************************************");
- HashMap<Integer, DataConnection> dcs = mDataConnections;
- if (dcs != null) {
- Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet();
- pw.println(" mDataConnections: count=" + mDcSet.size());
- for (Entry<Integer, DataConnection> entry : mDcSet) {
- pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
- entry.getValue().dump(fd, pw, args);
- }
- } else {
- pw.println("mDataConnections=null");
- }
- pw.println(" ***************************************");
- pw.flush();
- HashMap<String, Integer> apnToDcId = mApnToDataConnectionId;
- if (apnToDcId != null) {
- Set<Entry<String, Integer>> apnToDcIdSet = apnToDcId.entrySet();
- pw.println(" mApnToDataConnectonId size=" + apnToDcIdSet.size());
- for (Entry<String, Integer> entry : apnToDcIdSet) {
- pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
- }
- } else {
- pw.println("mApnToDataConnectionId=null");
- }
- pw.println(" ***************************************");
- pw.flush();
- ConcurrentHashMap<String, ApnContext> apnCtxs = mApnContexts;
- if (apnCtxs != null) {
- Set<Entry<String, ApnContext>> apnCtxsSet = apnCtxs.entrySet();
- pw.println(" mApnContexts size=" + apnCtxsSet.size());
- for (Entry<String, ApnContext> entry : apnCtxsSet) {
- entry.getValue().dump(fd, pw, args);
- }
- ApnContext.dumpLocalLog(fd, pw, args);
- pw.println(" ***************************************");
- } else {
- pw.println(" mApnContexts=null");
- }
- pw.flush();
-
- pw.println(" mAllApnSettings size=" + mAllApnSettings.size());
- for (int i = 0; i < mAllApnSettings.size(); i++) {
- pw.printf(" mAllApnSettings[%d]: %s\n", i, mAllApnSettings.get(i));
- }
- pw.flush();
-
- pw.println(" mPreferredApn=" + mPreferredApn);
- pw.println(" mIsPsRestricted=" + mIsPsRestricted);
- pw.println(" mIsDisposed=" + mIsDisposed);
- pw.println(" mIntentReceiver=" + mIntentReceiver);
- pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
- pw.println(" canSetPreferApn=" + mCanSetPreferApn);
- pw.println(" mApnObserver=" + mApnObserver);
- pw.println(" isAnyDataConnected=" + isAnyDataConnected());
- pw.println(" mAttached=" + mAttached.get());
- mDataEnabledSettings.dump(fd, pw, args);
- pw.flush();
- }
-
- public String[] getPcscfAddress(String apnType) {
- log("getPcscfAddress()");
- ApnContext apnContext = null;
-
- if(apnType == null){
- log("apnType is null, return null");
- return null;
- }
-
- if (TextUtils.equals(apnType, ApnSetting.TYPE_EMERGENCY_STRING)) {
- apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
- } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
- apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
- } else {
- log("apnType is invalid, return null");
- return null;
- }
-
- if (apnContext == null) {
- log("apnContext is null, return null");
- return null;
- }
-
- DataConnection dataConnection = apnContext.getDataConnection();
- String[] result = null;
-
- if (dataConnection != null) {
- result = dataConnection.getPcscfAddresses();
-
- if (result != null) {
- for (int i = 0; i < result.length; i++) {
- log("Pcscf[" + i + "]: " + result[i]);
- }
- }
- return result;
- }
- return null;
- }
-
- /**
- * Create default apn settings for the apn type like emergency, and ims
- */
- private ApnSetting buildDefaultApnSetting(@NonNull String entry,
- @NonNull String apn, @ApnType int apnTypeBitmask) {
- return new ApnSetting.Builder()
- .setEntryName(entry)
- .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
- .setApnName(apn)
- .setApnTypeBitmask(apnTypeBitmask)
- .setCarrierEnabled(true)
- .setApnSetId(Telephony.Carriers.MATCH_ALL_APN_SET_ID)
- .build();
- }
-
- /**
- * Add default APN settings to APN settings list as needed
- */
- private void addDefaultApnSettingsAsNeeded() {
- boolean isEmergencyApnConfigured = false;
- boolean isImsApnConfigured = false;
-
- for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(ApnSetting.TYPE_EMERGENCY)) {
- isEmergencyApnConfigured = true;
- }
- if (apn.canHandleType(ApnSetting.TYPE_IMS)) {
- isImsApnConfigured = true;
- }
- if (isEmergencyApnConfigured && isImsApnConfigured) {
- log("Both emergency and ims apn setting are already present");
- return;
- }
- }
-
- // Add default apn setting for emergency service if it is not present
- if (!isEmergencyApnConfigured) {
- mAllApnSettings.add(buildDefaultApnSetting(
- "DEFAULT EIMS", "sos", ApnSetting.TYPE_EMERGENCY));
- log("default emergency apn is created");
- }
-
- // Only add default apn setting for ims when it is not present and sim is loaded
- if (!isImsApnConfigured && mSimState == TelephonyManager.SIM_STATE_LOADED) {
- mAllApnSettings.add(buildDefaultApnSetting(
- "DEFAULT IMS", "ims", ApnSetting.TYPE_IMS));
- log("default ims apn is created");
- }
- }
-
- private void cleanUpConnectionsOnUpdatedApns(boolean detach, String reason) {
- if (DBG) log("cleanUpConnectionsOnUpdatedApns: detach=" + detach);
- if (mAllApnSettings.isEmpty()) {
- cleanUpAllConnectionsInternal(detach, Phone.REASON_APN_CHANGED);
- } else {
- if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
- // unknown rat is an exception for data rat change. Its only received when out of
- // service and is not applicable for apn bearer bitmask. We should bypass the check
- // of waiting apn list and keep the data connection on.
- return;
- }
- for (ApnContext apnContext : mApnContexts.values()) {
- boolean cleanupRequired = true;
- if (!apnContext.isDisconnected()) {
- ArrayList<ApnSetting> waitingApns = buildWaitingApns(
- apnContext.getApnType(), getDataRat());
- if (apnContext.getWaitingApns().size() != waitingApns.size()
- || !apnContext.getWaitingApns().containsAll(waitingApns)) {
- apnContext.setWaitingApns(waitingApns);
- }
- for (ApnSetting apnSetting : waitingApns) {
- if (areCompatible(apnSetting, apnContext.getApnSetting())) {
- cleanupRequired = false;
- break;
- }
- }
-
- if (cleanupRequired) {
- if (DBG) {
- log("cleanUpConnectionsOnUpdatedApns: APN type "
- + apnContext.getApnType() + " clean up is required. The new "
- + "waiting APN list " + waitingApns + " does not cover "
- + apnContext.getApnSetting());
- }
- apnContext.setReason(reason);
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
- }
- }
- }
- }
-
- if (!isAnyDataConnected()) {
- stopNetStatPoll();
- stopDataStallAlarm();
- }
-
- mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
- if (areAllDataDisconnected()) {
- notifyAllDataDisconnected();
- }
- }
-
- /**
- * Polling stuff
- */
- protected void resetPollStats() {
- mTxPkts = -1;
- mRxPkts = -1;
- mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
- }
-
- protected void startNetStatPoll() {
- if (isAnyDataConnected() && !mNetStatPollEnabled) {
- if (DBG) {
- log("startNetStatPoll");
- }
- resetPollStats();
- mNetStatPollEnabled = true;
- mPollNetStat.run();
- }
- if (mPhone != null) {
- mPhone.notifyDataActivity();
- }
- }
-
- protected void stopNetStatPoll() {
- mNetStatPollEnabled = false;
- removeCallbacks(mPollNetStat);
- if (DBG) {
- log("stopNetStatPoll");
- }
-
- // To sync data activity icon in the case of switching data connection to send MMS.
- if (mPhone != null) {
- mPhone.notifyDataActivity();
- }
- }
-
- public void sendStartNetStatPoll(DctConstants.Activity activity) {
- Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
- msg.arg1 = DctConstants.ENABLED;
- msg.obj = activity;
- sendMessage(msg);
- }
-
- private void handleStartNetStatPoll(DctConstants.Activity activity) {
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- setActivity(activity);
- }
-
- public void sendStopNetStatPoll(DctConstants.Activity activity) {
- Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
- msg.arg1 = DctConstants.DISABLED;
- msg.obj = activity;
- sendMessage(msg);
- }
-
- private void handleStopNetStatPoll(DctConstants.Activity activity) {
- stopNetStatPoll();
- stopDataStallAlarm();
- setActivity(activity);
- }
-
- private void onDataEnabledOverrideRulesChanged() {
- if (DBG) {
- log("onDataEnabledOverrideRulesChanged");
- }
-
- for (ApnContext apnContext : mPrioritySortedApnContexts) {
- if (isDataAllowed(apnContext, REQUEST_TYPE_NORMAL, null)) {
- if (apnContext.getDataConnection() != null) {
- apnContext.getDataConnection().reevaluateRestrictedState();
- }
- setupDataOnConnectableApn(apnContext, Phone.REASON_DATA_ENABLED_OVERRIDE,
- RetryFailures.ALWAYS);
- } else if (shouldCleanUpConnection(apnContext, true, false)) {
- apnContext.setReason(Phone.REASON_DATA_ENABLED_OVERRIDE);
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
- }
- }
- }
-
- private void updateDataActivity() {
- long sent, received;
-
- DctConstants.Activity newActivity;
-
- TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
- TxRxSum curTxRxSum = new TxRxSum();
- curTxRxSum.updateTotalTxRxSum();
- mTxPkts = curTxRxSum.txPkts;
- mRxPkts = curTxRxSum.rxPkts;
-
- if (VDBG) {
- log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
- }
-
- if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
- sent = mTxPkts - preTxRxSum.txPkts;
- received = mRxPkts - preTxRxSum.rxPkts;
-
- if (VDBG)
- log("updateDataActivity: sent=" + sent + " received=" + received);
- if (sent > 0 && received > 0) {
- newActivity = DctConstants.Activity.DATAINANDOUT;
- } else if (sent > 0 && received == 0) {
- newActivity = DctConstants.Activity.DATAOUT;
- } else if (sent == 0 && received > 0) {
- newActivity = DctConstants.Activity.DATAIN;
- } else {
- newActivity = (mActivity == DctConstants.Activity.DORMANT) ?
- mActivity : DctConstants.Activity.NONE;
- }
-
- if (mActivity != newActivity && mIsScreenOn) {
- if (VDBG)
- log("updateDataActivity: newActivity=" + newActivity);
- mActivity = newActivity;
- mPhone.notifyDataActivity();
- }
- }
- }
-
- private void handlePcoData(AsyncResult ar) {
- if (ar.exception != null) {
- loge("PCO_DATA exception: " + ar.exception);
- return;
- }
- PcoData pcoData = (PcoData)(ar.result);
- ArrayList<DataConnection> dcList = new ArrayList<>();
- DataConnection temp = mDcc.getActiveDcByCid(pcoData.cid);
- if (temp != null) {
- dcList.add(temp);
- }
- if (dcList.size() == 0) {
- loge("PCO_DATA for unknown cid: " + pcoData.cid + ", inferring");
- for (DataConnection dc : mDataConnections.values()) {
- final int cid = dc.getCid();
- if (cid == pcoData.cid) {
- if (VDBG) log(" found " + dc);
- dcList.clear();
- dcList.add(dc);
- break;
- }
- // check if this dc is still connecting
- if (cid == -1) {
- for (ApnContext apnContext : dc.getApnContexts()) {
- if (apnContext.getState() == DctConstants.State.CONNECTING) {
- if (VDBG) log(" found potential " + dc);
- dcList.add(dc);
- break;
- }
- }
- }
- }
- }
- if (dcList.size() == 0) {
- loge("PCO_DATA - couldn't infer cid");
- return;
- }
- for (DataConnection dc : dcList) {
- List<ApnContext> apnContextList = dc.getApnContexts();
- if (apnContextList.size() == 0) {
- break;
- }
- // send one out for each apn type in play
- for (ApnContext apnContext : apnContextList) {
- String apnType = apnContext.getApnType();
-
- final Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
- ApnSetting.getApnTypesBitmaskFromString(apnType));
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
- ApnSetting.getProtocolIntFromString(pcoData.bearerProto));
- intent.putExtra(TelephonyManager.EXTRA_PCO_ID, pcoData.pcoId);
- intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, pcoData.contents);
- mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
- }
- }
- }
-
- /**
- * Data-Stall
- */
-
- // Recovery action taken in case of data stall
- @IntDef(
- value = {
- RECOVERY_ACTION_GET_DATA_CALL_LIST,
- RECOVERY_ACTION_CLEANUP,
- RECOVERY_ACTION_REREGISTER,
- RECOVERY_ACTION_RADIO_RESTART
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RecoveryAction {};
- private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST = 0;
- private static final int RECOVERY_ACTION_CLEANUP = 1;
- private static final int RECOVERY_ACTION_REREGISTER = 2;
- private static final int RECOVERY_ACTION_RADIO_RESTART = 3;
-
- // Recovery handler class for cellular data stall
- private class DataStallRecoveryHandler {
- // Default minimum duration between each recovery steps
- private static final int
- DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = (3 * 60 * 1000); // 3 mins
-
- // The elapsed real time of last recovery attempted
- private long mTimeLastRecoveryStartMs;
- // Whether current network good or not
- private boolean mIsValidNetwork;
- // Whether data stall happened or not.
- private boolean mWasDataStall;
- // Whether the result of last action(RADIO_RESTART) reported.
- private boolean mLastActionReported;
- // The real time for data stall start.
- private long mDataStallStartMs;
- // Last data stall action.
- private @RecoveryAction int mLastAction;
-
- public DataStallRecoveryHandler() {
- reset();
- }
-
- public void reset() {
- mTimeLastRecoveryStartMs = 0;
- putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
- }
-
- private void setNetworkValidationState(boolean isValid) {
- // Validation status is true and was not data stall.
- if (isValid && !mWasDataStall) {
- return;
- }
-
- if (!mWasDataStall) {
- mWasDataStall = true;
- mDataStallStartMs = SystemClock.elapsedRealtime();
- if (DBG) log("data stall: start time = " + mDataStallStartMs);
- return;
- }
-
- if (!mLastActionReported) {
- int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
- if (DBG) {
- log("data stall: lastaction = " + mLastAction + ", isRecovered = "
- + isValid + ", mTimeDuration = " + timeDuration);
- }
- DataStallRecoveryStats.onDataStallEvent(mLastAction, mPhone, isValid,
- timeDuration);
- mLastActionReported = true;
- }
-
- if (isValid) {
- mLastActionReported = false;
- mWasDataStall = false;
- }
- }
-
- public boolean isAggressiveRecovery() {
- @RecoveryAction int action = getRecoveryAction();
-
- return ((action == RECOVERY_ACTION_CLEANUP)
- || (action == RECOVERY_ACTION_REREGISTER)
- || (action == RECOVERY_ACTION_RADIO_RESTART));
- }
-
- private long getMinDurationBetweenRecovery() {
- return Settings.Global.getLong(mResolver,
- Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
- DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS);
- }
-
- private long getElapsedTimeSinceRecoveryMs() {
- return (SystemClock.elapsedRealtime() - mTimeLastRecoveryStartMs);
- }
-
- @RecoveryAction
- private int getRecoveryAction() {
- @RecoveryAction int action = Settings.System.getInt(mResolver,
- "radio.data.stall.recovery.action", RECOVERY_ACTION_GET_DATA_CALL_LIST);
- if (VDBG_STALL) log("getRecoveryAction: " + action);
- return action;
- }
-
- private void putRecoveryAction(@RecoveryAction int action) {
- Settings.System.putInt(mResolver, "radio.data.stall.recovery.action", action);
- if (VDBG_STALL) log("putRecoveryAction: " + action);
- }
-
- private void broadcastDataStallDetected(@RecoveryAction int recoveryAction) {
- Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
- intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
- mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
- }
-
- private boolean isRecoveryAlreadyStarted() {
- return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST;
- }
-
- private boolean checkRecovery() {
- // To avoid back to back recovery wait for a grace period
- if (getElapsedTimeSinceRecoveryMs() < getMinDurationBetweenRecovery()) {
- if (VDBG_STALL) log("skip back to back data stall recovery");
- return false;
- }
-
- // Skip recovery if it can cause a call to drop
- if (mPhone.getState() != PhoneConstants.State.IDLE
- && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) {
- if (VDBG_STALL) log("skip data stall recovery as there is an active call");
- return false;
- }
-
- // Allow recovery if data is expected to work
- return mAttached.get() && isDataAllowed(null);
- }
-
- private void triggerRecovery() {
- // Updating the recovery start time early to avoid race when
- // the message is being processed in the Queue
- mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
- sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
- }
-
- public void doRecovery() {
- if (isAnyDataConnected()) {
- // Go through a series of recovery steps, each action transitions to the next action
- @RecoveryAction final int recoveryAction = getRecoveryAction();
- final int signalStrength = mPhone.getSignalStrength().getLevel();
- TelephonyMetrics.getInstance().writeSignalStrengthEvent(
- mPhone.getPhoneId(), signalStrength);
- TelephonyMetrics.getInstance().writeDataStallEvent(
- mPhone.getPhoneId(), recoveryAction);
- mLastAction = recoveryAction;
- mLastActionReported = false;
- broadcastDataStallDetected(recoveryAction);
-
- switch (recoveryAction) {
- case RECOVERY_ACTION_GET_DATA_CALL_LIST:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
- mSentSinceLastRecv);
- if (DBG) log("doRecovery() get data call list");
- mDataServiceManager.requestDataCallList(obtainMessage());
- putRecoveryAction(RECOVERY_ACTION_CLEANUP);
- break;
- case RECOVERY_ACTION_CLEANUP:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP,
- mSentSinceLastRecv);
- if (DBG) log("doRecovery() cleanup all connections");
- cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
- ApnSetting.TYPE_DEFAULT)));
- cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
- ApnSetting.TYPE_ENTERPRISE)));
- putRecoveryAction(RECOVERY_ACTION_REREGISTER);
- break;
- case RECOVERY_ACTION_REREGISTER:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
- mSentSinceLastRecv);
- if (DBG) log("doRecovery() re-register");
- mPhone.getServiceStateTracker().reRegisterNetwork(null);
- putRecoveryAction(RECOVERY_ACTION_RADIO_RESTART);
- break;
- case RECOVERY_ACTION_RADIO_RESTART:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
- mSentSinceLastRecv);
- if (DBG) log("restarting radio");
- restartRadio();
- reset();
- break;
- default:
- throw new RuntimeException("doRecovery: Invalid recoveryAction="
- + recoveryAction);
- }
- mSentSinceLastRecv = 0;
- }
- }
-
- public void processNetworkStatusChanged(boolean isValid) {
- setNetworkValidationState(isValid);
- if (isValid) {
- mIsValidNetwork = true;
- reset();
- } else {
- if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
- mIsValidNetwork = false;
- // Check and trigger a recovery if network switched from good
- // to bad or recovery is already started before.
- if (checkRecovery()) {
- if (DBG) log("trigger data stall recovery");
- triggerRecovery();
- }
- }
- }
- }
-
- public boolean isRecoveryOnBadNetworkEnabled() {
- return Settings.Global.getInt(mResolver,
- Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1) == 1;
- }
-
- public boolean isNoRxDataStallDetectionEnabled() {
- return mDataStallNoRxEnabled && !isRecoveryOnBadNetworkEnabled();
- }
- }
-
- private void updateDataStallInfo() {
- long sent, received;
-
- TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
- mDataStallTxRxSum.updateTotalTxRxSum();
-
- if (VDBG_STALL) {
- log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
- " preTxRxSum=" + preTxRxSum);
- }
-
- sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
- received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
-
- if (RADIO_TESTS) {
- if (SystemProperties.getBoolean("radio.test.data.stall", false)) {
- log("updateDataStallInfo: radio.test.data.stall true received = 0;");
- received = 0;
- }
- }
- if ( sent > 0 && received > 0 ) {
- if (VDBG_STALL) log("updateDataStallInfo: IN/OUT");
- mSentSinceLastRecv = 0;
- mDsRecoveryHandler.reset();
- } else if (sent > 0 && received == 0) {
- if (isPhoneStateIdle()) {
- mSentSinceLastRecv += sent;
- } else {
- mSentSinceLastRecv = 0;
- }
- if (DBG) {
- log("updateDataStallInfo: OUT sent=" + sent +
- " mSentSinceLastRecv=" + mSentSinceLastRecv);
- }
- } else if (sent == 0 && received > 0) {
- if (VDBG_STALL) log("updateDataStallInfo: IN");
- mSentSinceLastRecv = 0;
- mDsRecoveryHandler.reset();
- } else {
- if (VDBG_STALL) log("updateDataStallInfo: NONE");
- }
- }
-
- private boolean isPhoneStateIdle() {
- for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
- Phone phone = PhoneFactory.getPhone(i);
- if (phone != null && phone.getState() != PhoneConstants.State.IDLE) {
- log("isPhoneStateIdle false: Voice call active on phone " + i);
- return false;
- }
- }
- return true;
- }
-
- private void onDataStallAlarm(int tag) {
- if (mDataStallAlarmTag != tag) {
- if (DBG) {
- log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
- }
- return;
- }
-
- if (DBG) log("Data stall alarm");
- updateDataStallInfo();
-
- int hangWatchdogTrigger = Settings.Global.getInt(mResolver,
- Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
- NUMBER_SENT_PACKETS_OF_HANG);
-
- boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
- if (mSentSinceLastRecv >= hangWatchdogTrigger) {
- if (DBG) {
- log("onDataStallAlarm: tag=" + tag + " do recovery action="
- + mDsRecoveryHandler.getRecoveryAction());
- }
- suspectedStall = DATA_STALL_SUSPECTED;
- sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
- } else {
- if (VDBG_STALL) {
- log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
- " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
- }
- }
- startDataStallAlarm(suspectedStall);
- }
-
- protected void startDataStallAlarm(boolean suspectedStall) {
- int delayInMs;
-
- if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled() && isAnyDataConnected()) {
- // If screen is on or data stall is currently suspected, set the alarm
- // with an aggressive timeout.
- if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) {
- delayInMs = Settings.Global.getInt(mResolver,
- Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
- DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
- } else {
- delayInMs = Settings.Global.getInt(mResolver,
- Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
- DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
- }
-
- mDataStallAlarmTag += 1;
- if (VDBG_STALL) {
- log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
- " delay=" + (delayInMs / 1000) + "s");
- }
- Intent intent = new Intent(INTENT_DATA_STALL_ALARM);
- intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, mDataStallAlarmTag);
- intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, mTransportType);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
- mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
- SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
- } else {
- if (VDBG_STALL) {
- log("startDataStallAlarm: NOT started, no connection tag=" + mDataStallAlarmTag);
- }
- }
- }
-
- private void stopDataStallAlarm() {
- if (VDBG_STALL) {
- log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
- " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
- }
- mDataStallAlarmTag += 1;
- if (mDataStallAlarmIntent != null) {
- mAlarmManager.cancel(mDataStallAlarmIntent);
- mDataStallAlarmIntent = null;
- }
- }
-
- private void restartDataStallAlarm() {
- if (!isAnyDataConnected()) return;
- // To be called on screen status change.
- // Do not cancel the alarm if it is set with aggressive timeout.
- if (mDsRecoveryHandler.isAggressiveRecovery()) {
- if (DBG) log("restartDataStallAlarm: action is pending. not resetting the alarm.");
- return;
- }
- if (VDBG_STALL) log("restartDataStallAlarm: stop then start.");
- stopDataStallAlarm();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- }
-
- /**
- * Provisioning APN
- */
- private void onActionIntentProvisioningApnAlarm(Intent intent) {
- if (DBG) log("onActionIntentProvisioningApnAlarm: action=" + intent.getAction());
- Message msg = obtainMessage(DctConstants.EVENT_PROVISIONING_APN_ALARM,
- intent.getAction());
- msg.arg1 = intent.getIntExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, 0);
- sendMessage(msg);
- }
-
- private void startProvisioningApnAlarm() {
- int delayInMs = Settings.Global.getInt(mResolver,
- Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
- PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT);
- if (TelephonyUtils.IS_DEBUGGABLE) {
- // Allow debug code to use a system property to provide another value
- String delayInMsStrg = Integer.toString(delayInMs);
- delayInMsStrg = System.getProperty(DEBUG_PROV_APN_ALARM, delayInMsStrg);
- try {
- delayInMs = Integer.parseInt(delayInMsStrg);
- } catch (NumberFormatException e) {
- loge("startProvisioningApnAlarm: e=" + e);
- }
- }
- mProvisioningApnAlarmTag += 1;
- if (DBG) {
- log("startProvisioningApnAlarm: tag=" + mProvisioningApnAlarmTag +
- " delay=" + (delayInMs / 1000) + "s");
- }
- Intent intent = new Intent(INTENT_PROVISIONING_APN_ALARM);
- intent.putExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, mProvisioningApnAlarmTag);
- mProvisioningApnAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + delayInMs, mProvisioningApnAlarmIntent);
- }
-
- private void stopProvisioningApnAlarm() {
- if (DBG) {
- log("stopProvisioningApnAlarm: current tag=" + mProvisioningApnAlarmTag +
- " mProvsioningApnAlarmIntent=" + mProvisioningApnAlarmIntent);
- }
- mProvisioningApnAlarmTag += 1;
- if (mProvisioningApnAlarmIntent != null) {
- mAlarmManager.cancel(mProvisioningApnAlarmIntent);
- mProvisioningApnAlarmIntent = null;
- }
- }
-
- /**
- * 5G connection reevaluation alarm
- */
- private void startWatchdogAlarm() {
- sendMessageDelayed(obtainMessage(DctConstants.EVENT_NR_TIMER_WATCHDOG), mWatchdogTimeMs);
- mWatchdog = true;
- }
-
- private void stopWatchdogAlarm() {
- removeMessages(DctConstants.EVENT_NR_TIMER_WATCHDOG);
- mWatchdog = false;
- }
-
- private void onDataServiceBindingChanged(boolean bound) {
- if (!bound) {
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- boolean connPersistenceOnRestart = mPhone.getContext().getResources()
- .getBoolean(com.android
- .internal.R.bool.config_wlan_data_service_conn_persistence_on_restart);
- if (!connPersistenceOnRestart) {
- cleanUpAllConnectionsInternal(false, Phone.REASON_IWLAN_DATA_SERVICE_DIED);
- }
- }
- } else {
- //reset throttling after binding to data service
- mDataThrottler.reset();
- }
- mDataServiceBound = bound;
- }
-
- public static String requestTypeToString(@RequestNetworkType int type) {
- switch (type) {
- case REQUEST_TYPE_NORMAL: return "NORMAL";
- case REQUEST_TYPE_HANDOVER: return "HANDOVER";
- }
- return "UNKNOWN";
- }
-
- public static String releaseTypeToString(@ReleaseNetworkType int type) {
- switch (type) {
- case RELEASE_TYPE_NORMAL: return "NORMAL";
- case RELEASE_TYPE_DETACH: return "DETACH";
- case RELEASE_TYPE_HANDOVER: return "HANDOVER";
- }
- return "UNKNOWN";
- }
-
- @RilRadioTechnology
- protected int getDataRat() {
- ServiceState ss = mPhone.getServiceState();
- NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
- if (nrs != null) {
- return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology());
- }
- return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
- }
-
- @RilRadioTechnology
- private int getVoiceRat() {
- ServiceState ss = mPhone.getServiceState();
- NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_CS, mTransportType);
- if (nrs != null) {
- return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology());
- }
- return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
- }
-
- private void read5GConfiguration() {
- if (DBG) log("read5GConfiguration");
- String[] bandwidths = CarrierConfigManager.getDefaultConfig().getStringArray(
- CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY);
- boolean useLte = false;
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
- if (b != null) {
- if (b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY) != null) {
- bandwidths = b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY);
- }
- useLte = b.getBoolean(CarrierConfigManager
- .KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL);
- mWatchdogTimeMs = b.getLong(CarrierConfigManager.KEY_5G_WATCHDOG_TIME_MS_LONG);
- mNrNsaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL);
- mNrNsaMmwaveUnmetered = b.getBoolean(
- CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL);
- mNrNsaSub6Unmetered = b.getBoolean(
- CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL);
- mNrSaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_SA_BOOL);
- mNrSaMmwaveUnmetered = b.getBoolean(
- CarrierConfigManager.KEY_UNMETERED_NR_SA_MMWAVE_BOOL);
- mNrSaSub6Unmetered = b.getBoolean(
- CarrierConfigManager.KEY_UNMETERED_NR_SA_SUB6_BOOL);
- mNrNsaRoamingUnmetered = b.getBoolean(
- CarrierConfigManager.KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL);
- mLteEndcUsingUserDataForRrcDetection = b.getBoolean(
- CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
- }
- }
- updateLinkBandwidths(bandwidths, useLte);
- }
-
- public boolean getLteEndcUsingUserDataForIdleDetection() {
- return mLteEndcUsingUserDataForRrcDetection;
- }
-
- /**
- * Register for physical link status (i.e. RRC state) changed event.
- * if {@link CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
- * then physical link state is focusing on "internet data connection" instead of RRC state.
- *
- * @param h The handler
- * @param what The event
- */
- public void registerForPhysicalLinkStatusChanged(Handler h, int what) {
- mDcc.registerForPhysicalLinkStatusChanged(h, what);
- }
-
- /**
- * Unregister from physical link status (i.e. RRC state) changed event.
- *
- * @param h The previously registered handler
- */
- public void unregisterForPhysicalLinkStatusChanged(Handler h) {
- mDcc.unregisterForPhysicalLinkStatusChanged(h);
- }
-
- // We use a specialized equals function in Apn setting when checking if an active
- // data connection is still legitimate to use against a different apn setting.
- // This method is extracted to a function to ensure that any future changes to this check will
- // be applied to both cleanUpConnectionsOnUpdatedApns and checkForCompatibleDataConnection.
- // Fix for b/158908392.
- private boolean areCompatible(ApnSetting apnSetting1, ApnSetting apnSetting2) {
- return apnSetting1.equals(apnSetting2,
- mPhone.getServiceState().getDataRoamingFromRegistration());
- }
-
- @NonNull
- private PersistableBundle getCarrierConfig() {
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- // If an invalid subId is used, this bundle will contain default values.
- PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
- if (config != null) {
- return config;
- }
- }
- // Return static default defined in CarrierConfigManager.
- return CarrierConfigManager.getDefaultConfig();
- }
-
- /**
- * @return The data service manager.
- */
- public @NonNull DataServiceManager getDataServiceManager() {
- return mDataServiceManager;
- }
-
- /**
- * @return The data throttler
- */
- public @NonNull DataThrottler getDataThrottler() {
- return mDataThrottler;
- }
-
- private void showProvisioningNotification() {
- final Intent intent = new Intent(DcTracker.INTENT_PROVISION);
- intent.putExtra(DcTracker.EXTRA_PROVISION_PHONE_ID, mPhone.getPhoneId());
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mPhone.getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
-
- final Resources r = mPhone.getContext().getResources();
- final String title = r.getString(R.string.network_available_sign_in, 0);
- final String details = mTelephonyManager.getNetworkOperator(mPhone.getSubId());
- final Notification.Builder builder = new Notification.Builder(mPhone.getContext())
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.stat_notify_rssi_in_range)
- .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
- .setAutoCancel(true)
- .setTicker(title)
- .setColor(mPhone.getContext().getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(details)
- .setContentIntent(pendingIntent)
- .setLocalOnly(true)
- .setOnlyAlertOnce(true);
-
- final Notification notification = builder.build();
- try {
- getNotificationManager().notify(NOTIFICATION_TAG, mPhone.getPhoneId(), notification);
- } catch (final NullPointerException npe) {
- Log.e(mLogTag, "showProvisioningNotification: error showing notification", npe);
- }
- }
-
- private void hideProvisioningNotification() {
- try {
- getNotificationManager().cancel(NOTIFICATION_TAG, mPhone.getPhoneId());
- } catch (final NullPointerException npe) {
- Log.e(mLogTag, "hideProvisioningNotification: error hiding notification", npe);
- }
- }
-
- private NotificationManager getNotificationManager() {
- return (NotificationManager) mPhone.getContext()
- .createContextAsUser(UserHandle.ALL, 0 /* flags */)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/README.txt b/src/java/com/android/internal/telephony/dataconnection/README.txt
deleted file mode 100644
index e613a00..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/README.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-This package contains classes used to manage a DataConnection.
-
-A criticial aspect of this class is that most objects in this
-package run on the same thread except DataConnectionTracker
-This makes processing efficient as it minimizes context
-switching and it eliminates issues with multi-threading.
-
-This can be done because all actions are either asynchronous
-or are known to be non-blocking and fast. At this time only
-DcTesterDeactivateAll takes specific advantage of this
-single threading knowledge by using Dcc#mDcListAll so be
-very careful when making changes that break this assumption.
-
-A related change was in DataConnectionAc I added code that
-checks to see if the caller is on a different thread. If
-it is then the AsyncChannel#sendMessageSynchronously is
-used. If the caller is on the same thread then a getter
-is used. This allows the DCAC to be used from any thread
-and was required to fix a bug when Dcc called
-PhoneBase#notifyDataConnection which calls DCT#getLinkProperties
-and DCT#getLinkCapabilities which call Dcc all on the same
-thread. Without this change there was a dead lock when
-sendMessageSynchronously blocks.
-
-
-== Testing ==
-
-The following are Intents that can be sent for testing pruproses on
-DEBUGGABLE builds (userdebug, eng)
-
-*) Causes bringUp and retry requests to fail for all DC's
-
- adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup --ei counter 2 --ei fail_cause -3
-
-*) Causes all DC's to get torn down, simulating a temporary network outage:
-
- adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all
-
-*) To simplify testing we also have detach and attach simulations below where {x} is gsm, cdma or sip
-
- adb shell am broadcast -a com.android.internal.telephony.{x}.action_detached
- adb shell am broadcast -a com.android.internal.telephony.{x}.action_attached
-
-
-== System properties for Testing ==
-
-On debuggable builds (userdebug, eng) you can change additional
-settings through system properties. These properties can be set with
-"setprop" for the current boot, or added to local.prop to persist
-across boots.
-
-device# setprop key value
-
-device# echo "key=value" >> /data/local.prop
-device# chmod 644 /data/local.prop
-
-
--- Retry configuration --
-
-You can replace the connection retry configuration. For example, you
-could change it to perform 4 retries at 5 second intervals:
-
-device# setprop test.data_retry_config "5000,5000,5000"
-
-
--- Roaming --
-
-You can force the telephony stack to always assume that it's roaming
-to verify higher-level framework functionality:
-
-device# setprop telephony.test.forceRoaming true
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
deleted file mode 100644
index 63358f4..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.util.LocalLog;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.data.AccessNetworksManager;
-import com.android.internal.telephony.data.TelephonyNetworkFactory;
-import com.android.telephony.Rlog;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class represents the transport manager which manages available transports (i.e. WWAN or
- * WLAN) and determine the correct transport for {@link TelephonyNetworkFactory} to handle the data
- * requests.
- *
- * The device can operate in the following modes, which is stored in the system properties
- * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to
- * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode.
- *
- * Legacy mode:
- * Frameworks send all data requests to the default data service, which is the cellular data
- * service. IWLAN should be still reported as a RAT on cellular network service.
- *
- * AP-assisted mode:
- * IWLAN is handled by IWLAN data service extending {@link android.telephony.data.DataService},
- * IWLAN network service extending {@link android.telephony.NetworkService}, and qualified
- * network service extending {@link android.telephony.data.QualifiedNetworksService}.
- *
- * The following settings for service package name need to be configured properly for
- * frameworks to bind.
- *
- * Package name of data service:
- * The resource overlay 'config_wlan_data_service_package' or,
- * the carrier config
- * {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
- * The carrier config takes precedence over the resource overlay if both exist.
- *
- * Package name of network service
- * The resource overlay 'config_wlan_network_service_package' or
- * the carrier config
- * {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
- * The carrier config takes precedence over the resource overlay if both exist.
- *
- * Package name of qualified network service
- * The resource overlay 'config_qualified_networks_service_package' or
- * the carrier config
- * {@link CarrierConfigManager#
- * KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING}.
- * The carrier config takes precedence over the resource overlay if both exist.
- */
-public class TransportManager extends Handler {
- private final String mLogTag;
-
- private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1;
-
- private static final int EVENT_EVALUATE_TRANSPORT_PREFERENCE = 2;
-
- // Delay the re-evaluation if transport fall back. QNS will need to quickly change the
- // preference back to the original transport to avoid another handover request.
- private static final long FALL_BACK_REEVALUATE_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(3);
-
- private final Phone mPhone;
-
- private final LocalLog mLocalLog = new LocalLog(64);
-
- @Nullable
- private AccessNetworksManager mAccessNetworksManager;
-
- /**
- * The pending handover list. This is a list of APNs that are being handover to the new
- * transport. The entry will be removed once handover is completed. The key
- * is the APN type, and the value is the target transport that the APN is handovered to.
- */
- private final SparseIntArray mPendingHandoverApns;
-
- /**
- * The registrants for listening data handover needed events.
- */
- private final RegistrantList mHandoverNeededEventRegistrants;
-
- /**
- * Handover parameters
- */
- @VisibleForTesting
- public static final class HandoverParams {
- /**
- * The callback for handover complete.
- */
- public interface HandoverCallback {
- /**
- * Called when handover is completed.
- *
- * @param success {@true} if handover succeeded, otherwise failed.
- * @param fallback {@true} if handover failed, the data connection fallback to the
- * original transport
- */
- void onCompleted(boolean success, boolean fallback);
- }
-
- public final @ApnType int apnType;
- public final int targetTransport;
- public final HandoverCallback callback;
-
- @VisibleForTesting
- public HandoverParams(int apnType, int targetTransport, HandoverCallback callback) {
- this.apnType = apnType;
- this.targetTransport = targetTransport;
- this.callback = callback;
- }
- }
-
- public TransportManager(Phone phone) {
- mPhone = phone;
- mPendingHandoverApns = new SparseIntArray();
- mHandoverNeededEventRegistrants = new RegistrantList();
- mLogTag = TransportManager.class.getSimpleName() + "-" + mPhone.getPhoneId();
- mAccessNetworksManager = mPhone.getAccessNetworksManager();
- mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
- EVENT_QUALIFIED_NETWORKS_CHANGED);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_QUALIFIED_NETWORKS_CHANGED:
- if (!hasMessages(EVENT_EVALUATE_TRANSPORT_PREFERENCE)) {
- sendEmptyMessage(EVENT_EVALUATE_TRANSPORT_PREFERENCE);
- }
- break;
- case EVENT_EVALUATE_TRANSPORT_PREFERENCE:
- evaluateTransportPreference();
- break;
- default:
- loge("Unexpected event " + msg.what);
- break;
- }
- }
-
- /**
- * Set the current transport of apn type.
- *
- * @param apnType The APN type
- * @param transport The transport. Must be WWAN or WLAN.
- */
- private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
- mAccessNetworksManager.setCurrentTransport(apnType, transport);
- }
-
- private boolean isHandoverPending() {
- return mPendingHandoverApns.size() > 0;
- }
-
- /**
- * Evaluate the preferred transport for each APN type to see if handover is needed.
- */
- private void evaluateTransportPreference() {
- // Simultaneously handover is not supported today. Preference will be re-evaluated after
- // handover completed.
- if (isHandoverPending()) return;
- logl("evaluateTransportPreference");
- for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
- int targetTransport = mAccessNetworksManager.getPreferredTransport(apnType);
- if (targetTransport != mAccessNetworksManager.getCurrentTransport(apnType)) {
- logl("Handover started for APN type: "
- + ApnSetting.getApnTypeString(apnType)
- + ", target transport: "
- + AccessNetworkConstants.transportTypeToString(targetTransport));
- mPendingHandoverApns.put(apnType, targetTransport);
- mHandoverNeededEventRegistrants.notifyResult(
- new HandoverParams(apnType, targetTransport,
- (success, fallback) -> {
- // The callback for handover completed.
- if (success) {
- logl("Handover succeeded for APN type "
- + ApnSetting.getApnTypeString(apnType));
- } else {
- logl("APN type "
- + ApnSetting.getApnTypeString(apnType)
- + " handover to "
- + AccessNetworkConstants.transportTypeToString(
- targetTransport) + " failed"
- + ", fallback=" + fallback);
- }
-
- long delay = 0;
- if (fallback) {
- // No need to change the preference because we should
- // fallback. Re-evaluate after few seconds to give QNS
- // some time to change the preference back to the original
- // transport.
- delay = FALL_BACK_REEVALUATE_DELAY_MILLIS;
- } else {
- // If handover succeeds or failed without falling back
- // to the original transport, we should move to the new
- // transport (even if it is failed).
- setCurrentTransport(apnType, targetTransport);
- }
- mPendingHandoverApns.delete(apnType);
- sendEmptyMessageDelayed(EVENT_EVALUATE_TRANSPORT_PREFERENCE,
- delay);
- }));
-
- // Return here instead of processing the next APN type. The next APN type for
- // handover will be evaluate again once current handover is completed.
- return;
- }
- }
- }
-
- /**
- * Register for data handover needed event
- *
- * @param h The handler of the event
- * @param what The id of the event
- */
- public void registerForHandoverNeededEvent(Handler h, int what) {
- if (h != null) {
- mHandoverNeededEventRegistrants.addUnique(h, what, null);
- }
- }
-
- /**
- * Unregister for data handover needed event
- *
- * @param h The handler
- */
- public void unregisterForHandoverNeededEvent(Handler h) {
- mHandoverNeededEventRegistrants.remove(h);
- }
-
- /**
- * Registers the data throttler with DcTracker.
- */
- public void registerDataThrottler(DataThrottler dataThrottler) {
- if (mAccessNetworksManager != null) {
- mAccessNetworksManager.registerDataThrottler(dataThrottler);
- }
- }
-
- private void logl(String s) {
- log(s);
- mLocalLog.log(s);
- }
-
- private void log(String s) {
- Rlog.d(mLogTag, s);
- }
-
- private void loge(String s) {
- Rlog.e(mLogTag, s);
- }
-}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
new file mode 100644
index 0000000..9a75b43
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.AsyncResult;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.EmergencyScanType;
+import android.telephony.DomainSelector;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+
+/**
+ * Manages the information of request and the callback binder.
+ */
+public class DomainSelectionConnection {
+
+ private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
+
+ protected static final int EVENT_EMERGENCY_NETWORK_SCAN_RESULT = 1;
+ protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2;
+
+ /** Callback to receive responses from DomainSelectionConnection. */
+ public interface DomainSelectionConnectionCallback {
+ /**
+ * Notifies that selection has terminated because there is no decision that can be made
+ * or a timeout has occurred. The call should be terminated when this method is called.
+ *
+ * @param cause Indicates the reason.
+ */
+ void onSelectionTerminated(@DisconnectCauses int cause);
+ }
+
+ /** An internal class implementing {@link TransportSelectorCallback} interface. */
+ private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback {
+ @Override
+ public void onCreated(@NonNull DomainSelector selector) {
+ mDomainSelector = selector;
+ DomainSelectionConnection.this.onCreated();
+ }
+
+ @Override
+ public void onWlanSelected(boolean useEmergencyPdn) {
+ DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn);
+ }
+
+ @Override
+ public @NonNull WwanSelectorCallback onWwanSelected() {
+ if (mWwanSelectorCallback == null) {
+ mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+ }
+ DomainSelectionConnection.this.onWwanSelected();
+ return mWwanSelectorCallback;
+ }
+
+ @Override
+ public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) {
+ if (mWwanSelectorCallback == null) {
+ mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+ }
+ if (mWwanSelectedExecutor == null) {
+ mWwanSelectedExecutor = Executors.newSingleThreadExecutor();
+ }
+ mWwanSelectedExecutor.execute(() -> {
+ DomainSelectionConnection.this.onWwanSelected();
+ consumer.accept(mWwanSelectorCallback);
+ });
+ }
+
+ @Override
+ public void onSelectionTerminated(int cause) {
+ DomainSelectionConnection.this.onSelectionTerminated(cause);
+ dispose();
+ }
+ }
+
+ /** An internal class implementing {@link WwanSelectorCallback} interface. */
+ private final class WwanSelectorCallbackWrapper
+ implements WwanSelectorCallback, CancellationSignal.OnCancelListener {
+ @Override
+ public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
+ @EmergencyScanType int scanType, @NonNull CancellationSignal signal,
+ @NonNull Consumer<EmergencyRegResult> consumer) {
+ if (signal != null) signal.setOnCancelListener(this);
+ mResultCallback = consumer;
+ initHandler();
+ DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
+ preferredNetworks.stream().mapToInt(Integer::intValue).toArray(), scanType);
+ }
+
+ @Override
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn);
+ }
+
+ @Override
+ public void onCancel() {
+ DomainSelectionConnection.this.onCancel();
+ }
+ }
+
+ protected final class DomainSelectionConnectionHandler extends Handler {
+ DomainSelectionConnectionHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_EMERGENCY_NETWORK_SCAN_RESULT:
+ mIsWaitingForScanResult = false;
+ if (mResultCallback == null) break;
+ ar = (AsyncResult) msg.obj;
+ EmergencyRegResult regResult = (EmergencyRegResult) ar.result;
+ if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult);
+ CompletableFuture.runAsync(
+ () -> mResultCallback.accept(regResult),
+ mController.getDomainSelectionServiceExecutor()).join();
+ break;
+ case EVENT_QUALIFIED_NETWORKS_CHANGED:
+ onQualifiedNetworksChanged();
+ break;
+ default:
+ loge("handleMessage unexpected msg=" + msg.what);
+ break;
+ }
+ }
+ }
+
+ protected String mTag = "DomainSelectionConnection";
+
+ private final LocalLog mLocalLog = new LocalLog(30);
+ private final @NonNull TransportSelectorCallback mTransportSelectorCallback;
+
+ /**
+ * Controls the communication between {@link DomainSelectionConnection} and
+ * {@link DomainSelectionService}.
+ */
+ private final @NonNull DomainSelectionController mController;
+ /** Indicates whether the requested service is for emergency services. */
+ private final boolean mIsEmergency;
+
+ /** Interface to receive the request to trigger emergency network scan and selected domain. */
+ private @Nullable WwanSelectorCallback mWwanSelectorCallback;
+ /** Interface to return the result of emergency network scan. */
+ private @Nullable Consumer<EmergencyRegResult> mResultCallback;
+ /** Interface to the {@link DomainSelector} created for this service. */
+ private @Nullable DomainSelector mDomainSelector;
+
+ /** The slot requested this connection. */
+ protected @NonNull Phone mPhone;
+ /** The requested domain selector type. */
+ private @DomainSelectionService.SelectorType int mSelectorType;
+
+ /** The attributes required to determine the domain. */
+ private @Nullable DomainSelectionService.SelectionAttributes mSelectionAttributes;
+
+ private @Nullable Looper mLooper;
+ protected @Nullable DomainSelectionConnectionHandler mHandler;
+ private boolean mRegisteredRegistrant;
+ private boolean mIsWaitingForScanResult;
+
+ private @NonNull AndroidFuture<Integer> mOnComplete;
+
+ private @Nullable Executor mWwanSelectedExecutor;
+
+ /**
+ * Creates an instance.
+ *
+ * @param phone For which this service is requested.
+ * @param selectorType Indicates the type of the requested service.
+ * @param isEmergency Indicates whether this request is for emergency service.
+ * @param controller The controller to communicate with the domain selection service.
+ */
+ public DomainSelectionConnection(@NonNull Phone phone,
+ @DomainSelectionService.SelectorType int selectorType, boolean isEmergency,
+ @NonNull DomainSelectionController controller) {
+ mController = controller;
+ mPhone = phone;
+ mSelectorType = selectorType;
+ mIsEmergency = isEmergency;
+
+ mTransportSelectorCallback = new TransportSelectorCallbackWrapper();
+ mOnComplete = new AndroidFuture<>();
+ }
+
+ /**
+ * Returns the attributes required to determine the domain for a telephony service.
+ *
+ * @return The attributes required to determine the domain.
+ */
+ public @Nullable DomainSelectionService.SelectionAttributes getSelectionAttributes() {
+ return mSelectionAttributes;
+ }
+
+ /**
+ * Returns the interface for the callbacks.
+ *
+ * @return The {@link TransportSelectorCallback} interface.
+ */
+ @VisibleForTesting
+ public @NonNull TransportSelectorCallback getTransportSelectorCallback() {
+ return mTransportSelectorCallback;
+ }
+
+ /**
+ * Returns the {@link CompletableFuture} to receive the selected domain.
+ *
+ * @return The callback to receive response.
+ */
+ public @NonNull CompletableFuture<Integer> getCompletableFuture() {
+ return mOnComplete;
+ }
+
+ /**
+ * Returs the {@link Phone} which requested this connection.
+ *
+ * @return The {@link Phone} instance.
+ */
+ public @NonNull Phone getPhone() {
+ return mPhone;
+ }
+
+ /**
+ * Requests the domain selection servic to select a domain.
+ *
+ * @param attr The attributes required to determine the domain.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr) {
+ mSelectionAttributes = attr;
+ mController.selectDomain(attr, getTransportSelectorCallback());
+ }
+
+ /**
+ * Notifies that {@link DomainSelector} instance has been created for the selection request.
+ */
+ public void onCreated() {
+ // Can be overridden if required
+ }
+
+ /**
+ * Notifies that WLAN transport has been selected.
+ */
+ public void onWlanSelected() {
+ // Can be overridden.
+ }
+
+ /**
+ * Notifies that WLAN transport has been selected.
+ *
+ * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not.
+ */
+ public void onWlanSelected(boolean useEmergencyPdn) {
+ // Can be overridden.
+ onWlanSelected();
+ }
+
+ /**
+ * Notifies that WWAN transport has been selected.
+ */
+ public void onWwanSelected() {
+ // Can be overridden.
+ }
+
+ /**
+ * Notifies that selection has terminated because there is no decision that can be made
+ * or a timeout has occurred. The call should be terminated when this method is called.
+ *
+ * @param cause Indicates the reason.
+ */
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ // Can be overridden.
+ }
+
+ /**
+ * Requests the emergency network scan.
+ *
+ * @param preferredNetworks The ordered list of preferred networks to scan.
+ * @param scanType Indicates the scan preference, such as full service or limited service.
+ */
+ public void onRequestEmergencyNetworkScan(
+ @NonNull @RadioAccessNetworkType int[] preferredNetworks,
+ @EmergencyScanType int scanType) {
+ // Can be overridden if required
+ if (!mRegisteredRegistrant) {
+ mPhone.registerForEmergencyNetworkScan(mHandler,
+ EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
+ mRegisteredRegistrant = true;
+ }
+ mIsWaitingForScanResult = true;
+ mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
+ }
+
+ /**
+ * Notifies the domain selected.
+ *
+ * @param domain The selected domain.
+ */
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
+ // Can be overridden if required
+ CompletableFuture<Integer> future = getCompletableFuture();
+ future.complete(domain);
+ }
+
+ /**
+ * Notifies the domain selected.
+ *
+ * @param domain The selected domain.
+ * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not.
+ */
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ // Can be overridden if required
+ onDomainSelected(domain);
+ }
+
+ /**
+ * Notifies that the emergency network scan is canceled.
+ */
+ public void onCancel() {
+ // Can be overridden if required
+ onCancel(false);
+ }
+
+ private void onCancel(boolean resetScan) {
+ if (mIsWaitingForScanResult) {
+ mIsWaitingForScanResult = false;
+ mPhone.cancelEmergencyNetworkScan(resetScan, null);
+ }
+ }
+
+ /**
+ * Cancels an ongoing selection operation. It is up to the {@link DomainSelectionService}
+ * to clean up all ongoing operations with the framework.
+ */
+ public void cancelSelection() {
+ if (mDomainSelector == null) return;
+ mDomainSelector.cancelSelection();
+ dispose();
+ }
+
+ /**
+ * Requests the domain selection service to reselect a domain.
+ *
+ * @param attr The attributes required to determine the domain.
+ * @return The callback to receive the response.
+ */
+ public @NonNull CompletableFuture<Integer> reselectDomain(
+ @NonNull DomainSelectionService.SelectionAttributes attr) {
+ mSelectionAttributes = attr;
+ if (mDomainSelector == null) return null;
+ mOnComplete = new AndroidFuture<>();
+ mDomainSelector.reselectDomain(attr);
+ return mOnComplete;
+ }
+
+ /**
+ * Finishes the selection procedure and cleans everything up.
+ */
+ public void finishSelection() {
+ if (mDomainSelector == null) return;
+ mDomainSelector.finishSelection();
+ dispose();
+ }
+
+ /** Indicates that the service connection has been removed. */
+ public void onServiceDisconnected() {
+ // Can be overridden.
+ dispose();
+ }
+
+ private void dispose() {
+ if (mRegisteredRegistrant) {
+ mPhone.unregisterForEmergencyNetworkScan(mHandler);
+ mRegisteredRegistrant = false;
+ }
+ onCancel(true);
+ mController.removeConnection(this);
+ if (mLooper != null) mLooper.quitSafely();
+ mLooper = null;
+ mHandler = null;
+ }
+
+ protected void initHandler() {
+ if (mLooper == null) {
+ HandlerThread handlerThread = new HandlerThread(mTag);
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ }
+ if (mHandler == null) mHandler = new DomainSelectionConnectionHandler(mLooper);
+ }
+
+ /**
+ * Notifies the change of qualified networks.
+ */
+ protected void onQualifiedNetworksChanged() {
+ if (mIsEmergency
+ && (mSelectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)) {
+ // DomainSelectionConnection for emergency calls shall override this.
+ throw new IllegalStateException("DomainSelectionConnection for emergency calls"
+ + " should override onQualifiedNetworksChanged()");
+ }
+ }
+
+ /**
+ * Dumps local log.
+ */
+ public void dump(@NonNull PrintWriter printWriter) {
+ mLocalLog.dump(printWriter);
+ }
+
+ protected void logd(String msg) {
+ Log.d(mTag, msg);
+ }
+
+ protected void logi(String msg) {
+ Log.i(mTag, msg);
+ mLocalLog.log(msg);
+ }
+
+ protected void loge(String msg) {
+ Log.e(mTag, msg);
+ mLocalLog.log(msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
new file mode 100644
index 0000000..52c9960
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.BarringInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the connection to {@link DomainSelectionService}.
+ */
+public class DomainSelectionController {
+ private static final String TAG = "DomainSelectionController";
+ private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
+
+ private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+ private static final int EVENT_BARRING_INFO_CHANGED = 2;
+
+ private final HandlerThread mHandlerThread =
+ new HandlerThread("DomainSelectionControllerHandler");
+
+ private final DomainSelectionService mDomainSelectionService;
+ private final Handler mHandler;
+ // Only added or removed, never accessed on purpose.
+ private final LocalLog mLocalLog = new LocalLog(30);
+
+ protected final Object mLock = new Object();
+ protected final Context mContext;
+
+ protected final int[] mConnectionCounts;
+ private final ArrayList<DomainSelectionConnection> mConnections = new ArrayList<>();
+
+ private final class DomainSelectionControllerHandler extends Handler {
+ DomainSelectionControllerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_SERVICE_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ updateServiceState((Phone) ar.userObj, (ServiceState) ar.result);
+ break;
+ case EVENT_BARRING_INFO_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ updateBarringInfo((Phone) ar.userObj, (BarringInfo) ar.result);
+ break;
+ default:
+ loge("unexpected event=" + msg.what);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * @param context Context object from hosting application.
+ * @param service The {@link DomainSelectionService} instance.
+ */
+ public DomainSelectionController(@NonNull Context context,
+ @NonNull DomainSelectionService service) {
+ this(context, service, null);
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * @param context Context object from hosting application.
+ * @param service The {@link DomainSelectionService} instance.
+ * @param looper Handles event messages.
+ */
+ @VisibleForTesting
+ public DomainSelectionController(@NonNull Context context,
+ @NonNull DomainSelectionService service, @Nullable Looper looper) {
+ mContext = context;
+ mDomainSelectionService = service;
+
+ if (looper == null) {
+ mHandlerThread.start();
+ looper = mHandlerThread.getLooper();
+ }
+ mHandler = new DomainSelectionControllerHandler(looper);
+
+ int numPhones = TelephonyManager.getDefault().getActiveModemCount();
+ mConnectionCounts = new int[numPhones];
+ for (int i = 0; i < numPhones; i++) {
+ mConnectionCounts[i] = 0;
+ }
+ }
+
+ /**
+ * Returns a {@link DomainSelectionConnection} instance.
+ *
+ * @param phone Indicates who requests the service.
+ * @param selectorType Indicates the selector type requested.
+ * @param isEmergency Indicates whether this is for emergency service.
+ * @return A {@link DomainSelectiionConnection} instance for the requested service.
+ * Returns {@code null} if the requested service is not supported.
+ */
+ public @Nullable DomainSelectionConnection getDomainSelectionConnection(
+ @NonNull Phone phone,
+ @DomainSelectionService.SelectorType int selectorType,
+ boolean isEmergency) {
+ DomainSelectionConnection c = null;
+
+ if (selectorType == SELECTOR_TYPE_CALLING) {
+ if (isEmergency) {
+ c = new EmergencyCallDomainSelectionConnection(phone, this);
+ } else {
+ c = new NormalCallDomainSelectionConnection(phone, this);
+ }
+ } else if (selectorType == SELECTOR_TYPE_SMS) {
+ if (isEmergency) {
+ c = new EmergencySmsDomainSelectionConnection(phone, this);
+ } else {
+ c = new SmsDomainSelectionConnection(phone, this);
+ }
+ }
+
+ addConnection(c);
+ return c;
+ }
+
+ private void addConnection(@Nullable DomainSelectionConnection c) {
+ if (c == null) return;
+ mConnections.add(c);
+ registerForStateChange(c);
+ }
+
+ /**
+ * Releases resources for this connection.
+ */
+ public void removeConnection(@Nullable DomainSelectionConnection c) {
+ if (c == null) return;
+ mConnections.remove(c);
+ unregisterForStateChange(c);
+ }
+
+ /**
+ * Requests the domain selection.
+ *
+ * @param attr Attributetes required to determine the domain.
+ * @param callback A callback to receive the response.
+ */
+ public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr,
+ @NonNull TransportSelectorCallback callback) {
+ if (attr == null || callback == null) return;
+ if (DBG) logd("selectDomain");
+
+ Executor e = mDomainSelectionService.getCachedExecutor();
+ e.execute(() -> mDomainSelectionService.onDomainSelection(attr, callback));
+ }
+
+ /**
+ * Notifies the change in {@link ServiceState} for a specific slot.
+ *
+ * @param phone {@link Phone} which the state changed.
+ * @param serviceState Updated {@link ServiceState}.
+ */
+ private void updateServiceState(Phone phone, ServiceState serviceState) {
+ if (phone == null || serviceState == null) return;
+ if (DBG) logd("updateServiceState phoneId=" + phone.getPhoneId());
+
+ Executor e = mDomainSelectionService.getCachedExecutor();
+ e.execute(() -> mDomainSelectionService.onServiceStateUpdated(
+ phone.getPhoneId(), phone.getSubId(), serviceState));
+ }
+
+ /**
+ * Notifies the change in {@link BarringInfo} for a specific slot.
+ *
+ * @param phone {@link Phone} which the state changed.
+ * @param info Updated {@link BarringInfo}.
+ */
+ private void updateBarringInfo(Phone phone, BarringInfo info) {
+ if (phone == null || info == null) return;
+ if (DBG) logd("updateBarringInfo phoneId=" + phone.getPhoneId());
+
+ Executor e = mDomainSelectionService.getCachedExecutor();
+ e.execute(() -> mDomainSelectionService.onBarringInfoUpdated(
+ phone.getPhoneId(), phone.getSubId(), info));
+ }
+
+ /**
+ * Registers for the notification of {@link ServiceState} and {@link BarringInfo}.
+ *
+ * @param c {@link DomainSelectionConnection} for which the registration is requested.
+ */
+ private void registerForStateChange(DomainSelectionConnection c) {
+ Phone phone = c.getPhone();
+ int count = mConnectionCounts[phone.getPhoneId()];
+ if (count < 0) count = 0;
+
+ mConnectionCounts[phone.getPhoneId()] = count + 1;
+ if (count > 0) return;
+
+ phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, phone);
+ phone.mCi.registerForBarringInfoChanged(mHandler, EVENT_BARRING_INFO_CHANGED, phone);
+
+ updateServiceState(phone, phone.getServiceStateTracker().getServiceState());
+ updateBarringInfo(phone, phone.mCi.getLastBarringInfo());
+ }
+
+ /**
+ * Unregisters for the notification of {@link ServiceState} and {@link BarringInfo}.
+ *
+ * @param c {@link DomainSelectionConnection} for which the unregistration is requested.
+ */
+ private void unregisterForStateChange(DomainSelectionConnection c) {
+ Phone phone = c.getPhone();
+ int count = mConnectionCounts[phone.getPhoneId()];
+ if (count < 1) count = 1;
+
+ mConnectionCounts[phone.getPhoneId()] = count - 1;
+ if (count > 1) return;
+
+ phone.unregisterForServiceStateChanged(mHandler);
+ phone.mCi.unregisterForBarringInfoChanged(mHandler);
+ }
+
+ /**
+ * Notifies the {@link DomainSelectionConnection} instances registered
+ * of the service disconnection.
+ */
+ private void notifyServiceDisconnected() {
+ for (DomainSelectionConnection c : mConnections) {
+ c.onServiceDisconnected();
+ }
+ }
+
+ /**
+ * Gets the {@link Executor} which executes methods of {@link DomainSelectionService.}
+ * @return {@link Executor} instance.
+ */
+ public @NonNull Executor getDomainSelectionServiceExecutor() {
+ return mDomainSelectionService.getCachedExecutor();
+ }
+
+ /**
+ * Dumps logcal log
+ */
+ public void dump(@NonNull PrintWriter printWriter) {
+ mLocalLog.dump(printWriter);
+ }
+
+ private void logd(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private void logi(String msg) {
+ Log.i(TAG, msg);
+ mLocalLog.log(msg);
+ }
+
+ private void loge(String msg) {
+ Log.e(TAG, msg);
+ mLocalLog.log(msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
new file mode 100644
index 0000000..cbb74fa
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
+import static com.android.internal.telephony.RIL.RADIO_HAL_VERSION_2_1;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.DomainSelectionService;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * This class is an entry point to provide whether the AOSP domain selection is supported or not,
+ * and bind the {@link DomainSelectionController} with the given {@link DomainSelectionService} to
+ * provide a specific {@link DomainSelectionConnection} object for communicating with each domain
+ * selector.
+ */
+public class DomainSelectionResolver {
+ private static final String TAG = DomainSelectionResolver.class.getSimpleName();
+ private static DomainSelectionResolver sInstance = null;
+
+ /**
+ * Creates the DomainSelectionResolver singleton instance.
+ *
+ * @param context The context of the application.
+ * @param deviceConfigEnabled The flag to indicate whether or not the device supports
+ * the domain selection service or not.
+ */
+ public static void make(Context context, boolean deviceConfigEnabled) {
+ if (sInstance == null) {
+ sInstance = new DomainSelectionResolver(context, deviceConfigEnabled);
+ }
+ }
+
+ /**
+ * Returns the singleton instance of DomainSelectionResolver.
+ *
+ * @return A {@link DomainSelectionResolver} instance.
+ */
+ public static DomainSelectionResolver getInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("DomainSelectionResolver is not ready!");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Sets a {@link DomainSelectionResolver} for injecting mock DomainSelectionResolver.
+ *
+ * @param resolver A {@link DomainSelectionResolver} instance to test.
+ */
+ @VisibleForTesting
+ public static void setDomainSelectionResolver(DomainSelectionResolver resolver) {
+ sInstance = resolver;
+ }
+
+ /**
+ * Testing interface for injecting mock DomainSelectionController.
+ */
+ @VisibleForTesting
+ public interface DomainSelectionControllerFactory {
+ /**
+ * Returns a {@link DomainSelectionController} created using the specified
+ * context and {@link DomainSelectionService} instance.
+ */
+ DomainSelectionController create(@NonNull Context context,
+ @NonNull DomainSelectionService service);
+ }
+
+ private DomainSelectionControllerFactory mDomainSelectionControllerFactory =
+ new DomainSelectionControllerFactory() {
+ @Override
+ public DomainSelectionController create(@NonNull Context context,
+ @NonNull DomainSelectionService service) {
+ return new DomainSelectionController(context, service);
+ }
+ };
+
+ // Persistent Logging
+ private final LocalLog mEventLog = new LocalLog(10);
+ private final Context mContext;
+ // The flag to indicate whether the device supports the domain selection service or not.
+ private final boolean mDeviceConfigEnabled;
+ // DomainSelectionController, which are bound to DomainSelectionService.
+ private DomainSelectionController mController;
+
+ public DomainSelectionResolver(Context context, boolean deviceConfigEnabled) {
+ mContext = context;
+ mDeviceConfigEnabled = deviceConfigEnabled;
+ logi("DomainSelectionResolver created: device-config=" + deviceConfigEnabled);
+ }
+
+ /**
+ * Checks if the device supports the domain selection service to route the call / SMS /
+ * supplementary services to the appropriate domain.
+ * This checks the device-config and Radio HAL version for supporting the domain selection.
+ * The domain selection requires the Radio HAL version greater than or equal to 2.1.
+ *
+ * @return {@code true} if the domain selection is supported on the device,
+ * {@code false} otherwise.
+ */
+ public boolean isDomainSelectionSupported() {
+ return mDeviceConfigEnabled && PhoneFactory.getDefaultPhone()
+ .getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1);
+ }
+
+ /**
+ * Returns a {@link DomainSelectionConnection} instance.
+ *
+ * @param phone The Phone instance for witch this request is.
+ * @param selectorType Indicates the selector type requested.
+ * @param isEmergency Indicates whether this is for emergency service.
+ * @throws IllegalStateException If the {@link DomainSelectionController} is not created
+ * because {@link #initialize} method is not called even if the domain selection is
+ * supported.
+ * @return A {@link DomainSelectionConnection} instance if the device supports
+ * AOSP domain selection and IMS is available or {@code null} otherwise.
+ */
+ public @Nullable DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+ @DomainSelectionService.SelectorType int selectorType, boolean isEmergency) {
+ if (mController == null) {
+ // If the caller calls this method without checking whether the domain selection
+ // is supported or not, this exception will be thrown.
+ throw new IllegalStateException("DomainSelection is not supported!");
+ }
+
+ if (phone == null || !phone.isImsAvailable()) {
+ // If ImsPhone is null or the binder of ImsService is not available,
+ // CS domain is used for the telephony services.
+ return null;
+ }
+
+ return mController.getDomainSelectionConnection(phone, selectorType, isEmergency);
+ }
+
+ /** Sets a factory interface for creating {@link DomainSelectionController} instance. */
+ @VisibleForTesting
+ public void setDomainSelectionControllerFactory(DomainSelectionControllerFactory factory) {
+ mDomainSelectionControllerFactory = factory;
+ }
+
+ /**
+ * Needs to be called after the constructor to create a {@link DomainSelectionController} that
+ * is bound to the given {@link DomainSelectionService}.
+ *
+ * @param service A {@link DomainSelectionService} to be bound.
+ */
+ public void initialize(@NonNull DomainSelectionService service) {
+ logi("Initialize.");
+ mController = mDomainSelectionControllerFactory.create(mContext, service);
+ }
+
+ /**
+ * Dumps this instance into a readable format for dumpsys usage.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("Resolver:");
+ ipw.increaseIndent();
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ mEventLog.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+
+ ipw.println("Controller:");
+ ipw.increaseIndent();
+ DomainSelectionController controller = mController;
+ if (controller == null) {
+ ipw.println("no active controller");
+ } else {
+ controller.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+
+ private void logi(String s) {
+ Log.i(TAG, s);
+ mEventLog.log(s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
new file mode 100644
index 0000000..5f3c3b6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
+import static com.android.internal.telephony.PhoneConstants.DOMAIN_NON_3GPP_PS;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.NetCapability;
+import android.telephony.DomainSelectionService;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Manages the information of request and the callback binder for emergency calling.
+ */
+public class EmergencyCallDomainSelectionConnection extends DomainSelectionConnection {
+
+ private static final boolean DBG = false;
+
+ private @NonNull EmergencyStateTracker mEmergencyStateTracker = null;
+ private @Nullable DomainSelectionConnectionCallback mCallback;
+ private @TransportType int mPreferredTransportType = TRANSPORT_TYPE_INVALID;
+
+ /**
+ * Create an instance.
+ *
+ * @param phone For which this service is requested.
+ * @param controller The controller to communicate with the domain selection service.
+ */
+ public EmergencyCallDomainSelectionConnection(@NonNull Phone phone,
+ @NonNull DomainSelectionController controller) {
+ this(phone, controller, EmergencyStateTracker.getInstance());
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param phone For which this service is requested.
+ * @param controller The controller to communicate with the domain selection service.
+ * @param tracker The {@link EmergencyStateTracker} instance.
+ */
+ @VisibleForTesting
+ public EmergencyCallDomainSelectionConnection(@NonNull Phone phone,
+ @NonNull DomainSelectionController controller, @NonNull EmergencyStateTracker tracker) {
+ super(phone, SELECTOR_TYPE_CALLING, true, controller);
+ mTag = "EmergencyCallDomainSelectionConnection";
+
+ mEmergencyStateTracker = tracker;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWlanSelected(boolean useEmergencyPdn) {
+ mEmergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WLAN);
+ if (useEmergencyPdn) {
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ int transportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY);
+ logi("onWlanSelected curTransportType=" + transportType);
+ if (transportType != TRANSPORT_TYPE_WLAN) {
+ changePreferredTransport(TRANSPORT_TYPE_WLAN);
+ return;
+ }
+ }
+
+ CompletableFuture<Integer> future = getCompletableFuture();
+ if (future != null) future.complete(DOMAIN_NON_3GPP_PS);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWwanSelected() {
+ mEmergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ if (mCallback != null) mCallback.onSelectionTerminated(cause);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ if (domain == DOMAIN_PS && useEmergencyPdn) {
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ int transportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY);
+ logi("onDomainSelected curTransportType=" + transportType);
+ if (transportType != TRANSPORT_TYPE_WWAN) {
+ changePreferredTransport(TRANSPORT_TYPE_WWAN);
+ return;
+ }
+ }
+ super.onDomainSelected(domain, useEmergencyPdn);
+ }
+
+ /**
+ * Request a domain for emergency call.
+ *
+ * @param attr The attributes required to determine the domain.
+ * @param callback A callback to receive the response.
+ * @return the callback to receive the response.
+ */
+ public @NonNull CompletableFuture<Integer> createEmergencyConnection(
+ @NonNull DomainSelectionService.SelectionAttributes attr,
+ @NonNull DomainSelectionConnectionCallback callback) {
+ mCallback = callback;
+ selectDomain(attr);
+ return getCompletableFuture();
+ }
+
+ private void changePreferredTransport(@TransportType int transportType) {
+ logi("changePreferredTransport " + transportType);
+ initHandler();
+ mPreferredTransportType = transportType;
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ anm.registerForQualifiedNetworksChanged(mHandler, EVENT_QUALIFIED_NETWORKS_CHANGED);
+ mPhone.notifyEmergencyDomainSelected(transportType);
+ }
+
+ private AccessNetworksManager.AccessNetworksManagerCallback mPreferredTransportCallback =
+ new AccessNetworksManager.AccessNetworksManagerCallback(Runnable::run) {
+ @Override
+ public void onPreferredTransportChanged(@NetCapability int capability) {
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onQualifiedNetworksChanged() {
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ int preferredTransport = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY);
+ logi("onQualifiedNetworksChanged preferred=" + mPreferredTransportType
+ + ", current=" + preferredTransport);
+ if (preferredTransport == mPreferredTransportType) {
+ CompletableFuture<Integer> future = getCompletableFuture();
+ if (future != null) {
+ if (preferredTransport == TRANSPORT_TYPE_WLAN) {
+ future.complete(DOMAIN_NON_3GPP_PS);
+ } else {
+ future.complete(DOMAIN_PS);
+ }
+ }
+ anm.unregisterForQualifiedNetworksChanged(mHandler);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void cancelSelection() {
+ logi("cancelSelection");
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ anm.unregisterForQualifiedNetworksChanged(mHandler);
+ super.cancelSelection();
+ }
+
+ /**
+ * Returns the attributes required to determine the domain for a telephony service.
+ *
+ * @param slotId The slot identifier.
+ * @param subId The subscription identifier.
+ * @param exited {@code true} if the request caused the device to move out of airplane mode.
+ * @param callId The call identifier.
+ * @param number The dialed number.
+ * @param callFailCause The reason why the last CS attempt failed.
+ * @param imsReasonInfo The reason why the last PS attempt failed.
+ * @param emergencyRegResult The current registration result for emergency services.
+ * @return The attributes required to determine the domain.
+ */
+ public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes(
+ int slotId, int subId, boolean exited,
+ @NonNull String callId, @NonNull String number, int callFailCause,
+ @Nullable ImsReasonInfo imsReasonInfo,
+ @Nullable EmergencyRegResult emergencyRegResult) {
+ DomainSelectionService.SelectionAttributes.Builder builder =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ slotId, subId, SELECTOR_TYPE_CALLING)
+ .setEmergency(true)
+ .setExitedFromAirplaneMode(exited)
+ .setCallId(callId)
+ .setNumber(number)
+ .setCsDisconnectCause(callFailCause);
+
+ if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
+ if (emergencyRegResult != null) builder.setEmergencyRegResult(emergencyRegResult);
+
+ return builder.build();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java
new file mode 100644
index 0000000..efcdf11
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnection.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import android.annotation.NonNull;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.data.ApnSetting;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+
+/**
+ * Manages the information of request and the callback binder for an emergency SMS.
+ */
+public class EmergencySmsDomainSelectionConnection extends SmsDomainSelectionConnection {
+ private final Object mLock = new Object();
+ private @NonNull EmergencyStateTracker mEmergencyStateTracker;
+ private @TransportType int mPreferredTransportType =
+ AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+
+ public EmergencySmsDomainSelectionConnection(
+ Phone phone, DomainSelectionController controller) {
+ this(phone, controller, EmergencyStateTracker.getInstance());
+ }
+
+ @VisibleForTesting
+ public EmergencySmsDomainSelectionConnection(Phone phone,
+ DomainSelectionController controller, EmergencyStateTracker tracker) {
+ super(phone, controller, true);
+ mTag = "DomainSelectionConnection-EmergencySMS";
+ mEmergencyStateTracker = tracker;
+ }
+
+ /**
+ * Notifies that WLAN transport has been selected.
+ *
+ * @param useEmergencyPdn A flag specifying whether Wi-Fi emergency service uses emergency PDN
+ * or not.
+ */
+ @Override
+ public void onWlanSelected(boolean useEmergencyPdn) {
+ synchronized (mLock) {
+ if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
+ logi("Domain selection completion is in progress");
+ return;
+ }
+
+ mEmergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WLAN);
+
+ if (useEmergencyPdn) {
+ // Change the transport type if the current preferred transport type for
+ // an emergency is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}.
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY)
+ != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ // The {@link #onDomainSlected()} will be called after the preferred transport
+ // is successfully changed and notified from the {@link AccessNetworksManager}.
+ return;
+ }
+ }
+
+ super.onWlanSelected(useEmergencyPdn);
+ }
+ }
+
+ @Override
+ public void onWwanSelected() {
+ mEmergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN);
+ }
+
+ /**
+ * Notifies the domain selected.
+ *
+ * @param domain The selected domain.
+ * @param useEmergencyPdn A flag specifying whether emergency service uses emergency PDN or not.
+ */
+ @Override
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
+ synchronized (mLock) {
+ if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
+ logi("Domain selection completion is in progress");
+ return;
+ }
+
+ if (useEmergencyPdn && domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ // Change the transport type if the current preferred transport type for
+ // an emergency is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY)
+ != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ // The {@link #onDomainSlected()} will be called after the preferred transport
+ // is successfully changed and notified from the {@link AccessNetworksManager}.
+ return;
+ }
+ }
+
+ super.onDomainSelected(domain, useEmergencyPdn);
+ }
+ }
+
+ @Override
+ public void finishSelection() {
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+
+ synchronized (mLock) {
+ if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
+ mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ anm.unregisterForQualifiedNetworksChanged(mHandler);
+ }
+ }
+
+ super.finishSelection();
+ }
+
+ @Override
+ protected void onQualifiedNetworksChanged() {
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ int preferredTransportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY);
+
+ synchronized (mLock) {
+ if (preferredTransportType == mPreferredTransportType) {
+ mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ anm.unregisterForQualifiedNetworksChanged(mHandler);
+ }
+ }
+ }
+
+ private void changePreferredTransport(@TransportType int transportType) {
+ logi("Change preferred transport: " + transportType);
+ initHandler();
+ mPreferredTransportType = transportType;
+ AccessNetworksManager anm = mPhone.getAccessNetworksManager();
+ anm.registerForQualifiedNetworksChanged(mHandler, EVENT_QUALIFIED_NETWORKS_CHANGED);
+ mPhone.notifyEmergencyDomainSelected(transportType);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
new file mode 100644
index 0000000..e157d24
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.EmergencyScanType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.telephony.Phone;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Manages the information of request and the callback binder for normal calling.
+ */
+public class NormalCallDomainSelectionConnection extends DomainSelectionConnection {
+
+ private static final boolean DBG = false;
+
+ private static final String PREFIX_WPS = "*272";
+
+ // WPS prefix when CLIR is being activated for the call.
+ private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272";
+
+ // WPS prefix when CLIR is being deactivated for the call.
+ private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272";
+
+
+ private @Nullable DomainSelectionConnectionCallback mCallback;
+
+ /**
+ * Create an instance.
+ *
+ * @param phone For which this service is requested.
+ * @param controller The controller to communicate with the domain selection service.
+ */
+ public NormalCallDomainSelectionConnection(@NonNull Phone phone,
+ @NonNull DomainSelectionController controller) {
+ super(phone, SELECTOR_TYPE_CALLING, false, controller);
+ mTag = "NormalCallDomainSelectionConnection";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWlanSelected() {
+ CompletableFuture<Integer> future = getCompletableFuture();
+ future.complete(NetworkRegistrationInfo.DOMAIN_PS);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWwanSelected() {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ if (mCallback != null) mCallback.onSelectionTerminated(cause);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRequestEmergencyNetworkScan(@RadioAccessNetworkType int[] preferredNetworks,
+ @EmergencyScanType int scanType) {
+ // Not expected with normal calling.
+ // Override to prevent abnormal behavior.
+ }
+
+ /**
+ * Request a domain for normal call.
+ *
+ * @param attr The attributes required to determine the domain.
+ * @param callback A callback to receive the response.
+ * @return A {@link CompletableFuture} callback to receive the result.
+ */
+ public CompletableFuture<Integer> createNormalConnection(
+ @NonNull DomainSelectionService.SelectionAttributes attr,
+ @NonNull DomainSelectionConnectionCallback callback) {
+ mCallback = callback;
+ selectDomain(attr);
+ return getCompletableFuture();
+ }
+
+ /**
+ * Returns the attributes required to determine the domain for a normal call.
+ *
+ * @param slotId The slot identifier.
+ * @param subId The subscription identifier.
+ * @param callId The call identifier.
+ * @param number The dialed number.
+ * @param isVideoCall flag for video call.
+ * @param callFailCause The reason why the last CS attempt failed.
+ * @param imsReasonInfo The reason why the last PS attempt failed.
+ * @return The attributes required to determine the domain.
+ */
+ public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes(
+ int slotId, int subId, @NonNull String callId, @NonNull String number,
+ boolean isVideoCall, int callFailCause, @Nullable ImsReasonInfo imsReasonInfo) {
+
+ DomainSelectionService.SelectionAttributes.Builder builder =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ slotId, subId, SELECTOR_TYPE_CALLING)
+ .setEmergency(false)
+ .setCallId(callId)
+ .setNumber(number)
+ .setCsDisconnectCause(callFailCause)
+ .setVideoCall(isVideoCall);
+
+ if (imsReasonInfo != null) {
+ builder.setPsDisconnectCause(imsReasonInfo);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Check if the call is Wireless Priority Service call
+ * @param dialString The number being dialed.
+ * @return {@code true} if dialString matches WPS pattern and {@code false} otherwise.
+ */
+ public static boolean isWpsCall(String dialString) {
+ return (dialString != null) && (dialString.startsWith(PREFIX_WPS)
+ || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
+ || dialString.startsWith(PREFIX_WPS_CLIR_DEACTIVATE));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
new file mode 100644
index 0000000..36a7b17
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import android.annotation.NonNull;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.DomainSelectionService;
+import android.telephony.NetworkRegistrationInfo;
+
+import com.android.internal.telephony.Phone;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Manages the information of request and the callback binder for SMS.
+ */
+public class SmsDomainSelectionConnection extends DomainSelectionConnection {
+ private DomainSelectionConnectionCallback mCallback;
+
+ public SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller) {
+ this(phone, controller, false);
+ mTag = "DomainSelectionConnection-SMS";
+ }
+
+ protected SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller,
+ boolean isEmergency) {
+ super(phone, SELECTOR_TYPE_SMS, isEmergency, controller);
+ }
+
+ @Override
+ public void onWlanSelected() {
+ super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
+ }
+
+ @Override
+ public void onSelectionTerminated(@DisconnectCauses int cause) {
+ if (mCallback != null) mCallback.onSelectionTerminated(cause);
+ }
+
+ @Override
+ public void finishSelection() {
+ CompletableFuture<Integer> future = getCompletableFuture();
+
+ if (future != null && !future.isDone()) {
+ cancelSelection();
+ } else {
+ super.finishSelection();
+ }
+ }
+
+ /**
+ * Requests a domain selection for SMS.
+ *
+ * @param attr The attributes required to determine the domain.
+ * @param callback A callback to notify an error of the domain selection.
+ * @return A {@link CompletableFuture} to get the selected domain
+ * {@link NetworkRegistrationInfo#DOMAIN_PS} or
+ * {@link NetworkRegistrationInfo#DOMAIN_CS}.
+ */
+ public @NonNull CompletableFuture<Integer> requestDomainSelection(
+ @NonNull DomainSelectionService.SelectionAttributes attr,
+ @NonNull DomainSelectionConnectionCallback callback) {
+ mCallback = callback;
+ selectDomain(attr);
+ return getCompletableFuture();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java
new file mode 100644
index 0000000..6caf5ab
--- /dev/null
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Define the constants for emergency call domain selection.
+ */
+public class EmergencyConstants {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"MODE_EMERGENCY_"},
+ value = {
+ MODE_EMERGENCY_NONE,
+ MODE_EMERGENCY_WWAN,
+ MODE_EMERGENCY_WLAN,
+ MODE_EMERGENCY_CALLBACK,
+ })
+ public @interface EmergencyMode {}
+
+ /**
+ * Default value.
+ */
+ public static final int MODE_EMERGENCY_NONE = 0;
+ /**
+ * Mode Type Emergency WWAN, indicates that the current domain selected for the Emergency call
+ * is cellular.
+ */
+ public static final int MODE_EMERGENCY_WWAN = 1;
+ /**
+ * Mode Type Emergency WLAN, indicates that the current domain selected for the Emergency call
+ * is WLAN/WIFI.
+ */
+ public static final int MODE_EMERGENCY_WLAN = 2;
+ /**
+ * Mode Type Emergency Callback, indicates that the current mode set request is for Emergency
+ * callback.
+ */
+ public static final int MODE_EMERGENCY_CALLBACK = 3;
+
+ /** Converts the {@link EmergencyMode} to String */
+ public static String emergencyModeToString(int emcMode) {
+ switch (emcMode) {
+ case MODE_EMERGENCY_NONE: return "NONE";
+ case MODE_EMERGENCY_WWAN: return "WWAN";
+ case MODE_EMERGENCY_WLAN: return "WLAN";
+ case MODE_EMERGENCY_CALLBACK: return "CALLBACK";
+ default: return "UNKNOWN(" + emcMode + ")";
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 276d82a..9b44001 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -20,14 +20,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
-import android.os.SystemProperties;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -36,18 +37,21 @@
import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.HalVersion;
import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.metrics.EmergencyNumberStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nano.PersistAtomsProto;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.ecc.nano.ProtobufEccData;
import com.android.phone.ecc.nano.ProtobufEccData.EccInfo;
@@ -67,6 +71,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
@@ -95,9 +102,18 @@
private final CommandsInterface mCi;
private final Phone mPhone;
+ private int mPhoneId;
private String mCountryIso;
private String mLastKnownEmergencyCountryIso = "";
private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
+ private int mCurrentOtaDatabaseVersion = INVALID_DATABASE_VERSION;
+ private Resources mResources = null;
+ /**
+ * Used for storing all specific mnc's along with the list of emergency numbers
+ * for which normal routing should be supported.
+ */
+ private Map<String, Set<String>> mNormalRoutedNumbers = new ArrayMap<>();
+
/**
* Indicates if the country iso is set by another subscription.
* @hide
@@ -141,10 +157,6 @@
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- onCarrierConfigChanged();
- return;
- } else if (intent.getAction().equals(
TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, -1);
if (phoneId == mPhone.getPhoneId()) {
@@ -164,27 +176,35 @@
public EmergencyNumberTracker(Phone phone, CommandsInterface ci) {
mPhone = phone;
mCi = ci;
+ mResources = mPhone.getContext().getResources();
if (mPhone != null) {
+ mPhoneId = phone.getPhoneId();
CarrierConfigManager configMgr = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configMgr != null) {
- PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId());
- if (b != null) {
+ PersistableBundle b = CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
+ CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
+ if (!b.isEmpty()) {
mEmergencyNumberPrefix = b.getStringArray(
CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
}
+
+ // Callback which directly handle config change should be executed on handler thread
+ configMgr.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ onCarrierConfigUpdated(slotIndex));
+
+ //register country change listener
+ IntentFilter filter = new IntentFilter(
+ TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+ mPhone.getContext().registerReceiver(mIntentReceiver, filter);
+
} else {
loge("CarrierConfigManager is null.");
}
-
- // Receive Carrier Config Changes
- IntentFilter filter = new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- // Receive Telephony Network Country Changes
- filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
-
- mPhone.getContext().registerReceiver(mIntentReceiver, filter);
} else {
loge("mPhone is null.");
}
@@ -269,7 +289,7 @@
@VisibleForTesting
public boolean isSimAbsent() {
for (Phone phone: PhoneFactory.getPhones()) {
- int slotId = SubscriptionController.getInstance().getSlotIndex(phone.getSubId());
+ int slotId = SubscriptionManagerService.getInstance().getSlotIndex(phone.getSubId());
// If slot id is invalid, it means that there is no sim card.
if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
// If there is at least one sim active, sim is not absent; it returns false
@@ -284,7 +304,7 @@
// If country iso has been cached when listener is set, don't need to cache the initial
// country iso and initial database.
if (mCountryIso == null) {
- String countryForDatabaseCache = getInitialCountryIso().toLowerCase();
+ String countryForDatabaseCache = getInitialCountryIso().toLowerCase(Locale.ROOT);
updateEmergencyCountryIso(countryForDatabaseCache);
// Use the last known country to cache the database in APM
if (TextUtils.isEmpty(countryForDatabaseCache)
@@ -327,23 +347,26 @@
}
}
- private void onCarrierConfigChanged() {
+ private void onCarrierConfigUpdated(int slotIndex) {
if (mPhone != null) {
- CarrierConfigManager configMgr = (CarrierConfigManager)
- mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configMgr != null) {
- PersistableBundle b = configMgr.getConfigForSubId(mPhone.getSubId());
- if (b != null) {
- String[] emergencyNumberPrefix = b.getStringArray(
+ if (slotIndex != mPhone.getPhoneId()) return;
+
+ PersistableBundle b =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mPhone.getContext(),
+ mPhone.getSubId(),
CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
- if (!Arrays.equals(mEmergencyNumberPrefix, emergencyNumberPrefix)) {
- this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX,
- emergencyNumberPrefix).sendToTarget();
- }
+ if (!b.isEmpty()) {
+ String[] emergencyNumberPrefix =
+ b.getStringArray(
+ CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
+ if (!Arrays.equals(mEmergencyNumberPrefix, emergencyNumberPrefix)) {
+ this.obtainMessage(EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX, emergencyNumberPrefix)
+ .sendToTarget();
}
}
} else {
- loge("onCarrierConfigChanged mPhone is null.");
+ loge("onCarrierConfigurationChanged mPhone is null.");
}
}
@@ -404,7 +427,8 @@
EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, null).sendToTarget();
}
- private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso) {
+ private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso,
+ int emergencyCallRouting) {
String phoneNumber = eccInfo.phoneNumber.trim();
if (phoneNumber.isEmpty()) {
loge("EccInfo has empty phone number.");
@@ -445,13 +469,65 @@
// Ignores unknown types.
}
}
- return new EmergencyNumber(phoneNumber, countryIso, "", emergencyServiceCategoryBitmask,
- new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
- EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ return new EmergencyNumber(phoneNumber, countryIso, "",
+ emergencyServiceCategoryBitmask, new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, emergencyCallRouting);
+ }
+
+ /**
+ * Get routing type of emergency numbers from DB. Update mnc's list with numbers that are
+ * to supported as normal routing type in the respective mnc's.
+ */
+ private int getRoutingInfoFromDB(EccInfo eccInfo,
+ Map<String, Set<String>> normalRoutedNumbers) {
+ int emergencyCallRouting;
+ switch(eccInfo.routing)
+ {
+ case EccInfo.Routing.NORMAL :
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+ break;
+ case EccInfo.Routing.EMERGENCY :
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY;
+ break;
+ default:
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+ }
+ String phoneNumber = eccInfo.phoneNumber.trim();
+ if (phoneNumber.isEmpty()) {
+ loge("EccInfo has empty phone number.");
+ return emergencyCallRouting;
+ }
+
+ if (eccInfo.routing == EccInfo.Routing.NORMAL) {
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+
+ if (((eccInfo.normalRoutingMncs).length != 0)
+ && (eccInfo.normalRoutingMncs[0].length() > 0)) {
+ emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+ for (String routingMnc : eccInfo.normalRoutingMncs) {
+ boolean mncExist = normalRoutedNumbers.containsKey(routingMnc);
+ Set phoneNumberList;
+ if (!mncExist) {
+ phoneNumberList = new ArraySet<String>();
+ phoneNumberList.add(phoneNumber);
+ normalRoutedNumbers.put(routingMnc, phoneNumberList);
+ } else {
+ phoneNumberList = normalRoutedNumbers.get(routingMnc);
+ if (!phoneNumberList.contains(phoneNumber)) {
+ phoneNumberList.add(phoneNumber);
+ }
+ }
+ }
+ logd("Normal routed mncs with phoneNumbers:" + normalRoutedNumbers);
+ }
+ }
+ return emergencyCallRouting;
}
private void cacheEmergencyDatabaseByCountry(String countryIso) {
int assetsDatabaseVersion;
+ Map<String, Set<String>> assetNormalRoutedNumbers = new ArrayMap<>();
// Read the Asset emergency number database
List<EmergencyNumber> updatedAssetEmergencyNumberList = new ArrayList<>();
@@ -463,12 +539,17 @@
readInputStreamToByteArray(gzipInputStream));
assetsDatabaseVersion = allEccMessages.revision;
logd(countryIso + " asset emergency database is loaded. Ver: " + assetsDatabaseVersion
- + " Phone Id: " + mPhone.getPhoneId());
+ + " Phone Id: " + mPhone.getPhoneId() + " countryIso: " + countryIso);
for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
- if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
+ if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) {
for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
+ int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+ if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) {
+ emergencyCallRouting = getRoutingInfoFromDB(eccInfo,
+ assetNormalRoutedNumbers);
+ }
updatedAssetEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
- eccInfo, countryIso));
+ eccInfo, countryIso, emergencyCallRouting));
}
}
}
@@ -479,24 +560,27 @@
}
// Cache OTA emergency number database
- int otaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
+ mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
// Use a valid database that has higher version.
- if (otaDatabaseVersion == INVALID_DATABASE_VERSION
+ if (mCurrentOtaDatabaseVersion == INVALID_DATABASE_VERSION
&& assetsDatabaseVersion == INVALID_DATABASE_VERSION) {
loge("No database available. Phone Id: " + mPhone.getPhoneId());
- } else if (assetsDatabaseVersion > otaDatabaseVersion) {
+ } else if (assetsDatabaseVersion > mCurrentOtaDatabaseVersion) {
logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion);
mCurrentDatabaseVersion = assetsDatabaseVersion;
mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList;
+ mNormalRoutedNumbers.clear();
+ mNormalRoutedNumbers = assetNormalRoutedNumbers;
} else {
- logd("Using Ota Emergency database. Version: " + otaDatabaseVersion);
+ logd("Using Ota Emergency database. Version: " + mCurrentOtaDatabaseVersion);
}
}
private int cacheOtaEmergencyNumberDatabase() {
ProtobufEccData.AllInfo allEccMessages = null;
int otaDatabaseVersion = INVALID_DATABASE_VERSION;
+ Map<String, Set<String>> otaNormalRoutedNumbers = new ArrayMap<>();
// Read the OTA emergency number database
List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>();
@@ -525,10 +609,15 @@
logd(countryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion);
otaDatabaseVersion = allEccMessages.revision;
for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
- if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
+ if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) {
for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
+ int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+ if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) {
+ emergencyCallRouting = getRoutingInfoFromDB(eccInfo,
+ otaNormalRoutedNumbers);
+ }
updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
- eccInfo, countryIso));
+ eccInfo, countryIso, emergencyCallRouting));
}
}
}
@@ -543,6 +632,8 @@
&& mCurrentDatabaseVersion < otaDatabaseVersion) {
mCurrentDatabaseVersion = otaDatabaseVersion;
mEmergencyNumberListFromDatabase = updatedOtaEmergencyNumberList;
+ mNormalRoutedNumbers.clear();
+ mNormalRoutedNumbers = otaNormalRoutedNumbers;
}
return otaDatabaseVersion;
}
@@ -591,9 +682,9 @@
private void updateEmergencyNumberListDatabaseAndNotify(String countryIso) {
logd("updateEmergencyNumberListDatabaseAndNotify(): receiving countryIso: "
+ countryIso);
- updateEmergencyCountryIso(countryIso.toLowerCase());
+ updateEmergencyCountryIso(countryIso.toLowerCase(Locale.ROOT));
// Use cached country iso in APM to load emergency number database.
- if (TextUtils.isEmpty(countryIso) && isAirplaneModeEnabled()) {
+ if (TextUtils.isEmpty(countryIso)) {
countryIso = getCountryIsoForCachingDatabase();
logd("updateEmergencyNumberListDatabaseAndNotify(): using cached APM country "
+ countryIso);
@@ -622,7 +713,8 @@
private void updateOtaEmergencyNumberListDatabaseAndNotify() {
logd("updateOtaEmergencyNumberListDatabaseAndNotify():"
+ " receiving Emegency Number database OTA update");
- if (cacheOtaEmergencyNumberDatabase() != INVALID_DATABASE_VERSION) {
+ mCurrentOtaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
+ if (mCurrentOtaDatabaseVersion != INVALID_DATABASE_VERSION) {
writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase);
if (!DBG) {
mEmergencyNumberListDatabaseLocalLog.log(
@@ -687,7 +779,11 @@
}
mergedEmergencyNumberList.addAll(mEmergencyNumberListWithPrefix);
mergedEmergencyNumberList.addAll(mEmergencyNumberListFromTestMode);
- EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList);
+ if (shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored()) {
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList);
+ } else {
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList, true);
+ }
mEmergencyNumberList = mergedEmergencyNumberList;
}
@@ -698,11 +794,90 @@
* indication not support from the HAL.
*/
public List<EmergencyNumber> getEmergencyNumberList() {
+ List<EmergencyNumber> completeEmergencyNumberList;
if (!mEmergencyNumberListFromRadio.isEmpty()) {
- return Collections.unmodifiableList(mEmergencyNumberList);
+ completeEmergencyNumberList = Collections.unmodifiableList(mEmergencyNumberList);
} else {
- return getEmergencyNumberListFromEccListDatabaseAndTest();
+ completeEmergencyNumberList = getEmergencyNumberListFromEccListDatabaseAndTest();
}
+ if (shouldAdjustForRouting()) {
+ return adjustRoutingForEmergencyNumbers(completeEmergencyNumberList);
+ } else {
+ return completeEmergencyNumberList;
+ }
+ }
+
+ /**
+ * Util function to check whether routing type and mnc value in emergency number needs
+ * to be adjusted for the current network mnc.
+ */
+ private boolean shouldAdjustForRouting() {
+ if (!shouldEmergencyNumberRoutingFromDbBeIgnored() && !mNormalRoutedNumbers.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adjust emergency numbers with mnc and routing type based on the current network mnc.
+ */
+ private List<EmergencyNumber> adjustRoutingForEmergencyNumbers(
+ List<EmergencyNumber> emergencyNumbers) {
+ CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+ if (cellIdentity != null) {
+ String networkMnc = cellIdentity.getMncString();
+ Set<String> normalRoutedPhoneNumbers = mNormalRoutedNumbers.get(networkMnc);
+ Set<String> normalRoutedPhoneNumbersWithPrefix = new ArraySet<String>();
+
+ if (normalRoutedPhoneNumbers != null && !normalRoutedPhoneNumbers.isEmpty()) {
+ for (String num : normalRoutedPhoneNumbers) {
+ Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num);
+ if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) {
+ normalRoutedPhoneNumbersWithPrefix.addAll(phoneNumbersWithPrefix);
+ }
+ }
+ }
+ List<EmergencyNumber> adjustedEmergencyNumberList = new ArrayList<>();
+ int routing;
+ String mnc;
+ for (EmergencyNumber num : emergencyNumbers) {
+ routing = num.getEmergencyCallRouting();
+ mnc = num.getMnc();
+ if (num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+ if ((normalRoutedPhoneNumbers != null
+ && normalRoutedPhoneNumbers.contains(num.getNumber()))
+ || normalRoutedPhoneNumbersWithPrefix.contains(num.getNumber())) {
+ routing = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+ mnc = networkMnc;
+ logd("adjustRoutingForEmergencyNumbers for number" + num.getNumber());
+ } else if (routing == EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN) {
+ routing = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY;
+ }
+ }
+ adjustedEmergencyNumberList.add(new EmergencyNumber(num.getNumber(),
+ num.getCountryIso(), mnc,
+ num.getEmergencyServiceCategoryBitmask(),
+ num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
+ routing));
+ }
+ return adjustedEmergencyNumberList;
+ } else {
+ return emergencyNumbers;
+ }
+ }
+
+
+ /**
+ * Util function to add prefix to the given emergency number.
+ */
+ private Set<String> addPrefixToEmergencyNumber(String number) {
+ Set<String> phoneNumbersWithPrefix = new ArraySet<String>();
+ for (String prefix : mEmergencyNumberPrefix) {
+ if (!number.startsWith(prefix)) {
+ phoneNumbersWithPrefix.add(prefix + number);
+ }
+ }
+ return phoneNumbersWithPrefix;
}
/**
@@ -710,7 +885,7 @@
*
* @return {@code true} if it is; {@code false} otherwise.
*/
- public boolean isEmergencyNumber(String number, boolean exactMatch) {
+ public boolean isEmergencyNumber(String number) {
if (number == null) {
return false;
}
@@ -726,31 +901,14 @@
if (!mEmergencyNumberListFromRadio.isEmpty()) {
for (EmergencyNumber num : mEmergencyNumberList) {
- // According to com.android.i18n.phonenumbers.ShortNumberInfo, in
- // these countries, if extra digits are added to an emergency number,
- // it no longer connects to the emergency service.
- String countryIso = getLastKnownEmergencyCountryIso();
- if (countryIso.equals("br") || countryIso.equals("cl")
- || countryIso.equals("ni")) {
- exactMatch = true;
- } else {
- exactMatch = false || exactMatch;
- }
- if (exactMatch) {
- if (num.getNumber().equals(number)) {
- logd("Found in mEmergencyNumberList [exact match] ");
- return true;
- }
- } else {
- if (number.startsWith(num.getNumber())) {
- logd("Found in mEmergencyNumberList [not exact match] ");
- return true;
- }
+ if (num.getNumber().equals(number)) {
+ logd("Found in mEmergencyNumberList");
+ return true;
}
}
return false;
} else {
- boolean inEccList = isEmergencyNumberFromEccList(number, exactMatch);
+ boolean inEccList = isEmergencyNumberFromEccList(number);
boolean inEmergencyNumberDb = isEmergencyNumberFromDatabase(number);
boolean inEmergencyNumberTestList = isEmergencyNumberForTest(number);
logd("Search results - inRilEccList:" + inEccList
@@ -840,6 +998,10 @@
return mCurrentDatabaseVersion;
}
+ public int getEmergencyNumberOtaDbVersion() {
+ return mCurrentOtaDatabaseVersion;
+ }
+
private synchronized void updateEmergencyCountryIso(String countryIso) {
mCountryIso = countryIso;
if (!TextUtils.isEmpty(mCountryIso)) {
@@ -855,22 +1017,8 @@
*/
private List<EmergencyNumber> getEmergencyNumberListFromEccList() {
List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
- int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
- String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
- String emergencyNumbers = SystemProperties.get(ecclist, "");
- if (TextUtils.isEmpty(emergencyNumbers)) {
- // then read-only ecclist property since old RIL only uses this
- emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
- }
- if (!TextUtils.isEmpty(emergencyNumbers)) {
- // searches through the comma-separated list for a match,
- // return true if one is found.
- for (String emergencyNum : emergencyNumbers.split(",")) {
- emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum));
- }
- }
- emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
+ String emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
for (String emergencyNum : emergencyNumbers.split(",")) {
emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum));
}
@@ -886,15 +1034,14 @@
List<EmergencyNumber> emergencyNumberListWithPrefix = new ArrayList<>();
if (emergencyNumberList != null) {
for (EmergencyNumber num : emergencyNumberList) {
- for (String prefix : mEmergencyNumberPrefix) {
- // If an emergency number has started with the prefix,
- // no need to apply the prefix.
- if (!num.getNumber().startsWith(prefix)) {
+ Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num.getNumber());
+ if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) {
+ for (String numberWithPrefix : phoneNumbersWithPrefix) {
emergencyNumberListWithPrefix.add(new EmergencyNumber(
- prefix + num.getNumber(), num.getCountryIso(),
- num.getMnc(), num.getEmergencyServiceCategoryBitmask(),
- num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
- num.getEmergencyCallRouting()));
+ numberWithPrefix, num.getCountryIso(),
+ num.getMnc(), num.getEmergencyServiceCategoryBitmask(),
+ num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
+ num.getEmergencyCallRouting()));
}
}
}
@@ -913,7 +1060,7 @@
}
private boolean isEmergencyNumberFromDatabase(String number) {
- if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
+ if (mEmergencyNumberListFromDatabase.isEmpty()) {
return false;
}
number = PhoneNumberUtils.stripSeparators(number);
@@ -936,10 +1083,10 @@
number = PhoneNumberUtils.stripSeparators(number);
for (EmergencyNumber num : mEmergencyNumberListFromDatabase) {
if (num.getNumber().equals(number)) {
- return new EmergencyNumber(number, getLastKnownEmergencyCountryIso().toLowerCase(),
- "", num.getEmergencyServiceCategoryBitmask(),
+ return new EmergencyNumber(number, getLastKnownEmergencyCountryIso()
+ .toLowerCase(Locale.ROOT), "", num.getEmergencyServiceCategoryBitmask(),
new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
- EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ num.getEmergencyCallRouting());
}
}
return new EmergencyNumber(number, "", "",
@@ -952,7 +1099,7 @@
* Back-up old logics for {@link PhoneNumberUtils#isEmergencyNumberInternal} for legacy
* and deprecate purpose.
*/
- private boolean isEmergencyNumberFromEccList(String number, boolean useExactMatch) {
+ private boolean isEmergencyNumberFromEccList(String number) {
// If the number passed in is null, just return false:
if (number == null) return false;
@@ -976,59 +1123,8 @@
/// @}
String emergencyNumbers = "";
- int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
-
- String ecclist = null;
String countryIso = getLastKnownEmergencyCountryIso();
-
- if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
- //only use ril ecc list for older devices with HAL < 1.4
- // check read-write ecclist property first
- ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
- emergencyNumbers = SystemProperties.get(ecclist, "");
-
- logd("slotId:" + slotId + " country:" + countryIso + " emergencyNumbers: "
- + emergencyNumbers);
-
- if (TextUtils.isEmpty(emergencyNumbers)) {
- // then read-only ecclist property since old RIL only uses this
- emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
- }
-
- if (!TextUtils.isEmpty(emergencyNumbers)) {
- // searches through the comma-separated list for a match,
- // return true if one is found.
- for (String emergencyNum : emergencyNumbers.split(",")) {
- // According to com.android.i18n.phonenumbers.ShortNumberInfo, in
- // these countries, if extra digits are added to an emergency number,
- // it no longer connects to the emergency service.
- if (useExactMatch || countryIso.equals("br") || countryIso.equals("cl")
- || countryIso.equals("ni")) {
- if (number.equals(emergencyNum)) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (number.equals(prefix + emergencyNum)) {
- return true;
- }
- }
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (number.startsWith(prefix + emergencyNum)) {
- return true;
- }
- }
- }
- }
- }
- // no matches found against the list!
- return false;
- }
- }
+ logd("country:" + countryIso);
logd("System property doesn't provide any emergency numbers."
+ " Use embedded logic for determining ones.");
@@ -1038,57 +1134,32 @@
emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
for (String emergencyNum : emergencyNumbers.split(",")) {
- if (useExactMatch) {
- if (number.equals(emergencyNum)) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (number.equals(prefix + emergencyNum)) {
- return true;
- }
- }
- }
+ if (number.equals(emergencyNum)) {
+ return true;
} else {
- if (number.startsWith(emergencyNum)) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (number.equals(prefix + emergencyNum)) {
- return true;
- }
+ for (String prefix : mEmergencyNumberPrefix) {
+ if (number.equals(prefix + emergencyNum)) {
+ return true;
}
}
}
}
- if(isSimAbsent()) {
+ if (isSimAbsent()) {
// No ecclist system property, so use our own list.
if (countryIso != null) {
ShortNumberInfo info = ShortNumberInfo.getInstance();
- if (useExactMatch) {
- if (info.isEmergencyNumber(number, countryIso.toUpperCase())) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (info.isEmergencyNumber(prefix + number, countryIso.toUpperCase())) {
- return true;
- }
- }
- }
- return false;
+ if (info.isEmergencyNumber(number, countryIso.toUpperCase(Locale.ROOT))) {
+ return true;
} else {
- if (info.connectsToEmergencyNumber(number, countryIso.toUpperCase())) {
- return true;
- } else {
- for (String prefix : mEmergencyNumberPrefix) {
- if (info.connectsToEmergencyNumber(prefix + number,
- countryIso.toUpperCase())) {
- return true;
- }
+ for (String prefix : mEmergencyNumberPrefix) {
+ if (info.isEmergencyNumber(prefix + number,
+ countryIso.toUpperCase(Locale.ROOT))) {
+ return true;
}
}
- return false;
}
+ return false;
}
}
@@ -1107,7 +1178,7 @@
*/
private void updateEmergencyNumberListTestModeAndNotify(int action, EmergencyNumber num) {
if (action == ADD_EMERGENCY_NUMBER_TEST_MODE) {
- if (!isEmergencyNumber(num.getNumber(), true)) {
+ if (!isEmergencyNumber(num.getNumber())) {
mEmergencyNumberListFromTestMode.add(num);
}
} else if (action == RESET_EMERGENCY_NUMBER_TEST_MODE) {
@@ -1134,7 +1205,7 @@
private List<EmergencyNumber> getEmergencyNumberListFromEccListDatabaseAndTest() {
List<EmergencyNumber> mergedEmergencyNumberList = getEmergencyNumberListFromEccList();
- if (mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
+ if (!mEmergencyNumberListFromDatabase.isEmpty()) {
loge("getEmergencyNumberListFromEccListDatabaseAndTest: radio indication is"
+ " unavailable in 1.4 HAL.");
mergedEmergencyNumberList.addAll(mEmergencyNumberListFromDatabase);
@@ -1142,7 +1213,12 @@
mEmergencyNumberListFromDatabase));
}
mergedEmergencyNumberList.addAll(getEmergencyNumberListTestMode());
- EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList);
+
+ if (shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored()) {
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList);
+ } else {
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(mergedEmergencyNumberList, true);
+ }
return mergedEmergencyNumberList;
}
@@ -1158,16 +1234,16 @@
return new ArrayList<>(mEmergencyNumberListFromRadio);
}
- private static void logd(String str) {
- Rlog.d(TAG, str);
+ private void logd(String str) {
+ Rlog.d(TAG, "[" + mPhoneId + "]" + str);
}
- private static void logw(String str) {
- Rlog.w(TAG, str);
+ private void logw(String str) {
+ Rlog.w(TAG, "[" + mPhoneId + "]" + str);
}
- private static void loge(String str) {
- Rlog.e(TAG, str);
+ private void loge(String str) {
+ Rlog.e(TAG, "[" + mPhoneId + "]" + str);
}
private void writeUpdatedEmergencyNumberListMetrics(
@@ -1182,6 +1258,56 @@
}
/**
+ * @return {@code true} if emergency numbers sourced from modem/config should be ignored.
+ * {@code false} if emergency numbers sourced from modem/config should not be ignored.
+ */
+ @VisibleForTesting
+ public boolean shouldModemConfigEmergencyNumbersBeIgnored() {
+ return mResources.getBoolean(com.android.internal.R.bool
+ .ignore_modem_config_emergency_numbers);
+ }
+
+ /**
+ * @return {@code true} if emergency number routing from the android emergency number
+ * database should be ignored.
+ * {@code false} if emergency number routing from the android emergency number database
+ * should not be ignored.
+ */
+ @VisibleForTesting
+ public boolean shouldEmergencyNumberRoutingFromDbBeIgnored() {
+ return mResources.getBoolean(com.android.internal.R.bool
+ .ignore_emergency_number_routing_from_db);
+ }
+
+
+ /**
+ * @return {@code true} if determining of Urns & Service Categories while merging duplicate
+ * numbers should be ignored.
+ * {@code false} if determining of Urns & Service Categories while merging duplicate
+ * numbers should not be ignored.
+ */
+ @VisibleForTesting
+ public boolean shouldDeterminingOfUrnsAndCategoriesWhileMergingIgnored() {
+ // TODO: Device config
+ return false;
+ }
+
+ /**
+ * Captures the consolidated emergency numbers list and returns the array of
+ * {@link PersistAtomsProto.EmergencyNumber}.
+ */
+ public PersistAtomsProto.EmergencyNumbersInfo[] getEmergencyNumbersProtoArray() {
+ int otaVersion = Math.max(0, getEmergencyNumberOtaDbVersion());
+ int assetVersion = Math.max(0, getEmergencyNumberDbVersion());
+ boolean isDbRoutingIgnored = shouldEmergencyNumberRoutingFromDbBeIgnored();
+ List<EmergencyNumber> emergencyNumberList = getEmergencyNumberList();
+ logd("log emergency number list=" + emergencyNumberList + " for otaVersion=" + otaVersion
+ + ", assetVersion=" + assetVersion + ", isDbRoutingIgnored=" + isDbRoutingIgnored);
+ return EmergencyNumberStats.getInstance().convertEmergencyNumbersListToProto(
+ emergencyNumberList, assetVersion, otaVersion, isDbRoutingIgnored);
+ }
+
+ /**
* Dump Emergency Number List info in the tracking
*
* @param fd FileDescriptor
@@ -1190,9 +1316,6 @@
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println(" Hal Version:" + mPhone.getHalVersion());
- ipw.println(" ========================================= ");
-
ipw.println(" Country Iso:" + getEmergencyCountryIso());
ipw.println(" ========================================= ");
@@ -1229,11 +1352,6 @@
ipw.decreaseIndent();
ipw.println(" ========================================= ");
- int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
- String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
- ipw.println(" ril.ecclist: " + SystemProperties.get(ecclist, ""));
- ipw.println(" ========================================= ");
-
ipw.println("Emergency Number List for Phone" + "(" + mPhone.getPhoneId() + ")");
ipw.increaseIndent();
ipw.println(getEmergencyNumberList());
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
new file mode 100644
index 0000000..96cd880
--- /dev/null
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -0,0 +1,1208 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.sysprop.TelephonyProperties;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+/**
+ * Tracks the emergency call state and notifies listeners of changes to the emergency mode.
+ */
+public class EmergencyStateTracker {
+
+ private static final String TAG = "EmergencyStateTracker";
+
+ /**
+ * Timeout before we continue with the emergency call without waiting for DDS switch response
+ * from the modem.
+ */
+ private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
+ /** Default value for if Emergency Callback Mode is supported. */
+ private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true;
+ /** Default Emergency Callback Mode exit timeout value. */
+ private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000;
+
+ /** The emergency types used when setting the emergency mode on modem. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "EMERGENCY_TYPE_",
+ value = {
+ EMERGENCY_TYPE_CALL,
+ EMERGENCY_TYPE_SMS})
+ public @interface EmergencyType {}
+
+ /** Indicates the emergency type is call. */
+ public static final int EMERGENCY_TYPE_CALL = 1;
+ /** Indicates the emergency type is SMS. */
+ public static final int EMERGENCY_TYPE_SMS = 2;
+
+ private static EmergencyStateTracker INSTANCE = null;
+
+ private final Context mContext;
+ private final CarrierConfigManager mConfigManager;
+ private final Handler mHandler;
+ private final boolean mIsSuplDdsSwitchRequiredForEmergencyCall;
+ private final PowerManager.WakeLock mWakeLock;
+ private RadioOnHelper mRadioOnHelper;
+ @EmergencyConstants.EmergencyMode
+ private int mEmergencyMode = MODE_EMERGENCY_NONE;
+ private boolean mWasEmergencyModeSetOnModem;
+ private EmergencyRegResult mLastEmergencyRegResult;
+ private boolean mIsEmergencyModeInProgress;
+ private boolean mIsEmergencyCallStartedDuringEmergencySms;
+
+ /** For emergency calls */
+ private final long mEcmExitTimeoutMs;
+ // A runnable which is used to automatically exit from Ecm after a period of time.
+ private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode;
+ // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}.
+ private final Set<String> mActiveEmergencyCalls = new ArraySet<>();
+ private Phone mPhone;
+ // Tracks ongoing emergency callId to handle a second emergency call
+ private String mOngoingCallId;
+ // Domain of the active emergency call. Assuming here that there will only be one domain active.
+ private int mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+ private CompletableFuture<Integer> mCallEmergencyModeFuture;
+ private boolean mIsInEmergencyCall;
+ private boolean mIsInEcm;
+ private boolean mIsTestEmergencyNumber;
+ private Runnable mOnEcmExitCompleteRunnable;
+
+ /** For emergency SMS */
+ private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>();
+ private Phone mSmsPhone;
+ private CompletableFuture<Integer> mSmsEmergencyModeFuture;
+ private boolean mIsTestEmergencyNumberForSms;
+
+ /**
+ * Listens for Emergency Callback Mode state change intents
+ */
+ private final BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(
+ TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
+
+ boolean isInEcm = intent.getBooleanExtra(
+ TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false);
+ Rlog.d(TAG, "Received ACTION_EMERGENCY_CALLBACK_MODE_CHANGED isInEcm = " + isInEcm);
+
+ // If we exit ECM mode, notify all connections.
+ if (!isInEcm) {
+ exitEmergencyCallbackMode();
+ }
+ }
+ }
+ };
+
+ /** PhoneFactory Dependencies for testing. */
+ @VisibleForTesting
+ public interface PhoneFactoryProxy {
+ Phone[] getPhones();
+ }
+
+ private PhoneFactoryProxy mPhoneFactoryProxy = PhoneFactory::getPhones;
+
+ /** PhoneSwitcher dependencies for testing. */
+ @VisibleForTesting
+ public interface PhoneSwitcherProxy {
+
+ PhoneSwitcher getPhoneSwitcher();
+ }
+
+ private PhoneSwitcherProxy mPhoneSwitcherProxy = PhoneSwitcher::getInstance;
+
+ /**
+ * TelephonyManager dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface TelephonyManagerProxy {
+ int getPhoneCount();
+ }
+
+ private final TelephonyManagerProxy mTelephonyManagerProxy;
+
+ private static class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
+ private final TelephonyManager mTelephonyManager;
+
+
+ TelephonyManagerProxyImpl(Context context) {
+ mTelephonyManager = new TelephonyManager(context);
+ }
+
+ @Override
+ public int getPhoneCount() {
+ return mTelephonyManager.getActiveModemCount();
+ }
+ }
+
+ /**
+ * Return the handler for testing.
+ */
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @VisibleForTesting
+ public static final int MSG_SET_EMERGENCY_MODE_DONE = 1;
+ @VisibleForTesting
+ public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2;
+ @VisibleForTesting
+ public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3;
+
+ private class MyHandler extends Handler {
+
+ MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_EMERGENCY_MODE_DONE: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Integer emergencyType = (Integer) ar.userObj;
+ Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE_DONE for "
+ + emergencyTypeToString(emergencyType));
+ if (ar.exception == null) {
+ mLastEmergencyRegResult = (EmergencyRegResult) ar.result;
+ } else {
+ mLastEmergencyRegResult = null;
+ Rlog.w(TAG, "LastEmergencyRegResult not set. AsyncResult.exception: "
+ + ar.exception);
+ }
+ setEmergencyModeInProgress(false);
+
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ setIsInEmergencyCall(true);
+ completeEmergencyMode(emergencyType);
+
+ // Case 1) When the emergency call is setting the emergency mode and
+ // the emergency SMS is being sent, completes the SMS future also.
+ // Case 2) When the emergency SMS is setting the emergency mode and
+ // the emergency call is beint started, the SMS request is cancelled and
+ // the call request will be handled.
+ if (mSmsPhone != null) {
+ completeEmergencyMode(EMERGENCY_TYPE_SMS);
+ }
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ if (mPhone != null && mSmsPhone != null) {
+ // Clear call phone temporarily to exit the emergency mode
+ // if the emergency call is started.
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ Phone phone = mPhone;
+ mPhone = null;
+ exitEmergencyMode(mSmsPhone, emergencyType);
+ // Restore call phone for further use.
+ mPhone = phone;
+
+ if (!isSamePhone(mPhone, mSmsPhone)) {
+ completeEmergencyMode(emergencyType,
+ DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED);
+ }
+ } else {
+ completeEmergencyMode(emergencyType);
+ }
+ break;
+ } else {
+ completeEmergencyMode(emergencyType);
+ }
+
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
+ mIsTestEmergencyNumber);
+ }
+ }
+ break;
+ }
+ case MSG_EXIT_EMERGENCY_MODE_DONE: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Integer emergencyType = (Integer) ar.userObj;
+ Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE_DONE for "
+ + emergencyTypeToString(emergencyType));
+ setEmergencyModeInProgress(false);
+
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ setIsInEmergencyCall(false);
+ if (mOnEcmExitCompleteRunnable != null) {
+ mOnEcmExitCompleteRunnable.run();
+ mOnEcmExitCompleteRunnable = null;
+ }
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
+ mIsTestEmergencyNumber);
+ }
+ }
+ break;
+ }
+ case MSG_SET_EMERGENCY_CALLBACK_MODE_DONE: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Integer emergencyType = (Integer) ar.userObj;
+ Rlog.v(TAG, "MSG_SET_EMERGENCY_CALLBACK_MODE_DONE for "
+ + emergencyTypeToString(emergencyType));
+ setEmergencyModeInProgress(false);
+ // When the emergency callback mode is in progress and the emergency SMS is
+ // started, it needs to be completed here for the emergency SMS.
+ if (mSmsPhone != null) {
+ completeEmergencyMode(EMERGENCY_TYPE_SMS);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates the EmergencyStateTracker singleton instance.
+ *
+ * @param context The context of the application.
+ * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for
+ * emergency call.
+ */
+ public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall) {
+ if (INSTANCE == null) {
+ INSTANCE = new EmergencyStateTracker(context, Looper.myLooper(),
+ isSuplDdsSwitchRequiredForEmergencyCall);
+ }
+ }
+
+ /**
+ * Returns the singleton instance of EmergencyStateTracker.
+ *
+ * @return {@link EmergencyStateTracker} instance.
+ */
+ public static EmergencyStateTracker getInstance() {
+ if (INSTANCE == null) {
+ throw new IllegalStateException("EmergencyStateTracker is not ready!");
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * Initializes EmergencyStateTracker.
+ */
+ private EmergencyStateTracker(Context context, Looper looper,
+ boolean isSuplDdsSwitchRequiredForEmergencyCall) {
+ mEcmExitTimeoutMs = DEFAULT_ECM_EXIT_TIMEOUT_MS;
+ mContext = context;
+ mHandler = new MyHandler(looper);
+ mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall;
+
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "telephony:" + TAG) : null;
+ mConfigManager = context.getSystemService(CarrierConfigManager.class);
+
+ // Register receiver for ECM exit.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+ context.registerReceiver(mEcmExitReceiver, filter, null, mHandler);
+ mTelephonyManagerProxy = new TelephonyManagerProxyImpl(context);
+ }
+
+ /**
+ * Initializes EmergencyStateTracker with injections for testing.
+ *
+ * @param context The context of the application.
+ * @param looper The {@link Looper} of the application.
+ * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for
+ * emergency call.
+ * @param phoneFactoryProxy The {@link PhoneFactoryProxy} to be injected.
+ * @param phoneSwitcherProxy The {@link PhoneSwitcherProxy} to be injected.
+ * @param telephonyManagerProxy The {@link TelephonyManagerProxy} to be
+ * injected.
+ * @param radioOnHelper The {@link RadioOnHelper} to be injected.
+ */
+ @VisibleForTesting
+ public EmergencyStateTracker(Context context, Looper looper,
+ boolean isSuplDdsSwitchRequiredForEmergencyCall, PhoneFactoryProxy phoneFactoryProxy,
+ PhoneSwitcherProxy phoneSwitcherProxy, TelephonyManagerProxy telephonyManagerProxy,
+ RadioOnHelper radioOnHelper, long ecmExitTimeoutMs) {
+ mContext = context;
+ mHandler = new MyHandler(looper);
+ mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall;
+ mPhoneFactoryProxy = phoneFactoryProxy;
+ mPhoneSwitcherProxy = phoneSwitcherProxy;
+ mTelephonyManagerProxy = telephonyManagerProxy;
+ mRadioOnHelper = radioOnHelper;
+ mEcmExitTimeoutMs = ecmExitTimeoutMs;
+ mWakeLock = null; // Don't declare a wakelock in tests
+ mConfigManager = context.getSystemService(CarrierConfigManager.class);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+ context.registerReceiver(mEcmExitReceiver, filter, null, mHandler);
+ }
+
+ /**
+ * Starts the process of an emergency call.
+ *
+ * <p>
+ * Handles turning on radio and switching DDS.
+ *
+ * @param phone the {@code Phone} on which to process the emergency call.
+ * @param callId the call id on which to process the emergency call.
+ * @param isTestEmergencyNumber whether this is a test emergency number.
+ * @return a {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED}
+ * if emergency call successfully started.
+ */
+ public CompletableFuture<Integer> startEmergencyCall(@NonNull Phone phone,
+ @NonNull String callId, boolean isTestEmergencyNumber) {
+ Rlog.i(TAG, "startEmergencyCall: phoneId=" + phone.getPhoneId() + ", callId=" + callId);
+
+ if (mPhone != null) {
+ // Create new future to return as to not interfere with any uncompleted futures.
+ // Case1) When 2nd emergency call is initiated during an active call on the same phone.
+ // Case2) While the device is in ECBM, an emergency call is initiated on the same phone.
+ if (isSamePhone(mPhone, phone) && (!mActiveEmergencyCalls.isEmpty() || isInEcm())) {
+ mOngoingCallId = callId;
+ mIsTestEmergencyNumber = isTestEmergencyNumber;
+ return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ Rlog.e(TAG, "startEmergencyCall failed. Existing emergency call in progress.");
+ return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ }
+
+ mCallEmergencyModeFuture = new CompletableFuture<>();
+
+ if (mSmsPhone != null) {
+ mIsEmergencyCallStartedDuringEmergencySms = true;
+ // Case1) While exiting the emergency mode on the other phone,
+ // the emergency mode for this call will be restarted after the exit complete.
+ // Case2) While entering the emergency mode on the other phone,
+ // exit the emergency mode when receiving the result of setting the emergency mode and
+ // the emergency mode for this call will be restarted after the exit complete.
+ if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
+ exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ }
+
+ mPhone = phone;
+ mOngoingCallId = callId;
+ mIsTestEmergencyNumber = isTestEmergencyNumber;
+ return mCallEmergencyModeFuture;
+ }
+
+ mPhone = phone;
+ mOngoingCallId = callId;
+ mIsTestEmergencyNumber = isTestEmergencyNumber;
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber);
+ return mCallEmergencyModeFuture;
+ }
+
+ /**
+ * Ends emergency call.
+ *
+ * <p>
+ * Enter ECM only once all active emergency calls have ended. If a call never reached
+ * {@link Call.State#ACTIVE}, then no need to enter ECM.
+ *
+ * @param callId the call id on which to end the emergency call.
+ */
+ public void endCall(@NonNull String callId) {
+ boolean wasActive = mActiveEmergencyCalls.remove(callId);
+
+ if (Objects.equals(mOngoingCallId, callId)) {
+ mOngoingCallId = null;
+ }
+
+ if (wasActive && mActiveEmergencyCalls.isEmpty()
+ && isEmergencyCallbackModeSupported()) {
+ enterEmergencyCallbackMode();
+
+ if (mOngoingCallId == null) {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ mCallEmergencyModeFuture = null;
+ }
+ } else if (mOngoingCallId == null) {
+ if (isInEcm()) {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ mCallEmergencyModeFuture = null;
+ // If the emergency call was initiated during the emergency callback mode,
+ // the emergency callback mode should be restored when the emergency call is ended.
+ if (mActiveEmergencyCalls.isEmpty()) {
+ setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
+ MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
+ }
+ } else {
+ exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL);
+ clearEmergencyCallInfo();
+ }
+ }
+ }
+
+ private void clearEmergencyCallInfo() {
+ mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+ mIsTestEmergencyNumber = false;
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ mCallEmergencyModeFuture = null;
+ mOngoingCallId = null;
+ mPhone = null;
+ }
+
+ private void switchDdsAndSetEmergencyMode(Phone phone, @EmergencyType int emergencyType) {
+ switchDdsDelayed(phone, result -> {
+ Rlog.i(TAG, "switchDdsDelayed: result = " + result);
+ if (!result) {
+ // DDS Switch timed out/failed, but continue with call as it may still succeed.
+ Rlog.e(TAG, "DDS Switch failed.");
+ }
+ // Once radio is on and DDS switched, must call setEmergencyMode() before selecting
+ // emergency domain. EmergencyRegResult is required to determine domain and this is the
+ // only API that can receive it before starting domain selection. Once domain selection
+ // is finished, the actual emergency mode will be set when onEmergencyTransportChanged()
+ // is called.
+ setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_WWAN,
+ MSG_SET_EMERGENCY_MODE_DONE);
+ });
+ }
+
+ /**
+ * Triggers modem to set new emergency mode.
+ *
+ * @param phone the {@code Phone} to set the emergency mode on modem.
+ * @param emergencyType the emergency type to identify an emergency call or SMS.
+ * @param mode the new emergency mode.
+ * @param msg the message to be sent once mode has been set.
+ */
+ private void setEmergencyMode(Phone phone, @EmergencyType int emergencyType,
+ @EmergencyConstants.EmergencyMode int mode, int msg) {
+ Rlog.i(TAG, "setEmergencyMode from " + mEmergencyMode + " to " + mode + " for "
+ + emergencyTypeToString(emergencyType));
+
+ if (mEmergencyMode == mode) {
+ return;
+ }
+ mEmergencyMode = mode;
+ setEmergencyModeInProgress(true);
+
+ Message m = mHandler.obtainMessage(msg, Integer.valueOf(emergencyType));
+ if ((mIsTestEmergencyNumber && emergencyType == EMERGENCY_TYPE_CALL)
+ || (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS)) {
+ Rlog.d(TAG, "TestEmergencyNumber for " + emergencyTypeToString(emergencyType)
+ + ": Skipping setting emergency mode on modem.");
+ // Send back a response for the command, but with null information
+ AsyncResult.forMessage(m, null, null);
+ // Ensure that we do not accidentally block indefinitely when trying to validate test
+ // emergency numbers
+ m.sendToTarget();
+ return;
+ }
+
+ mWasEmergencyModeSetOnModem = true;
+ phone.setEmergencyMode(mode, m);
+ }
+
+ private void completeEmergencyMode(@EmergencyType int emergencyType) {
+ completeEmergencyMode(emergencyType, DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ private void completeEmergencyMode(@EmergencyType int emergencyType,
+ @DisconnectCauses int result) {
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ if (mCallEmergencyModeFuture != null && !mCallEmergencyModeFuture.isDone()) {
+ mCallEmergencyModeFuture.complete(result);
+ }
+
+ if (result != DisconnectCause.NOT_DISCONNECTED) {
+ clearEmergencyCallInfo();
+ }
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ if (mSmsEmergencyModeFuture != null && !mSmsEmergencyModeFuture.isDone()) {
+ mSmsEmergencyModeFuture.complete(result);
+ }
+
+ if (result != DisconnectCause.NOT_DISCONNECTED) {
+ clearEmergencySmsInfo();
+ }
+ }
+ }
+
+ /**
+ * Checks if the device is currently in the emergency mode or not.
+ */
+ @VisibleForTesting
+ public boolean isInEmergencyMode() {
+ return mEmergencyMode != MODE_EMERGENCY_NONE;
+ }
+
+ /**
+ * Sets the flag to inidicate whether setting the emergency mode on modem is in progress or not.
+ */
+ private void setEmergencyModeInProgress(boolean isEmergencyModeInProgress) {
+ mIsEmergencyModeInProgress = isEmergencyModeInProgress;
+ }
+
+ /**
+ * Checks whether setting the emergency mode on modem is in progress or not.
+ */
+ private boolean isEmergencyModeInProgress() {
+ return mIsEmergencyModeInProgress;
+ }
+
+ /**
+ * Notifies external app listeners of emergency mode changes.
+ *
+ * @param isInEmergencyCall a flag to indicate whether there is an active emergency call.
+ */
+ private void setIsInEmergencyCall(boolean isInEmergencyCall) {
+ mIsInEmergencyCall = isInEmergencyCall;
+ }
+
+ /**
+ * Checks if there is an ongoing emergency call.
+ *
+ * @return true if in emergency call
+ */
+ public boolean isInEmergencyCall() {
+ return mIsInEmergencyCall;
+ }
+
+ /**
+ * Triggers modem to exit emergency mode.
+ *
+ * @param phone the {@code Phone} to exit the emergency mode.
+ * @param emergencyType the emergency type to identify an emergency call or SMS.
+ */
+ private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) {
+ Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType));
+
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ if (mSmsPhone != null && isSamePhone(phone, mSmsPhone)) {
+ // Waits for exiting the emergency mode until the emergency SMS is ended.
+ Rlog.i(TAG, "exitEmergencyMode: waits for emergency SMS end.");
+ setIsInEmergencyCall(false);
+ return;
+ }
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ if (mPhone != null && isSamePhone(phone, mPhone)) {
+ // Waits for exiting the emergency mode until the emergency call is ended.
+ Rlog.i(TAG, "exitEmergencyMode: waits for emergency call end.");
+ return;
+ }
+ }
+
+ if (mEmergencyMode == MODE_EMERGENCY_NONE) {
+ return;
+ }
+ mEmergencyMode = MODE_EMERGENCY_NONE;
+ setEmergencyModeInProgress(true);
+
+ Message m = mHandler.obtainMessage(
+ MSG_EXIT_EMERGENCY_MODE_DONE, Integer.valueOf(emergencyType));
+ if (!mWasEmergencyModeSetOnModem) {
+ Rlog.d(TAG, "Emergency mode was not set on modem: Skipping exiting emergency mode.");
+ // Send back a response for the command, but with null information
+ AsyncResult.forMessage(m, null, null);
+ // Ensure that we do not accidentally block indefinitely when trying to validate
+ // the exit condition.
+ m.sendToTarget();
+ return;
+ }
+
+ mWasEmergencyModeSetOnModem = false;
+ phone.exitEmergencyMode(m);
+ }
+
+ /** Returns last {@link EmergencyRegResult} as set by {@code setEmergencyMode()}. */
+ public EmergencyRegResult getEmergencyRegResult() {
+ return mLastEmergencyRegResult;
+ }
+
+ /**
+ * Handles emergency transport change by setting new emergency mode.
+ *
+ * @param emergencyType the emergency type to identify an emergency call or SMS
+ * @param mode the new emergency mode
+ */
+ public void onEmergencyTransportChanged(@EmergencyType int emergencyType,
+ @EmergencyConstants.EmergencyMode int mode) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ Phone phone = null;
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ phone = mPhone;
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ phone = mSmsPhone;
+ }
+
+ if (phone != null) {
+ setEmergencyMode(phone, emergencyType, mode, MSG_SET_EMERGENCY_MODE_DONE);
+ }
+ } else {
+ mHandler.post(() -> {
+ onEmergencyTransportChanged(emergencyType, mode);
+ });
+ }
+ }
+
+ /**
+ * Notify the tracker that the emergency call domain has been updated.
+ * @param phoneType The new PHONE_TYPE_* of the call.
+ * @param callId The ID of the call
+ */
+ public void onEmergencyCallDomainUpdated(int phoneType, String callId) {
+ Rlog.d(TAG, "domain update for callId: " + callId);
+ int domain = -1;
+ switch(phoneType) {
+ case (PhoneConstants.PHONE_TYPE_CDMA_LTE):
+ //fallthrough
+ case (PhoneConstants.PHONE_TYPE_GSM):
+ //fallthrough
+ case (PhoneConstants.PHONE_TYPE_CDMA): {
+ domain = NetworkRegistrationInfo.DOMAIN_CS;
+ break;
+ }
+ case (PhoneConstants.PHONE_TYPE_IMS): {
+ domain = NetworkRegistrationInfo.DOMAIN_PS;
+ break;
+ }
+ default: {
+ Rlog.w(TAG, "domain updated: Unexpected phoneType:" + phoneType);
+ }
+ }
+ if (mEmergencyCallDomain == domain) return;
+ Rlog.i(TAG, "domain updated: from " + mEmergencyCallDomain + " to " + domain);
+ mEmergencyCallDomain = domain;
+ }
+
+ /**
+ * Handles emergency call state change.
+ *
+ * @param state the new call state
+ * @param callId the callId whose state has changed
+ */
+ public void onEmergencyCallStateChanged(Call.State state, String callId) {
+ if (state == Call.State.ACTIVE) {
+ mActiveEmergencyCalls.add(callId);
+ }
+ }
+
+ /**
+ * Returns {@code true} if device and carrier support emergency callback mode.
+ */
+ private boolean isEmergencyCallbackModeSupported() {
+ return getConfig(mPhone.getSubId(),
+ CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
+ DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED);
+ }
+
+ /**
+ * Trigger entry into emergency callback mode.
+ */
+ private void enterEmergencyCallbackMode() {
+ Rlog.d(TAG, "enter ECBM");
+ setIsInEmergencyCall(false);
+ // Check if not in ECM already.
+ if (!isInEcm()) {
+ setIsInEcm(true);
+ if (!mPhone.getUnitTestMode()) {
+ TelephonyProperties.in_ecm_mode(true);
+ }
+
+ // Notify listeners of the entrance to ECM.
+ sendEmergencyCallbackModeChange();
+ if (isInImsEcm()) {
+ // emergency call registrants are not notified of new emergency call until entering
+ // ECBM (see ImsPhone#handleEnterEmergencyCallbackMode)
+ ((GsmCdmaPhone) mPhone).notifyEmergencyCallRegistrants(true);
+ }
+
+ // Set emergency mode on modem.
+ setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
+ MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
+
+ // Post this runnable so we will automatically exit if no one invokes
+ // exitEmergencyCallbackMode() directly.
+ long delayInMillis = TelephonyProperties.ecm_exit_timer()
+ .orElse(mEcmExitTimeoutMs);
+ mHandler.postDelayed(mExitEcmRunnable, delayInMillis);
+
+ // We don't want to go to sleep while in ECM.
+ if (mWakeLock != null) mWakeLock.acquire(delayInMillis);
+ }
+ }
+
+ /**
+ * Exits emergency callback mode and notifies relevant listeners.
+ */
+ public void exitEmergencyCallbackMode() {
+ Rlog.d(TAG, "exit ECBM");
+ // Remove pending exit ECM runnable, if any.
+ mHandler.removeCallbacks(mExitEcmRunnable);
+
+ if (isInEcm()) {
+ setIsInEcm(false);
+ if (!mPhone.getUnitTestMode()) {
+ TelephonyProperties.in_ecm_mode(false);
+ }
+
+ // Release wakeLock.
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (Exception e) {
+ // Ignore the exception if the system has already released this WakeLock.
+ Rlog.d(TAG, "WakeLock already released: " + e.toString());
+ }
+ }
+
+ GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) mPhone;
+ // Send intents that ECM has changed.
+ sendEmergencyCallbackModeChange();
+ gsmCdmaPhone.notifyEmergencyCallRegistrants(false);
+
+ // Exit emergency mode on modem.
+ exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL);
+ }
+
+ mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+ mIsTestEmergencyNumber = false;
+ mPhone = null;
+ }
+
+ /**
+ * Exits emergency callback mode and triggers runnable after exit response is received.
+ */
+ public void exitEmergencyCallbackMode(Runnable onComplete) {
+ mOnEcmExitCompleteRunnable = onComplete;
+ exitEmergencyCallbackMode();
+ }
+
+ /**
+ * Sends intents that emergency callback mode changed.
+ */
+ private void sendEmergencyCallbackModeChange() {
+ Rlog.d(TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
+
+ Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+ intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm());
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Returns {@code true} if currently in emergency callback mode.
+ *
+ * <p>
+ * This is a period where the phone should be using as little power as possible and be ready to
+ * receive an incoming call from the emergency operator.
+ */
+ public boolean isInEcm() {
+ return mIsInEcm;
+ }
+
+ /**
+ * Sets the emergency callback mode state.
+ *
+ * @param isInEcm {@code true} if currently in emergency callback mode, {@code false} otherwise.
+ */
+ private void setIsInEcm(boolean isInEcm) {
+ mIsInEcm = isInEcm;
+ }
+
+ /**
+ * Returns {@code true} if currently in emergency callback mode over PS
+ */
+ public boolean isInImsEcm() {
+ return mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS && isInEcm();
+ }
+
+ /**
+ * Returns {@code true} if currently in emergency callback mode over CS
+ */
+ public boolean isInCdmaEcm() {
+ // Phone can be null in the case where we are not actively tracking an emergency call.
+ if (mPhone == null) return false;
+ // Ensure that this method doesn't return true when we are attached to GSM.
+ return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
+ && mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_CS && isInEcm();
+ }
+
+ /**
+ * Starts the process of an emergency SMS.
+ *
+ * @param phone the {@code Phone} on which to process the emergency SMS.
+ * @param smsId the SMS id on which to process the emergency SMS.
+ * @param isTestEmergencyNumber whether this is a test emergency number.
+ * @return A {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED}
+ * if the emergency SMS is successfully started.
+ */
+ public CompletableFuture<Integer> startEmergencySms(@NonNull Phone phone, @NonNull String smsId,
+ boolean isTestEmergencyNumber) {
+ Rlog.i(TAG, "startEmergencySms: phoneId=" + phone.getPhoneId() + ", smsId=" + smsId);
+
+ // When an emergency call is in progress, it checks whether an emergency call is already in
+ // progress on the different phone.
+ if (mPhone != null && !isSamePhone(mPhone, phone)) {
+ Rlog.e(TAG, "Emergency call is in progress on the other slot.");
+ return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ }
+
+ // When an emergency SMS is in progress, it checks whether an emergency SMS is already in
+ // progress on the different phone.
+ if (mSmsPhone != null && !isSamePhone(mSmsPhone, phone)) {
+ Rlog.e(TAG, "Emergency SMS is in progress on the other slot.");
+ return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ }
+
+ // When the previous emergency SMS is not completed yet,
+ // this new request will not be allowed.
+ if (mSmsPhone != null && isInEmergencyMode() && isEmergencyModeInProgress()) {
+ Rlog.e(TAG, "Existing emergency SMS is in progress.");
+ return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ }
+
+ mSmsPhone = phone;
+ mIsTestEmergencyNumberForSms = isTestEmergencyNumber;
+ mOngoingEmergencySmsIds.add(smsId);
+
+ // When the emergency mode is already set by the previous emergency call or SMS,
+ // completes the future immediately.
+ if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
+ return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ mSmsEmergencyModeFuture = new CompletableFuture<>();
+ if (!isInEmergencyMode()) {
+ setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN,
+ MSG_SET_EMERGENCY_MODE_DONE);
+ }
+ return mSmsEmergencyModeFuture;
+ }
+
+ /**
+ * Ends an emergency SMS.
+ * This should be called once an emergency SMS is sent.
+ *
+ * @param smsId the SMS id on which to end the emergency SMS.
+ * @param emergencyNumber the emergency number which was used for the emergency SMS.
+ */
+ public void endSms(@NonNull String smsId, EmergencyNumber emergencyNumber) {
+ mOngoingEmergencySmsIds.remove(smsId);
+
+ // If the outgoing emergency SMSs are empty, we can try to exit the emergency mode.
+ if (mOngoingEmergencySmsIds.isEmpty()) {
+ if (isInEcm()) {
+ // When the emergency mode is not in MODE_EMERGENCY_CALLBACK,
+ // it needs to notify the emergency callback mode to modem.
+ if (mActiveEmergencyCalls.isEmpty() && mOngoingCallId == null) {
+ setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
+ MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
+ }
+ } else {
+ exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ }
+
+ clearEmergencySmsInfo();
+ }
+ }
+
+ private void clearEmergencySmsInfo() {
+ mOngoingEmergencySmsIds.clear();
+ mIsTestEmergencyNumberForSms = false;
+ mSmsEmergencyModeFuture = null;
+ mSmsPhone = null;
+ }
+
+ /**
+ * Returns {@code true} if any phones from PhoneFactory have radio on.
+ */
+ private boolean isRadioOn() {
+ boolean result = false;
+ for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+ result |= phone.isRadioOn();
+ }
+ return result;
+ }
+
+ /**
+ * Returns {@code true} if airplane mode is on.
+ */
+ private boolean isAirplaneModeOn(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+ }
+
+ /**
+ * Ensures that the radio is switched on and that DDS is switched for emergency call/SMS.
+ *
+ * <p>
+ * Once radio is on and DDS switched, must call setEmergencyMode() before completing the future
+ * and selecting emergency domain. EmergencyRegResult is required to determine domain and
+ * setEmergencyMode() is the only API that can receive it before starting domain selection.
+ * Once domain selection is finished, the actual emergency mode will be set when
+ * onEmergencyTransportChanged() is called.
+ *
+ * @param phone the {@code Phone} for the emergency call/SMS.
+ * @param emergencyType the emergency type to identify an emergency call or SMS.
+ * @param isTestEmergencyNumber a flag to inidicate whether the emergency call/SMS uses the test
+ * emergency number.
+ */
+ private void turnOnRadioAndSwitchDds(Phone phone, @EmergencyType int emergencyType,
+ boolean isTestEmergencyNumber) {
+ final boolean isAirplaneModeOn = isAirplaneModeOn(mContext);
+ boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn;
+
+ if (needToTurnOnRadio) {
+ Rlog.i(TAG, "turnOnRadioAndSwitchDds: phoneId=" + phone.getPhoneId() + " for "
+ + emergencyTypeToString(emergencyType));
+ if (mRadioOnHelper == null) {
+ mRadioOnHelper = new RadioOnHelper(mContext);
+ }
+
+ mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
+ @Override
+ public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
+ if (!isRadioReady) {
+ // Could not turn radio on
+ Rlog.e(TAG, "Failed to turn on radio.");
+ completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF);
+ } else {
+ switchDdsAndSetEmergencyMode(phone, emergencyType);
+ }
+ }
+
+ @Override
+ public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
+ // We currently only look to make sure that the radio is on before dialing. We
+ // should be able to make emergency calls at any time after the radio has been
+ // powered on and isn't in the UNAVAILABLE state, even if it is reporting the
+ // OUT_OF_SERVICE state.
+ return phone.getServiceStateTracker().isRadioOn();
+ }
+
+ @Override
+ public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
+ return true;
+ }
+ }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, 0);
+ } else {
+ switchDdsAndSetEmergencyMode(phone, emergencyType);
+ }
+ }
+
+ /**
+ * If needed, block until the default data is switched for outgoing emergency call, or
+ * timeout expires.
+ *
+ * @param phone The Phone to switch the DDS on.
+ * @param completeConsumer The consumer to call once the default data subscription has been
+ * switched, provides {@code true} result if the switch happened
+ * successfully or {@code false} if the operation timed out/failed.
+ */
+ @VisibleForTesting
+ public void switchDdsDelayed(Phone phone, Consumer<Boolean> completeConsumer) {
+ if (phone == null) {
+ // Do not block indefinitely.
+ completeConsumer.accept(false);
+ }
+ try {
+ // Waiting for PhoneSwitcher to complete the operation.
+ CompletableFuture<Boolean> future = possiblyOverrideDefaultDataForEmergencyCall(phone);
+ // In the case that there is an issue or bug in PhoneSwitcher logic, do not wait
+ // indefinitely for the future to complete. Instead, set a timeout that will complete
+ // the future as to not block the outgoing call indefinitely.
+ CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+ mHandler.postDelayed(() -> timeout.complete(false), DEFAULT_DATA_SWITCH_TIMEOUT_MS);
+ // Also ensure that the Consumer is completed on the main thread.
+ CompletableFuture<Void> unused = future.acceptEitherAsync(timeout, completeConsumer,
+ mHandler::post);
+ } catch (Exception e) {
+ Rlog.w(TAG, "switchDdsDelayed - exception= " + e.getMessage());
+ }
+ }
+
+ /**
+ * If needed, block until Default Data subscription is switched for outgoing emergency call.
+ *
+ * <p>
+ * In some cases, we need to try to switch the Default Data subscription before placing the
+ * emergency call on DSDS devices. This includes the following situation: - The modem does not
+ * support processing GNSS SUPL requests on the non-default data subscription. For some carriers
+ * that do not provide a control plane fallback mechanism, the SUPL request will be dropped and
+ * we will not be able to get the user's location for the emergency call. In this case, we need
+ * to swap default data temporarily.
+ *
+ * @param phone Evaluates whether or not the default data should be moved to the phone
+ * specified. Should not be null.
+ */
+ private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall(
+ @NonNull Phone phone) {
+ int phoneCount = mTelephonyManagerProxy.getPhoneCount();
+ // Do not override DDS if this is a single SIM device.
+ if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
+ // Do not switch Default data if this device supports emergency SUPL on non-DDS.
+ if (!mIsSuplDdsSwitchRequiredForEmergencyCall) {
+ Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
+ + "require DDS switch.");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
+ // Only override default data if we are IN_SERVICE already.
+ if (!isAvailableForEmergencyCalls(phone)) {
+ Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
+ // Only override default data if we are not roaming, we do not want to switch onto a network
+ // that only supports data plane only (if we do not know).
+ boolean isRoaming = phone.getServiceState().getVoiceRoaming();
+ // In some roaming conditions, we know the roaming network doesn't support control plane
+ // fallback even though the home operator does. For these operators we will need to do a DDS
+ // switch anyway to make sure the SUPL request doesn't fail.
+ boolean roamingNetworkSupportsControlPlaneFallback = true;
+ String[] dataPlaneRoamPlmns = getConfig(phone.getSubId(),
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY);
+ if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns)
+ .contains(phone.getServiceState().getOperatorNumeric())) {
+ roamingNetworkSupportsControlPlaneFallback = false;
+ }
+ if (isRoaming && roamingNetworkSupportsControlPlaneFallback) {
+ Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed "
+ + "to support CP fallback, not switching DDS.");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+ // Do not try to swap default data if we support CS fallback or it is assumed that the
+ // roaming network supports control plane fallback, we do not want to introduce a lag in
+ // emergency call setup time if possible.
+ final boolean supportsCpFallback = getConfig(phone.getSubId(),
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY)
+ != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY;
+ if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) {
+ Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier "
+ + "supports CP fallback.");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
+ // Get extension time, may be 0 for some carriers that support ECBM as well. Use
+ // CarrierConfig default if format fails.
+ int extensionTime = 0;
+ try {
+ extensionTime = Integer.parseInt(getConfig(phone.getSubId(),
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0"));
+ } catch (NumberFormatException e) {
+ // Just use default.
+ }
+ CompletableFuture<Boolean> modemResultFuture = new CompletableFuture<>();
+ try {
+ Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for "
+ + extensionTime + "seconds");
+ mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency(
+ phone.getPhoneId(), extensionTime, modemResultFuture);
+ // Catch all exceptions, we want to continue with emergency call if possible.
+ } catch (Exception e) {
+ Rlog.w(TAG,
+ "possiblyOverrideDefaultDataForEmergencyCall: exception = " + e.getMessage());
+ modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE);
+ }
+ return modemResultFuture;
+ }
+
+ // Helper functions for easy CarrierConfigManager access
+ private String getConfig(int subId, String key, String defVal) {
+ return getConfigBundle(subId, key).getString(key, defVal);
+ }
+ private int getConfig(int subId, String key, int defVal) {
+ return getConfigBundle(subId, key).getInt(key, defVal);
+ }
+ private String[] getConfig(int subId, String key) {
+ return getConfigBundle(subId, key).getStringArray(key);
+ }
+ private boolean getConfig(int subId, String key, boolean defVal) {
+ return getConfigBundle(subId, key).getBoolean(key, defVal);
+ }
+ private PersistableBundle getConfigBundle(int subId, String key) {
+ if (mConfigManager == null) return new PersistableBundle();
+ return mConfigManager.getConfigForSubId(subId, key);
+ }
+
+ /**
+ * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
+ */
+ private boolean isAvailableForEmergencyCalls(Phone phone) {
+ return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState()
+ || phone.getServiceState().isEmergencyOnly();
+ }
+
+ /**
+ * Checks whether both {@code Phone}s are same or not.
+ */
+ private static boolean isSamePhone(Phone p1, Phone p2) {
+ return p1 != null && p2 != null && (p1.getPhoneId() == p2.getPhoneId());
+ }
+
+ private static String emergencyTypeToString(@EmergencyType int emergencyType) {
+ switch (emergencyType) {
+ case EMERGENCY_TYPE_CALL: return "CALL";
+ case EMERGENCY_TYPE_SMS: return "SMS";
+ default: return "UNKNOWN";
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
new file mode 100644
index 0000000..9c4ebfa
--- /dev/null
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class that implements special behavior related to emergency calls or making phone calls
+ * when the radio is in the POWER_OFF STATE. Specifically, this class handles the case of the user
+ * trying to dial an emergency number while the radio is off (i.e. the device is in airplane mode)
+ * or a normal number while the radio is off (because of the device is on Bluetooth), by turning the
+ * radio back on, waiting for it to come up, and then retrying the call.
+ */
+public class RadioOnHelper implements RadioOnStateListener.Callback {
+
+ private static final String TAG = "RadioOnStateListener";
+
+ private final Context mContext;
+ private RadioOnStateListener.Callback mCallback;
+ private List<RadioOnStateListener> mListeners;
+ private List<RadioOnStateListener> mInProgressListeners;
+ private boolean mIsRadioReady;
+
+ public RadioOnHelper(Context context) {
+ mContext = context;
+ mInProgressListeners = new ArrayList<>(2);
+ }
+
+ private void setupListeners() {
+ if (mListeners == null) {
+ mListeners = new ArrayList<>(2);
+ }
+ int activeModems = TelephonyManager.from(mContext).getActiveModemCount();
+ // Add new listeners if active modem count increased.
+ while (mListeners.size() < activeModems) {
+ mListeners.add(new RadioOnStateListener());
+ }
+ // Clean up listeners if active modem count decreased.
+ while (mListeners.size() > activeModems) {
+ mListeners.get(mListeners.size() - 1).cleanup();
+ mListeners.remove(mListeners.size() - 1);
+ }
+ }
+
+ /**
+ * Starts the "turn on radio" sequence. This is the (single) external API of the RadioOnHelper
+ * class.
+ *
+ * This method kicks off the following sequence:
+ * - Power on the radio for each Phone and disable the satellite modem
+ * - Listen for events telling us the radio has come up or the satellite modem is disabled.
+ * - Retry if we've gone a significant amount of time without any response.
+ * - Finally, clean up any leftover state.
+ *
+ * This method is safe to call from any thread, since it simply posts a message to the
+ * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely serialized,
+ * and runs on the main looper.)
+ */
+ public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
+ boolean forEmergencyCall, Phone phoneForEmergencyCall, boolean isTestEmergencyNumber,
+ int emergencyTimeoutIntervalMillis) {
+ setupListeners();
+ mCallback = callback;
+ mInProgressListeners.clear();
+ mIsRadioReady = false;
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
+ Phone phone = PhoneFactory.getPhone(i);
+ if (phone == null) {
+ continue;
+ }
+
+ int timeoutCallbackInterval = (forEmergencyCall && phone == phoneForEmergencyCall)
+ ? emergencyTimeoutIntervalMillis : 0;
+ mInProgressListeners.add(mListeners.get(i));
+ mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall
+ && phone == phoneForEmergencyCall, timeoutCallbackInterval);
+ }
+ powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber);
+ if (SatelliteController.getInstance().isSatelliteEnabled()) {
+ powerOffSatellite(phoneForEmergencyCall);
+ }
+ }
+
+ /**
+ * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
+ * get an onServiceStateChanged() callback when the radio successfully comes up.
+ */
+ private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall,
+ boolean isTestEmergencyNumber) {
+
+ // Always try to turn on the radio here independent of APM setting - if we got here in the
+ // first place, the radio is off independent of APM setting.
+ for (Phone phone : PhoneFactory.getPhones()) {
+ Rlog.d(TAG, "powerOnRadio, enabling Radio");
+ if (isTestEmergencyNumber) {
+ phone.setRadioPowerOnForTestEmergencyCall(phone == phoneForEmergencyCall);
+ } else {
+ phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall,
+ false);
+ }
+ }
+
+ // If airplane mode is on, we turn it off the same way that the Settings activity turns it
+ // off to keep the setting in sync.
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
+ Rlog.d(TAG, "==> Turning off airplane mode for emergency call.");
+
+ // Change the system setting
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
+
+ // Post the broadcast intend for change in airplane mode TODO: We really should not be
+ // in charge of sending this broadcast. If changing the setting is sufficient to trigger
+ // all of the rest of the logic, then that should also trigger the broadcast intent.
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", false);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ /**
+ * Attempt to power off the satellite modem. We'll eventually get an
+ * onSatelliteModemStateChanged() callback when the satellite modem is successfully disabled.
+ */
+ private void powerOffSatellite(Phone phoneForEmergencyCall) {
+ SatelliteController satelliteController = SatelliteController.getInstance();
+ satelliteController.requestSatelliteEnabled(phoneForEmergencyCall.getSubId(),
+ false /* enableSatellite */, false /* enableDemoMode */,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+
+ }
+ });
+ }
+
+ /**
+ * This method is called from multiple Listeners on the Main Looper. Synchronization is not
+ * necessary.
+ */
+ @Override
+ public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
+ mIsRadioReady |= isRadioReady;
+ mInProgressListeners.remove(listener);
+ if (mCallback != null && mInProgressListeners.isEmpty()) {
+ mCallback.onComplete(null, mIsRadioReady);
+ }
+ }
+
+ @Override
+ public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
+ return (mCallback == null)
+ ? false : mCallback.isOkToCall(phone, serviceState, imsVoiceCapable);
+ }
+
+ @Override
+ public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
+ return (mCallback == null)
+ ? false : mCallback.onTimeout(phone, serviceState, imsVoiceCapable);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
new file mode 100644
index 0000000..d61c146
--- /dev/null
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.satellite.ISatelliteStateCallback;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.telephony.Rlog;
+
+import java.util.Locale;
+
+/**
+ * Helper class that listens to a Phone's radio state and sends an onComplete callback when we
+ * return true for isOkToCall.
+ */
+public class RadioOnStateListener {
+
+ public interface Callback {
+ /**
+ * Receives the result of the RadioOnStateListener's attempt to turn on the radio
+ * and turn off the satellite modem.
+ */
+ void onComplete(RadioOnStateListener listener, boolean isRadioReady);
+
+ /**
+ * Returns whether or not this phone is ok to call.
+ * If it is, onComplete will be called shortly after.
+ *
+ * @param phone The Phone associated.
+ * @param serviceState The service state of that phone.
+ * @param imsVoiceCapable The IMS voice capability of that phone.
+ * @return {@code true} if this phone is ok to call. Otherwise, {@code false}.
+ */
+ boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable);
+
+ /**
+ * Returns whether or not this phone is ok to call.
+ * This callback will be called when timeout happens.
+ * If this returns {@code true}, onComplete will be called shortly after.
+ * Otherwise, a new timer will be started again to keep waiting for next timeout.
+ * The timeout interval will be passed to {@link #waitForRadioOn()} when registering
+ * this callback.
+ *
+ * @param phone The Phone associated.
+ * @param serviceState The service state of that phone.
+ * @param imsVoiceCapable The IMS voice capability of that phone.
+ * @return {@code true} if this phone is ok to call. Otherwise, {@code false}.
+ */
+ boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable);
+ }
+
+ private static final String TAG = "RadioOnStateListener";
+
+ // Number of times to retry the call, and time between retry attempts.
+ // not final for testing
+ private static int MAX_NUM_RETRIES = 5;
+ // not final for testing
+ private static long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec
+
+ // Handler message codes; see handleMessage()
+ private static final int MSG_START_SEQUENCE = 1;
+ @VisibleForTesting
+ public static final int MSG_SERVICE_STATE_CHANGED = 2;
+ private static final int MSG_RETRY_TIMEOUT = 3;
+ @VisibleForTesting
+ public static final int MSG_RADIO_ON = 4;
+ public static final int MSG_RADIO_OFF_OR_NOT_AVAILABLE = 5;
+ public static final int MSG_IMS_CAPABILITY_CHANGED = 6;
+ public static final int MSG_TIMEOUT_ONTIMEOUT_CALLBACK = 7;
+ @VisibleForTesting
+ public static final int MSG_SATELLITE_ENABLED_CHANGED = 8;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_SEQUENCE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Phone phone = (Phone) args.arg1;
+ RadioOnStateListener.Callback callback =
+ (RadioOnStateListener.Callback) args.arg2;
+ boolean forEmergencyCall = (boolean) args.arg3;
+ boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
+ int onTimeoutCallbackInterval = args.argi1;
+ startSequenceInternal(phone, callback, forEmergencyCall,
+ isSelectedPhoneForEmergencyCall, onTimeoutCallbackInterval);
+ } finally {
+ args.recycle();
+ }
+ break;
+ case MSG_SERVICE_STATE_CHANGED:
+ onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
+ break;
+ case MSG_RADIO_ON:
+ onRadioOn();
+ break;
+ case MSG_RADIO_OFF_OR_NOT_AVAILABLE:
+ registerForRadioOn();
+ break;
+ case MSG_RETRY_TIMEOUT:
+ onRetryTimeout();
+ break;
+ case MSG_IMS_CAPABILITY_CHANGED:
+ onImsCapabilityChanged();
+ break;
+ case MSG_TIMEOUT_ONTIMEOUT_CALLBACK:
+ onTimeoutCallbackTimeout();
+ break;
+ case MSG_SATELLITE_ENABLED_CHANGED:
+ onSatelliteEnabledChanged();
+ break;
+ default:
+ Rlog.w(TAG, String.format(Locale.getDefault(),
+ "handleMessage: unexpected message: %d.", msg.what));
+ break;
+ }
+ }
+ };
+
+ private final ISatelliteStateCallback mSatelliteCallback = new ISatelliteStateCallback.Stub() {
+ @Override
+ public void onSatelliteModemStateChanged(int state) {
+ mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED).sendToTarget();
+ }
+ };
+
+ private Callback mCallback; // The callback to notify upon completion.
+ private Phone mPhone; // The phone that will attempt to place the call.
+ // SatelliteController instance to check whether satellite has been disabled.
+ private SatelliteController mSatelliteController;
+ private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
+ // Whether this phone is selected to place emergency call. Can be true only if
+ // mForEmergencyCall is true.
+ private boolean mSelectedPhoneForEmergencyCall;
+ private int mNumRetriesSoFar;
+ private int mOnTimeoutCallbackInterval; // the interval between onTimeout callbacks
+
+ /**
+ * Starts the "wait for radio" sequence. This is the (single) external API of the
+ * RadioOnStateListener class.
+ *
+ * This method kicks off the following sequence:
+ * - Listen for the service state change event telling us the radio has come up.
+ * - Listen for the satellite state changed event telling us the satellite service is disabled.
+ * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
+ * radio.
+ * - Finally, clean up any leftover state.
+ *
+ * This method is safe to call from any thread, since it simply posts a message to the
+ * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
+ * serialized, and runs only on the handler thread.)
+ */
+ public void waitForRadioOn(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall,
+ int onTimeoutCallbackInterval) {
+ Rlog.d(TAG, "waitForRadioOn: Phone " + phone.getPhoneId());
+
+ if (mPhone != null) {
+ // If there already is an ongoing request, ignore the new one!
+ return;
+ }
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = phone;
+ args.arg2 = callback;
+ args.arg3 = forEmergencyCall;
+ args.arg4 = isSelectedPhoneForEmergencyCall;
+ args.argi1 = onTimeoutCallbackInterval;
+ mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
+ }
+
+ /**
+ * Actual implementation of waitForRadioOn(), guaranteed to run on the handler thread.
+ *
+ * @see #waitForRadioOn
+ */
+ private void startSequenceInternal(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall,
+ int onTimeoutCallbackInterval) {
+ Rlog.d(TAG, "startSequenceInternal: Phone " + phone.getPhoneId());
+ mSatelliteController = SatelliteController.getInstance();
+
+ // First of all, clean up any state left over from a prior RadioOn call sequence. This
+ // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
+ // we're already in the middle of the sequence.
+ cleanup();
+
+ mPhone = phone;
+ mCallback = callback;
+ mForEmergencyCall = forEmergencyCall;
+ mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
+ mOnTimeoutCallbackInterval = onTimeoutCallbackInterval;
+
+ registerForServiceStateChanged();
+ // Register for RADIO_OFF to handle cases where emergency call is dialed before
+ // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF.
+ registerForRadioOff();
+ if (mSatelliteController.isSatelliteEnabled()) {
+ // Register for satellite modem state changed to notify when satellite is disabled.
+ registerForSatelliteEnabledChanged();
+ }
+ // Next step: when the SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED event comes in,
+ // we'll retry the call; see onServiceStateChanged() and onSatelliteEnabledChanged().
+ // But also, just in case, start a timer to make sure we'll retry the call even if the
+ // SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED events never come in for some reason.
+ startRetryTimer();
+ registerForImsCapabilityChanged();
+ startOnTimeoutCallbackTimer();
+ }
+
+ private void onImsCapabilityChanged() {
+ if (mPhone == null) {
+ return;
+ }
+
+ boolean imsVoiceCapable = mPhone.isVoiceOverCellularImsEnabled();
+
+ Rlog.d(TAG, String.format("onImsCapabilityChanged, capable = %s, Phone = %s",
+ imsVoiceCapable, mPhone.getPhoneId()));
+
+ if (isOkToCall(mPhone.getServiceState().getState(), imsVoiceCapable)) {
+ Rlog.d(TAG, "onImsCapabilityChanged: ok to call!");
+
+ onComplete(true);
+ cleanup();
+ } else {
+ // The IMS capability changed, but we're still not ready to call yet.
+ Rlog.d(TAG, "onImsCapabilityChanged: not ready to call yet, keep waiting.");
+ }
+ }
+
+ private void onTimeoutCallbackTimeout() {
+ if (mPhone == null) {
+ return;
+ }
+
+ if (onTimeout(mPhone.getServiceState().getState(),
+ mPhone.isVoiceOverCellularImsEnabled())) {
+ Rlog.d(TAG, "onTimeout: ok to call!");
+
+ onComplete(true);
+ cleanup();
+ } else if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
+ Rlog.w(TAG, "onTimeout: Hit MAX_NUM_RETRIES; giving up.");
+ cleanup();
+ } else {
+ Rlog.d(TAG, "onTimeout: not ready to call yet, keep waiting.");
+ startOnTimeoutCallbackTimer();
+ }
+ }
+
+ /**
+ * Handles the SERVICE_STATE_CHANGED event. This event tells us that the radio state has changed
+ * and is probably coming up. We can now check to see if the conditions are met to place the
+ * call with {@link Callback#isOkToCall}
+ */
+ private void onServiceStateChanged(ServiceState state) {
+ if (mPhone == null) {
+ return;
+ }
+ Rlog.d(TAG, String.format("onServiceStateChanged(), new state = %s, Phone = %s", state,
+ mPhone.getPhoneId()));
+
+ // Possible service states:
+ // - STATE_IN_SERVICE // Normal operation
+ // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to,
+ // // or no radio signal
+ // - STATE_EMERGENCY_ONLY // Only emergency numbers are allowed; currently not used
+ // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode)
+
+ if (isOkToCall(state.getState(), mPhone.isVoiceOverCellularImsEnabled())) {
+ // Woo hoo! It's OK to actually place the call.
+ Rlog.d(TAG, "onServiceStateChanged: ok to call!");
+
+ onComplete(true);
+ cleanup();
+ } else {
+ // The service state changed, but we're still not ready to call yet.
+ Rlog.d(TAG, "onServiceStateChanged: not ready to call yet, keep waiting.");
+ }
+ }
+
+ private void onRadioOn() {
+ if (mPhone == null) {
+ return;
+ }
+ ServiceState state = mPhone.getServiceState();
+ Rlog.d(TAG, String.format("onRadioOn, state = %s, Phone = %s", state, mPhone.getPhoneId()));
+ if (isOkToCall(state.getState(), mPhone.isVoiceOverCellularImsEnabled())) {
+ onComplete(true);
+ cleanup();
+ } else {
+ Rlog.d(TAG, "onRadioOn: not ready to call yet, keep waiting.");
+ }
+ }
+
+ private void onSatelliteEnabledChanged() {
+ if (mPhone == null) {
+ return;
+ }
+ if (isOkToCall(mPhone.getServiceState().getState(),
+ mPhone.isVoiceOverCellularImsEnabled())) {
+ onComplete(true);
+ cleanup();
+ } else {
+ Rlog.d(TAG, "onSatelliteEnabledChanged: not ready to call yet, keep waiting.");
+ }
+ }
+
+ /**
+ * Callback to see if it is okay to call yet, given the current conditions.
+ */
+ private boolean isOkToCall(int serviceState, boolean imsVoiceCapable) {
+ return (mCallback == null)
+ ? false : mCallback.isOkToCall(mPhone, serviceState, imsVoiceCapable);
+ }
+
+ /**
+ * Callback to see if it is okay to call yet, given the current conditions.
+ */
+ private boolean onTimeout(int serviceState, boolean imsVoiceCapable) {
+ return (mCallback == null)
+ ? false : mCallback.onTimeout(mPhone, serviceState, imsVoiceCapable);
+ }
+
+ /**
+ * Handles the retry timer expiring.
+ */
+ private void onRetryTimeout() {
+ if (mPhone == null) {
+ return;
+ }
+ int serviceState = mPhone.getServiceState().getState();
+ Rlog.d(TAG,
+ String.format(Locale.getDefault(),
+ "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
+ mPhone.getState(), serviceState, mNumRetriesSoFar));
+
+ // - If we're actually in a call, we've succeeded.
+ // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode
+ // but somehow didn't get the service state change event. In that case, try to place the
+ // call.
+ // - If the radio is still powered off, try powering it on again.
+
+ if (isOkToCall(serviceState, mPhone.isVoiceOverCellularImsEnabled())) {
+ Rlog.d(TAG, "onRetryTimeout: Radio is on. Cleaning up.");
+
+ // Woo hoo -- we successfully got out of airplane mode.
+ onComplete(true);
+ cleanup();
+ } else {
+ // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not
+ // powered-on. Try again.
+
+ mNumRetriesSoFar++;
+ Rlog.d(TAG, "mNumRetriesSoFar is now " + mNumRetriesSoFar);
+
+ if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
+ if (mHandler.hasMessages(MSG_TIMEOUT_ONTIMEOUT_CALLBACK)) {
+ Rlog.w(TAG, "Hit MAX_NUM_RETRIES; waiting onTimeout callback");
+ return;
+ }
+ Rlog.w(TAG, "Hit MAX_NUM_RETRIES; giving up.");
+ cleanup();
+ } else {
+ Rlog.d(TAG, "Trying (again) to turn the radio on and satellite modem off.");
+ mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
+ false);
+ if (mSatelliteController.isSatelliteEnabled()) {
+ mSatelliteController.requestSatelliteEnabled(mPhone.getSubId(),
+ false /* enableSatellite */, false /* enableDemoMode */,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED)
+ .sendToTarget();
+ }
+ });
+ }
+ startRetryTimer();
+ }
+ }
+ }
+
+ /**
+ * Clean up when done with the whole sequence: either after successfully turning on the radio,
+ * or after bailing out because of too many failures.
+ *
+ * The exact cleanup steps are:
+ * - Notify callback if we still hadn't sent it a response.
+ * - Double-check that we're not still registered for any telephony events
+ * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
+ *
+ * Basically this method guarantees that there will be no more activity from the
+ * RadioOnStateListener until someone kicks off the whole sequence again with another call to
+ * {@link #waitForRadioOn}
+ *
+ * TODO: Do the work for the comment below: Note we don't call this method simply after a
+ * successful call to placeCall(), since it's still possible the call will disconnect very
+ * quickly with an OUT_OF_SERVICE error.
+ */
+ public void cleanup() {
+ Rlog.d(TAG, "cleanup()");
+
+ // This will send a failure call back if callback has yet to be invoked. If the callback was
+ // already invoked, it's a no-op.
+ onComplete(false);
+
+ unregisterForServiceStateChanged();
+ unregisterForRadioOff();
+ unregisterForRadioOn();
+ unregisterForSatelliteEnabledChanged();
+ cancelRetryTimer();
+ unregisterForImsCapabilityChanged();
+
+ // Used for unregisterForServiceStateChanged() so we null it out here instead.
+ mPhone = null;
+ mNumRetriesSoFar = 0;
+ mOnTimeoutCallbackInterval = 0;
+ }
+
+ private void startRetryTimer() {
+ cancelRetryTimer();
+ mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS);
+ }
+
+ private void cancelRetryTimer() {
+ mHandler.removeMessages(MSG_RETRY_TIMEOUT);
+ }
+
+ private void registerForServiceStateChanged() {
+ // Unregister first, just to make sure we never register ourselves twice. (We need this
+ // because Phone.registerForServiceStateChanged() does not prevent multiple registration of
+ // the same handler.)
+ unregisterForServiceStateChanged();
+ mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null);
+ }
+
+ private void unregisterForServiceStateChanged() {
+ // This method is safe to call even if we haven't set mPhone yet.
+ if (mPhone != null) {
+ mPhone.unregisterForServiceStateChanged(mHandler); // Safe even if unnecessary
+ }
+ mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED); // Clean up any pending messages too
+ }
+
+ private void registerForRadioOff() {
+ mPhone.mCi.registerForOffOrNotAvailable(mHandler, MSG_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ }
+
+ private void unregisterForRadioOff() {
+ // This method is safe to call even if we haven't set mPhone yet.
+ if (mPhone != null) {
+ mPhone.mCi.unregisterForOffOrNotAvailable(mHandler); // Safe even if unnecessary
+ }
+ mHandler.removeMessages(MSG_RADIO_OFF_OR_NOT_AVAILABLE); // Clean up any pending messages
+ }
+
+ private void registerForRadioOn() {
+ unregisterForRadioOff();
+ mPhone.mCi.registerForOn(mHandler, MSG_RADIO_ON, null);
+ }
+
+ private void unregisterForRadioOn() {
+ // This method is safe to call even if we haven't set mPhone yet.
+ if (mPhone != null) {
+ mPhone.mCi.unregisterForOn(mHandler); // Safe even if unnecessary
+ }
+ mHandler.removeMessages(MSG_RADIO_ON); // Clean up any pending messages too
+ }
+
+ private void registerForSatelliteEnabledChanged() {
+ mSatelliteController.registerForSatelliteModemStateChanged(
+ mPhone.getSubId(), mSatelliteCallback);
+ }
+
+ private void unregisterForSatelliteEnabledChanged() {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (mPhone != null) {
+ subId = mPhone.getSubId();
+ }
+ mSatelliteController.unregisterForSatelliteModemStateChanged(subId, mSatelliteCallback);
+ mHandler.removeMessages(MSG_SATELLITE_ENABLED_CHANGED);
+ }
+
+ private void registerForImsCapabilityChanged() {
+ unregisterForImsCapabilityChanged();
+ mPhone.getServiceStateTracker()
+ .registerForImsCapabilityChanged(mHandler, MSG_IMS_CAPABILITY_CHANGED, null);
+ }
+
+ private void unregisterForImsCapabilityChanged() {
+ if (mPhone != null) {
+ mPhone.getServiceStateTracker()
+ .unregisterForImsCapabilityChanged(mHandler);
+ }
+ mHandler.removeMessages(MSG_IMS_CAPABILITY_CHANGED);
+ }
+
+ private void startOnTimeoutCallbackTimer() {
+ Rlog.d(TAG, "startOnTimeoutCallbackTimer: mOnTimeoutCallbackInterval="
+ + mOnTimeoutCallbackInterval);
+ mHandler.removeMessages(MSG_TIMEOUT_ONTIMEOUT_CALLBACK);
+ if (mOnTimeoutCallbackInterval > 0) {
+ mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT_ONTIMEOUT_CALLBACK,
+ mOnTimeoutCallbackInterval);
+ }
+ }
+
+ private void onComplete(boolean isRadioReady) {
+ if (mCallback != null) {
+ Callback tempCallback = mCallback;
+ mCallback = null;
+ tempCallback.onComplete(this, isRadioReady);
+ }
+ }
+
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @VisibleForTesting
+ public void setMaxNumRetries(int retries) {
+ MAX_NUM_RETRIES = retries;
+ }
+
+ @VisibleForTesting
+ public void setTimeBetweenRetriesMillis(long timeMs) {
+ TIME_BETWEEN_RETRIES_MILLIS = timeMs;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || !getClass().equals(o.getClass()))
+ return false;
+
+ RadioOnStateListener that = (RadioOnStateListener) o;
+
+ if (mNumRetriesSoFar != that.mNumRetriesSoFar) {
+ return false;
+ }
+ if (mCallback != null ? !mCallback.equals(that.mCallback) : that.mCallback != null) {
+ return false;
+ }
+ return mPhone != null ? mPhone.equals(that.mPhone) : that.mPhone == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 31 * hash + mNumRetriesSoFar;
+ hash = 31 * hash + (mCallback == null ? 0 : mCallback.hashCode());
+ hash = 31 * hash + (mPhone == null ? 0 : mPhone.hashCode());
+ return hash;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index f337141..2f73c91 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -39,7 +39,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccPort;
@@ -51,6 +51,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/** Backing implementation of {@link EuiccCardManager}. */
public class EuiccCardController extends IEuiccCardController.Stub {
@@ -413,7 +414,7 @@
// if there is no iccid enabled on this port, return null.
if (TextUtils.isEmpty(iccId)) {
try {
- callback.onComplete(EuiccCardManager.RESULT_PROFILE_NOT_FOUND, null);
+ callback.onComplete(EuiccCardManager.RESULT_PROFILE_DOES_NOT_EXIST, null);
} catch (RemoteException exception) {
loge("getEnabledProfile callback failure.", exception);
}
@@ -649,9 +650,8 @@
@Override
public void onResult(Void result) {
Log.i(TAG, "Request subscription info list refresh after delete.");
- SubscriptionController.getInstance()
- .requestEmbeddedSubscriptionInfoListRefresh(
- mUiccController.convertToPublicCardId(cardId));
+ SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
+ List.of(mUiccController.convertToPublicCardId(cardId)), null);
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
@@ -701,9 +701,8 @@
@Override
public void onResult(Void result) {
Log.i(TAG, "Request subscription info list refresh after reset memory.");
- SubscriptionController.getInstance()
- .requestEmbeddedSubscriptionInfoListRefresh(
- mUiccController.convertToPublicCardId(cardId));
+ SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
+ List.of(mUiccController.convertToPublicCardId(cardId)), null);
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
@@ -1190,9 +1189,8 @@
@Override
public void onResult(byte[] result) {
Log.i(TAG, "Request subscription info list refresh after install.");
- SubscriptionController.getInstance()
- .requestEmbeddedSubscriptionInfoListRefresh(
- mUiccController.convertToPublicCardId(cardId));
+ SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
+ List.of(mUiccController.convertToPublicCardId(cardId)), null);
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 974acf9..c417a34 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -101,7 +101,8 @@
* true or onServiceDisconnected is called (and no package change has occurred which should
* force us to reestablish the binding).
*/
- private static final int BIND_TIMEOUT_MILLIS = 30000;
+ @VisibleForTesting
+ static final int BIND_TIMEOUT_MILLIS = 30000;
/**
* Maximum amount of idle time to hold the binding while in {@link ConnectedState}. After this,
@@ -225,6 +226,8 @@
static class GetMetadataRequest {
DownloadableSubscription mSubscription;
boolean mForceDeactivateSim;
+ boolean mSwitchAfterDownload;
+ int mPortIndex;
GetMetadataCommandCallback mCallback;
}
@@ -389,6 +392,9 @@
mSm = (SubscriptionManager)
context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ // TODO(b/239277548): Disable debug logging after analysing this bug.
+ setDbg(true);
+
// Unavailable/Available both monitor for package changes and update mSelectedComponent but
// do not need to adjust the binding.
mUnavailableState = new UnavailableState();
@@ -444,13 +450,15 @@
/** Asynchronously fetch metadata for the given downloadable subscription. */
@VisibleForTesting(visibility = PACKAGE)
- public void getDownloadableSubscriptionMetadata(int cardId,
- DownloadableSubscription subscription,
+ public void getDownloadableSubscriptionMetadata(int cardId, int portIndex,
+ DownloadableSubscription subscription, boolean switchAfterDownload,
boolean forceDeactivateSim, GetMetadataCommandCallback callback) {
GetMetadataRequest request =
new GetMetadataRequest();
request.mSubscription = subscription;
request.mForceDeactivateSim = forceDeactivateSim;
+ request.mSwitchAfterDownload = switchAfterDownload;
+ request.mPortIndex = portIndex;
request.mCallback = callback;
sendMessage(CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA, cardId, 0 /* arg2 */, request);
}
@@ -549,6 +557,11 @@
callback);
}
+ @VisibleForTesting
+ public final IEuiccService getBinder() {
+ return mEuiccService;
+ }
+
/**
* State in which no EuiccService is available.
*
@@ -686,6 +699,7 @@
}
return HANDLED;
} else if (message.what == CMD_CONNECT_TIMEOUT) {
+ unbind();
transitionTo(mAvailableState);
return HANDLED;
} else if (isEuiccCommand(message.what)) {
@@ -749,7 +763,9 @@
case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA: {
GetMetadataRequest request = (GetMetadataRequest) message.obj;
mEuiccService.getDownloadableSubscriptionMetadata(slotId,
+ request.mPortIndex,
request.mSubscription,
+ request.mSwitchAfterDownload,
request.mForceDeactivateSim,
new IGetDownloadableSubscriptionMetadataCallback.Stub() {
@Override
@@ -1057,9 +1073,8 @@
for (int slotIndex = 0; slotIndex < slotInfos.length; slotIndex++) {
// Report Anomaly in case UiccSlotInfo is not.
if (slotInfos[slotIndex] == null) {
- AnomalyReporter.reportAnomaly(
- UUID.fromString("4195b83d-6cee-4999-a02f-d0b9f7079b9d"),
- "EuiccConnector: Found UiccSlotInfo Null object.");
+ Log.i(TAG, "No UiccSlotInfo found for slotIndex: " + slotIndex);
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
}
String retrievedCardId = slotInfos[slotIndex] != null
? slotInfos[slotIndex].getCardId() : null;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 965dd22..a5b95c3 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -59,8 +59,8 @@
import com.android.internal.telephony.CarrierPrivilegesTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccPort;
@@ -385,6 +385,7 @@
void getDownloadableSubscriptionMetadata(int cardId, DownloadableSubscription subscription,
boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) {
+ Log.d(TAG, " getDownloadableSubscriptionMetadata callingPackage: " + callingPackage);
if (!callerCanWriteEmbeddedSubscriptions()) {
throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata");
}
@@ -392,7 +393,8 @@
long token = Binder.clearCallingIdentity();
try {
mConnector.getDownloadableSubscriptionMetadata(cardId,
- subscription, forceDeactivateSim,
+ TelephonyManager.DEFAULT_PORT_INDEX, subscription,
+ false /* switchAfterDownload */, forceDeactivateSim,
new GetMetadataCommandCallback(
token, subscription, callingPackage, callbackIntent));
} finally {
@@ -601,8 +603,8 @@
if (!isConsentNeededToResolvePortIndex
&& canManageSubscriptionOnTargetSim(cardId, callingPackage, true,
portIndex)) {
- mConnector.getDownloadableSubscriptionMetadata(cardId, subscription,
- forceDeactivateSim,
+ mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex,
+ subscription, switchAfterDownload, forceDeactivateSim,
new DownloadSubscriptionGetMetadataCommandCallback(token, subscription,
switchAfterDownload, callingPackage, forceDeactivateSim,
callbackIntent, false /* withUserConsent */, portIndex));
@@ -713,7 +715,8 @@
Log.d(TAG, " downloadSubscriptionPrivilegedCheckMetadata cardId: " + cardId
+ " switchAfterDownload: " + switchAfterDownload + " portIndex: " + portIndex
+ " forceDeactivateSim: " + forceDeactivateSim);
- mConnector.getDownloadableSubscriptionMetadata(cardId, subscription, forceDeactivateSim,
+ mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex,
+ subscription, switchAfterDownload, forceDeactivateSim,
new DownloadSubscriptionGetMetadataCommandCallback(callingToken, subscription,
switchAfterDownload, callingPackage, forceDeactivateSim, callbackIntent,
true /* withUserConsent */, portIndex));
@@ -862,6 +865,7 @@
void getDefaultDownloadableSubscriptionList(int cardId,
boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) {
+ Log.d(TAG, " getDefaultDownloadableSubscriptionList callingPackage: " + callingPackage);
if (!callerCanWriteEmbeddedSubscriptions()) {
throw new SecurityException(
"Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get default list");
@@ -1147,7 +1151,8 @@
* Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if calling
* cannot manage any active subscription.
*/
- private int getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage,
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public int getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage,
boolean callerCanWriteEmbeddedSubscriptions) {
List<SubscriptionInfo> subInfoList = mSubscriptionManager
.getActiveSubscriptionInfoList(/* userVisibleOnly */false);
@@ -1175,7 +1180,8 @@
* Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if no port
* is available without user consent.
*/
- private int getResolvedPortIndexForSubscriptionSwitch(int cardId) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public int getResolvedPortIndexForSubscriptionSwitch(int cardId) {
int slotIndex = getSlotIndexFromCardId(cardId);
// Euicc Slot
UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex);
@@ -1585,9 +1591,9 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void refreshSubscriptionsAndSendResult(
PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
- SubscriptionController.getInstance()
- .requestEmbeddedSubscriptionInfoListRefresh(
- () -> sendResult(callbackIntent, resultCode, extrasIntent));
+ SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
+ List.of(mTelephonyManager.getCardIdForDefaultEuicc()),
+ () -> sendResult(callbackIntent, resultCode, extrasIntent));
}
/** Dispatch the given callback intent with the given result code and data. */
@@ -1840,8 +1846,11 @@
// other SIM. The target SIM should be an eUICC.
// For a single-active subscription phone, checks whether the caller can manage any active
// embedded subscription.
- // From Android T, If embedded slot supports Multiple Enabled Profiles then should check if
- // the calling app has carrier privilege over the subscription on the target port index.
+ // From Android T, if usePortIndex is true then verify whether the calling app has carrier
+ // privilege over the active embedded subscription on the target port index.
+ // If usePortIndex is false then check whether the calling app can manage any active
+ // subscription on any of the active ports, if there are no active embedded subscriptions then
+ // verify whether the calling app can manage any active subscription on any of the other SIM.
private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage,
boolean usePortIndex, int targetPortIndex) {
List<SubscriptionInfo> subInfoList = mSubscriptionManager
@@ -1861,11 +1870,9 @@
return false;
}
boolean isEuicc = false;
- boolean isMultipleEnabledProfilesSupported = false;
for (UiccCardInfo info : cardInfos) {
if (info != null && info.getCardId() == cardId && info.isEuicc()) {
isEuicc = true;
- isMultipleEnabledProfilesSupported = info.isMultipleEnabledProfilesSupported();
break;
}
}
@@ -1874,21 +1881,34 @@
return false;
}
- // If the caller can't manage the active embedded subscription on the target SIM, return
- // false. If the caller can manage the active embedded subscription on the target SIM,
- // return true directly.
- for (SubscriptionInfo subInfo : subInfoList) {
- // 1. subInfo.isEmbedded() can only be true for the target SIM.
- // 2. Check whether the caller can manage subscription on the target portIndex
- // (i.e. subInfo.getPortIndex() == targetPortIndex condition) only in case if
- // isMultipleEnabledProfilesSupported and usePortIndex both are true.
- if (subInfo.isEmbedded() && subInfo.getCardId() == cardId
- && (!isMultipleEnabledProfilesSupported || !usePortIndex
- || subInfo.getPortIndex() == targetPortIndex)) {
- return mSubscriptionManager.canManageSubscription(subInfo, callingPackage);
- }
- }
+ // If the caller can't manage the active embedded subscription on the target SIM port,
+ // return false. If the caller can manage the active embedded subscription on the
+ // target SIM port, return true directly.
+ boolean hasActiveEmbeddedSubscription = subInfoList.stream().anyMatch(
+ subInfo -> subInfo.isEmbedded() && subInfo.getCardId() == cardId
+ && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex));
+ if (hasActiveEmbeddedSubscription) {
+ // hasActiveEmbeddedSubscription is true if there is an active embedded subscription
+ // on the target port(in case of usePortIndex is true) or if there is an active
+ // embedded subscription on any of the active ports.
+ // 1. If usePortIndex is true, check whether the caller can manage subscription on
+ // the target port.
+ // 2. If usePortIndex is false, check whether the caller can manage subscription on
+ // any of the active ports.
+ for (SubscriptionInfo subInfo : subInfoList) {
+ // subInfo.isEmbedded() can only be true for the target SIM.
+ if (subInfo.isEmbedded()
+ && subInfo.getCardId() == cardId
+ && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex)
+ && mSubscriptionManager.canManageSubscription(
+ subInfo, callingPackage)) {
+ return true;
+ }
+ }
+ Log.i(TAG, "canManageSubscriptionOnTargetSim cannot manage embedded subscription");
+ return false;
+ }
// There is no active subscription on the target SIM, checks whether the caller can
// manage any active subscription on any other SIM.
final long token = Binder.clearCallingIdentity();
@@ -1923,59 +1943,84 @@
@Override
public boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage) {
- List<UiccCardInfo> cardInfos;
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+ // If calling app is targeted for Android U and beyond, check for other conditions
+ // to decide the port availability.
+ boolean shouldCheckConditionsForInactivePort = isCompatChangeEnabled(callingPackage,
+ EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK);
+ // In the event that this check is coming from ONS, WRITE_EMBEDDED_SUBSCRIPTIONS will be
+ // required for the case where a port is inactive but could trivially be enabled without
+ // requiring user consent.
+ boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
final long token = Binder.clearCallingIdentity();
try {
- cardInfos = mTelephonyManager.getUiccCardsInfo();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- for (UiccCardInfo info : cardInfos) {
- if (info == null || info.getCardId() != cardId) {
- continue;
- }
- // Return false in case of non esim or passed port index is greater than
- // the available ports.
- if (!info.isEuicc() || (portIndex == TelephonyManager.INVALID_PORT_INDEX)
- || portIndex >= info.getPorts().size()) {
- return false;
- }
- for (UiccPortInfo portInfo : info.getPorts()) {
- if (portInfo == null || portInfo.getPortIndex() != portIndex) {
+ List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
+ for (UiccCardInfo info : cardInfos) {
+ if (info == null || info.getCardId() != cardId) {
continue;
}
- // Return false if port is not active.
- if (!portInfo.isActive()) {
+ // Return false in case of non esim or passed port index is greater than
+ // the available ports.
+ if (!info.isEuicc() || (portIndex == TelephonyManager.INVALID_PORT_INDEX)
+ || portIndex >= info.getPorts().size()) {
return false;
}
- // A port is available if it has no profiles enabled on it or calling app has
- // Carrier privilege over the profile installed on the selected port.
- if (TextUtils.isEmpty(portInfo.getIccId())) {
- return true;
+ for (UiccPortInfo portInfo : info.getPorts()) {
+ if (portInfo == null || portInfo.getPortIndex() != portIndex) {
+ continue;
+ }
+ if (!portInfo.isActive()) {
+ // port is inactive, check whether the caller can activate a new profile
+ // seamlessly. This is possible in below condition:
+ // 1. Device in DSDS Mode(P+E).
+ // 2. pSIM slot is active but no active subscription.
+ // 3. Caller has carrier privileges on any phone or has
+ // WRITE_EMBEDDED_SUBSCRIPTIONS. The latter covers calls from ONS
+ // which does not have carrier privileges.
+ if (!shouldCheckConditionsForInactivePort) {
+ return false;
+ }
+ boolean hasActiveRemovableNonEuiccSlot = getRemovableNonEuiccSlot() != null
+ && getRemovableNonEuiccSlot().isActive();
+ boolean hasCarrierPrivileges = mTelephonyManager
+ .checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ return mTelephonyManager.isMultiSimEnabled()
+ && hasActiveRemovableNonEuiccSlot
+ && !isRemovalNonEuiccSlotHasActiveSubscription()
+ && (hasCarrierPrivileges || callerCanWriteEmbeddedSubscriptions);
+ }
+ // A port is available if it has no profiles enabled on it or calling app has
+ // Carrier privilege over the profile installed on the selected port.
+ if (TextUtils.isEmpty(portInfo.getIccId())) {
+ return true;
+ }
+ UiccPort uiccPort =
+ UiccController.getInstance().getUiccPortForSlot(
+ info.getPhysicalSlotIndex(), portIndex);
+ // Some eSim Vendors return boot profile iccid if no profile is installed.
+ // So in this case if profile is empty, port is available.
+ if (uiccPort != null
+ && uiccPort.getUiccProfile() != null
+ && uiccPort.getUiccProfile().isEmptyProfile()) {
+ return true;
+ }
+ Phone phone = PhoneFactory.getPhone(portInfo.getLogicalSlotIndex());
+ if (phone == null) {
+ Log.e(TAG, "Invalid logical slot: " + portInfo.getLogicalSlotIndex());
+ return false;
+ }
+ CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker();
+ if (cpt == null) {
+ Log.e(TAG, "No CarrierPrivilegesTracker");
+ return false;
+ }
+ return (cpt.getCarrierPrivilegeStatusForPackage(callingPackage)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
}
- UiccPort uiccPort =
- UiccController.getInstance().getUiccPortForSlot(
- info.getPhysicalSlotIndex(), portIndex);
- // Some eSim Vendors return boot profile iccid if no profile is installed.
- // So in this case if profile is empty, port is available.
- if (uiccPort != null
- && uiccPort.getUiccProfile() != null
- && uiccPort.getUiccProfile().isEmptyProfile()) {
- return true;
- }
- Phone phone = PhoneFactory.getPhone(portInfo.getLogicalSlotIndex());
- if (phone == null) {
- Log.e(TAG, "Invalid logical slot: " + portInfo.getLogicalSlotIndex());
- return false;
- }
- CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker();
- if (cpt == null) {
- Log.e(TAG, "No CarrierPrivilegesTracker");
- return false;
- }
- return (cpt.getCarrierPrivilegeStatusForPackage(callingPackage)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return false;
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 0abd4ab..907f158 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -24,10 +24,12 @@
import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Build;
+import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
+import com.android.ims.ImsManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
@@ -43,7 +45,7 @@
*/
public class GsmInboundSmsHandler extends InboundSmsHandler {
- private static BroadcastReceiver sTestBroadcastReceiver;
+ private BroadcastReceiver mTestBroadcastReceiver;
/** Handler for SMS-PP data download messages to UICC. */
private final UsimDataDownloadHandler mDataDownloadHandler;
@@ -56,18 +58,18 @@
* Create a new GSM inbound SMS handler.
*/
private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
- Phone phone) {
- super("GsmInboundSmsHandler", context, storageMonitor, phone);
+ Phone phone, Looper looper) {
+ super("GsmInboundSmsHandler", context, storageMonitor, phone, looper);
phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
mCellBroadcastServiceManager.enable();
if (TEST_MODE) {
- if (sTestBroadcastReceiver == null) {
- sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
+ if (mTestBroadcastReceiver == null) {
+ mTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(TEST_ACTION);
- context.registerReceiver(sTestBroadcastReceiver, filter,
+ context.registerReceiver(mTestBroadcastReceiver, filter,
Context.RECEIVER_EXPORTED);
}
}
@@ -127,8 +129,9 @@
* Wait for state machine to enter startup state. We can't send any messages until then.
*/
public static GsmInboundSmsHandler makeInboundSmsHandler(Context context,
- SmsStorageMonitor storageMonitor, Phone phone) {
- GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context, storageMonitor, phone);
+ SmsStorageMonitor storageMonitor, Phone phone, Looper looper) {
+ GsmInboundSmsHandler handler =
+ new GsmInboundSmsHandler(context, storageMonitor, phone, looper);
handler.start();
return handler;
}
@@ -153,7 +156,8 @@
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource,
+ int token) {
SmsMessage sms = (SmsMessage) smsb;
if (sms.isTypeZero()) {
@@ -177,7 +181,7 @@
// Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
if (sms.isUsimDataDownload()) {
UsimServiceTable ust = mPhone.getUsimServiceTable();
- return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource);
+ return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource, token);
}
boolean handled = false;
@@ -268,4 +272,15 @@
android.telephony.SmsMessage.FORMAT_3GPP);
mPhone.getSmsStats().onIncomingSmsVoicemail(false /* is3gpp2 */, smsSource);
}
+
+ /**
+ * sets ImsManager object.
+ */
+ public boolean setImsManager(ImsManager imsManager) {
+ if (mDataDownloadHandler != null) {
+ mDataDownloadHandler.setImsManager(imsManager);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index dec2468..9de3ee9 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -49,6 +49,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallWaitingController;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.GsmCdmaPhone;
@@ -61,6 +62,7 @@
import com.android.internal.telephony.util.ArrayUtils;
import com.android.telephony.Rlog;
+import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -455,7 +457,7 @@
return "";
}
- private String getActionStringFromReqType(SsData.RequestType rType) {
+ private static String getActionStringFromReqType(SsData.RequestType rType) {
switch (rType) {
case SS_ACTIVATION:
return ACTION_ACTIVATE;
@@ -520,6 +522,40 @@
}
}
+ public static SsData.ServiceType cfReasonToServiceType(int commandInterfaceCFReason) {
+ switch (commandInterfaceCFReason) {
+ case CommandsInterface.CF_REASON_UNCONDITIONAL:
+ return SsData.ServiceType.SS_CFU;
+ case CommandsInterface.CF_REASON_BUSY:
+ return SsData.ServiceType.SS_CF_BUSY;
+ case CommandsInterface.CF_REASON_NO_REPLY:
+ return SsData.ServiceType.SS_CF_NO_REPLY;
+ case CommandsInterface.CF_REASON_NOT_REACHABLE:
+ return SsData.ServiceType.SS_CF_NOT_REACHABLE;
+ case CommandsInterface.CF_REASON_ALL:
+ return SsData.ServiceType.SS_CF_ALL;
+ case CommandsInterface.CF_REASON_ALL_CONDITIONAL:
+ return SsData.ServiceType.SS_CF_ALL_CONDITIONAL;
+ default:
+ return null;
+ }
+ }
+
+ public static SsData.RequestType cfActionToRequestType(int commandInterfaceCFAction) {
+ switch (commandInterfaceCFAction) {
+ case CommandsInterface.CF_ACTION_DISABLE:
+ return SsData.RequestType.SS_DEACTIVATION;
+ case CommandsInterface.CF_ACTION_ENABLE:
+ return SsData.RequestType.SS_ACTIVATION;
+ case CommandsInterface.CF_ACTION_REGISTRATION:
+ return SsData.RequestType.SS_REGISTRATION;
+ case CommandsInterface.CF_ACTION_ERASURE:
+ return SsData.RequestType.SS_ERASURE;
+ default:
+ return null;
+ }
+ }
+
@UnsupportedAppUsage
private static int
siToServiceClass(String si) {
@@ -623,6 +659,29 @@
}
}
+ public static SsData.ServiceType cbFacilityToServiceType(String commandInterfaceCBFacility) {
+ switch(commandInterfaceCBFacility) {
+ case CommandsInterface.CB_FACILITY_BAOC:
+ return SsData.ServiceType.SS_BAOC;
+ case CommandsInterface.CB_FACILITY_BAOIC:
+ return SsData.ServiceType.SS_BAOIC;
+ case CommandsInterface.CB_FACILITY_BAOICxH:
+ return SsData.ServiceType.SS_BAOIC_EXC_HOME;
+ case CommandsInterface.CB_FACILITY_BAIC:
+ return SsData.ServiceType.SS_BAIC;
+ case CommandsInterface.CB_FACILITY_BAICr:
+ return SsData.ServiceType.SS_BAIC_ROAMING;
+ case CommandsInterface.CB_FACILITY_BA_ALL:
+ return SsData.ServiceType.SS_ALL_BARRING;
+ case CommandsInterface.CB_FACILITY_BA_MO:
+ return SsData.ServiceType.SS_OUTGOING_BARRING;
+ case CommandsInterface.CB_FACILITY_BA_MT:
+ return SsData.ServiceType.SS_INCOMING_BARRING;
+ default:
+ return null;
+ }
+ }
+
//***** Constructor
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -892,6 +951,17 @@
return CommandsInterface.CLIR_DEFAULT;
}
+ public static SsData.RequestType clirModeToRequestType(int commandInterfaceCLIRMode) {
+ switch (commandInterfaceCLIRMode) {
+ case CommandsInterface.CLIR_SUPPRESSION:
+ return SsData.RequestType.SS_ACTIVATION;
+ case CommandsInterface.CLIR_INVOCATION:
+ return SsData.RequestType.SS_DEACTIVATION;
+ default:
+ return null;
+ }
+ }
+
/**
* Returns true if the Service Code is FAC to dial as a normal call.
*
@@ -1099,11 +1169,25 @@
int serviceClass = siToServiceClass(mSia);
if (isActivate() || isDeactivate()) {
+ if (serviceClass == SERVICE_CLASS_NONE
+ || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+ if (mPhone.getTerminalBasedCallWaitingState(true)
+ != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+ mPhone.setCallWaiting(isActivate(), serviceClass,
+ obtainMessage(EVENT_SET_COMPLETE, this));
+ return;
+ }
+ }
mPhone.mCi.setCallWaiting(isActivate(), serviceClass,
obtainMessage(EVENT_SET_COMPLETE, this));
} else if (isInterrogate()) {
- mPhone.mCi.queryCallWaiting(serviceClass,
- obtainMessage(EVENT_QUERY_COMPLETE, this));
+ if (mPhone.getTerminalBasedCallWaitingState(true)
+ != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+ mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+ } else {
+ mPhone.mCi.queryCallWaiting(serviceClass,
+ obtainMessage(EVENT_QUERY_COMPLETE, this));
+ }
} else {
throw new RuntimeException ("Invalid or Unsupported MMI Code");
}
@@ -1333,8 +1417,8 @@
}
}
//***** Private instance methods
-
- private CharSequence getErrorMessage(AsyncResult ar) {
+ @VisibleForTesting
+ public CharSequence getErrorMessage(AsyncResult ar) {
if (ar.exception instanceof CommandException) {
CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
@@ -1362,6 +1446,13 @@
} else if (err == CommandException.Error.OEM_ERROR_1) {
Rlog.i(LOG_TAG, "OEM_ERROR_1 USSD_MODIFIED_TO_DIAL_VIDEO");
return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_dial_video);
+ } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED
+ || err == CommandException.Error.OPERATION_NOT_ALLOWED) {
+ Rlog.i(LOG_TAG, "REQUEST_NOT_SUPPORTED/OPERATION_NOT_ALLOWED");
+ // getResources().getText() is the same as getText(), however getText() is final and
+ // cannot be mocked in tests.
+ return mContext.getResources().getText(
+ com.android.internal.R.string.mmiErrorNotSupported);
}
}
@@ -1827,6 +1918,120 @@
return this.mCallbackReceiver;
}
+ /**
+ * Returns list of control strings for a supplementary service request
+ * as defined in TS 22.030 6.5
+ * @param requestType request type associated with the supplementary service
+ * @param serviceType supplementary service type
+ * @return list of control strings associated with the supplementary service.
+ */
+ public static ArrayList<String> getControlStrings(SsData.RequestType requestType,
+ SsData.ServiceType serviceType) {
+ ArrayList<String> controlStrings = new ArrayList<>();
+ if (requestType == null || serviceType == null) {
+ return controlStrings;
+ }
+
+ String actionStr = getActionStringFromReqType(requestType);
+ switch (serviceType) {
+ case SS_CFU:
+ controlStrings.add(actionStr + SC_CFU);
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CF_BUSY:
+ controlStrings.add(actionStr + SC_CFB);
+ controlStrings.add(actionStr + SC_CF_All_Conditional);
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CF_NO_REPLY:
+ controlStrings.add(actionStr + SC_CFNRy);
+ controlStrings.add(actionStr + SC_CF_All_Conditional);
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CF_NOT_REACHABLE:
+ controlStrings.add(actionStr + SC_CFNR);
+ controlStrings.add(actionStr + SC_CF_All_Conditional);
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CF_ALL:
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CF_ALL_CONDITIONAL:
+ controlStrings.add(actionStr + SC_CF_All_Conditional);
+ controlStrings.add(actionStr + SC_CF_All);
+ break;
+ case SS_CLIP:
+ controlStrings.add(actionStr + SC_CLIP);
+ break;
+ case SS_CLIR:
+ controlStrings.add(actionStr + SC_CLIR);
+ break;
+ case SS_WAIT:
+ controlStrings.add(actionStr + SC_WAIT);
+ break;
+ case SS_BAOC:
+ controlStrings.add(actionStr + SC_BAOC);
+ controlStrings.add(actionStr + SC_BA_MO);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_BAOIC:
+ controlStrings.add(actionStr + SC_BAOIC);
+ controlStrings.add(actionStr + SC_BA_MO);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_BAOIC_EXC_HOME:
+ controlStrings.add(actionStr + SC_BAOICxH);
+ controlStrings.add(actionStr + SC_BA_MO);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_BAIC:
+ controlStrings.add(actionStr + SC_BAIC);
+ controlStrings.add(actionStr + SC_BA_MT);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_BAIC_ROAMING:
+ controlStrings.add(actionStr + SC_BAICr);
+ controlStrings.add(actionStr + SC_BA_MT);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_ALL_BARRING:
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_OUTGOING_BARRING:
+ controlStrings.add(actionStr + SC_BA_MO);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ case SS_INCOMING_BARRING:
+ controlStrings.add(actionStr + SC_BA_MT);
+ controlStrings.add(actionStr + SC_BA_ALL);
+ break;
+ }
+ return controlStrings;
+ }
+
+ /**
+ * Returns control strings for registration of new password as per TS 22.030 6.5.4
+ * @param requestType request type associated with the supplementary service
+ * @param serviceType supplementary service type
+ * @return list of control strings for new password registration.
+ */
+ public static ArrayList<String> getControlStringsForPwd(SsData.RequestType requestType,
+ SsData.ServiceType serviceType) {
+ ArrayList<String> controlStrings = new ArrayList<>();
+ if (requestType == null || serviceType == null) {
+ return controlStrings;
+ }
+
+ controlStrings = getControlStrings(SsData.RequestType.SS_ACTIVATION, serviceType);
+ String actionStr = getActionStringFromReqType(requestType);
+ ArrayList<String> controlStringsPwd = new ArrayList<>();
+ for(String controlString : controlStrings) {
+ // Prepend each control string with **SC_PWD
+ controlStringsPwd.add(actionStr + SC_PWD + controlString);
+ }
+ return controlStringsPwd;
+ }
+
/***
* TODO: It would be nice to have a method here that can take in a dialstring and
* figure out if there is an MMI code embedded within it. This code would replace
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 1f237b2..5b1f36d 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -120,6 +120,21 @@
}
@Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+ validityPeriod, messageRef);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+ return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message,
+ statusReportRequested, messageRef);
+ }
+
+ @Override
protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly);
}
diff --git a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
index 41e76ae..396e94c 100644
--- a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
+++ b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
@@ -18,6 +18,8 @@
import android.telephony.PhoneNumberUtils;
+import java.util.Arrays;
+
/**
* Represents a Supplementary Service Notification received from the network.
*
@@ -200,7 +202,7 @@
+ (notificationType == 0 ? " originated " : " terminated ")
+ " code: " + code
+ " index: " + index
- + " history: " + history
+ + " history: " + Arrays.toString(history)
+ " \""
+ PhoneNumberUtils.stringFromStringAndTOA(number, type) + "\" ";
}
diff --git a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
index ed819c1..bae56d1 100644
--- a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
@@ -17,13 +17,19 @@
package com.android.internal.telephony.gsm;
import android.app.Activity;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.PhoneFactory;
@@ -61,10 +67,14 @@
private final CommandsInterface mCi;
private final int mPhoneId;
+ private ImsManager mImsManager;
+ Resources mResource;
public UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId) {
mCi = commandsInterface;
mPhoneId = phoneId;
+ mImsManager = null; // will get initialized when ImsManager connection is ready
+ mResource = Resources.getSystem();
}
/**
@@ -79,7 +89,7 @@
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage,
- @InboundSmsHandler.SmsSource int smsSource) {
+ @InboundSmsHandler.SmsSource int smsSource, int token) {
// If we receive an SMS-PP message before the UsimServiceTable has been loaded,
// assume that the data download service is not present. This is very unlikely to
// happen because the IMS connection will not be established until after the ISIM
@@ -87,7 +97,7 @@
if (ust != null && ust.isAvailable(
UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) {
Rlog.d(TAG, "Received SMS-PP data download, sending to UICC.");
- return startDataDownload(smsMessage, smsSource);
+ return startDataDownload(smsMessage, smsSource, token);
} else {
Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
String smsc = IccUtils.bytesToHexString(
@@ -95,7 +105,8 @@
smsMessage.getServiceCenterAddress()));
mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
IccUtils.bytesToHexString(smsMessage.getPdu()),
- obtainMessage(EVENT_WRITE_SMS_COMPLETE));
+ obtainMessage(EVENT_WRITE_SMS_COMPLETE,
+ new int[]{ smsSource, smsMessage.mMessageRef, token }));
addUsimDataDownloadToMetrics(false, smsSource);
return Activity.RESULT_OK; // acknowledge after response from write to USIM
}
@@ -111,9 +122,9 @@
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
public int startDataDownload(SmsMessage smsMessage,
- @InboundSmsHandler.SmsSource int smsSource) {
+ @InboundSmsHandler.SmsSource int smsSource, int token) {
if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD,
- smsSource, 0 /* unused */, smsMessage))) {
+ smsSource, token, smsMessage))) {
return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response
} else {
Rlog.e(TAG, "startDataDownload failed to send message to start data download.");
@@ -122,7 +133,7 @@
}
private void handleDataDownload(SmsMessage smsMessage,
- @InboundSmsHandler.SmsSource int smsSource) {
+ @InboundSmsHandler.SmsSource int smsSource, int token) {
int dcs = smsMessage.getDataCodingScheme();
int pid = smsMessage.getProtocolIdentifier();
byte[] pdu = smsMessage.getPdu(); // includes SC address
@@ -139,6 +150,7 @@
byte[] envelope = new byte[totalLength];
int index = 0;
+ Rlog.d(TAG, "smsSource: " + smsSource + "Token: " + token);
// SMS-PP download tag and length (assumed to be < 256 bytes).
envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG;
@@ -173,14 +185,16 @@
// Verify that we calculated the payload size correctly.
if (index != envelope.length) {
Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting.");
- acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR);
+ acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR,
+ smsSource, token, smsMessage.mMessageRef);
addUsimDataDownloadToMetrics(false, smsSource);
return;
}
String encodedEnvelope = IccUtils.bytesToHexString(envelope);
mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage(
- EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid }));
+ EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid, smsSource,
+ smsMessage.mMessageRef, token }));
addUsimDataDownloadToMetrics(true, smsSource);
}
@@ -211,7 +225,8 @@
* @param response UICC response encoded as hexadecimal digits. First two bytes are the
* UICC SW1 and SW2 status bytes.
*/
- private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) {
+ private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid,
+ int smsSource, int token, int messageRef) {
int sw1 = response.sw1;
int sw2 = response.sw2;
@@ -221,7 +236,8 @@
success = true;
} else if (sw1 == 0x93 && sw2 == 0x00) {
Rlog.e(TAG, "USIM data download failed: Toolkit busy");
- acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY);
+ acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY,
+ smsSource, token, messageRef);
return;
} else if (sw1 == 0x62 || sw1 == 0x63) {
Rlog.e(TAG, "USIM data download failed: " + response.toString());
@@ -234,10 +250,11 @@
byte[] responseBytes = response.payload;
if (responseBytes == null || responseBytes.length == 0) {
if (success) {
- mCi.acknowledgeLastIncomingGsmSms(true, 0, null);
+ acknowledgeSmsWithSuccess(0, smsSource, token, messageRef);
} else {
acknowledgeSmsWithError(
- CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR);
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, smsSource,
+ token, messageRef);
}
return;
}
@@ -268,12 +285,32 @@
System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length);
- mCi.acknowledgeIncomingGsmSmsWithPdu(success,
- IccUtils.bytesToHexString(smsAckPdu), null);
+ if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) {
+ acknowledgeImsSms(token, messageRef, true, smsAckPdu);
+ } else {
+ mCi.acknowledgeIncomingGsmSmsWithPdu(success,
+ IccUtils.bytesToHexString(smsAckPdu), null);
+ }
}
- private void acknowledgeSmsWithError(int cause) {
- mCi.acknowledgeLastIncomingGsmSms(false, cause, null);
+ private void acknowledgeSmsWithSuccess(int cause, int smsSource, int token, int messageRef) {
+ Rlog.d(TAG, "acknowledgeSmsWithSuccess- cause: " + cause + " smsSource: " + smsSource
+ + " token: " + token + " messageRef: " + messageRef);
+ if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) {
+ acknowledgeImsSms(token, messageRef, true, null);
+ } else {
+ mCi.acknowledgeLastIncomingGsmSms(true, cause, null);
+ }
+ }
+
+ private void acknowledgeSmsWithError(int cause, int smsSource, int token, int messageRef) {
+ Rlog.d(TAG, "acknowledgeSmsWithError- cause: " + cause + " smsSource: " + smsSource
+ + " token: " + token + " messageRef: " + messageRef);
+ if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) {
+ acknowledgeImsSms(token, messageRef, false, null);
+ } else {
+ mCi.acknowledgeLastIncomingGsmSms(false, cause, null);
+ }
}
/**
@@ -300,6 +337,45 @@
}
/**
+ * Route resposes via ImsManager based on config
+ */
+ private boolean ackViaIms() {
+ boolean isViaIms;
+
+ try {
+ isViaIms = mResource.getBoolean(
+ com.android.internal.R.bool.config_smppsim_response_via_ims);
+ } catch (NotFoundException e) {
+ isViaIms = false;
+ }
+
+ Rlog.d(TAG, "ackViaIms : " + isViaIms);
+ return isViaIms;
+ }
+
+ /**
+ * Acknowledges IMS SMS and delivers the result based on the envelope or SIM saving respose
+ * received from SIM for SMS-PP Data.
+ */
+ private void acknowledgeImsSms(int token, int messageRef, boolean success, byte[] pdu) {
+ int result = success ? ImsSmsImplBase.DELIVER_STATUS_OK :
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
+ Rlog.d(TAG, "sending result via acknowledgeImsSms: " + result + " token: " + token);
+
+ try {
+ if (mImsManager != null) {
+ if (pdu != null && pdu.length > 0) {
+ mImsManager.acknowledgeSms(token, messageRef, result, pdu);
+ } else {
+ mImsManager.acknowledgeSms(token, messageRef, result);
+ }
+ }
+ } catch (ImsException e) {
+ Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
+ }
+ }
+
+ /**
* Handle UICC envelope response and send SMS acknowledgement.
*
* @param msg the message to handle
@@ -307,35 +383,60 @@
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
+ int smsSource = InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN;
+ int token = 0;
+ int messageRef = 0;
+ int[] responseInfo;
switch (msg.what) {
case EVENT_START_DATA_DOWNLOAD:
- handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */);
+ Rlog.d(TAG, "EVENT_START_DATA_DOWNLOAD");
+ handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */,
+ msg.arg2 /* token */);
break;
case EVENT_SEND_ENVELOPE_RESPONSE:
ar = (AsyncResult) msg.obj;
+ responseInfo = (int[]) ar.userObj;
+ smsSource = responseInfo[2];
+ messageRef = responseInfo[3];
+ token = responseInfo[4];
+
+ Rlog.d(TAG, "Received EVENT_SEND_ENVELOPE_RESPONSE from source : " + smsSource);
+
if (ar.exception != null) {
Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception);
+
acknowledgeSmsWithError(
- CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR);
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR,
+ smsSource, token, messageRef);
return;
}
- int[] dcsPid = (int[]) ar.userObj;
- sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]);
+ Rlog.d(TAG, "Successful in sending envelope response");
+ sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, responseInfo[0],
+ responseInfo[1], smsSource, token, messageRef);
break;
case EVENT_WRITE_SMS_COMPLETE:
ar = (AsyncResult) msg.obj;
+
+ responseInfo = (int[]) ar.userObj;
+ smsSource = responseInfo[0];
+ messageRef = responseInfo[1];
+ token = responseInfo[2];
+
+ Rlog.d(TAG, "Received EVENT_WRITE_SMS_COMPLETE from source : " + smsSource);
+
if (ar.exception == null) {
Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC");
- mCi.acknowledgeLastIncomingGsmSms(true, 0, null);
+ acknowledgeSmsWithSuccess(0, smsSource, token, messageRef);
} else {
Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception);
- mCi.acknowledgeLastIncomingGsmSms(false,
- CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null);
+ acknowledgeSmsWithError(
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR,
+ smsSource, token, messageRef);
}
break;
@@ -343,4 +444,23 @@
Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what);
}
}
+
+ /**
+ * Called when ImsManager connection is ready. ImsManager object will be used to send ACK to IMS
+ * which doesn't use RIL interface.
+ * @param imsManager object
+ */
+ public void setImsManager(ImsManager imsManager) {
+ mImsManager = imsManager;
+ }
+
+ /**
+ * Called to set mocked object of type Resources during unit testing of this file.
+ * @param resource object
+ */
+ @VisibleForTesting
+ public void setResourcesForTest(Resources resource) {
+ mResource = resource;
+ Rlog.d(TAG, "setResourcesForTest");
+ }
}
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
old mode 100755
new mode 100644
index e594ab6..48be16c
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -32,6 +32,7 @@
import com.android.telephony.Rlog;
import java.util.ArrayList;
+import java.util.Locale;
/**
* This class implements reading and parsing USIM records.
@@ -233,7 +234,7 @@
int emailEfid = email.getEfid();
log("EF_EMAIL exists in PBR. efid = 0x" +
- Integer.toHexString(emailEfid).toUpperCase());
+ Integer.toHexString(emailEfid).toUpperCase(Locale.ROOT));
/**
* Make sure this EF_EMAIL was never read earlier. Sometimes two PBR record points
@@ -348,7 +349,7 @@
emailList = new ArrayList<String>();
}
log("Adding email #" + i + " list to index 0x" +
- Integer.toHexString(index).toUpperCase());
+ Integer.toHexString(index).toUpperCase(Locale.ROOT));
emailList.add(email);
mEmailsForAdnRec.put(index, emailList);
}
@@ -402,7 +403,7 @@
}
emailList.add(email);
log("Adding email list to index 0x" +
- Integer.toHexString(index).toUpperCase());
+ Integer.toHexString(index).toUpperCase(Locale.ROOT));
mEmailsForAdnRec.put(index, emailList);
}
}
@@ -446,8 +447,9 @@
System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size());
rec.setEmails(emails);
log("Adding email list to ADN (0x" +
- Integer.toHexString(mPhoneBookRecords.get(i).getEfid()).toUpperCase() +
- ") record #" + mPhoneBookRecords.get(i).getRecId());
+ Integer.toHexString(mPhoneBookRecords.get(i).getEfid())
+ .toUpperCase(Locale.ROOT) + ") record #"
+ + mPhoneBookRecords.get(i).getRecId());
mPhoneBookRecords.set(i, rec);
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java
new file mode 100644
index 0000000..e54561f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.ims;
+
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class will abstract away all the new enablement logic and take the reset/enable/disable
+ * IMS commands as inputs.
+ * The IMS commands will call enableIms, disableIms or resetIms to match the enablement state only
+ * when it changes.
+ */
+public class ImsEnablementTracker {
+ private static final String LOG_TAG = "ImsEnablementTracker";
+ private static final long REQUEST_THROTTLE_TIME_MS = 3 * 1000L; // 3 seconds
+
+ private static final int COMMAND_NONE_MSG = 0;
+ // Indicate that the enableIms command has been received.
+ @VisibleForTesting
+ public static final int COMMAND_ENABLE_MSG = 1;
+ // Indicate that the disableIms command has been received.
+ @VisibleForTesting
+ public static final int COMMAND_DISABLE_MSG = 2;
+ // Indicate that the resetIms command has been received.
+ private static final int COMMAND_RESET_MSG = 3;
+ // Indicate that the internal enable message with delay has been received.
+ private static final int COMMAND_ENABLING_DONE = 4;
+ // Indicate that the internal disable message with delay has been received.
+ private static final int COMMAND_DISABLING_DONE = 5;
+ // Indicate that the internal reset message with delay has been received.
+ @VisibleForTesting
+ public static final int COMMAND_RESETTING_DONE = 6;
+ // The ImsServiceController binder is connected.
+ private static final int COMMAND_CONNECTED_MSG = 7;
+ // The ImsServiceController binder is disconnected.
+ private static final int COMMAND_DISCONNECTED_MSG = 8;
+ // The subId is changed to INVALID_SUBSCRIPTION_ID.
+ private static final int COMMAND_INVALID_SUBID_MSG = 9;
+ // Indicate that the internal post reset message with delay has been received.
+ @VisibleForTesting
+ public static final int COMMAND_POST_RESETTING_DONE = 10;
+
+ private static final Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>();
+ static {
+ EVENT_DESCRIPTION.put(COMMAND_NONE_MSG, "COMMAND_NONE_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_ENABLE_MSG, "COMMAND_ENABLE_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_DISABLE_MSG, "COMMAND_DISABLE_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_RESET_MSG, "COMMAND_RESET_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_ENABLING_DONE, "COMMAND_ENABLING_DONE");
+ EVENT_DESCRIPTION.put(COMMAND_DISABLING_DONE, "COMMAND_DISABLING_DONE");
+ EVENT_DESCRIPTION.put(COMMAND_RESETTING_DONE, "COMMAND_RESETTING_DONE");
+ EVENT_DESCRIPTION.put(COMMAND_CONNECTED_MSG, "COMMAND_CONNECTED_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_DISCONNECTED_MSG, "COMMAND_DISCONNECTED_MSG");
+ EVENT_DESCRIPTION.put(COMMAND_INVALID_SUBID_MSG, "COMMAND_INVALID_SUBID_MSG");
+ }
+
+ @VisibleForTesting
+ protected static final int STATE_IMS_DISCONNECTED = 0;
+ @VisibleForTesting
+ protected static final int STATE_IMS_DEFAULT = 1;
+ @VisibleForTesting
+ protected static final int STATE_IMS_ENABLED = 2;
+ @VisibleForTesting
+ protected static final int STATE_IMS_DISABLING = 3;
+ @VisibleForTesting
+ protected static final int STATE_IMS_DISABLED = 4;
+ @VisibleForTesting
+ protected static final int STATE_IMS_ENABLING = 5;
+ @VisibleForTesting
+ protected static final int STATE_IMS_RESETTING = 6;
+
+ @VisibleForTesting
+ protected static final int STATE_IMS_POSTRESETTING = 7;
+
+ protected final Object mLock = new Object();
+ private IImsServiceController mIImsServiceController;
+ private long mLastImsOperationTimeMs = 0L;
+ private final ComponentName mComponentName;
+ private final SparseArray<ImsEnablementTrackerStateMachine> mStateMachines;
+
+ private final Looper mLooper;
+ private final int mState;
+
+ /**
+ * Provides Ims Enablement Tracker State Machine responsible for ims enable/disable/reset
+ * command interactions with Ims service controller binder.
+ * The enable/disable/reset ims commands have a time interval of at least
+ * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second between
+ * processing each command.
+ * For example, the enableIms command is received and the binder's enableIms is called.
+ * After that, if the disableIms command is received, the binder's disableIms will be
+ * called after {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second.
+ * A time of {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} will be used
+ * {@link Handler#sendMessageDelayed(Message, long)},
+ * and the enabled, disabled and reset states are responsible for waiting for
+ * that delay message.
+ */
+ class ImsEnablementTrackerStateMachine extends StateMachine {
+ /**
+ * The initial state of this class and waiting for an ims commands.
+ */
+ @VisibleForTesting
+ public final Default mDefault;
+
+ /**
+ * Indicates that {@link IImsServiceController#enableIms(int, int)} has been called and
+ * waiting for an ims commands.
+ * Common transitions are to
+ * {@link #mDisabling} state when the disable command is received
+ * or {@link #mResetting} state when the reset command is received
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ @VisibleForTesting
+ public final Enabled mEnabled;
+
+ /**
+ * Indicates that the state waiting for the throttle time to elapse before calling
+ * {@link IImsServiceController#disableIms(int, int)}.
+ * Common transitions are to
+ * {@link #mEnabled} when the enable command is received.
+ * or {@link #mResetting} when the reset command is received.
+ * or {@link #mDisabled} the previous binder API call has passed
+ * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and if
+ * {@link IImsServiceController#disableIms(int, int)} called.
+ * or {@link #mDisabling} received a disableIms message and the previous binder API call
+ * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second.
+ * Then send a disableIms message with delay.
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ @VisibleForTesting
+ public final Disabling mDisabling;
+
+ /**
+ * Indicates that {@link IImsServiceController#disableIms(int, int)} has been called and
+ * waiting for an ims commands.
+ * Common transitions are to
+ * {@link #mEnabling} state when the enable command is received.
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ @VisibleForTesting
+ public final Disabled mDisabled;
+
+ /**
+ * Indicates that the state waiting for the throttle time to elapse before calling
+ * {@link IImsServiceController#enableIms(int, int)}.
+ * Common transitions are to
+ * {@link #mEnabled} the previous binder API call has passed
+ * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and
+ * {@link IImsServiceController#enableIms(int, int)} called.
+ * or {@link #mDisabled} when the disable command is received.
+ * or {@link #mEnabling} received an enableIms message and the previous binder API call
+ * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second.
+ * Then send an enableIms message with delay.
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ @VisibleForTesting
+ public final Enabling mEnabling;
+
+ /**
+ * Indicates that the state waiting for the throttle time to elapse before calling
+ * {@link IImsServiceController#resetIms(int, int)}.
+ * Common transitions are to
+ * {@link #mPostResetting} state to call either enableIms or disableIms after calling
+ * {@link IImsServiceController#resetIms(int, int)}
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ @VisibleForTesting
+ public final Resetting mResetting;
+
+ /**
+ * Indicates that the state waiting after resetIms for the throttle time to elapse before
+ * calling {@link IImsServiceController#enableIms(int, int)} or
+ * {@link IImsServiceController#disableIms(int, int)}.
+ * Common transitions are to
+ * {@link #mEnabled} state when the disable command is received,
+ * {@link #mDisabled} state when the enable command is received after calling
+ * {@link IImsServiceController#enableIms(int, int)},
+ * {@link IImsServiceController#disableIms(int, int)}
+ * or {@link #mDisconnected} if the binder is disconnected.
+ */
+ public final PostResetting mPostResetting;
+
+ /**
+ * Indicates that {@link IImsServiceController} has not been set.
+ * Common transition is to
+ * {@link #mDefault} state when the binder is set.
+ * or {@link #mDisabling} If the disable command is received while the binder is
+ * disconnected
+ * or {@link #mEnabling} If the enable command is received while the binder is
+ * disconnected
+ */
+
+ private final Disconnected mDisconnected;
+ private int mSlotId;
+ private int mSubId;
+
+ private final int mPhoneId;
+
+ private IState mPreviousState;
+
+ private int mLastMsg = COMMAND_NONE_MSG;
+
+ ImsEnablementTrackerStateMachine(String name, Looper looper, int state, int slotId) {
+ super(name, looper);
+ mPhoneId = slotId;
+ mDefault = new Default();
+ mEnabled = new Enabled();
+ mDisabling = new Disabling();
+ mDisabled = new Disabled();
+ mEnabling = new Enabling();
+ mResetting = new Resetting();
+ mDisconnected = new Disconnected();
+ mPostResetting = new PostResetting();
+
+ addState(mDefault);
+ addState(mEnabled);
+ addState(mDisabling);
+ addState(mDisabled);
+ addState(mEnabling);
+ addState(mResetting);
+ addState(mDisconnected);
+ addState(mPostResetting);
+
+ setInitialState(getState(state));
+ mPreviousState = getState(state);
+ }
+
+ public void clearAllMessage() {
+ Log.d(LOG_TAG, "clearAllMessage");
+ removeMessages(COMMAND_ENABLE_MSG);
+ removeMessages(COMMAND_DISABLE_MSG);
+ removeMessages(COMMAND_RESET_MSG);
+ removeMessages(COMMAND_ENABLING_DONE);
+ removeMessages(COMMAND_DISABLING_DONE);
+ removeMessages(COMMAND_RESETTING_DONE);
+ removeMessages(COMMAND_POST_RESETTING_DONE);
+ }
+
+ public void serviceBinderConnected() {
+ clearAllMessage();
+ sendMessage(COMMAND_CONNECTED_MSG);
+ }
+
+ public void serviceBinderDisconnected() {
+ clearAllMessage();
+ sendMessage(COMMAND_DISCONNECTED_MSG);
+ }
+
+ @VisibleForTesting
+ public boolean isState(int state) {
+ State expect = null;
+ switch (state) {
+ case Default.STATE_NO:
+ expect = mDefault;
+ break;
+ case Enabled.STATE_NO:
+ expect = mEnabled;
+ break;
+ case Disabling.STATE_NO:
+ expect = mDisabling;
+ break;
+ case Disabled.STATE_NO:
+ expect = mDisabled;
+ break;
+ case Enabling.STATE_NO:
+ expect = mEnabling;
+ break;
+ case Resetting.STATE_NO:
+ expect = mResetting;
+ break;
+ case Disconnected.STATE_NO:
+ expect = mDisconnected;
+ break;
+ case PostResetting.STATE_NO:
+ expect = mPostResetting;
+ break;
+ default:
+ break;
+ }
+ return (getCurrentState() == expect) ? true : false;
+ }
+
+ private State getState(int state) {
+ switch (state) {
+ case ImsEnablementTracker.STATE_IMS_ENABLED:
+ return mEnabled;
+ case ImsEnablementTracker.STATE_IMS_DISABLING:
+ return mDisabling;
+ case ImsEnablementTracker.STATE_IMS_DISABLED:
+ return mDisabled;
+ case ImsEnablementTracker.STATE_IMS_ENABLING:
+ return mEnabling;
+ case ImsEnablementTracker.STATE_IMS_RESETTING:
+ return mResetting;
+ case ImsEnablementTracker.STATE_IMS_DISCONNECTED:
+ return mDisconnected;
+ case ImsEnablementTracker.STATE_IMS_POSTRESETTING:
+ return mPostResetting;
+ default:
+ return mDefault;
+ }
+ }
+
+ private void handleInvalidSubIdMessage() {
+ clearAllMessage();
+ transitionState(mDefault);
+ }
+
+ private void transitionState(State state) {
+ mPreviousState = getCurrentState();
+ transitionTo(state);
+ }
+
+ class Default extends State {
+ private static final int STATE_NO = STATE_IMS_DEFAULT;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:enter");
+ mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ // When enableIms() is called, enableIms of binder is call and the state
+ // change to the enabled state.
+ case COMMAND_ENABLE_MSG:
+ sendEnableIms(message.arg1, message.arg2);
+ transitionState(mEnabled);
+ return HANDLED;
+ // When disableIms() is called, disableIms of binder is call and the state
+ // change to the disabled state.
+ case COMMAND_DISABLE_MSG:
+ sendDisableIms(message.arg1, message.arg2);
+ transitionState(mDisabled);
+ return HANDLED;
+ // When resetIms() is called, change to the resetting state to call enableIms
+ // after calling resetIms of binder.
+ case COMMAND_RESET_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mResetting);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Enabled extends State {
+ private static final int STATE_NO = STATE_IMS_ENABLED;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Enabled state:enter");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Enabled state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ // the disableIms() is called.
+ case COMMAND_DISABLE_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mDisabling);
+ return HANDLED;
+ // the resetIms() is called.
+ case COMMAND_RESET_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mResetting);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Disabling extends State {
+ private static final int STATE_NO = STATE_IMS_DISABLING;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disabling state:enter");
+ sendMessageDelayed(COMMAND_DISABLING_DONE, mSlotId, mSubId,
+ getRemainThrottleTime());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disabling state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ case COMMAND_ENABLE_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ clearAllMessage();
+ if (mPreviousState == mResetting) {
+ // if we are moving from Resetting -> Disabling and receive
+ // the COMMAND_ENABLE_MSG, we need to send the enableIms command,
+ // so move to Enabling state.
+ transitionState(mEnabling);
+ } else {
+ // When moving from Enabled -> Disabling and we receive an ENABLE_MSG,
+ // we can move straight back to Enabled state because we have not sent
+ // the disableIms command to IMS yet.
+ transitionState(mEnabled);
+ }
+ return HANDLED;
+ case COMMAND_DISABLING_DONE:
+ // If the disable command is received before disableIms is processed,
+ // it will be ignored because the disable command processing is in progress.
+ removeMessages(COMMAND_DISABLE_MSG);
+ sendDisableIms(message.arg1, message.arg2);
+ transitionState(mDisabled);
+ return HANDLED;
+ case COMMAND_RESET_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ clearAllMessage();
+ transitionState(mResetting);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Disabled extends State {
+ private static final int STATE_NO = STATE_IMS_DISABLED;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disabled state:enter");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disabled state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ case COMMAND_ENABLE_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mEnabling);
+ return HANDLED;
+ case COMMAND_RESET_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mResetting);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Enabling extends State {
+ private static final int STATE_NO = STATE_IMS_ENABLING;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Enabling state:enter");
+ sendMessageDelayed(COMMAND_ENABLING_DONE, mSlotId, mSubId, getRemainThrottleTime());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Enabling state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ // Enabling state comes from Resetting and disableIms() is called.
+ // In this case disableIms() of binder should be called.
+ // When enabling state comes from disabled, just change state to the disabled.
+ case COMMAND_DISABLE_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ clearAllMessage();
+ if (mPreviousState == mResetting) {
+ transitionState(mDisabling);
+ } else {
+ transitionState(mDisabled);
+ }
+ return HANDLED;
+ case COMMAND_RESET_MSG:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ transitionState(mResetting);
+ return HANDLED;
+ case COMMAND_ENABLING_DONE:
+ // If the enable command is received before enableIms is processed,
+ // it will be ignored because the enable command processing is in progress.
+ removeMessages(COMMAND_ENABLE_MSG);
+ sendEnableIms(message.arg1, message.arg2);
+ transitionState(mEnabled);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Resetting extends State {
+ private static final int STATE_NO = STATE_IMS_RESETTING;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Resetting state:enter");
+ sendMessageDelayed(COMMAND_RESETTING_DONE, mSlotId, mSubId,
+ getRemainThrottleTime());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Resetting state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ case COMMAND_DISABLE_MSG:
+ mLastMsg = COMMAND_DISABLE_MSG;
+ return HANDLED;
+ case COMMAND_ENABLE_MSG:
+ mLastMsg = COMMAND_ENABLE_MSG;
+ return HANDLED;
+ case COMMAND_RESETTING_DONE:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ // If the reset command is received before disableIms is processed,
+ // it will be ignored because the reset command processing is in progress.
+ removeMessages(COMMAND_RESET_MSG);
+ sendResetIms(mSlotId, mSubId);
+ transitionState(mPostResetting);
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class Disconnected extends State {
+ private static final int STATE_NO = STATE_IMS_DISCONNECTED;
+
+ private int mLastMsg = COMMAND_NONE_MSG;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disconnected state:enter");
+ clearAllMessage();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]Disconnected state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ case COMMAND_CONNECTED_MSG:
+ clearAllMessage();
+ transitionState(mDefault);
+ if (mLastMsg != COMMAND_NONE_MSG) {
+ sendMessageDelayed(mLastMsg, mSlotId, mSubId, 0);
+ mLastMsg = COMMAND_NONE_MSG;
+ }
+ return HANDLED;
+ case COMMAND_ENABLE_MSG:
+ case COMMAND_DISABLE_MSG:
+ case COMMAND_RESET_MSG:
+ mLastMsg = message.what;
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class PostResetting extends State {
+ private static final int STATE_NO = STATE_IMS_POSTRESETTING;
+
+ @Override
+ public void enter() {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]PostResetting state:enter");
+ sendMessageDelayed(COMMAND_POST_RESETTING_DONE, mSlotId, mSubId,
+ getRemainThrottleTime());
+ }
+
+ @Override
+ public void exit() {
+ mLastMsg = COMMAND_NONE_MSG;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(LOG_TAG, "[" + mPhoneId + "]PostResetting state:processMessage. msg.what="
+ + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName);
+
+ switch (message.what) {
+ case COMMAND_POST_RESETTING_DONE:
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ if (mLastMsg == COMMAND_DISABLE_MSG) {
+ sendDisableIms(mSlotId, mSubId);
+ transitionState(mDisabled);
+ } else {
+ // if mLastMsg is COMMAND_NONE_MSG or COMMAND_ENABLE_MSG
+ sendEnableIms(mSlotId, mSubId);
+ transitionState(mEnabled);
+ }
+ return HANDLED;
+ case COMMAND_ENABLE_MSG:
+ case COMMAND_DISABLE_MSG:
+ mLastMsg = message.what;
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ return HANDLED;
+ case COMMAND_RESET_MSG:
+ // when resetIms() called again, skip to call
+ // IImsServiceController.resetIms(slotId, subId), but after throttle time
+ // IImsServiceController.enableIms(slotId, subId) should be called.
+ mLastMsg = COMMAND_ENABLE_MSG;
+ mSlotId = message.arg1;
+ mSubId = message.arg2;
+ return HANDLED;
+ case COMMAND_DISCONNECTED_MSG:
+ transitionState(mDisconnected);
+ return HANDLED;
+ case COMMAND_INVALID_SUBID_MSG:
+ handleInvalidSubIdMessage();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+ }
+
+ public ImsEnablementTracker(Looper looper, ComponentName componentName) {
+ mIImsServiceController = null;
+ mStateMachines = new SparseArray<>();
+ mLooper = looper;
+ mState = ImsEnablementTracker.STATE_IMS_DISCONNECTED;
+ mComponentName = componentName;
+ }
+
+ @VisibleForTesting
+ public ImsEnablementTracker(Looper looper, IImsServiceController controller, int state,
+ int numSlots) {
+ mIImsServiceController = controller;
+ mStateMachines = new SparseArray<>();
+ mLooper = looper;
+ mState = state;
+ mComponentName = null;
+
+ setNumOfSlots(numSlots);
+ }
+
+ /**
+ * Set the number of SIM slots.
+ * @param numOfSlots the number of SIM slots.
+ */
+ public void setNumOfSlots(int numOfSlots) {
+ int oldNumSlots = mStateMachines.size();
+ Log.d(LOG_TAG, "set the slots: old[" + oldNumSlots + "], new[" + numOfSlots + "],"
+ + "component:" + mComponentName);
+ if (numOfSlots == oldNumSlots) {
+ return;
+ }
+ ImsEnablementTrackerStateMachine enablementStateMachine = null;
+ if (oldNumSlots < numOfSlots) {
+ for (int i = oldNumSlots; i < numOfSlots; i++) {
+ enablementStateMachine = new ImsEnablementTrackerStateMachine(
+ "ImsEnablementTracker", mLooper, mState, i);
+ enablementStateMachine.start();
+ mStateMachines.put(i, enablementStateMachine);
+ }
+ } else if (oldNumSlots > numOfSlots) {
+ for (int i = (oldNumSlots - 1); i > (numOfSlots - 1); i--) {
+ enablementStateMachine = mStateMachines.get(i);
+ mStateMachines.remove(i);
+ enablementStateMachine.quitNow();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public Handler getHandler(int slotId) {
+ return mStateMachines.get(slotId).getHandler();
+ }
+
+ /**
+ * Check that the current state and the input state are the same.
+ * @param state the input state.
+ * @return true if the current state and input state are the same or false.
+ */
+ @VisibleForTesting
+ public boolean isState(int slotId, int state) {
+ return mStateMachines.get(slotId).isState(state);
+ }
+
+ /**
+ * Notify the state machine that the subId has changed to invalid.
+ * @param slotId subscription id
+ */
+ public void subIdChangedToInvalid(int slotId) {
+ Log.d(LOG_TAG, "[" + slotId + "] subId changed to invalid, component:" + mComponentName);
+ ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId);
+ if (stateMachine != null) {
+ stateMachine.sendMessage(COMMAND_INVALID_SUBID_MSG, slotId);
+ } else {
+ Log.w(LOG_TAG, "There is no state machine associated with this slotId.");
+ }
+ }
+
+ /**
+ * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
+ * trigger ImsFeature status updates.
+ * @param slotId slot id
+ * @param subId subscription id
+ */
+ public void enableIms(int slotId, int subId) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]enableIms, component:" + mComponentName);
+ ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId);
+ if (stateMachine != null) {
+ stateMachine.sendMessage(COMMAND_ENABLE_MSG, slotId, subId);
+ } else {
+ Log.w(LOG_TAG, "There is no state machine associated with this slotId.");
+ }
+ }
+
+ /**
+ * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
+ * trigger ImsFeature capability status to become false.
+ * @param slotId slot id
+ * @param subId subscription id
+ */
+ public void disableIms(int slotId, int subId) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]disableIms, component:" + mComponentName);
+ ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId);
+ if (stateMachine != null) {
+ stateMachine.sendMessage(COMMAND_DISABLE_MSG, slotId, subId);
+ } else {
+ Log.w(LOG_TAG, "There is no state machine associated with this slotId.");
+ }
+ }
+
+ /**
+ * Notify ImsService to reset IMS for the framework. This will trigger ImsService to perform
+ * de-registration and release all resource. After that, if enaleIms is called, the ImsService
+ * performs registration and appropriate initialization to bring up all ImsFeatures.
+ * @param slotId slot id
+ * @param subId subscription id
+ */
+ public void resetIms(int slotId, int subId) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]resetIms, component:" + mComponentName);
+ ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId);
+ if (stateMachine != null) {
+ stateMachine.sendMessage(COMMAND_RESET_MSG, slotId, subId);
+ } else {
+ Log.w(LOG_TAG, "There is no state machine associated with this slotId.");
+ }
+ }
+
+ /**
+ * Sets the IImsServiceController instance.
+ */
+ protected void setServiceController(IBinder serviceController) {
+ synchronized (mLock) {
+ mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+ Log.d(LOG_TAG, "setServiceController with Binder:" + mIImsServiceController
+ + ", component:" + mComponentName);
+ ImsEnablementTrackerStateMachine stateMachine = null;
+ for (int i = 0; i < mStateMachines.size(); i++) {
+ stateMachine = mStateMachines.get(i);
+ if (stateMachine == null) {
+ Log.w(LOG_TAG, "There is no state machine associated with"
+ + "the slotId[" + i + "]");
+ continue;
+ }
+ if (isServiceControllerAvailable()) {
+ stateMachine.serviceBinderConnected();
+ } else {
+ stateMachine.serviceBinderDisconnected();
+ }
+ }
+ }
+ }
+
+ protected long getLastOperationTimeMillis() {
+ return mLastImsOperationTimeMs;
+ }
+
+ /**
+ * Get remaining throttle time value
+ * @return remaining throttle time value
+ */
+ @VisibleForTesting
+ public long getRemainThrottleTime() {
+ long remainTime = REQUEST_THROTTLE_TIME_MS - (System.currentTimeMillis()
+ - getLastOperationTimeMillis());
+
+ if (remainTime < 0) {
+ remainTime = 0L;
+ }
+ Log.d(LOG_TAG, "getRemainThrottleTime:" + remainTime);
+
+ return remainTime;
+ }
+
+ /**
+ * Check to see if the service controller is available.
+ * @return true if available, false otherwise
+ */
+ private boolean isServiceControllerAvailable() {
+ if (mIImsServiceController != null) {
+ return true;
+ }
+ Log.d(LOG_TAG, "isServiceControllerAvailable : binder is not alive");
+ return false;
+ }
+
+ private void sendEnableIms(int slotId, int subId) {
+ try {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendEnableIms,"
+ + "componentName[" + mComponentName + "]");
+ mIImsServiceController.enableIms(slotId, subId);
+ mLastImsOperationTimeMs = System.currentTimeMillis();
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
+ }
+ }
+
+ private void sendDisableIms(int slotId, int subId) {
+ try {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendDisableIms"
+ + " componentName[" + mComponentName + "]");
+ mIImsServiceController.disableIms(slotId, subId);
+ mLastImsOperationTimeMs = System.currentTimeMillis();
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
+ }
+ }
+
+ private void sendResetIms(int slotId, int subId) {
+ try {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendResetIms");
+ mIImsServiceController.resetIms(slotId, subId);
+ mLastImsOperationTimeMs = System.currentTimeMillis();
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't reset IMS: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index c3331d9..49b7e62 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -30,6 +30,7 @@
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -130,6 +131,7 @@
// Delay between dynamic ImsService queries.
private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
+ private static final HandlerThread sHandlerThread = new HandlerThread(TAG);
private static ImsResolver sInstance;
@@ -139,9 +141,9 @@
public static void make(Context context, String defaultMmTelPackageName,
String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
if (sInstance == null) {
- Looper looper = Looper.getMainLooper();
+ sHandlerThread.start();
sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
- numSlots, repo, looper);
+ numSlots, repo, sHandlerThread.getLooper());
}
}
@@ -312,7 +314,8 @@
@VisibleForTesting
public interface SubscriptionManagerProxy {
/**
- * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing.
+ * Mock-able interface for {@link SubscriptionManager#getSubscriptionId(int)} used for
+ * testing.
*/
int getSubId(int slotId);
/**
@@ -346,12 +349,7 @@
private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
@Override
public int getSubId(int slotId) {
- int[] subIds = SubscriptionManager.getSubId(slotId);
- if (subIds != null) {
- // This is done in all other places getSubId is used.
- return subIds[0];
- }
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ return SubscriptionManager.getSubscriptionId(slotId);
}
@Override
@@ -634,8 +632,13 @@
/**
* Needs to be called after the constructor to kick off the process of binding to ImsServices.
+ * Should be run on the handler thread of ImsResolver
*/
public void initialize() {
+ mHandler.post(()-> initializeInternal());
+ }
+
+ private void initializeInternal() {
mEventLog.log("Initializing");
Log.i(TAG, "Initializing cache.");
PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
@@ -742,6 +745,15 @@
}
/**
+ * Notify ImsService to disable IMS for the framework.
+ * And notify ImsService back to enable IMS for the framework.
+ */
+ public void resetIms(int slotId) {
+ getImsServiceControllers(slotId).forEach(
+ (controller) -> controller.resetIms(slotId, getSubId(slotId)));
+ }
+
+ /**
* Returns the ImsRegistration structure associated with the slotId and feature specified.
*/
public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 92e7d71..6af7a08 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -85,14 +85,49 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ onServiceConnectedInternal(name, service);
+ } else {
+ mHandler.post(() -> onServiceConnectedInternal(name, service));
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ onServiceDisconnectedInternal(name);
+ } else {
+ mHandler.post(() -> onServiceDisconnectedInternal(name));
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ onBindingDiedInternal(name);
+ } else {
+ mHandler.post(() -> onBindingDiedInternal(name));
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ onNullBindingInternal(name);
+ } else {
+ mHandler.post(() -> onNullBindingInternal(name));
+ }
+ }
+
+ private void onServiceConnectedInternal(ComponentName name, IBinder service) {
synchronized (mLock) {
mBackoff.stop();
mIsBound = true;
mIsBinding = false;
try {
- mLocalLog.log("onServiceConnected");
- Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
- + service);
+ mLocalLog.log("onServiceConnectedInternal");
+ Log.d(LOG_TAG, "ImsService(" + name
+ + "): onServiceConnectedInternal with binder: " + service);
setServiceController(service);
notifyImsServiceReady();
retrieveStaticImsServiceCapabilities();
@@ -118,20 +153,18 @@
}
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ private void onServiceDisconnectedInternal(ComponentName name) {
synchronized (mLock) {
mIsBinding = false;
cleanupConnection();
}
- mLocalLog.log("onServiceDisconnected");
- Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting...");
+ mLocalLog.log("onServiceDisconnectedInternal");
+ Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnectedInternal. Waiting...");
// Service disconnected, but we are still technically bound. Waiting for reconnect.
checkAndReportAnomaly(name);
}
- @Override
- public void onBindingDied(ComponentName name) {
+ private void onBindingDiedInternal(ComponentName name) {
mIsServiceConnectionDead = true;
synchronized (mLock) {
mIsBinding = false;
@@ -141,15 +174,15 @@
unbindService();
startDelayedRebindToService();
}
- Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
- mLocalLog.log("onBindingDied, retrying in " + mBackoff.getCurrentDelay() + " mS");
+ Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDiedInternal. Starting rebind...");
+ mLocalLog.log("onBindingDiedInternal, retrying in "
+ + mBackoff.getCurrentDelay() + " mS");
}
- @Override
- public void onNullBinding(ComponentName name) {
- Log.w(LOG_TAG, "ImsService(" + name + "): onNullBinding. Is service dead = "
+ private void onNullBindingInternal(ComponentName name) {
+ Log.w(LOG_TAG, "ImsService(" + name + "): onNullBindingInternal. Is service dead = "
+ mIsServiceConnectionDead);
- mLocalLog.log("onNullBinding, is service dead = " + mIsServiceConnectionDead);
+ mLocalLog.log("onNullBindingInternal, is service dead = " + mIsServiceConnectionDead);
// onNullBinding will happen after onBindingDied. In this case, we should not
// permanently unbind and instead let the automatic rebind occur.
if (mIsServiceConnectionDead) return;
@@ -230,6 +263,7 @@
private static final boolean ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT = false;
private final ComponentName mComponentName;
private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
+ private final Handler mHandler;
private final LegacyPermissionManager mPermissionManager;
private ImsFeatureBinderRepository mRepo;
private ImsServiceControllerCallbacks mCallbacks;
@@ -241,6 +275,7 @@
private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
private SparseIntArray mSlotIdToSubIdMap;
private IImsServiceController mIImsServiceController;
+ private final ImsEnablementTracker mImsEnablementTracker;
// The Capabilities bitmask of the connected ImsService (see ImsService#ImsServiceCapability).
private long mServiceCapabilities;
private ImsServiceConnection mImsServiceConnection;
@@ -323,16 +358,17 @@
mComponentName = componentName;
mCallbacks = callbacks;
mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
mBackoff = new ExponentialBackoff(
mRebindRetry.getStartDelay(),
mRebindRetry.getMaximumDelay(),
2, /* multiplier */
- mHandlerThread.getLooper(),
+ mHandler,
mRestartImsServiceRunnable);
mPermissionManager = (LegacyPermissionManager) mContext.getSystemService(
Context.LEGACY_PERMISSION_SERVICE);
mRepo = repo;
-
+ mImsEnablementTracker = new ImsEnablementTracker(mHandlerThread.getLooper(), componentName);
mPackageManager = mContext.getPackageManager();
if (mPackageManager != null) {
mChangedPackages = mPackageManager.getChangedPackages(mLastSequenceNumber);
@@ -351,6 +387,7 @@
mContext = context;
mComponentName = componentName;
mCallbacks = callbacks;
+ mHandler = handler;
mBackoff = new ExponentialBackoff(
rebindRetry.getStartDelay(),
rebindRetry.getMaximumDelay(),
@@ -359,6 +396,7 @@
mRestartImsServiceRunnable);
mPermissionManager = null;
mRepo = repo;
+ mImsEnablementTracker = new ImsEnablementTracker(handler.getLooper(), componentName);
}
/**
@@ -378,6 +416,8 @@
sanitizeFeatureConfig(imsFeatureSet);
mImsFeatures = imsFeatureSet;
mSlotIdToSubIdMap = slotIdToSubIdMap;
+ // Set the number of slots that support the feature
+ mImsEnablementTracker.setNumOfSlots(mSlotIdToSubIdMap.size());
grantPermissionsToService();
Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
mComponentName);
@@ -464,6 +504,14 @@
+ newSubId);
Log.i(LOG_TAG, "subId changed for slot: " + slotID + ", " + oldSubId + " -> "
+ newSubId);
+ if (newSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ /* An INVALID subId can also be set in bind(), however
+ the ImsEnablementTracker will move into the DEFAULT state, so we only
+ need to track changes in subId that result in requiring we move
+ the state machine back to DEFAULT.
+ */
+ mImsEnablementTracker.subIdChangedToInvalid(slotID);
+ }
}
}
mSlotIdToSubIdMap = slotIdToSubIdMap;
@@ -553,15 +601,7 @@
* trigger ImsFeature status updates.
*/
public void enableIms(int slotId, int subId) {
- try {
- synchronized (mLock) {
- if (isServiceControllerAvailable()) {
- mIImsServiceController.enableIms(slotId, subId);
- }
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
- }
+ mImsEnablementTracker.enableIms(slotId, subId);
}
/**
@@ -569,15 +609,15 @@
* trigger ImsFeature capability status to become false.
*/
public void disableIms(int slotId, int subId) {
- try {
- synchronized (mLock) {
- if (isServiceControllerAvailable()) {
- mIImsServiceController.disableIms(slotId, subId);
- }
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
- }
+ mImsEnablementTracker.disableIms(slotId, subId);
+ }
+
+ /**
+ * Notify ImsService to disable IMS for the framework.
+ * And notify ImsService back to enable IMS for the framework
+ */
+ public void resetIms(int slotId, int subId) {
+ mImsEnablementTracker.resetIms(slotId, subId);
}
/**
@@ -651,6 +691,7 @@
*/
protected void setServiceController(IBinder serviceController) {
mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+ mImsEnablementTracker.setServiceController(serviceController);
}
/**
@@ -910,7 +951,7 @@
return;
}
ChangedPackages curChangedPackages =
- mPackageManager.getChangedPackages(mLastSequenceNumber);
+ mPackageManager.getChangedPackages(mLastSequenceNumber);
if (curChangedPackages != null) {
mLastSequenceNumber = curChangedPackages.getSequenceNumber();
List<String> packagesNames = curChangedPackages.getPackageNames();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java b/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java
new file mode 100644
index 0000000..79ab9c5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.imsphone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ServiceState;
+
+import com.android.internal.telephony.Call;
+
+/**
+ * Contains call state to be notified to modem.
+ */
+public class ImsCallInfo {
+
+ private final int mIndex;
+ private @Nullable ImsPhoneConnection mConnection = null;
+ private Call.State mState = Call.State.IDLE;
+ private boolean mIsHeldByRemote = false;
+
+ public ImsCallInfo(int index) {
+ mIndex = index;
+ }
+
+ /** Clears the call state. */
+ public void reset() {
+ mConnection = null;
+ mState = Call.State.IDLE;
+ mIsHeldByRemote = false;
+ }
+
+ /**
+ * Updates the state of the IMS call.
+ *
+ * @param c The instance of {@link ImsPhoneConnection}.
+ */
+ public void update(@NonNull ImsPhoneConnection c) {
+ mConnection = c;
+ mState = c.getState();
+ }
+
+ /**
+ * Updates the state of the IMS call.
+ *
+ * @param c The instance of {@link ImsPhoneConnection}.
+ * @param holdReceived {@code true} if the remote party held the call.
+ * @param resumeReceived {@code true} if the remote party resumed the call.
+ */
+ public boolean update(@NonNull ImsPhoneConnection c,
+ boolean holdReceived, boolean resumeReceived) {
+ Call.State state = c.getState();
+ boolean changed = mState != state;
+ mState = state;
+
+ if (holdReceived && !mIsHeldByRemote) {
+ changed = true;
+ mIsHeldByRemote = true;
+ } else if (resumeReceived && mIsHeldByRemote) {
+ changed = true;
+ mIsHeldByRemote = false;
+ }
+
+ return changed;
+ }
+
+ /** Called when clearing orphaned connection. */
+ public void onDisconnect() {
+ mState = Call.State.DISCONNECTED;
+ }
+
+ /** @return the call index. */
+ public int getIndex() {
+ return mIndex;
+ }
+
+ /** @return the call state. */
+ public Call.State getCallState() {
+ return mState;
+ }
+
+ /** @return whether the remote party is holding the call. */
+ public boolean isHeldByRemote() {
+ return mIsHeldByRemote;
+ }
+
+ /** @return {@code true} if the call is an incoming call. */
+ public boolean isIncoming() {
+ return mConnection.isIncoming();
+ }
+
+ /** @return {@code true} if the call is an emergency call. */
+ public boolean isEmergencyCall() {
+ return mConnection.isEmergencyCall();
+ }
+
+ /** @return the radio technology used for current connection. */
+ public @AccessNetworkConstants.RadioAccessNetworkType int getCallRadioTech() {
+ return ServiceState.rilRadioTechnologyToAccessNetworkType(mConnection.getCallRadioTech());
+ }
+
+ @Override
+ public String toString() {
+ return "[ id=" + mIndex + ", state=" + mState
+ + ", isMT=" + isIncoming() + ", heldByRemote=" + mIsHeldByRemote + " ]";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java
new file mode 100644
index 0000000..5783e48
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.imsphone;
+
+import static com.android.internal.telephony.Call.State.DISCONNECTED;
+import static com.android.internal.telephony.Call.State.IDLE;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the state of all IMS calls.
+ */
+public class ImsCallInfoTracker {
+ private static final String LOG_TAG = "ImsCallInfoTracker";
+ private static final boolean DBG = false;
+
+ private final Phone mPhone;
+ private final List<ImsCallInfo> mQueue = new ArrayList<>();
+ private int mNextIndex = 1;
+
+ private final Map<Connection, ImsCallInfo> mImsCallInfo = new HashMap<>();
+
+ public ImsCallInfoTracker(Phone phone) {
+ mPhone = phone;
+ }
+
+ /**
+ * Adds a new instance of the IMS call.
+ *
+ * @param c The instance of {@link ImsPhoneConnection}.
+ */
+ public void addImsCallStatus(@NonNull ImsPhoneConnection c) {
+ if (DBG) Rlog.d(LOG_TAG, "addImsCallStatus");
+
+ synchronized (mImsCallInfo) {
+ if (mQueue.isEmpty()) {
+ mQueue.add(new ImsCallInfo(mNextIndex++));
+ }
+
+ Iterator<ImsCallInfo> it = mQueue.iterator();
+ ImsCallInfo imsCallInfo = it.next();
+ mQueue.remove(imsCallInfo);
+
+ imsCallInfo.update(c);
+ mImsCallInfo.put(c, imsCallInfo);
+
+ notifyImsCallStatus();
+
+ if (DBG) dump();
+ }
+ }
+
+ /**
+ * Updates the list of IMS calls.
+ *
+ * @param c The instance of {@link ImsPhoneConnection}.
+ */
+ public void updateImsCallStatus(@NonNull ImsPhoneConnection c) {
+ updateImsCallStatus(c, false, false);
+ }
+
+ /**
+ * Updates the list of IMS calls.
+ *
+ * @param c The instance of {@link ImsPhoneConnection}.
+ * @param holdReceived {@code true} if the remote party held the call.
+ * @param resumeReceived {@code true} if the remote party resumed the call.
+ */
+ public void updateImsCallStatus(@NonNull ImsPhoneConnection c,
+ boolean holdReceived, boolean resumeReceived) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateImsCallStatus holdReceived=" + holdReceived
+ + ", resumeReceived=" + resumeReceived);
+ }
+
+ synchronized (mImsCallInfo) {
+ ImsCallInfo info = mImsCallInfo.get(c);
+
+ if (info == null) {
+ // This happens when the user tries to hangup the call after handover has completed.
+ return;
+ }
+
+ boolean changed = info.update(c, holdReceived, resumeReceived);
+
+ if (changed) notifyImsCallStatus();
+
+ Call.State state = c.getState();
+
+ if (DBG) Rlog.d(LOG_TAG, "updateImsCallStatus state=" + state);
+ // Call is disconnected. There are 2 cases in disconnected state:
+ // if silent redial, state == IDLE, otherwise, state == DISCONNECTED.
+ if (state == DISCONNECTED || state == IDLE) {
+ // clear the disconnected call
+ mImsCallInfo.remove(c);
+ info.reset();
+ if (info.getIndex() < (mNextIndex - 1)) {
+ mQueue.add(info);
+ sort(mQueue);
+ } else {
+ mNextIndex--;
+ }
+ }
+
+ if (DBG) dump();
+ }
+ }
+
+ /** Clears all orphaned IMS call information. */
+ public void clearAllOrphanedConnections() {
+ if (DBG) Rlog.d(LOG_TAG, "clearAllOrphanedConnections");
+
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ infos.stream().forEach(info -> { info.onDisconnect(); });
+ notifyImsCallStatus();
+ clearAllCallInfo();
+
+ if (DBG) dump();
+ }
+
+ /** Notifies that SRVCC has completed. */
+ public void notifySrvccCompleted() {
+ if (DBG) Rlog.d(LOG_TAG, "notifySrvccCompleted");
+
+ clearAllCallInfo();
+ notifyImsCallStatus();
+
+ if (DBG) dump();
+ }
+
+ private void clearAllCallInfo() {
+ try {
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ infos.stream().forEach(info -> { info.reset(); });
+ mImsCallInfo.clear();
+ mQueue.clear();
+ mNextIndex = 1;
+ } catch (UnsupportedOperationException e) {
+ Rlog.e(LOG_TAG, "e=" + e);
+ }
+ }
+
+ private void notifyImsCallStatus() {
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
+ sort(imsCallInfo);
+ mPhone.updateImsCallStatus(imsCallInfo, null);
+ }
+
+ /**
+ * Sorts the list of IMS calls by the call index.
+ *
+ * @param infos The list of IMS calls.
+ */
+ @VisibleForTesting
+ public static void sort(List<ImsCallInfo> infos) {
+ Collections.sort(infos, new Comparator<ImsCallInfo>() {
+ @Override
+ public int compare(ImsCallInfo l, ImsCallInfo r) {
+ if (l.getIndex() > r.getIndex()) {
+ return 1;
+ } else if (l.getIndex() < r.getIndex()) {
+ return -1;
+ }
+ return 0;
+ }
+ });
+ }
+
+ private void dump() {
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
+ sort(imsCallInfo);
+ Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
new file mode 100644
index 0000000..3dedde8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.imsphone;
+
+import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA;
+import static android.telephony.CarrierConfigManager.Ims.KEY_NR_SA_DISABLE_POLICY_INT;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_NONE;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED;
+import static android.telephony.CarrierConfigManager.Ims.NrSaDisablePolicy;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Call;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Enables or Disables NR-SA mode temporarily under certain conditions where WFC is established or
+ * IMS is registered over WiFi in order to improve the delay or voice mute issue when the handover
+ * from ePDG to NR is not supported in UE or network.
+ */
+public class ImsNrSaModeHandler extends Handler{
+
+ public static final String TAG = "ImsNrSaModeHandler";
+ public static final String MMTEL_FEATURE_TAG =
+ "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
+
+ private static final int MSG_PRECISE_CALL_STATE_CHANGED = 101;
+ private static final int MSG_REQUEST_IS_VONR_ENABLED = 102;
+ private static final int MSG_RESULT_IS_VONR_ENABLED = 103;
+
+ private final @NonNull ImsPhone mPhone;
+ private @Nullable CarrierConfigManager mCarrierConfigManager;
+
+ private @NrSaDisablePolicy int mNrSaDisablePolicy;
+ private boolean mIsNrSaDisabledForWfc;
+ private boolean mIsVowifiRegistered;
+ private boolean mIsInImsCall;
+ private boolean mIsNrSaSupported;
+
+ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ (slotIndex, subId, carrierId, specificCarrierId) -> setNrSaDisablePolicy(subId);
+
+ public ImsNrSaModeHandler(@NonNull ImsPhone phone, Looper looper) {
+ super(looper);
+ mPhone = phone;
+ mCarrierConfigManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+ registerForCarrierConfigChanges();
+ }
+
+ /**
+ * Performs any cleanup required before the ImsNrSaModeHandler is destroyed.
+ */
+ public void tearDown() {
+ unregisterForCarrierConfigChanges();
+ unregisterForPreciseCallStateChanges();
+
+ if (isNrSaDisabledForWfc()) {
+ setNrSaMode(true);
+ }
+ }
+
+ /**
+ * Based on changed VoWiFi reg state and call state, handles NR SA mode if needed.
+ * It is including handover case.
+ *
+ * @param imsRadioTech The current registered RAT.
+ */
+ public void onImsRegistered(
+ @ImsRegistrationTech int imsRadioTech, @NonNull Set<String> featureTags) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) {
+ return;
+ }
+
+ Log.d(TAG, "onImsRegistered: ImsRegistrationTech = " + imsRadioTech);
+
+ boolean isVowifiRegChanged = false;
+
+ if (isVowifiRegistered() && imsRadioTech != REGISTRATION_TECH_IWLAN) {
+ setVowifiRegStatus(false);
+ isVowifiRegChanged = true;
+ } else if (!isVowifiRegistered() && imsRadioTech == REGISTRATION_TECH_IWLAN
+ && featureTags.contains(MMTEL_FEATURE_TAG)) {
+ setVowifiRegStatus(true);
+ isVowifiRegChanged = true;
+ }
+
+ if (isVowifiRegChanged) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED) {
+ setNrSaMode(!isVowifiRegistered());
+ } else if ((mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED
+ || mNrSaDisablePolicy
+ == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED)
+ && isImsCallOngoing()) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
+ requestIsVonrEnabled(!isVowifiRegistered());
+ return;
+ }
+
+ setNrSaMode(!isVowifiRegistered());
+ }
+ }
+ }
+
+ /**
+ * Based on changed VoWiFi reg state and call state, handles NR SA mode if needed.
+ *
+ * @param imsRadioTech The current un-registered RAT.
+ */
+ public void onImsUnregistered(
+ @ImsRegistrationTech int imsRadioTech) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE
+ || imsRadioTech != REGISTRATION_TECH_IWLAN || !isVowifiRegistered()) {
+ return;
+ }
+
+ Log.d(TAG, "onImsUnregistered : ImsRegistrationTech = " + imsRadioTech);
+
+ setVowifiRegStatus(false);
+
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED) {
+ setNrSaMode(true);
+ } else if ((mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED
+ || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED)
+ && isImsCallOngoing()) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
+ requestIsVonrEnabled(true);
+ return;
+ }
+
+ setNrSaMode(true);
+ }
+ }
+
+ /**
+ * Based on changed precise call state and VoWiFi reg state, handles NR SA mode if needed.
+ */
+ public void onPreciseCallStateChanged() {
+ Log.d(TAG, "onPreciseCallStateChanged : foreground state = "
+ + mPhone.getForegroundCall().getState() + ", background state = "
+ + mPhone.getBackgroundCall().getState());
+
+ boolean isImsCallStatusChanged = false;
+
+ if (isImsCallJustEstablished()) {
+ setImsCallStatus(true);
+ isImsCallStatusChanged = true;
+ } else if (isImsCallJustTerminated()) {
+ setImsCallStatus(false);
+ isImsCallStatusChanged = true;
+ }
+
+ if (isVowifiRegistered() && isImsCallStatusChanged) {
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
+ requestIsVonrEnabled(!isImsCallOngoing());
+ return;
+ }
+
+ setNrSaMode(!isImsCallOngoing());
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case MSG_PRECISE_CALL_STATE_CHANGED :
+ onPreciseCallStateChanged();
+ break;
+ case MSG_REQUEST_IS_VONR_ENABLED :
+ Log.d(TAG, "request isVoNrEnabled");
+ mPhone.getDefaultPhone().mCi.isVoNrEnabled(
+ obtainMessage(MSG_RESULT_IS_VONR_ENABLED, msg.obj), null);
+ break;
+ case MSG_RESULT_IS_VONR_ENABLED :
+ ar = (AsyncResult) msg.obj;
+
+ if (ar.result != null) {
+ boolean vonrEnabled = ((Boolean) ar.result).booleanValue();
+
+ Log.d(TAG, "result isVoNrEnabled = " + vonrEnabled);
+ if (!vonrEnabled) {
+ setNrSaMode(((Boolean) ar.userObj).booleanValue());
+ }
+ }
+
+ ar = null;
+ break;
+ default :
+ break;
+ }
+ }
+
+ /**
+ * Registers for precise call state changes.
+ */
+ private void registerForPreciseCallStateChanges() {
+ mPhone.registerForPreciseCallStateChanged(this, MSG_PRECISE_CALL_STATE_CHANGED, null);
+ }
+
+ /**
+ * Unregisters for precise call state changes.
+ */
+ private void unregisterForPreciseCallStateChanges() {
+ mPhone.unregisterForPreciseCallStateChanged(this);
+ }
+
+ /**
+ * Registers for carrier config changes.
+ */
+ private void registerForCarrierConfigChanges() {
+ if (mCarrierConfigManager != null) {
+ mCarrierConfigManager.registerCarrierConfigChangeListener(
+ this::post, mCarrierConfigChangeListener);
+ }
+ }
+
+ /**
+ * Unregisters for carrier config changes.
+ */
+ private void unregisterForCarrierConfigChanges() {
+ if (mCarrierConfigManager != null) {
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
+ }
+ }
+
+ private void setNrSaDisablePolicy(int subId) {
+ if (mPhone.getSubId() == subId && mCarrierConfigManager != null) {
+ PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
+ KEY_NR_SA_DISABLE_POLICY_INT, KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT);
+ mIsNrSaSupported = Arrays.stream(
+ bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY)).anyMatch(
+ value -> value == CARRIER_NR_AVAILABILITY_SA);
+
+ Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = "
+ + mNrSaDisablePolicy + ", IsNrSaSupported = " + mIsNrSaSupported);
+
+ if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED
+ || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED) {
+ registerForPreciseCallStateChanges();
+ } else {
+ unregisterForPreciseCallStateChanges();
+ }
+ }
+ }
+
+ private void requestIsVonrEnabled(boolean onOrOff) {
+ Message msg = obtainMessage(MSG_REQUEST_IS_VONR_ENABLED, onOrOff);
+ msg.sendToTarget();
+ }
+
+ private void setNrSaMode(boolean onOrOff) {
+ if (mIsNrSaSupported) {
+ mPhone.getDefaultPhone().mCi.setN1ModeEnabled(onOrOff, null);
+ Log.i(TAG, "setNrSaMode : " + onOrOff);
+
+ setNrSaDisabledForWfc(!onOrOff);
+ }
+ }
+
+ /**
+ * Sets VoWiFi reg status.
+ */
+ @VisibleForTesting
+ public void setVowifiRegStatus(boolean registered) {
+ Log.d(TAG, "setVowifiRegStatus : " + registered);
+ mIsVowifiRegistered = registered;
+ }
+
+ /**
+ * Sets IMS call status
+ */
+ @VisibleForTesting
+ public void setImsCallStatus(boolean inImsCall) {
+ Log.d(TAG, "setImsCallStatus : " + inImsCall);
+ mIsInImsCall = inImsCall;
+ }
+
+ @VisibleForTesting
+ public boolean isVowifiRegistered() {
+ return mIsVowifiRegistered;
+ }
+
+ @VisibleForTesting
+ public boolean isImsCallOngoing() {
+ return mIsInImsCall;
+ }
+
+ @VisibleForTesting
+ public boolean isNrSaDisabledForWfc() {
+ return mIsNrSaDisabledForWfc;
+ }
+
+ @VisibleForTesting
+ public void setNrSaDisabledForWfc(boolean disabled) {
+ mIsNrSaDisabledForWfc = disabled;
+ }
+
+ private boolean isImsCallJustEstablished() {
+ if (!isImsCallOngoing()) {
+ if ((mPhone.getForegroundCall().getState() == Call.State.ACTIVE)
+ || (mPhone.getBackgroundCall().getState() == Call.State.ACTIVE)) {
+ Log.d(TAG, "isImsCallJustEstablished");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isImsCallJustTerminated() {
+ if (isImsCallOngoing() && (!mPhone.getForegroundCall().getState().isAlive()
+ && !mPhone.getBackgroundCall().getState().isAlive())) {
+ Log.d(TAG, "isImsCallJustTerminated");
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 71e85f3..4b150cc 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -16,9 +16,14 @@
package com.android.internal.telephony.imsphone;
-import static android.provider.Telephony.SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS;
import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE;
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.SUGGESTED_ACTION_NONE;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
@@ -42,6 +47,7 @@
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
@@ -71,7 +77,6 @@
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UssdResponse;
@@ -79,9 +84,12 @@
import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.ImsSsData;
import android.telephony.ims.ImsSsInfo;
import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -107,15 +115,17 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.telephony.util.TelephonyUtils;
@@ -183,6 +193,7 @@
return new ImsDialArgs.Builder()
.setUusInfo(dialArgs.uusInfo)
.setIsEmergency(dialArgs.isEmergency)
+ .setEccCategory(dialArgs.eccCategory)
.setVideoState(dialArgs.videoState)
.setIntentExtras(dialArgs.intentExtras)
.setRttTextStream(((ImsDialArgs)dialArgs).rttTextStream)
@@ -252,6 +263,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ImsPhoneCallTracker mCT;
ImsExternalCallTracker mExternalCallTracker;
+ ImsNrSaModeHandler mImsNrSaModeHandler;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -288,6 +300,16 @@
private ImsStats mImsStats;
+ private int mImsRegistrationState;
+ // The access network type where IMS is registered
+ private @ImsRegistrationImplBase.ImsRegistrationTech int mImsRegistrationTech =
+ REGISTRATION_TECH_NONE;
+ private @RegistrationManager.SuggestedAction int mImsRegistrationSuggestedAction;
+ private @ImsRegistrationImplBase.ImsRegistrationTech int mImsDeregistrationTech =
+ REGISTRATION_TECH_NONE;
+ private int mImsRegistrationCapabilities;
+ private boolean mNotifiedRegisteredState;
+
// A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@Override
@@ -452,16 +474,16 @@
TelephonyComponentFactory.getInstance()
.inject(ImsExternalCallTracker.class.getName())
.makeImsExternalCallTracker(this);
+ mImsNrSaModeHandler =
+ TelephonyComponentFactory.getInstance()
+ .inject(ImsNrSaModeHandler.class.getName())
+ .makeImsNrSaModeHandler(this);
mCT = TelephonyComponentFactory.getInstance().inject(ImsPhoneCallTracker.class.getName())
.makeImsPhoneCallTracker(this);
mCT.registerPhoneStateListener(mExternalCallTracker);
mExternalCallTracker.setCallPuller(mCT);
- boolean legacyMode = true;
- if (mDefaultPhone.getAccessNetworksManager() != null) {
- legacyMode = mDefaultPhone.getAccessNetworksManager().isInLegacyMode();
- }
- mSS.setOutOfService(legacyMode, false);
+ mSS.setOutOfService(false);
mPhoneId = mDefaultPhone.getPhoneId();
@@ -503,6 +525,7 @@
//super.dispose();
mPendingMMIs.clear();
mExternalCallTracker.tearDown();
+ mImsNrSaModeHandler.tearDown();
mCT.unregisterPhoneStateListener(mExternalCallTracker);
mCT.unregisterForVoiceCallEnded(this);
mCT.dispose();
@@ -891,6 +914,9 @@
@Override
public boolean isInImsEcm() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ return EmergencyStateTracker.getInstance().isInImsEcm();
+ }
return mIsInImsEcm;
}
@@ -1583,7 +1609,7 @@
}
@Override
- public void notifySrvccState(Call.SrvccState state) {
+ public void notifySrvccState(int state) {
mCT.notifySrvccState(state);
}
@@ -2037,6 +2063,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleEnterEmergencyCallbackMode() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ logd("DomainSelection enabled: ignore ECBM enter event.");
+ return;
+ }
if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm());
// if phone is not in Ecm mode, and it's changed to Ecm mode
if (!isInEcm()) {
@@ -2058,6 +2088,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void handleExitEmergencyCallbackMode() {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ logd("DomainSelection enabled: ignore ECBM exit event.");
+ return;
+ }
if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm());
if (isInEcm()) {
@@ -2087,6 +2121,7 @@
* Ecm timer and notify apps the timer is restarted.
*/
void handleTimerInEmergencyCallbackMode(int action) {
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return;
switch (action) {
case CANCEL_ECM_TIMER:
removeCallbacks(mExitEcmRunnable);
@@ -2370,7 +2405,7 @@
/**
* Update roaming state and WFC mode in the following situations:
* 1) voice is in service.
- * 2) data is in service and it is not IWLAN (if in legacy mode).
+ * 2) data is in service.
* @param ss non-null ServiceState
*/
private void updateRoamingState(ServiceState ss) {
@@ -2391,15 +2426,7 @@
logi("updateRoamingState: we are not IN_SERVICE, ignoring roaming change.");
return;
}
- // We ignore roaming changes when moving to IWLAN because it always sets the roaming
- // mode to home and masks the actual cellular roaming status if voice is not registered. If
- // we just moved to IWLAN because WFC roaming mode is IWLAN preferred and WFC home mode is
- // cell preferred, we can get into a condition where the modem keeps bouncing between
- // IWLAN->cell->IWLAN->cell...
- if (isCsNotInServiceAndPsWwanReportingWlan(ss)) {
- logi("updateRoamingState: IWLAN masking roaming, ignore roaming change.");
- return;
- }
+
if (mCT.getState() == PhoneConstants.State.IDLE) {
if (DBG) logd("updateRoamingState now: " + newRoamingState);
mLastKnownRoamingState = newRoamingState;
@@ -2418,30 +2445,6 @@
}
}
- /**
- * In legacy mode, data registration will report IWLAN when we are using WLAN for data,
- * effectively masking the true roaming state of the device if voice is not registered.
- *
- * @return true if we are reporting not in service for CS domain over WWAN transport and WLAN
- * for PS domain over WWAN transport.
- */
- private boolean isCsNotInServiceAndPsWwanReportingWlan(ServiceState ss) {
- // We can not get into this condition if we are in AP-Assisted mode.
- if (mDefaultPhone.getAccessNetworksManager() == null
- || !mDefaultPhone.getAccessNetworksManager().isInLegacyMode()) {
- return false;
- }
- NetworkRegistrationInfo csInfo = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- NetworkRegistrationInfo psInfo = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- // We will return roaming state correctly if the CS domain is in service because
- // ss.getRoaming() returns isVoiceRoaming||isDataRoaming result and isDataRoaming==false
- // when the modem reports IWLAN RAT.
- return psInfo != null && csInfo != null && !csInfo.isInService()
- && psInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN;
- }
-
public RegistrationManager.RegistrationCallback getImsMmTelRegistrationCallback() {
return mImsMmTelRegistrationHelper.getCallback();
}
@@ -2452,12 +2455,18 @@
public void resetImsRegistrationState() {
if (DBG) logd("resetImsRegistrationState");
mImsMmTelRegistrationHelper.reset();
+ int subId = getSubId();
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
+ REGISTRATION_TECH_NONE, SUGGESTED_ACTION_NONE);
+ }
}
private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mMmTelRegistrationUpdate = new
ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
@Override
- public void handleImsRegistered(int imsRadioTech) {
+ public void handleImsRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ int imsRadioTech = attributes.getTransportType();
if (DBG) {
logd("handleImsRegistered: onImsMmTelConnected imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
@@ -2468,6 +2477,10 @@
getDefaultPhone().setImsRegistrationState(true);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
mImsStats.onImsRegistered(imsRadioTech);
+ mImsNrSaModeHandler.onImsRegistered(
+ attributes.getRegistrationTechnology(), attributes.getFeatureTags());
+ updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
+ attributes.getRegistrationTechnology(), SUGGESTED_ACTION_NONE);
}
@Override
@@ -2486,10 +2499,13 @@
}
@Override
- public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) {
+ public void handleImsUnregistered(ImsReasonInfo imsReasonInfo,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
if (DBG) {
logd("handleImsUnregistered: onImsMmTelDisconnected imsReasonInfo="
- + imsReasonInfo);
+ + imsReasonInfo + ", suggestedAction=" + suggestedAction
+ + ", disconnectedRadioTech=" + imsRadioTech);
}
mRegLocalLog.log("handleImsUnregistered: onImsMmTelDisconnected imsRadioTech="
+ imsReasonInfo);
@@ -2499,6 +2515,17 @@
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED,
imsReasonInfo);
mImsStats.onImsUnregistered(imsReasonInfo);
+ mImsNrSaModeHandler.onImsUnregistered(imsRadioTech);
+ mImsRegistrationTech = REGISTRATION_TECH_NONE;
+ int suggestedModemAction = SUGGESTED_ACTION_NONE;
+ if (imsReasonInfo.getCode() == ImsReasonInfo.CODE_REGISTRATION_ERROR) {
+ if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK)
+ || (suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT)) {
+ suggestedModemAction = suggestedAction;
+ }
+ }
+ updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
+ imsRadioTech, suggestedModemAction);
}
@Override
@@ -2519,27 +2546,22 @@
int subId = getSubId();
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
// Defending b/219080264:
- // SubscriptionController.setSubscriptionProperty validates input subId
+ // SubscriptionManagerService.setSubscriptionProperty validates input subId
// so do not proceed if subId invalid. This may be happening because cached
// IMS callbacks are sent back to telephony after SIM state changed.
return;
}
- SubscriptionController subController = SubscriptionController.getInstance();
- String countryIso = getCountryIso(subController, subId);
- // Format the number as one more defense to reject garbage values:
- // phoneNumber will become null.
- phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber, countryIso);
- if (phoneNumber == null) {
- return;
- }
- subController.setSubscriptionProperty(subId, COLUMN_PHONE_NUMBER_SOURCE_IMS, phoneNumber);
- }
- private static String getCountryIso(SubscriptionController subController, int subId) {
- SubscriptionInfo info = subController.getSubscriptionInfo(subId);
- String countryIso = info == null ? "" : info.getCountryIso();
- // info.getCountryIso() may return null
- return countryIso == null ? "" : countryIso;
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null) {
+ phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber,
+ subInfo.getCountryIso());
+ if (phoneNumber == null) {
+ return;
+ }
+ mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
+ }
}
/**
@@ -2616,6 +2638,131 @@
return mLastKnownRoamingState;
}
+ /**
+ * Update IMS registration information to modem.
+ *
+ * @param capabilities indicate MMTEL capability such as VOICE, VIDEO and SMS.
+ */
+ public void updateImsRegistrationInfo(int capabilities) {
+ if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
+ if (mNotifiedRegisteredState && (capabilities == mImsRegistrationCapabilities)) {
+ // Duplicated notification, no change in capabilities.
+ return;
+ }
+
+ mImsRegistrationCapabilities = capabilities;
+ if (capabilities == 0) {
+ // Ignore this as this usually happens just before onUnregistered callback.
+ // We can notify modem when onUnregistered() flow occurs.
+ return;
+ }
+
+ mDefaultPhone.mCi.updateImsRegistrationInfo(mImsRegistrationState,
+ mImsRegistrationTech, 0, capabilities, null);
+ mNotifiedRegisteredState = true;
+ }
+ }
+
+ /**
+ * Update IMS registration info
+ *
+ * @param regState indicates IMS registration state.
+ * @param imsRadioTech indicates the type of the radio access network where IMS is registered.
+ * @param suggestedAction indicates the suggested action for the radio to perform.
+ */
+ private void updateImsRegistrationInfo(
+ @RegistrationManager.ImsRegistrationState int regState,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+ @RegistrationManager.SuggestedAction int suggestedAction) {
+
+ if (regState == mImsRegistrationState) {
+ if ((regState == REGISTRATION_STATE_REGISTERED && imsRadioTech == mImsRegistrationTech)
+ || (regState == REGISTRATION_STATE_NOT_REGISTERED
+ && suggestedAction == mImsRegistrationSuggestedAction
+ && imsRadioTech == mImsDeregistrationTech)) {
+ // Filter duplicate notification.
+ return;
+ }
+ }
+
+ if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
+ mDefaultPhone.mCi.updateImsRegistrationInfo(regState,
+ imsRadioTech, suggestedAction, 0, null);
+ } else if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
+ // This happens when radio tech is changed while in REGISTERED state.
+ if (mImsRegistrationCapabilities > 0) {
+ // Capability has been updated. Notify REGISTRATION_STATE_REGISTERED.
+ mDefaultPhone.mCi.updateImsRegistrationInfo(regState, imsRadioTech, 0,
+ mImsRegistrationCapabilities, null);
+ mImsRegistrationTech = imsRadioTech;
+ mNotifiedRegisteredState = true;
+ return;
+ }
+ }
+
+ mImsRegistrationState = regState;
+ mImsRegistrationTech = imsRadioTech;
+ mImsRegistrationSuggestedAction = suggestedAction;
+ if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
+ mImsDeregistrationTech = imsRadioTech;
+ } else {
+ mImsDeregistrationTech = REGISTRATION_TECH_NONE;
+ }
+ mImsRegistrationCapabilities = 0;
+ // REGISTRATION_STATE_REGISTERED will be notified when the capability is updated.
+ mNotifiedRegisteredState = false;
+ }
+
+ @Override
+ public void setTerminalBasedCallWaitingStatus(int state) {
+ mCT.setTerminalBasedCallWaitingStatus(state);
+ }
+
+ @Override
+ public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) {
+ mDefaultPhone.triggerEpsFallback(reason, response);
+ }
+
+ @Override
+ public void startImsTraffic(int token,
+ @MmTelFeature.ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) {
+ mDefaultPhone.startImsTraffic(token, trafficType,
+ accessNetworkType, trafficDirection, response);
+ }
+
+ @Override
+ public void stopImsTraffic(int token, Message response) {
+ mDefaultPhone.stopImsTraffic(token, response);
+ }
+
+ @Override
+ public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+ mDefaultPhone.registerForConnectionSetupFailure(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForConnectionSetupFailure(Handler h) {
+ mDefaultPhone.unregisterForConnectionSetupFailure(h);
+ }
+
+ @Override
+ public void triggerImsDeregistration(
+ @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
+ mCT.triggerImsDeregistration(reason);
+ }
+
+ @Override
+ public void updateImsCallStatus(List<ImsCallInfo> imsCallInfo, Message response) {
+ mDefaultPhone.updateImsCallStatus(imsCallInfo, response);
+ }
+
+ @Override
+ public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ mCT.triggerNotifyAnbr(mediaType, direction, bitsPerSecond);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index d19b8d3..8a1041d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -24,11 +24,13 @@
import android.sysprop.TelephonyProperties;
import android.telephony.Annotation.DataActivityType;
import android.telephony.CallQuality;
+import android.telephony.CallState;
import android.telephony.NetworkScanRequest;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,7 +43,6 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
-import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.telephony.Rlog;
@@ -139,6 +140,10 @@
mNotifier.notifyCallQualityChanged(this, callQuality, callNetworkType);
}
+ public void onMediaQualityStatusChanged(MediaQualityStatus status) {
+ mNotifier.notifyMediaQualityStatusChanged(this, status);
+ }
+
@Override
public ServiceState getServiceState() {
// FIXME: we may need to provide this when data connectivity is lost
@@ -174,11 +179,6 @@
}
@Override
- public PhoneConstants.DataState getDataConnectionState() {
- return PhoneConstants.DataState.DISCONNECTED;
- }
-
- @Override
public @DataActivityType int getDataActivityState() {
return TelephonyManager.DATA_ACTIVITY_NONE;
}
@@ -198,7 +198,43 @@
*/
public void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyPreciseCallStateChangedP();
+ AsyncResult ar = new AsyncResult(null, this, null);
+ mPreciseCallStateRegistrants.notifyRegistrants(ar);
+
+ notifyPreciseCallStateToNotifier();
+ }
+
+ public void notifyPreciseCallStateToNotifier() {
+ ImsPhoneCall ringingCall = (ImsPhoneCall) getRingingCall();
+ ImsPhoneCall foregroundCall = (ImsPhoneCall) getForegroundCall();
+ ImsPhoneCall backgroundCall = (ImsPhoneCall) getBackgroundCall();
+
+ if (ringingCall != null && foregroundCall != null && backgroundCall != null) {
+ //Array for IMS call session ID of RINGING/FOREGROUND/BACKGROUND call
+ String[] imsCallIds = new String[CallState.CALL_CLASSIFICATION_MAX];
+ //Array for IMS call service type of RINGING/FOREGROUND/BACKGROUND call
+ int[] imsCallServiceTypes = new int[CallState.CALL_CLASSIFICATION_MAX];
+ //Array for IMS call type of RINGING/FOREGROUND/BACKGROUND call
+ int[] imsCallTypes = new int[CallState.CALL_CLASSIFICATION_MAX];
+ imsCallIds[CallState.CALL_CLASSIFICATION_RINGING] =
+ ringingCall.getCallSessionId();
+ imsCallIds[CallState.CALL_CLASSIFICATION_FOREGROUND] =
+ foregroundCall.getCallSessionId();
+ imsCallIds[CallState.CALL_CLASSIFICATION_BACKGROUND] =
+ backgroundCall.getCallSessionId();
+ imsCallServiceTypes[CallState.CALL_CLASSIFICATION_RINGING] =
+ ringingCall.getServiceType();
+ imsCallServiceTypes[CallState.CALL_CLASSIFICATION_FOREGROUND] =
+ foregroundCall.getServiceType();
+ imsCallServiceTypes[CallState.CALL_CLASSIFICATION_BACKGROUND] =
+ backgroundCall.getServiceType();
+ imsCallTypes[CallState.CALL_CLASSIFICATION_RINGING] = ringingCall.getCallType();
+ imsCallTypes[CallState.CALL_CLASSIFICATION_FOREGROUND] =
+ foregroundCall.getCallType();
+ imsCallTypes[CallState.CALL_CLASSIFICATION_BACKGROUND] =
+ backgroundCall.getCallType();
+ mNotifier.notifyPreciseCallState(this, imsCallIds, imsCallServiceTypes, imsCallTypes);
+ }
}
public void notifyDisconnect(Connection cn) {
@@ -306,6 +342,11 @@
}
@Override
+ public int getImeiType() {
+ return Phone.IMEI_TYPE_UNKNOWN;
+ }
+
+ @Override
public String getEsn() {
Rlog.e(LOG_TAG, "[VoltePhone] getEsn() is a CDMA method");
return "0";
@@ -434,10 +475,6 @@
Message response) {
}
- public List<DataConnection> getCurrentDataConnectionList () {
- return null;
- }
-
@Override
public void updateServiceLocation() {
}
@@ -540,4 +577,14 @@
notifyPhoneStateChanged();
}
}
+
+ @Override
+ public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+ return getDefaultPhone().getTerminalBasedCallWaitingState(forCsOnly);
+ }
+
+ @Override
+ public void setTerminalBasedCallWaitingSupported(boolean supported) {
+ getDefaultPhone().setTerminalBasedCallWaitingSupported(supported);
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index 98cc441..eaa6ab6 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -19,6 +19,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.telephony.DisconnectCause;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsStreamMediaProfile;
import android.util.Log;
@@ -327,6 +329,41 @@
return (connection == null) ? null : connection.getImsCall();
}
+ /**
+ * Retrieves the {@link ImsCallSession#getCallId()} for the current {@link ImsPhoneCall}.
+ */
+ @VisibleForTesting
+ public String getCallSessionId() {
+ return ((getImsCall() == null) ? null : getImsCall().getSession()) == null
+ ? null : getImsCall().getSession().getCallId();
+ }
+
+ /**
+ * Retrieves the service type in {@link ImsCallProfile} for the current {@link ImsPhoneCall}.
+ */
+ @VisibleForTesting
+ public int getServiceType() {
+ if (getFirstConnection() == null) {
+ return ImsCallProfile.SERVICE_TYPE_NONE;
+ } else {
+ return getFirstConnection().isEmergencyCall()
+ ? ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
+ }
+ }
+
+ /**
+ * Retrieves the call type in {@link ImsCallProfile} for the current {@link ImsPhoneCall}.
+ */
+ @VisibleForTesting
+ public int getCallType() {
+ if (getImsCall() == null) {
+ return ImsCallProfile.CALL_TYPE_NONE;
+ } else {
+ return getImsCall().isVideoCall()
+ ? ImsCallProfile.CALL_TYPE_VT : ImsCallProfile.CALL_TYPE_VOICE;
+ }
+ }
+
/*package*/ static boolean isLocalTone(ImsCall imsCall) {
if ((imsCall == null) || (imsCall.getCallProfile() == null)
|| (imsCall.getCallProfile().mMediaProfile == null)) {
@@ -432,6 +469,15 @@
return mIsRingbackTonePlaying;
}
+ public void maybeClearRemotelyHeldStatus() {
+ for (Connection conn : getConnections()) {
+ ImsPhoneConnection c = (ImsPhoneConnection) conn;
+ if (c.isHeldByRemote()) {
+ c.setRemotelyUnheld();
+ }
+ }
+ }
+
private void takeOver(ImsPhoneCall that) {
copyConnectionFrom(that);
mState = that.mState;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 5b8aca5..c3ee0f6 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -16,12 +16,32 @@
package com.android.internal.telephony.imsphone;
+import static android.telephony.CarrierConfigManager.ImsVoice.ALERTING_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.BASIC_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.MIDCALL_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.PREALERTING_SRVCC_SUPPORT;
import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING;
+import static android.telephony.ims.ImsService.CAPABILITY_TERMINAL_BASED_CALL_WAITING;
+import static android.telephony.ims.feature.ConnectionFailureInfo.REASON_UNSPECIFIED;
+import static android.telephony.ims.feature.MmTelFeature.ImsTrafficSessionCallbackWrapper.INVALID_TOKEN;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_SMS;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE;
import static com.android.internal.telephony.Phone.CS_FALLBACK;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
@@ -56,6 +76,7 @@
import android.telecom.Connection.VideoProvider;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CallQuality;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
@@ -65,6 +86,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyLocalConnection;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.DataEnabledChangedReason;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
@@ -73,9 +95,16 @@
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.MediaQualityStatus;
+import android.telephony.ims.MediaThreshold;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.aidl.IImsTrafficSessionCallback;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
+import android.telephony.ims.feature.ConnectionFailureInfo;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -113,18 +142,20 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.SrvccConnection;
import com.android.internal.telephony.d2d.RtpTransport;
import com.android.internal.telephony.data.DataSettingsManager;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs;
import com.android.internal.telephony.metrics.CallQualityMetrics;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -132,8 +163,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -144,11 +177,14 @@
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* {@hide}
@@ -179,6 +215,20 @@
SharedPreferences getDefaultSharedPreferences(Context context);
}
+ private static class ImsTrafficSession {
+ private final @MmTelFeature.ImsTrafficType int mTrafficType;
+ private final @MmTelFeature.ImsTrafficDirection int mTrafficDirection;
+ private final @NonNull IImsTrafficSessionCallback mCallback;
+
+ ImsTrafficSession(@MmTelFeature.ImsTrafficType int trafficType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection,
+ @NonNull IImsTrafficSessionCallback callback) {
+ mTrafficType = trafficType;
+ mTrafficDirection = trafficDirection;
+ mCallback = callback;
+ }
+ }
+
private static final boolean DBG = true;
// When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -209,16 +259,18 @@
private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
private class MmTelFeatureListener extends MmTelFeature.Listener {
- private void processIncomingCall(IImsCallSession c, Bundle extras) {
+ private IImsCallSessionListener processIncomingCall(@NonNull IImsCallSession c,
+ @Nullable String callId, @Nullable Bundle extras) {
if (DBG) log("processIncomingCall: incoming call intent");
if (extras == null) extras = new Bundle();
- if (mImsManager == null) return;
+ if (mImsManager == null) return null;
try {
+ IImsCallSessionListener iimsCallSessionListener;
// Network initiated USSD will be treated by mImsUssdListener
boolean isUssd = extras.getBoolean(MmTelFeature.EXTRA_IS_USSD, false);
- // For compatibility purposes with older vendor implmentations.
+ // For compatibility purposes with older vendor implementations.
isUssd |= extras.getBoolean(ImsManager.EXTRA_USSD, false);
if (isUssd) {
if (DBG) log("processIncomingCall: USSD");
@@ -227,11 +279,14 @@
if (mUssdSession != null) {
mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
}
- return;
+ if (callId != null) mUssdSession.getCallSession().setCallId(callId);
+ iimsCallSessionListener = (IImsCallSessionListener) mUssdSession
+ .getCallSession().getIImsCallSessionListenerProxy();
+ return iimsCallSessionListener;
}
boolean isUnknown = extras.getBoolean(MmTelFeature.EXTRA_IS_UNKNOWN_CALL, false);
- // For compatibility purposes with older vendor implmentations.
+ // For compatibility purposes with older vendor implementations.
isUnknown |= extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
if (DBG) {
log("processIncomingCall: isUnknown = " + isUnknown
@@ -241,6 +296,9 @@
// Normal MT/Unknown call
ImsCall imsCall = mImsManager.takeCall(c, mImsCallListener);
+ if (callId != null) imsCall.getCallSession().setCallId(callId);
+ iimsCallSessionListener = (IImsCallSessionListener) imsCall
+ .getCallSession().getIImsCallSessionListenerProxy();
ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
ImsPhoneCallTracker.this,
(isUnknown ? mForegroundCall : mRingingCall), isUnknown);
@@ -263,13 +321,13 @@
if ((c != null) && (c.getCallProfile() != null)
&& (c.getCallProfile().getCallExtras() != null)
&& (c.getCallProfile().getCallExtras()
- .containsKey(ImsCallProfile.EXTRA_CALL_DISCONNECT_CAUSE))) {
+ .containsKey(ImsCallProfile.EXTRA_CALL_DISCONNECT_CAUSE))) {
String error = c.getCallProfile()
.getCallExtra(ImsCallProfile.EXTRA_CALL_DISCONNECT_CAUSE, null);
if (error != null) {
try {
int cause = getDisconnectCauseFromReasonInfo(
- new ImsReasonInfo(Integer.parseInt(error), 0, null),
+ new ImsReasonInfo(Integer.parseInt(error), 0, null),
conn.getState());
if (cause == DisconnectCause.INCOMING_AUTO_REJECTED) {
conn.setDisconnectCause(cause);
@@ -314,18 +372,20 @@
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
+ mImsCallInfoTracker.addImsCallStatus(conn);
+ return iimsCallSessionListener;
} catch (ImsException | RemoteException e) {
loge("processIncomingCall: exception " + e);
mOperationLocalLog.log("onIncomingCall: exception processing: " + e);
+ return null;
}
}
@Override
- public void onIncomingCall(IImsCallSession c, Bundle extras) {
- // we want to ensure we block this binder thread until incoming call setup completes
- // as to avoid race conditions where the ImsService tries to update the state of the
- // call before the listeners have been attached.
- executeAndWait(()-> processIncomingCall(c, extras));
+ @Nullable
+ public IImsCallSessionListener onIncomingCall(
+ @NonNull IImsCallSession c, @Nullable String callId, @Nullable Bundle extras) {
+ return executeAndWaitForReturn(()-> processIncomingCall(c, callId, extras));
}
@Override
@@ -340,6 +400,102 @@
}, mExecutor);
}
+ @Override
+ public void onAudioModeIsVoipChanged(int imsAudioHandler) {
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ ImsCall imsCall = null;
+ if (mForegroundCall.hasConnections()) {
+ imsCall = mForegroundCall.getImsCall();
+ } else if (mBackgroundCall.hasConnections()) {
+ imsCall = mBackgroundCall.getImsCall();
+ } else if (mRingingCall.hasConnections()) {
+ imsCall = mRingingCall.getImsCall();
+ } else if (mHandoverCall.hasConnections()) {
+ imsCall = mHandoverCall.getImsCall();
+ } else {
+ Rlog.e(LOG_TAG, "onAudioModeIsVoipChanged: no Call");
+ }
+
+ if (imsCall != null) {
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ conn.onAudioModeIsVoipChanged(imsAudioHandler);
+ }
+ } else {
+ Rlog.e(LOG_TAG, "onAudioModeIsVoipChanged: no ImsCall");
+ }
+ }, mExecutor);
+ }
+
+ @Override
+ public void onTriggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason) {
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (DBG) log("onTriggerEpsFallback reason=" + reason);
+ mPhone.triggerEpsFallback(reason, null);
+ }, mExecutor);
+ }
+
+ @Override
+ public void onStartImsTrafficSession(int token,
+ @MmTelFeature.ImsTrafficType int trafficType,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection,
+ IImsTrafficSessionCallback callback) {
+ registerImsTrafficSession(token, trafficType, trafficDirection, callback);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (DBG) {
+ log("onStartImsTrafficSession token=" + token + ", traffic=" + trafficType
+ + ", networkType=" + accessNetworkType
+ + ", direction=" + trafficDirection);
+ }
+ mPhone.startImsTraffic(token, trafficType, accessNetworkType, trafficDirection,
+ obtainMessage(EVENT_START_IMS_TRAFFIC_DONE, callback));
+ }, mExecutor);
+ }
+
+ @Override
+ public void onModifyImsTrafficSession(int token,
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
+ ImsTrafficSession session = getImsTrafficSession(token);
+ if (session == null) {
+ loge("onModifyImsTrafficSession unknown session, token=" + token);
+ return;
+ }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (DBG) {
+ log("onModifyImsTrafficSession token=" + token
+ + ", networkType=" + accessNetworkType);
+ }
+ mPhone.startImsTraffic(token, session.mTrafficType,
+ accessNetworkType, session.mTrafficDirection,
+ obtainMessage(EVENT_START_IMS_TRAFFIC_DONE, session.mCallback));
+ }, mExecutor);
+ }
+
+ @Override
+ public void onStopImsTrafficSession(int token) {
+ unregisterImsTrafficSession(token);
+ if (token == INVALID_TOKEN) return;
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (DBG) {
+ log("onStopImsTrafficSession token=" + token);
+ }
+ mPhone.stopImsTraffic(token, null);
+ }, mExecutor);
+ }
+
+ @Override
+ public void onMediaQualityStatusChanged(MediaQualityStatus status) {
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mPhone != null && mPhone.mDefaultPhone != null) {
+ if (DBG) log("onMediaQualityStatusChanged " + status);
+ mPhone.onMediaQualityStatusChanged(status);
+ } else {
+ loge("onMediaQualityStatusChanged: null phone");
+ }
+ }, mExecutor);
+ }
+
/**
* Schedule the given Runnable on mExecutor and block this thread until it finishes.
* @param r The Runnable to run.
@@ -352,6 +508,24 @@
logw("Binder - exception: " + e.getMessage());
}
}
+
+ /**
+ * Schedule the given Runnable on mExecutor and block this thread until it finishes.
+ * @param r The Runnable to run.
+ */
+ private <T> T executeAndWaitForReturn(Supplier<T> r) {
+
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsPhoneCallTracker : executeAndWaitForReturn exception: "
+ + e.getMessage());
+ return null;
+ }
+ }
}
/**
@@ -396,31 +570,35 @@
}
}
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ if (mPhone.getPhoneId() != slotIndex) {
+ log("onReceive: Skipping indication for other phoneId: " + slotIndex);
+ return;
+ }
+
+ PersistableBundle carrierConfig = getCarrierConfigBundle(subId);
+ mCarrierConfigForSubId = new Pair<>(subId, carrierConfig);
+ if (!mCurrentlyConnectedSubId.isEmpty()
+ && subId == mCurrentlyConnectedSubId.get()) {
+ log("onReceive: Applying carrier config for subId: " + subId);
+ updateCarrierConfiguration(subId, carrierConfig);
+ } else {
+ // cache the latest config update until ImsService connects for this subId.
+ // Once it has connected, startListeningForCalls will apply the config.
+ log("onReceive: caching carrier config until ImsService connects for "
+ + "subId: " + subId);
+ }
+ }
+ };
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
- SubscriptionManager.INVALID_PHONE_INDEX);
- if (mPhone.getPhoneId() != phoneId) {
- log("onReceive: Skipping indication for other phoneId: " + phoneId);
- return;
- }
- PersistableBundle carrierConfig = getCarrierConfigBundle(subId);
- mCarrierConfigForSubId = new Pair<>(subId, carrierConfig);
- if (!mCurrentlyConnectedSubId.isEmpty()
- && subId == mCurrentlyConnectedSubId.get()) {
- log("onReceive: Applying carrier config for subId: " + subId);
- updateCarrierConfiguration(subId, carrierConfig);
- } else {
- // cache the latest config update until ImsService connects for this subId.
- // Once it has connected, startListeningForCalls will apply the config.
- log("onReceive: caching carrier config until ImsService connects for subId: "
- + subId);
- }
- } else if (TelecomManager.ACTION_DEFAULT_DIALER_CHANGED.equals(intent.getAction())) {
+ if (TelecomManager.ACTION_DEFAULT_DIALER_CHANGED.equals(intent.getAction())) {
mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
}
@@ -483,6 +661,9 @@
private static final int EVENT_ANSWER_WAITING_CALL = 30;
private static final int EVENT_RESUME_NOW_FOREGROUND_CALL = 31;
private static final int EVENT_REDIAL_WITHOUT_RTT = 32;
+ private static final int EVENT_START_IMS_TRAFFIC_DONE = 33;
+ private static final int EVENT_CONNECTION_SETUP_FAILURE = 34;
+ private static final int EVENT_NEW_ACTIVE_CALL_STARTED = 35;
private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
@@ -554,6 +735,13 @@
}
}
+ private class SrvccStartedCallback extends ISrvccStartedCallback.Stub {
+ @Override
+ public void onSrvccCallNotified(List<SrvccCall> profiles) {
+ handleSrvccConnectionInfo(profiles);
+ }
+ }
+
private volatile NetworkStats mVtDataUsageSnapshot = null;
private volatile NetworkStats mVtDataUsageUidSnapshot = null;
private final VtDataUsageProvider mVtDataUsageProvider = new VtDataUsageProvider();
@@ -608,14 +796,22 @@
private boolean mSupportCepOnPeer = true;
private boolean mSupportD2DUsingRtp = false;
private boolean mSupportSdpForRtpHeaderExtensions = false;
+ private int mThresholdRtpPacketLoss;
+ private int mThresholdRtpJitter;
+ private long mThresholdRtpInactivityTime;
+ private final List<Integer> mSrvccTypeSupported = new ArrayList<>();
+ private final SrvccStartedCallback mSrvccStartedCallback = new SrvccStartedCallback();
// Tracks the state of our background/foreground calls while a call hold/swap operation is
// in progress. Values listed above.
private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE;
+ private MediaThreshold mMediaThreshold;
private String mLastDialString = null;
private ImsDialArgs mLastDialArgs = null;
private Executor mExecutor = Runnable::run;
+ private final ImsCallInfoTracker mImsCallInfoTracker;
+
/**
* Listeners to changes in the phone state. Intended for use by other interested IMS components
* without the need to register a full blown {@link android.telephony.PhoneStateListener}.
@@ -965,8 +1161,7 @@
}
};
- // TODO: make @NonNull after removing DataEnabledSettings
- private DataSettingsManager.DataSettingsManagerCallback mSettingsCallback;
+ private @NonNull DataSettingsManager.DataSettingsManagerCallback mSettingsCallback;
/**
* Allows the FeatureConnector used to be swapped for easier testing.
@@ -988,6 +1183,9 @@
// Used for important operational related events for logging.
private final LocalLog mOperationLocalLog = new LocalLog(64);
+ private final ConcurrentHashMap<Integer, ImsTrafficSession> mImsTrafficSessions =
+ new ConcurrentHashMap<>();
+
/**
* Container to ease passing around a tuple of two objects. This object provides a sensible
* implementation of equals(), returning true/false using equals() for one object (Integer)
@@ -1036,6 +1234,7 @@
return (first == null ? 0 : first.hashCode());
}
}
+
//***** Events
@@ -1055,45 +1254,30 @@
mMetrics = TelephonyMetrics.getInstance();
IntentFilter intentfilter = new IntentFilter();
- intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intentfilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
mPhone.getContext().registerReceiver(mReceiver, intentfilter);
- updateCarrierConfiguration(mPhone.getSubId(), getCarrierConfigBundle(mPhone.getSubId()));
- if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
- mSettingsCallback = new DataSettingsManager.DataSettingsManagerCallback(this::post) {
- @Override
- public void onDataEnabledChanged(boolean enabled,
- @TelephonyManager.DataEnabledChangedReason int reason,
- @NonNull String callingPackage) {
- int internalReason;
- switch (reason) {
- case TelephonyManager.DATA_ENABLED_REASON_USER:
- internalReason = DataEnabledSettings.REASON_USER_DATA_ENABLED;
- break;
- case TelephonyManager.DATA_ENABLED_REASON_POLICY:
- internalReason = DataEnabledSettings.REASON_POLICY_DATA_ENABLED;
- break;
- case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
- internalReason = DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER;
- break;
- case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
- internalReason = DataEnabledSettings.REASON_THERMAL_DATA_ENABLED;
- break;
- case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
- internalReason = DataEnabledSettings.REASON_OVERRIDE_RULE_CHANGED;
- break;
- default:
- internalReason = DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED;
- }
- ImsPhoneCallTracker.this.onDataEnabledChanged(enabled, internalReason);
- }};
- mPhone.getDefaultPhone().getDataSettingsManager().registerCallback(mSettingsCallback);
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ // Callback of listener directly access global states which are limited on main thread.
+ // The callback can only be executed on main thread.
+ if (ccm != null) {
+ ccm.registerCarrierConfigChangeListener(
+ mPhone.getContext().getMainExecutor(), mCarrierConfigChangeListener);
+ updateCarrierConfiguration(
+ mPhone.getSubId(), getCarrierConfigBundle(mPhone.getSubId()));
} else {
- mPhone.getDefaultPhone().getDataEnabledSettings().registerForDataEnabledChanged(
- this, EVENT_DATA_ENABLED_CHANGED, null);
+ loge("CarrierConfigManager is not available.");
}
+ mSettingsCallback = new DataSettingsManager.DataSettingsManagerCallback(this::post) {
+ @Override
+ public void onDataEnabledChanged(boolean enabled,
+ @DataEnabledChangedReason int reason,
+ @NonNull String callingPackage) {
+ ImsPhoneCallTracker.this.onDataEnabledChanged(enabled, reason);
+ }};
+ mPhone.getDefaultPhone().getDataSettingsManager().registerCallback(mSettingsCallback);
+
final TelecomManager telecomManager =
(TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
mDefaultDialerUid.set(
@@ -1122,10 +1306,15 @@
postDelayed(mConnectorRunnable, CONNECTOR_RETRY_DELAY_MS);
}
stopListeningForCalls();
+ stopAllImsTrafficTypes();
}
}, executor);
// It can take some time for ITelephony to get published, so defer connecting.
post(mConnectorRunnable);
+
+ mImsCallInfoTracker = new ImsCallInfoTracker(phone);
+
+ mPhone.registerForConnectionSetupFailure(this, EVENT_CONNECTION_SETUP_FAILURE, null);
}
/**
@@ -1202,6 +1391,8 @@
// For compatibility with apps that still use deprecated intent
sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP);
mCurrentlyConnectedSubId = Optional.of(subId);
+
+ initializeTerminalBasedCallWaiting();
}
/**
@@ -1262,6 +1453,7 @@
mUtInterface = null;
}
mCurrentlyConnectedSubId = Optional.empty();
+ mMediaThreshold = null;
resetImsCapabilities();
hangupAllOrphanedConnections(DisconnectCause.LOST_SIGNAL);
// For compatibility with apps that still use deprecated intent
@@ -1302,6 +1494,7 @@
// above. Remove all references to it.
mPendingMO = null;
updatePhoneState();
+ mImsCallInfoTracker.clearAllOrphanedConnections();
}
/**
@@ -1335,17 +1528,19 @@
clearDisconnected();
mPhone.getContext().unregisterReceiver(mReceiver);
- if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
- mPhone.getDefaultPhone().getDataSettingsManager().unregisterCallback(mSettingsCallback);
- } else {
- mPhone.getDefaultPhone().getDataEnabledSettings().unregisterForDataEnabledChanged(this);
+ CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ if (ccm != null && mCarrierConfigChangeListener != null) {
+ ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
}
+ mPhone.getDefaultPhone().getDataSettingsManager().unregisterCallback(mSettingsCallback);
mImsManagerConnector.disconnect();
final NetworkStatsManager statsManager =
(NetworkStatsManager) mPhone.getContext().getSystemService(
Context.NETWORK_STATS_SERVICE);
statsManager.unregisterNetworkStatsProvider(mVtDataUsageProvider);
+
+ mPhone.unregisterForConnectionSetupFailure(this);
}
@Override
@@ -1537,7 +1732,7 @@
mLastDialString = dialString;
mLastDialArgs = dialArgs;
mPendingMO = new ImsPhoneConnection(mPhone, dialString, this, mForegroundCall,
- isEmergencyNumber, isWpsCall);
+ isEmergencyNumber, isWpsCall, dialArgs);
mOperationLocalLog.log("dial requested. connId=" + System.identityHashCode(mPendingMO));
if (isEmergencyNumber && dialArgs != null && dialArgs.intentExtras != null) {
Rlog.i(LOG_TAG, "dial ims emergency dialer: " + dialArgs.intentExtras.getBoolean(
@@ -1554,9 +1749,22 @@
addConnection(mPendingMO);
if (!holdBeforeDial) {
+ // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
dialInternal(mPendingMO, clirMode, videoState, dialArgs.retryCallFailCause,
dialArgs.retryCallFailNetworkType, dialArgs.intentExtras);
+ } else if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ final int finalClirMode = clirMode;
+ final int finalVideoState = videoState;
+ Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ dialInternal(mPendingMO, finalClirMode, finalVideoState,
+ dialArgs.retryCallFailCause, dialArgs.retryCallFailNetworkType,
+ dialArgs.intentExtras);
+ }
+ };
+ EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete);
} else {
try {
getEcbmInterface().exitEmergencyCallbackMode();
@@ -1648,7 +1856,9 @@
// Check for changes due to carrier config.
maybeConfigureRtpHeaderExtensions();
- if (!SubscriptionController.getInstance().isActiveSubId(subId)) {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null || !subInfo.isActive()) {
loge("updateCarrierConfiguration: skipping notification to ImsService, non"
+ "active subId = " + subId);
return;
@@ -1678,6 +1888,8 @@
logi("updateCarrierConfiguration: Updating ImsService configs.");
mCarrierConfigLoadedForSubscription = true;
updateImsServiceConfig();
+ updateMediaThreshold(
+ mThresholdRtpPacketLoss, mThresholdRtpJitter, mThresholdRtpInactivityTime);
}
/**
@@ -1726,6 +1938,13 @@
mSupportSdpForRtpHeaderExtensions = carrierConfig.getBoolean(
CarrierConfigManager
.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
+ mThresholdRtpPacketLoss = carrierConfig.getInt(
+ CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT);
+ mThresholdRtpInactivityTime = carrierConfig.getLong(
+ CarrierConfigManager.ImsVoice
+ .KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG);
+ mThresholdRtpJitter = carrierConfig.getInt(
+ CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT);
if (mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_allow_ussd_over_ims)) {
@@ -1772,6 +1991,41 @@
} else {
log("No carrier ImsReasonInfo mappings defined.");
}
+
+ mSrvccTypeSupported.clear();
+ int[] srvccType =
+ carrierConfig.getIntArray(CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY);
+ if (srvccType != null && srvccType.length > 0) {
+ mSrvccTypeSupported.addAll(
+ Arrays.stream(srvccType).boxed().collect(Collectors.toList()));
+ }
+ }
+
+ private void updateMediaThreshold(
+ int thresholdPacketLoss, int thresholdJitter, long thresholdInactivityTime) {
+ if (!MediaThreshold.isValidRtpInactivityTimeMillis(thresholdInactivityTime)
+ && !MediaThreshold.isValidJitterMillis(thresholdJitter)
+ && !MediaThreshold.isValidRtpPacketLossRate(thresholdPacketLoss)) {
+ logi("There is no valid RTP threshold value");
+ return;
+ }
+ int[] thPacketLosses = {thresholdPacketLoss};
+ long[] thInactivityTimesMillis = {thresholdInactivityTime};
+ int[] thJitters = {thresholdJitter};
+ MediaThreshold threshold = new MediaThreshold.Builder()
+ .setThresholdsRtpPacketLossRate(thPacketLosses)
+ .setThresholdsRtpInactivityTimeMillis(thInactivityTimesMillis)
+ .setThresholdsRtpJitterMillis(thJitters).build();
+ if (mMediaThreshold == null || !mMediaThreshold.equals(threshold)) {
+ logi("setMediaThreshold :" + threshold);
+ try {
+ mImsManager.setMediaThreshold(MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO,
+ threshold);
+ mMediaThreshold = threshold;
+ } catch (ImsException e) {
+ loge("setMediaThreshold Failed: " + e);
+ }
+ }
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1874,6 +2128,11 @@
conn.setPulledDialogId(dialogId);
}
+ if (intentExtras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE)) {
+ logi("dialInternal containing EXTRA_CALL_RAT_TYPE, "
+ + intentExtras.getString(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
+ }
+
// Pack the OEM-specific call extras.
profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras);
@@ -1895,6 +2154,7 @@
setVideoCallProvider(conn, imsCall);
conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
conn.setAllowHoldingVideoCall(mAllowHoldingVideoCall);
+ mImsCallInfoTracker.addImsCallStatus(conn);
} catch (ImsException e) {
loge("dialInternal : " + e);
mOperationLocalLog.log("dialInternal exception: " + e);
@@ -2616,6 +2876,7 @@
+ System.identityHashCode(conn));
call.onHangupLocal();
+ mImsCallInfoTracker.updateImsCallStatus(conn);
try {
if (imsCall != null) {
@@ -2735,6 +2996,9 @@
mPendingMO.finalize();
mPendingMO = null;
}
+ // Ensure aggregate state for this tracker is also updated to reflect the new state.
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2813,20 +3077,38 @@
conn.maybeChangeRingbackState();
maybeSetVideoCallProvider(conn, imsCall);
+ // IMS call profile might be changed while call state is maintained. In this case, it's
+ // required to notify to CallAttributesListener.
+ // Since call state is not changed, TelephonyRegistry will not notify to
+ // PreciseCallStateListener.
+ mPhone.notifyPreciseCallStateToNotifier();
return;
}
// Do not log operations that do not change the state
mOperationLocalLog.log("processCallStateChange: state=" + state + " cause=" + cause
+ " connId=" + System.identityHashCode(conn));
-
+ boolean noActiveCall = false;
+ if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE
+ && mBackgroundCall.getState() != ImsPhoneCall.State.ACTIVE) {
+ noActiveCall = true;
+ }
changed = conn.update(imsCall, state);
+ if (noActiveCall && changed && state == ImsPhoneCall.State.ACTIVE) {
+ sendMessage(obtainMessage(EVENT_NEW_ACTIVE_CALL_STARTED));
+ }
if (state == ImsPhoneCall.State.DISCONNECTED) {
changed = conn.onDisconnect(cause) || changed;
//detach the disconnected connections
conn.getCall().detach(conn);
removeConnection(conn);
+ // If the call being disconnected was the pending MO call we should clear it.
+ if (mPendingMO == conn) {
+ mPendingMO.finalize();
+ mPendingMO = null;
+ }
+
// remove conference participants from the cached list when call is disconnected
List<ConferenceParticipant> cpList = imsCall.getConferenceParticipants();
if (cpList != null) {
@@ -2839,9 +3121,12 @@
}
}
}
+ } else {
+ mPhone.getVoiceCallSessionStats().onCallStateChanged(conn.getCall());
}
if (changed) {
+ mImsCallInfoTracker.updateImsCallStatus(conn);
if (conn.getCall() == mHandoverCall) return;
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
@@ -2850,7 +3135,7 @@
private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) {
android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider();
- ImsCallSession callSession = imsCall.getCallSession();
+ ImsCallSession callSession = imsCall.getCallSession();
if (connVideoProvider != null || callSession == null
|| callSession.getVideoCallProvider() == null) {
return;
@@ -2873,7 +3158,7 @@
@VisibleForTesting
public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) {
if (message != null) {
- message = message.toLowerCase();
+ message = message.toLowerCase(Locale.ROOT);
}
mImsReasonCodeMap.put(new ImsReasonInfoKeyPair(fromCode, message), toCode);
}
@@ -2894,7 +3179,7 @@
if (reason == null) {
reason = "";
} else {
- reason = reason.toLowerCase();
+ reason = reason.toLowerCase(Locale.ROOT);
}
log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
+ reason);
@@ -3104,6 +3389,13 @@
break;
case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
+ // Auto-missed/rejected calls can sometimes use this reason cause, but if we see it
+ // for outgoing calls it is just a server error.
+ if (callState == Call.State.DIALING || callState == Call.State.ALERTING) {
+ return DisconnectCause.SERVER_ERROR;
+ } else {
+ return DisconnectCause.INCOMING_AUTO_REJECTED;
+ }
case ImsReasonInfo.CODE_REJECT_CALL_ON_OTHER_SUB:
case ImsReasonInfo.CODE_REJECT_ONGOING_E911_CALL:
case ImsReasonInfo.CODE_REJECT_ONGOING_CALL_SETUP:
@@ -3220,7 +3512,6 @@
DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
mMetrics.writeImsCallState(mPhone.getPhoneId(),
imsCall.getCallSession(), conn.getCall().mState);
- mPhone.getVoiceCallSessionStats().onCallStateChanged(conn.getCall());
}
}
@@ -3249,6 +3540,36 @@
}
}
+ mPhone.getVoiceCallSessionStats()
+ .onImsCallStartFailed(
+ findConnection(imsCall),
+ new ImsReasonInfo(
+ maybeRemapReasonCode(reasonInfo),
+ reasonInfo.mExtraCode,
+ reasonInfo.mExtraMessage));
+
+ if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ ImsPhoneConnection conn = findConnection(imsCall);
+ // Since onCallInitiating and onCallProgressing reset mPendingMO,
+ // we can't depend on mPendingMO.
+ if ((reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
+ || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED
+ || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)
+ && conn != null) {
+ logi("onCallStartFailed eccCategory=" + eccCategory);
+ if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
+ || reasonInfo.getExtraCode()
+ == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY) {
+ conn.setNonDetectableEmergencyCallInfo(eccCategory);
+ }
+ conn.setImsReasonInfo(reasonInfo);
+ sendCallStartFailedDisconnect(imsCall, reasonInfo);
+ mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(),
+ imsCall.getCallSession(), reasonInfo);
+ return;
+ }
+ }
+
if (mPendingMO != null) {
// To initiate dialing circuit-switched call
if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
@@ -3256,6 +3577,7 @@
&& isForegroundHigherPriority()) {
mForegroundCall.detach(mPendingMO);
removeConnection(mPendingMO);
+ mImsCallInfoTracker.updateImsCallStatus(mPendingMO);
mPendingMO.finalize();
mPendingMO = null;
// if we need to perform CSFB of call, hang up any background call
@@ -3287,6 +3609,7 @@
if (conn != null) {
mForegroundCall.detach(conn);
removeConnection(conn);
+ mImsCallInfoTracker.updateImsCallStatus(conn);
}
updatePhoneState();
mPhone.initiateSilentRedial(reasonInfo.getExtraCode() ==
@@ -3376,29 +3699,34 @@
cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY;
}
- String callId = imsCall.getSession().getCallId();
+ // Ensure the background call is correctly marked as MERGE_COMPLETE before it is
+ // disconnected as part of the IMS merge conference process:
+ if (cause == DisconnectCause.IMS_MERGED_SUCCESSFULLY && conn != null) {
+ conn.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
+ }
+
EmergencyNumberTracker emergencyNumberTracker = null;
EmergencyNumber num = null;
- if (conn != null) {
+ if (conn != null && imsCall.getSession() != null) {
+ String callId = imsCall.getSession().getCallId();
emergencyNumberTracker = conn.getEmergencyNumberTracker();
num = conn.getEmergencyNumberInfo();
- }
-
- mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
+ mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
reasonInfo, mCallQualityMetrics.get(callId), num,
getNetworkCountryIso(), emergencyNumberTracker != null
- ? emergencyNumberTracker.getEmergencyNumberDbVersion()
- : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION);
- mPhone.getVoiceCallSessionStats().onImsCallTerminated(conn, new ImsReasonInfo(
+ ? emergencyNumberTracker.getEmergencyNumberDbVersion()
+ : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION);
+ mPhone.getVoiceCallSessionStats().onImsCallTerminated(conn, new ImsReasonInfo(
maybeRemapReasonCode(reasonInfo),
reasonInfo.mExtraCode, reasonInfo.mExtraMessage));
- // Remove info for the callId from the current calls and add it to the history
- CallQualityMetrics lastCallMetrics = mCallQualityMetrics.remove(callId);
- if (lastCallMetrics != null) {
- mCallQualityMetricsHistory.add(lastCallMetrics);
+ // Remove info for the callId from the current calls and add it to the history
+ CallQualityMetrics lastCallMetrics = mCallQualityMetrics.remove(callId);
+ if (lastCallMetrics != null) {
+ mCallQualityMetricsHistory.add(lastCallMetrics);
+ }
+ pruneCallQualityMetricsHistory();
}
- pruneCallQualityMetricsHistory();
mPhone.notifyImsReason(reasonInfo);
if (conn != null) {
@@ -3407,6 +3735,18 @@
}
if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
+ && DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+ if (conn != null) {
+ int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+ if (imsCall != null && imsCall.getCallProfile() != null) {
+ eccCategory = imsCall.getCallProfile().getEmergencyServiceCategories();
+ logi("onCallTerminated eccCategory=" + eccCategory);
+ }
+ conn.setNonDetectableEmergencyCallInfo(eccCategory);
+ }
+ processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
+ return;
+ } else if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
&& mAutoRetryFailedWifiEmergencyCall) {
Pair<ImsCall, ImsReasonInfo> callInfo = new Pair<>(imsCall, reasonInfo);
mPhone.getDefaultPhone().mCi.registerForOn(ImsPhoneCallTracker.this,
@@ -3706,7 +4046,8 @@
mPhone.stopOnHoldTone(conn);
mOnHoldToneStarted = false;
}
- conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
+ conn.setRemotelyUnheld();
+ mImsCallInfoTracker.updateImsCallStatus(conn, false, true);
}
boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
@@ -3875,15 +4216,11 @@
@Override
public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
ImsReasonInfo reasonInfo) {
- // Check with the DCTracker to see if data is enabled; there may be a case when
+ // Check if data is enabled; there may be a case when
// ImsPhoneCallTracker isn't being informed of the right data enabled state via its
// registration, so we'll refresh now.
boolean isDataEnabled;
- if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
- isDataEnabled = mPhone.getDefaultPhone().getDataSettingsManager().isDataEnabled();
- } else {
- isDataEnabled = mPhone.getDefaultPhone().getDataEnabledSettings().isDataEnabled();
- }
+ isDataEnabled = mPhone.getDefaultPhone().getDataSettingsManager().isDataEnabled();
if (DBG) {
log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech="
@@ -4120,6 +4457,28 @@
conn.receivedRtpHeaderExtensions(rtpHeaderExtensionData);
}
}
+
+ /**
+ * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114.
+ * This API triggers radio to send ANBRQ message to the access network to query the desired
+ * bitrate.
+ *
+ * @param imsCall The ImsCall the data was received on.
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+ * RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+ * (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+ */
+ @Override
+ public void onCallSessionSendAnbrQuery(ImsCall imsCall, int mediaType, int direction,
+ int bitsPerSecond) {
+ if (DBG) {
+ log("onCallSessionSendAnbrQuery mediaType=" + mediaType + ", direction="
+ + direction + ", bitPerSecond=" + bitsPerSecond);
+ }
+ handleSendAnbrQuery(mediaType, direction, bitsPerSecond);
+ }
};
/**
@@ -4257,7 +4616,8 @@
configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
if (mPhone != null && mPhone.getContext() != null) {
- mPhone.getContext().sendBroadcast(configChangedIntent);
+ mPhone.getContext().sendBroadcast(
+ configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
}
}
};
@@ -4337,21 +4697,58 @@
* Notify of a change to SRVCC state
* @param state the new SRVCC state.
*/
- public void notifySrvccState(Call.SrvccState state) {
+ public void notifySrvccState(int state) {
if (DBG) log("notifySrvccState state=" + state);
- mSrvccState = state;
+ if (mImsManager != null) {
+ try {
+ if (state == TelephonyManager.SRVCC_STATE_HANDOVER_STARTED) {
+ mImsManager.notifySrvccStarted(mSrvccStartedCallback);
+ } else if (state == TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED) {
+ mImsManager.notifySrvccCompleted();
+ } else if (state == TelephonyManager.SRVCC_STATE_HANDOVER_FAILED) {
+ mImsManager.notifySrvccFailed();
+ } else if (state == TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED) {
+ mImsManager.notifySrvccCanceled();
+ }
+ } catch (ImsException e) {
+ loge("notifySrvccState : exception " + e);
+ }
+ }
- if (mSrvccState == Call.SrvccState.COMPLETED) {
- // If the dialing call had ringback, ensure it stops now, otherwise it'll keep playing
- // afer the SRVCC completes.
- mForegroundCall.maybeStopRingback();
+ switch(state) {
+ case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
+ mSrvccState = Call.SrvccState.STARTED;
+ break;
- resetState();
- transferHandoverConnections(mForegroundCall);
- transferHandoverConnections(mBackgroundCall);
- transferHandoverConnections(mRingingCall);
- updatePhoneState();
+ case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
+ mSrvccState = Call.SrvccState.COMPLETED;
+
+ // If the dialing call had ringback, ensure it stops now,
+ // otherwise it'll keep playing afer the SRVCC completes.
+ mForegroundCall.maybeStopRingback();
+ mForegroundCall.maybeClearRemotelyHeldStatus();
+ mBackgroundCall.maybeClearRemotelyHeldStatus();
+
+ resetState();
+ transferHandoverConnections(mForegroundCall);
+ transferHandoverConnections(mBackgroundCall);
+ transferHandoverConnections(mRingingCall);
+ updatePhoneState();
+ mImsCallInfoTracker.notifySrvccCompleted();
+ break;
+
+ case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
+ mSrvccState = Call.SrvccState.FAILED;
+ break;
+
+ case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
+ mSrvccState = Call.SrvccState.CANCELED;
+ break;
+
+ default:
+ //ignore invalid state
+ return;
}
}
@@ -4482,6 +4879,7 @@
try {
ImsFeature.Capabilities capabilities = (ImsFeature.Capabilities) args.arg1;
handleFeatureCapabilityChanged(capabilities);
+ updateImsRegistrationInfo();
} finally {
args.recycle();
}
@@ -4564,6 +4962,55 @@
}
break;
}
+
+ case EVENT_START_IMS_TRAFFIC_DONE: // fallthrough
+ case EVENT_CONNECTION_SETUP_FAILURE: {
+ ar = (AsyncResult) msg.obj;
+ // Not-null with EVENT_START_IMS_TRAFFIC_DONE
+ IImsTrafficSessionCallback callback = (IImsTrafficSessionCallback) ar.userObj;
+ try {
+ if (ar.exception == null) {
+ Object[] result = (Object[]) ar.result;
+ if (result != null && result.length > 1) {
+ if (callback == null) {
+ //EVENT_CONNECTION_SETUP_FAILURE
+ ImsTrafficSession session =
+ getImsTrafficSession((int) result[0]);
+ if (session != null) callback = session.mCallback;
+ }
+ if (callback == null) break;
+
+ if (result[1] == null) callback.onReady();
+ else callback.onError((ConnectionFailureInfo) result[1]);
+ break;
+ }
+ }
+ if (callback != null) {
+ callback.onError(new ConnectionFailureInfo(REASON_UNSPECIFIED, 0, -1));
+ }
+ } catch (RemoteException e) {
+ Rlog.e(LOG_TAG, "Exception: " + e);
+ }
+ break;
+ }
+
+ case EVENT_NEW_ACTIVE_CALL_STARTED: {
+ try {
+ MediaQualityStatus status = mImsManager
+ .queryMediaQualityStatus(MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO);
+ if (status != null) {
+ if (mPhone != null && mPhone.mDefaultPhone != null) {
+ if (DBG) log("notify media quality status: " + status);
+ mPhone.onMediaQualityStatusChanged(status);
+ } else {
+ loge("onMediaQualityStatusChanged: null phone");
+ }
+ }
+ } catch (ImsException e) {
+ Rlog.e(LOG_TAG, "Exception in queryMediaQualityStatus: " + e);
+ }
+ break;
+ }
}
}
@@ -4738,6 +5185,7 @@
+ mSupportSdpForRtpHeaderExtensions);
}
}
+ pw.println(" mSrvccTypeSupported=" + mSrvccTypeSupported);
pw.println(" Event Log:");
pw.increaseIndent();
mOperationLocalLog.dump(pw);
@@ -5062,10 +5510,9 @@
/**
* Handler of data enabled changed event
* @param enabled True if data is enabled, otherwise disabled.
- * @param reason Reason for data enabled/disabled. See {@link DataEnabledChangedReason}.
+ * @param reason Reason for data enabled/disabled.
*/
private void onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason) {
- // TODO: TelephonyManager.DataEnabledChangedReason instead once DataEnabledSettings is gone
log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
mIsDataEnabled = enabled;
@@ -5085,9 +5532,9 @@
}
int reasonCode;
- if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
+ if (reason == TelephonyManager.DATA_ENABLED_REASON_POLICY) {
reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
- } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
+ } else if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
} else {
// Unexpected code, default to data disabled.
@@ -5100,10 +5547,10 @@
// Handle video state changes required as a result of data being enabled/disabled.
handleDataEnabledChange(enabled, reasonCode);
- // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
+ // We do not want to update the ImsConfig for REASON_UNKNOWN, since it can happen before
// the carrier config has loaded and will deregister IMS.
if (!mShouldUpdateImsConfigOnDisconnect
- && reason != DataEnabledSettings.REASON_REGISTERED
+ && reason != TelephonyManager.DATA_ENABLED_REASON_UNKNOWN
&& mCarrierConfigLoadedForSubscription) {
// This will call into updateVideoCallFeatureValue and eventually all clients will be
// asynchronously notified that the availability of VT over LTE has changed.
@@ -5382,7 +5829,8 @@
mOnHoldToneStarted = true;
mOnHoldToneId = System.identityHashCode(conn);
}
- conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+ conn.setRemotelyHeld();
+ mImsCallInfoTracker.updateImsCallStatus(conn, true, false);
boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_useVideoPauseWorkaround);
@@ -5510,4 +5958,256 @@
}
return false;
}
+
+ private void initializeTerminalBasedCallWaiting() {
+ boolean capable = false;
+ if (mImsManager != null) {
+ try {
+ capable = mImsManager.isCapable(CAPABILITY_TERMINAL_BASED_CALL_WAITING);
+ } catch (ImsException e) {
+ loge("initializeTerminalBasedCallWaiting : exception " + e);
+ }
+ }
+ logi("initializeTerminalBasedCallWaiting capable=" + capable);
+ mPhone.setTerminalBasedCallWaitingSupported(capable);
+
+ if (capable) {
+ setTerminalBasedCallWaitingStatus(mPhone.getTerminalBasedCallWaitingState(false));
+ }
+ }
+
+ /**
+ * Notifies the change of the user setting of the terminal-based call waiting service
+ * to IMS service.
+ */
+ public void setTerminalBasedCallWaitingStatus(int state) {
+ if (state == TERMINAL_BASED_NOT_SUPPORTED) return;
+ if (mImsManager != null) {
+ try {
+ log("setTerminalBasedCallWaitingStatus state=" + state);
+ mImsManager.setTerminalBasedCallWaitingStatus(
+ state == TERMINAL_BASED_ACTIVATED);
+ } catch (ImsException e) {
+ loge("setTerminalBasedCallWaitingStatus : exception " + e);
+ }
+ }
+ }
+
+ /** Send the list of SrvccConnection instances to the radio */
+ public void handleSrvccConnectionInfo(List<SrvccCall> profileList) {
+ mPhone.getDefaultPhone().mCi.setSrvccCallInfo(
+ convertToSrvccConnectionInfo(profileList), null);
+ }
+
+ /** Converts SrvccCall to SrvccConnection. */
+ @VisibleForTesting
+ public SrvccConnection[] convertToSrvccConnectionInfo(List<SrvccCall> profileList) {
+ if (mSrvccTypeSupported.isEmpty() || profileList == null || profileList.isEmpty()) {
+ loge("convertToSrvccConnectionInfo empty list");
+ return null;
+ }
+
+ List<SrvccConnection> connList = new ArrayList<>();
+ for (SrvccCall profile : profileList) {
+ if (isCallProfileSupported(profile)) {
+ addConnection(connList,
+ profile, findConnection(profile.getCallId()));
+ } else {
+ logi("convertToSrvccConnectionInfo not supported"
+ + " state=" + profile.getPreciseCallState());
+ }
+ }
+
+ logi("convertToSrvccConnectionInfo size=" + connList.size());
+ if (connList.isEmpty()) return null;
+ return connList.toArray(new SrvccConnection[0]);
+ }
+
+ /** Send the mediaType, direction, bitrate for ANBR Query to the radio */
+ public void handleSendAnbrQuery(int mediaType, int direction, int bitsPerSecond) {
+ if (DBG) log("handleSendAnbrQuery - mediaType=" + mediaType);
+ mPhone.getDefaultPhone().mCi.sendAnbrQuery(mediaType, direction, bitsPerSecond, null);
+ }
+
+
+ /**
+ * Notifies the recommended bit rate for the indicated logical channel and direction.
+ *
+ * @param mediaType MediaType is used to identify media stream such as audio or video.
+ * @param direction Direction of this packet stream (e.g. uplink or downlink).
+ * @param bitsPerSecond The recommended bit rate for the UE for a specific logical channel and
+ * a specific direction by NW.
+ */
+ public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+ ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
+
+ if (activeCall != null) {
+ if (DBG) log("triggerNotifyAnbr - mediaType=" + mediaType);
+ activeCall.callSessionNotifyAnbr(mediaType, direction, bitsPerSecond);
+ }
+ }
+
+ private boolean isCallProfileSupported(SrvccCall profile) {
+ if (profile == null) {
+ loge("isCallProfileSupported null profile");
+ return false;
+ }
+
+ switch (profile.getPreciseCallState()) {
+ case PRECISE_CALL_STATE_ACTIVE:
+ return mSrvccTypeSupported.contains(BASIC_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_HOLDING:
+ return mSrvccTypeSupported.contains(MIDCALL_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_DIALING:
+ return mSrvccTypeSupported.contains(PREALERTING_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_ALERTING:
+ return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_INCOMING:
+ return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_WAITING:
+ return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+ case PRECISE_CALL_STATE_INCOMING_SETUP:
+ return mSrvccTypeSupported.contains(PREALERTING_SRVCC_SUPPORT);
+ default:
+ loge("isCallProfileSupported invalid state="
+ + profile.getPreciseCallState());
+ break;
+ }
+ return false;
+ }
+
+ private synchronized ImsPhoneConnection findConnection(String callId) {
+ for (ImsPhoneConnection c : mConnections) {
+ ImsCall imsCall = c.getImsCall();
+ if (imsCall == null) continue;
+ ImsCallSession session = imsCall.getCallSession();
+ if (session == null) continue;
+
+ if (TextUtils.equals(session.getCallId(), callId)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Update the list of SrvccConnection with the given SrvccCall and ImsPhoneconnection.
+ *
+ * @param destList the list of SrvccConnection the new connection will be added
+ * @param profile the SrvccCall of the connection to be added
+ * @param c the ImsPhoneConnection of the connection to be added
+ */
+ private void addConnection(
+ List<SrvccConnection> destList, SrvccCall profile, ImsPhoneConnection c) {
+ if (destList == null) return;
+ if (profile == null) return;
+
+ int preciseCallState = profile.getPreciseCallState();
+ if (!isAlive(preciseCallState)) {
+ Rlog.i(LOG_TAG, "addConnection not alive, " + preciseCallState);
+ return;
+ }
+
+ List<ConferenceParticipant> participants = getConferenceParticipants(c);
+ if (participants != null) {
+ for (ConferenceParticipant cp : participants) {
+ if (cp.getState() == android.telecom.Connection.STATE_DISCONNECTED) {
+ Rlog.i(LOG_TAG, "addConnection participant is disconnected");
+ continue;
+ }
+ SrvccConnection srvccConnection = new SrvccConnection(cp, preciseCallState);
+ Rlog.i(LOG_TAG, "addConnection " + srvccConnection);
+ destList.add(srvccConnection);
+ }
+ } else {
+ SrvccConnection srvccConnection =
+ new SrvccConnection(profile.getImsCallProfile(), c, preciseCallState);
+ Rlog.i(LOG_TAG, "addConnection " + srvccConnection);
+ destList.add(srvccConnection);
+ }
+ }
+
+ private List<ConferenceParticipant> getConferenceParticipants(ImsPhoneConnection c) {
+ if (!mSrvccTypeSupported.contains(MIDCALL_SRVCC_SUPPORT)) return null;
+
+ ImsCall imsCall = c.getImsCall();
+ if (imsCall == null) return null;
+
+ List<ConferenceParticipant> participants = imsCall.getConferenceParticipants();
+ if (participants == null || participants.isEmpty()) return null;
+ return participants;
+ }
+
+ private static boolean isAlive(int preciseCallState) {
+ switch (preciseCallState) {
+ case PRECISE_CALL_STATE_ACTIVE: return true;
+ case PRECISE_CALL_STATE_HOLDING: return true;
+ case PRECISE_CALL_STATE_DIALING: return true;
+ case PRECISE_CALL_STATE_ALERTING: return true;
+ case PRECISE_CALL_STATE_INCOMING: return true;
+ case PRECISE_CALL_STATE_WAITING: return true;
+ case PRECISE_CALL_STATE_INCOMING_SETUP: return true;
+ default:
+ }
+ return false;
+ }
+
+ /**
+ * Notifies that radio triggered IMS deregistration.
+ * @param reason the reason why the deregistration is triggered.
+ */
+ public void triggerImsDeregistration(
+ @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
+ if (mImsManager == null) return;
+ try {
+ mImsManager.triggerDeregistration(reason);
+ } catch (ImsException e) {
+ loge("triggerImsDeregistration: exception " + e);
+ }
+ }
+
+ private void updateImsRegistrationInfo() {
+ int capabilities = 0;
+
+ if (mMmTelCapabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)) {
+ capabilities |= IMS_MMTEL_CAPABILITY_VOICE;
+ }
+ if (mMmTelCapabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO)) {
+ capabilities |= IMS_MMTEL_CAPABILITY_VIDEO;
+ }
+ if (mMmTelCapabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS)) {
+ capabilities |= IMS_MMTEL_CAPABILITY_SMS;
+ }
+
+ mPhone.updateImsRegistrationInfo(capabilities);
+ }
+
+ private void registerImsTrafficSession(int token,
+ @MmTelFeature.ImsTrafficType int trafficType,
+ @MmTelFeature.ImsTrafficDirection int trafficDirection,
+ @NonNull IImsTrafficSessionCallback callback) {
+ mImsTrafficSessions.put(Integer.valueOf(token),
+ new ImsTrafficSession(trafficType, trafficDirection, callback));
+ }
+
+ private void unregisterImsTrafficSession(int token) {
+ mImsTrafficSessions.remove(Integer.valueOf(token));
+ }
+
+ private ImsTrafficSession getImsTrafficSession(int token) {
+ return mImsTrafficSessions.get(Integer.valueOf(token));
+ }
+
+ private void stopAllImsTrafficTypes() {
+ boolean isEmpty = mImsTrafficSessions.isEmpty();
+ logi("stopAllImsTrafficTypes empty=" + isEmpty);
+
+ if (isEmpty) return;
+
+ mImsTrafficSessions.forEachKey(1, token -> mPhone.stopImsTraffic(token, null));
+ mImsTrafficSessions.clear();
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 14952b7..7125763 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -511,6 +511,10 @@
}
@Override
+ public void getImei(Message response) {
+ }
+
+ @Override
public void getCDMASubscription(Message response) {
}
@@ -603,12 +607,13 @@
public void iccOpenLogicalChannel(String AID, int p2, Message response) {}
@Override
- public void iccCloseLogicalChannel(int channel, Message response) {}
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response) {}
@Override
public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
int p1, int p2, int p3, String data,
- Message response) {}
+ boolean isEs10Command, Message response) {}
+
@Override
public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
int p3, String data, Message response) {}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
old mode 100755
new mode 100644
index c60a4db..b984d84
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -152,6 +152,11 @@
*/
private ImsReasonInfo mImsReasonInfo;
+ /**
+ * Used to indicate that this call is held by remote party.
+ */
+ private boolean mIsHeldByRemote = false;
+
//***** Event Constants
private static final int EVENT_DTMF_DONE = 1;
private static final int EVENT_PAUSE_DONE = 2;
@@ -244,7 +249,8 @@
/** This is an MO call, created when dialing */
public ImsPhoneConnection(Phone phone, String dialString, ImsPhoneCallTracker ct,
- ImsPhoneCall parent, boolean isEmergency, boolean isWpsCall) {
+ ImsPhoneCall parent, boolean isEmergency, boolean isWpsCall,
+ ImsPhone.ImsDialArgs dialArgs) {
super(PhoneConstants.PHONE_TYPE_IMS);
createWakeLock(phone.getContext());
acquireWakeLock();
@@ -272,6 +278,13 @@
mIsEmergency = isEmergency;
if (isEmergency) {
setEmergencyCallInfo(mOwner);
+
+ if (getEmergencyNumberInfo() == null) {
+ // There was no emergency number info found for this call, however it is
+ // still marked as an emergency number. This may happen if it was a redialed
+ // non-detectable emergency call from IMS.
+ setNonDetectableEmergencyCallInfo(dialArgs.eccCategory);
+ }
}
mIsWpsCall = isWpsCall;
@@ -1166,17 +1179,24 @@
*/
public void startRtt(android.telecom.Connection.RttTextStream textStream) {
ImsCall imsCall = getImsCall();
- if (imsCall != null) {
- getImsCall().sendRttModifyRequest(true);
- setCurrentRttTextStream(textStream);
+ if (imsCall == null) {
+ Rlog.w(LOG_TAG, "startRtt failed, imsCall is null");
+ return;
}
+ imsCall.sendRttModifyRequest(true);
+ setCurrentRttTextStream(textStream);
}
/**
* Terminate the current RTT session.
*/
public void stopRtt() {
- getImsCall().sendRttModifyRequest(false);
+ ImsCall imsCall = getImsCall();
+ if (imsCall == null) {
+ Rlog.w(LOG_TAG, "stopRtt failed, imsCall is null");
+ return;
+ }
+ imsCall.sendRttModifyRequest(false);
}
/**
@@ -1188,14 +1208,15 @@
public void sendRttModifyResponse(android.telecom.Connection.RttTextStream textStream) {
boolean accept = textStream != null;
ImsCall imsCall = getImsCall();
-
- if (imsCall != null) {
- imsCall.sendRttModifyResponse(accept);
- if (accept) {
- setCurrentRttTextStream(textStream);
- } else {
- Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
- }
+ if (imsCall == null) {
+ Rlog.w(LOG_TAG, "sendRttModifyResponse failed, imsCall is null");
+ return;
+ }
+ imsCall.sendRttModifyResponse(accept);
+ if (accept) {
+ setCurrentRttTextStream(textStream);
+ } else {
+ Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
}
}
@@ -1276,6 +1297,8 @@
ImsCall imsCall = getImsCall();
if (imsCall != null) {
imsCall.sendRttMessage(message);
+ } else {
+ Rlog.w(LOG_TAG, "createRttTextHandler: imsCall is null");
}
});
mRttTextHandler.initialize(mRttTextStream);
@@ -1570,7 +1593,30 @@
*/
public void handleMergeComplete() {
mIsMergeInProcess = false;
- onConnectionEvent(android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
+ }
+
+ /**
+ * Mark the call is held by remote party and inform to the UI.
+ */
+ public void setRemotelyHeld() {
+ mIsHeldByRemote = true;
+ onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+ }
+
+ /**
+ * Mark the call is Unheld by remote party and inform to the UI.
+ */
+ public void setRemotelyUnheld() {
+ mIsHeldByRemote = false;
+ onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
+ }
+
+ /**
+ * @return whether the remote party is holding the call.
+ */
+ public boolean isHeldByRemote() {
+ Rlog.i(LOG_TAG, "isHeldByRemote=" + mIsHeldByRemote);
+ return mIsHeldByRemote;
}
public void changeToPausedState() {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 359079d..25fa8a2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -58,6 +58,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallWaitingController;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.MmiCode;
@@ -891,9 +892,15 @@
Rlog.d(LOG_TAG, "processCode: isShortCode");
// These just get treated as USSD.
- Rlog.d(LOG_TAG, "processCode: Sending short code '"
- + mDialingNumber + "' over CS pipe.");
- throw new CallStateException(Phone.CS_FALLBACK);
+ if (isUssdOverImsAllowed()) {
+ Rlog.d(LOG_TAG, "processCode: Sending short code '"
+ + mDialingNumber + "' over IMS pipe.");
+ sendUssd(mDialingNumber);
+ } else {
+ Rlog.d(LOG_TAG, "processCode: Sending short code '"
+ + mDialingNumber + "' over CS pipe.");
+ throw new CallStateException(Phone.CS_FALLBACK);
+ }
} else if (isServiceCodeCallForwarding(mSc)) {
Rlog.d(LOG_TAG, "processCode: is CF");
@@ -1096,55 +1103,34 @@
int serviceClass = siToServiceClass(mSia);
if (isActivate() || isDeactivate()) {
+ if (serviceClass == SERVICE_CLASS_NONE
+ || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+ if (mPhone.getTerminalBasedCallWaitingState(false)
+ != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+ mPhone.getDefaultPhone().setCallWaiting(isActivate(), serviceClass,
+ obtainMessage(EVENT_SET_COMPLETE, this));
+ return;
+ }
+ }
mPhone.setCallWaiting(isActivate(), serviceClass,
obtainMessage(EVENT_SET_COMPLETE, this));
} else if (isInterrogate()) {
- mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+ if (mPhone.getTerminalBasedCallWaitingState(false)
+ != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+ mPhone.getDefaultPhone()
+ .getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+ } else {
+ mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+ }
} else {
throw new RuntimeException ("Invalid or Unsupported MMI Code");
}
} else if (mPoundString != null) {
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allow_ussd_over_ims)) {
- int ussd_method = getIntCarrierConfig(
- CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
-
- switch (ussd_method) {
- case USSD_OVER_CS_PREFERRED:
- // We'll normally send USSD over the CS pipe, but if it happens that
- // the CS phone is out of service, we'll just try over IMS instead.
- if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
- == STATE_IN_SERVICE) {
- Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe "
- + "(allowed over ims).");
- throw new CallStateException(Phone.CS_FALLBACK);
- } else {
- Rlog.i(LOG_TAG, "processCode: CS is out of service, "
- + "sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
- sendUssd(mPoundString);
- }
- break;
- case USSD_OVER_IMS_PREFERRED:
- case USSD_OVER_IMS_ONLY:
- Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
- sendUssd(mPoundString);
- break;
- case USSD_OVER_CS_ONLY:
- Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
- throw new CallStateException(Phone.CS_FALLBACK);
- default:
- Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe."
- + "(unsupported method)");
- throw new CallStateException(Phone.CS_FALLBACK);
- }
+ if (isUssdOverImsAllowed()) {
+ Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
+ sendUssd(mPoundString);
} else {
- // USSD codes are not supported over IMS due to modem limitations; send over
- // the CS pipe instead. This should be fixed in the future.
Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
throw new CallStateException(Phone.CS_FALLBACK);
@@ -1161,6 +1147,40 @@
}
}
+ private boolean isUssdOverImsAllowed() {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims)) {
+ int ussd_method = getIntCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
+
+ switch (ussd_method) {
+ case USSD_OVER_CS_PREFERRED:
+ // We'll normally send USSD over the CS pipe, but if it happens that
+ // the CS phone is out of service, we'll just try over IMS instead.
+ if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
+ == STATE_IN_SERVICE) {
+ return false;
+ } else {
+ Rlog.i(LOG_TAG, "isUssdOverImsAllowed: CS is out of service");
+ return true;
+ }
+ case USSD_OVER_IMS_PREFERRED:
+ case USSD_OVER_IMS_ONLY:
+ return true;
+ case USSD_OVER_CS_ONLY:
+ return false;
+ default:
+ Rlog.i(LOG_TAG, "isUssdOverImsAllowed: Unsupported method");
+ return false;
+ }
+ } else {
+ // USSD codes are not supported over IMS due to modem limitations; send over
+ // the CS pipe instead. This should be fixed in the future.
+ Rlog.i(LOG_TAG, "isUssdOverImsAllowed: USSD over IMS pipe is not supported.");
+ return false;
+ }
+ }
+
/**
* Called from ImsPhone
*
@@ -1330,7 +1350,8 @@
mContext.getText(com.android.internal.R.string.mmiError);
}
- private CharSequence getMmiErrorMessage(AsyncResult ar) {
+ @VisibleForTesting
+ public CharSequence getMmiErrorMessage(AsyncResult ar) {
if (ar.exception instanceof ImsException) {
switch (((ImsException) ar.exception).getCode()) {
case ImsReasonInfo.CODE_FDN_BLOCKED:
@@ -1360,6 +1381,12 @@
return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
} else if (err.getCommandError() == CommandException.Error.INTERNAL_ERR) {
return mContext.getText(com.android.internal.R.string.mmiError);
+ } else if (err.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED
+ || err.getCommandError() == CommandException.Error.OPERATION_NOT_ALLOWED) {
+ // getResources().getText() is the same as getText(), however getText() is final and
+ // cannot be mocked in tests.
+ return mContext.getResources().getText(
+ com.android.internal.R.string.mmiErrorNotSupported);
}
}
return null;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java b/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
index 115f6fe..9452e2a 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
@@ -20,8 +20,10 @@
import android.annotation.NonNull;
import android.net.Uri;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import java.util.concurrent.Executor;
@@ -40,7 +42,7 @@
/**
* Handle the callback when IMS is registered.
*/
- void handleImsRegistered(int imsRadioTech);
+ void handleImsRegistered(@NonNull ImsRegistrationAttributes attributes);
/**
* Handle the callback when IMS is registering.
@@ -50,7 +52,9 @@
/**
* Handle the callback when IMS is unregistered.
*/
- void handleImsUnregistered(ImsReasonInfo imsReasonInfo);
+ void handleImsUnregistered(ImsReasonInfo imsReasonInfo,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech);
/**
* Handle the callback when the list of subscriber {@link Uri}s associated with this IMS
@@ -66,9 +70,9 @@
private final RegistrationManager.RegistrationCallback mImsRegistrationCallback =
new RegistrationManager.RegistrationCallback() {
@Override
- public void onRegistered(int imsRadioTech) {
+ public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
updateRegistrationState(RegistrationManager.REGISTRATION_STATE_REGISTERED);
- mImsRegistrationUpdate.handleImsRegistered(imsRadioTech);
+ mImsRegistrationUpdate.handleImsRegistered(attributes);
}
@Override
@@ -79,8 +83,17 @@
@Override
public void onUnregistered(ImsReasonInfo imsReasonInfo) {
+ onUnregistered(imsReasonInfo, RegistrationManager.SUGGESTED_ACTION_NONE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo imsReasonInfo,
+ @RegistrationManager.SuggestedAction int suggestedAction,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
updateRegistrationState(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
- mImsRegistrationUpdate.handleImsUnregistered(imsReasonInfo);
+ mImsRegistrationUpdate.handleImsUnregistered(imsReasonInfo, suggestedAction,
+ imsRadioTech);
}
@Override
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index ae760f9..cfa16d0 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -16,10 +16,6 @@
package com.android.internal.telephony.metrics;
-import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
-import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
-import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
-import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
import android.annotation.Nullable;
@@ -29,18 +25,22 @@
import android.telephony.Annotation.NetworkType;
import android.telephony.DataFailCause;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ProtocolType;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataService;
-import android.telephony.data.DataService.DeactivateDataReason;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.data.DataNetwork;
import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;
import java.util.Arrays;
@@ -49,7 +49,6 @@
/** Collects data call change events per DataConnection for the pulled atom. */
public class DataCallSessionStats {
private static final String TAG = DataCallSessionStats.class.getSimpleName();
- private static final int SIZE_LIMIT_HANDOVER_FAILURE_CAUSES = 15;
private final Phone mPhone;
private long mStartTime;
@@ -60,6 +59,8 @@
private static final Random RANDOM = new Random();
+ public static final int SIZE_LIMIT_HANDOVER_FAILURES = 15;
+
public DataCallSessionStats(Phone phone) {
mPhone = phone;
}
@@ -78,14 +79,14 @@
* @param currentRat The data call current Network Type
* @param apnTypeBitmask APN type bitmask
* @param protocol Data connection protocol
- * @param failureCause failure cause as per android.telephony.DataFailCause
+ * @param failureCause The raw failure cause from modem/IWLAN data service.
*/
public synchronized void onSetupDataCallResponse(
@Nullable DataCallResponse response,
@NetworkType int currentRat,
@ApnType int apnTypeBitmask,
@ProtocolType int protocol,
- @DataFailureCause int failureCause) {
+ int failureCause) {
// there should've been a call to onSetupDataCall to initiate the atom,
// so this method is being called out of order -> no metric will be logged
if (mDataCallSession == null) {
@@ -122,33 +123,17 @@
/**
* Updates the dataCall atom when data call is deactivated.
*
- * @param reason Deactivate reason
+ * @param reason Tear down reason
*/
- public synchronized void setDeactivateDataCallReason(@DeactivateDataReason int reason) {
+ public synchronized void setDeactivateDataCallReason(@DataNetwork.TearDownReason int reason) {
// there should've been another call to initiate the atom,
// so this method is being called out of order -> no metric will be logged
if (mDataCallSession == null) {
loge("setDeactivateDataCallReason: no DataCallSession atom has been initiated.");
return;
}
- switch (reason) {
- case DataService.REQUEST_REASON_NORMAL:
- mDataCallSession.deactivateReason =
- DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
- break;
- case DataService.REQUEST_REASON_SHUTDOWN:
- mDataCallSession.deactivateReason =
- DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
- break;
- case DataService.REQUEST_REASON_HANDOVER:
- mDataCallSession.deactivateReason =
- DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
- break;
- default:
- mDataCallSession.deactivateReason =
- DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
- break;
- }
+ // Skip the pre-U enum. See enum DataDeactivateReasonEnum in enums.proto
+ mDataCallSession.deactivateReason = reason + DataService.REQUEST_REASON_HANDOVER + 1;
}
/**
@@ -175,17 +160,30 @@
*
* @param failureCause failure cause as per android.telephony.DataFailCause
*/
- public synchronized void onHandoverFailure(@DataFailureCause int failureCause) {
+ public synchronized void onHandoverFailure(@DataFailureCause int failureCause,
+ @NetworkType int sourceRat, @NetworkType int targetRat) {
if (mDataCallSession != null
&& mDataCallSession.handoverFailureCauses.length
- < SIZE_LIMIT_HANDOVER_FAILURE_CAUSES) {
+ < SIZE_LIMIT_HANDOVER_FAILURES) {
+
int[] failureCauses = mDataCallSession.handoverFailureCauses;
- for (int cause : failureCauses) {
- if (failureCause == cause) return;
+ int[] handoverFailureRats = mDataCallSession.handoverFailureRat;
+ int failureDirection = sourceRat | (targetRat << 16);
+
+ for (int i = 0; i < failureCauses.length; i++) {
+ if (failureCauses[i] == failureCause
+ && handoverFailureRats[i] == failureDirection) {
+ return;
+ }
}
+
mDataCallSession.handoverFailureCauses = Arrays.copyOf(
failureCauses, failureCauses.length + 1);
mDataCallSession.handoverFailureCauses[failureCauses.length] = failureCause;
+
+ mDataCallSession.handoverFailureRat = Arrays.copyOf(handoverFailureRats,
+ handoverFailureRats.length + 1);
+ mDataCallSession.handoverFailureRat[handoverFailureRats.length] = failureDirection;
}
}
@@ -209,12 +207,22 @@
}
}
+ /** Stores the current unmetered network types information in permanent storage. */
+ public void onUnmeteredUpdate(@NetworkType int networkType) {
+ mAtomsStorage
+ .addUnmeteredNetworks(
+ mPhone.getPhoneId(),
+ mPhone.getCarrierId(),
+ TelephonyManager.getBitMaskForNetworkType(networkType));
+ }
+
/**
* Take a snapshot of the on-going data call segment to add to the atom storage.
*
* Note the following fields are reset after the snapshot:
* - rat switch count
* - handover failure causes
+ * - handover failure rats
*/
public synchronized void conclude() {
if (mDataCallSession != null) {
@@ -224,6 +232,7 @@
mStartTime = nowMillis;
mDataCallSession.ratSwitchCount = 0L;
mDataCallSession.handoverFailureCauses = new int[0];
+ mDataCallSession.handoverFailureRat = new int[0];
mAtomsStorage.addDataCallSession(call);
}
}
@@ -232,6 +241,16 @@
private void endDataCallSession() {
mDataCallSession.oosAtEnd = getIsOos();
mDataCallSession.ongoing = false;
+ // set if this data call is established for internet on the non-Dds
+ SubscriptionInfo subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfo(mPhone.getSubId());
+ if (mPhone.getSubId() != SubscriptionManager.getDefaultDataSubscriptionId()
+ && ((mDataCallSession.apnTypeBitmask & ApnSetting.TYPE_DEFAULT)
+ == ApnSetting.TYPE_DEFAULT)
+ && subInfo != null && !subInfo.isOpportunistic()) {
+ mDataCallSession.isNonDds = true;
+ }
+
// store for the data call list event, after DataCall is disconnected and entered into
// inactive mode
PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this);
@@ -265,6 +284,9 @@
copy.bandAtEnd = call.bandAtEnd;
copy.handoverFailureCauses = Arrays.copyOf(call.handoverFailureCauses,
call.handoverFailureCauses.length);
+ copy.handoverFailureRat = Arrays.copyOf(call.handoverFailureRat,
+ call.handoverFailureRat.length);
+ copy.isNonDds = call.isNonDds;
return copy;
}
@@ -284,10 +306,12 @@
proto.setupFailed = false;
proto.failureCause = DataFailCause.NONE;
proto.suggestedRetryMillis = 0;
- proto.deactivateReason = DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+ proto.deactivateReason = DataNetwork.TEAR_DOWN_REASON_NONE;
proto.durationMinutes = 0;
proto.ongoing = true;
proto.handoverFailureCauses = new int[0];
+ proto.handoverFailureRat = new int[0];
+ proto.isNonDds = false;
return proto;
}
@@ -295,12 +319,13 @@
ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
ServiceState serviceState =
serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
- return serviceState != null ? serviceState.getRoaming() : false;
+ return serviceState != null && serviceState.getRoaming();
}
private boolean getIsOpportunistic() {
- SubscriptionController subController = SubscriptionController.getInstance();
- return subController != null ? subController.isOpportunistic(mPhone.getSubId()) : false;
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(mPhone.getSubId());
+ return subInfo != null && subInfo.isOpportunistic();
}
private boolean getIsOos() {
@@ -308,8 +333,7 @@
ServiceState serviceState =
serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
return serviceState != null
- ? serviceState.getDataRegistrationState() == ServiceState.STATE_OUT_OF_SERVICE
- : false;
+ && serviceState.getDataRegistrationState() == ServiceState.STATE_OUT_OF_SERVICE;
}
private void logi(String format, Object... args) {
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 5ade0bb..2f22196 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -16,17 +16,21 @@
package com.android.internal.telephony.metrics;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
+import android.telephony.CellSignalStrength;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.data.DataStallRecoveryManager;
-import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
/** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
public class DataStallRecoveryStats {
@@ -41,42 +45,6 @@
private static final int RECOVERY_ACTION_RADIO_RESTART_MAPPING = 3;
private static final int RECOVERY_ACTION_RESET_MODEM_MAPPING = 4;
-
- /** TODO: b/214044479 : Remove this function when new data design(Android T) start. */
- public static void onDataStallEvent(
- @DcTracker.RecoveryAction int recoveryAction,
- Phone phone,
- boolean isRecovered,
- int durationMillis) {
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
- phone = phone.getDefaultPhone();
- }
-
- int carrierId = phone.getCarrierId();
- int rat = getRat(phone);
- int band =
- (rat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(phone);
- // the number returned here matches the SignalStrength enum we have
- int signalStrength = phone.getSignalStrength().getLevel();
- boolean isOpportunistic = getIsOpportunistic(phone);
- boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
-
- // Not use this field in Android S, so we send RECOVERED_REASON_NONE for default value.
- int recoveryReason = 0;
- TelephonyStatsLog.write(
- TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
- carrierId,
- rat,
- signalStrength,
- recoveryAction,
- isOpportunistic,
- isMultiSim,
- band,
- isRecovered,
- durationMillis,
- recoveryReason);
- }
-
/**
* Called when data stall happened.
*
@@ -85,13 +53,16 @@
* @param isRecovered The data stall symptom recovered or not.
* @param durationMillis The duration from data stall symptom occurred.
* @param reason The recovered(data resume) reason.
+ * @param isFirstValidation The validation status if it's the first come after recovery.
*/
public static void onDataStallEvent(
@DataStallRecoveryManager.RecoveryAction int recoveryAction,
Phone phone,
boolean isRecovered,
int durationMillis,
- @DataStallRecoveryManager.RecoveredReason int reason) {
+ @DataStallRecoveryManager.RecoveredReason int reason,
+ boolean isFirstValidation,
+ int durationMillisOfCurrentAction) {
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
phone = phone.getDefaultPhone();
}
@@ -111,6 +82,39 @@
recoveryAction = RECOVERY_ACTION_RESET_MODEM_MAPPING;
}
+ // collect info of the other device in case of DSDS
+ int otherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ // the number returned here matches the NetworkRegistrationState enum we have
+ int otherNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ for (Phone otherPhone : PhoneFactory.getPhones()) {
+ if (otherPhone.getPhoneId() == phone.getPhoneId()) continue;
+ if (!getIsOpportunistic(otherPhone)) {
+ otherSignalStrength = otherPhone.getSignalStrength().getLevel();
+ NetworkRegistrationInfo regInfo = otherPhone.getServiceState()
+ .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (regInfo != null) {
+ otherNetworkRegState = regInfo.getRegistrationState();
+ }
+ break;
+ }
+ }
+
+ // the number returned here matches the NetworkRegistrationState enum we have
+ int phoneNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+
+ NetworkRegistrationInfo phoneRegInfo = phone.getServiceState()
+ .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (phoneRegInfo != null) {
+ phoneNetworkRegState = phoneRegInfo.getRegistrationState();
+ }
+
+ // reserve 0 for default value
+ int phoneId = phone.getPhoneId() + 1;
+
TelephonyStatsLog.write(
TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
carrierId,
@@ -122,7 +126,13 @@
band,
isRecovered,
durationMillis,
- reason);
+ reason,
+ otherSignalStrength,
+ otherNetworkRegState,
+ phoneNetworkRegState,
+ isFirstValidation,
+ phoneId,
+ durationMillisOfCurrentAction);
}
/** Returns the RAT used for data (including IWLAN). */
@@ -136,7 +146,8 @@
}
private static boolean getIsOpportunistic(Phone phone) {
- SubscriptionController subController = SubscriptionController.getInstance();
- return subController != null ? subController.isOpportunistic(phone.getSubId()) : false;
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(phone.getSubId());
+ return subInfo != null && subInfo.isOpportunistic();
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java
new file mode 100644
index 0000000..29729c8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DeviceStateHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_FLIPPED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_HALF_OPENED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_OPENED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN;
+
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+
+import com.android.internal.telephony.Phone;
+
+/** Device state information like the fold state. */
+public class DeviceStateHelper {
+ private int mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN;
+
+ public DeviceStateHelper(Context context) {
+ HandlerThread mHandlerThread = new HandlerThread("DeviceStateHelperThread");
+ mHandlerThread.start();
+ context.getSystemService(DeviceStateManager.class)
+ .registerCallback(
+ new HandlerExecutor(new Handler(mHandlerThread.getLooper())),
+ state -> {
+ updateFoldState(state);
+ });
+ }
+
+ private void updateFoldState(int posture) {
+ switch (posture) {
+ case 0:
+ mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED;
+ break;
+ case 1:
+ mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_HALF_OPENED;
+ break;
+ case 2:
+ mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_OPENED;
+ break;
+ case 4:
+ mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_FLIPPED;
+ break;
+ default:
+ mFoldState = CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN;
+ }
+ updateServiceStateStats();
+ }
+
+ private void updateServiceStateStats() {
+ for (Phone phone : MetricsCollector.getPhonesIfAny()) {
+ phone.getServiceStateTracker().getServiceStateStats().onFoldStateChanged(mFoldState);
+ }
+ }
+
+ public int getFoldState() {
+ return mFoldState;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java b/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java
new file mode 100644
index 0000000..51fe20c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import com.android.internal.telephony.PhoneFactory;
+
+/** Metrics for the telephony related properties on the device. */
+public class DeviceTelephonyPropertiesStats {
+ private static final String TAG = DeviceTelephonyPropertiesStats.class.getSimpleName();
+
+ /**
+ * Record whenever the auto data switch feature is toggled.
+ */
+ public static void recordAutoDataSwitchFeatureToggle() {
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ storage.recordToggledAutoDataSwitch();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/EmergencyNumberStats.java b/src/java/com/android/internal/telephony/metrics/EmergencyNumberStats.java
new file mode 100644
index 0000000..2867b46
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/EmergencyNumberStats.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM;
+
+import android.telephony.emergency.EmergencyNumber;
+import android.util.SparseIntArray;
+
+import com.android.internal.telephony.nano.PersistAtomsProto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * EmergencyStats logs the atoms for consolidated emergency number list in framework. It also logs
+ * the details of a dialed emergency number. To avoid repeated information this class stores the
+ * emergency numbers list in map and verifies the information for duplicacy before logging it. Note:
+ * This locally stored information will erase on process restart scenarios (like reboot, crash,
+ * etc.).
+ */
+public class EmergencyNumberStats {
+
+ private static final String TAG = EmergencyNumberStats.class.getSimpleName();
+ private static final SparseIntArray sRoutesMap;
+ private static final SparseIntArray sServiceCategoriesMap;
+ private static final SparseIntArray sSourcesMap;
+ private static EmergencyNumberStats sInstance;
+
+ static {
+ sRoutesMap = new SparseIntArray() {
+ {
+ put(EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY,
+ EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY);
+ put(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL,
+ EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL);
+ put(EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN,
+ EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN);
+ }
+ };
+
+ sServiceCategoriesMap = new SparseIntArray() {
+ {
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC);
+ put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC,
+ EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC);
+ }
+ };
+
+ sSourcesMap = new SparseIntArray() {
+ {
+ put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+ put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM);
+ put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE);
+ put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+ EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+ put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT,
+ EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT);
+ }
+ };
+ }
+
+ private EmergencyNumberStats() {
+ }
+
+ /** Static method to provide singleton instance for EmergencyNumberStats. */
+ public static EmergencyNumberStats getInstance() {
+ if (sInstance == null) {
+ sInstance = new EmergencyNumberStats();
+ }
+ return sInstance;
+ }
+
+ /**
+ * It converts the {@link android.telephony.emergency.EmergencyNumber} to
+ * {@link PersistAtomsProto.EmergencyNumber} for
+ * logging the EmergencyNumber atoms with pulled event.
+ *
+ * @param emergencyNumberList android.telephony.EmergencyNumber list
+ * @param assetVersion assert version
+ * @param otaVersion ota version
+ * @param isDbRoutingIgnored flag that defines if routing is ignored through database.
+ */
+ public PersistAtomsProto.EmergencyNumbersInfo[] convertEmergencyNumbersListToProto(
+ List<EmergencyNumber> emergencyNumberList, int assetVersion, int otaVersion,
+ boolean isDbRoutingIgnored) {
+ List<PersistAtomsProto.EmergencyNumbersInfo> numberProtoList = new ArrayList<>();
+ for (EmergencyNumber number : emergencyNumberList) {
+ numberProtoList.add(convertEmergencyNumberToProto(number, assetVersion, otaVersion,
+ isDbRoutingIgnored));
+ }
+ return numberProtoList.toArray(new PersistAtomsProto.EmergencyNumbersInfo[0]);
+ }
+
+ private PersistAtomsProto.EmergencyNumbersInfo convertEmergencyNumberToProto(
+ EmergencyNumber number, int assetVer, int otaVer, boolean isDbRoutingIgnored) {
+ String dialNumber = number.getNumber();
+ PersistAtomsProto.EmergencyNumbersInfo emergencyNumber =
+ new PersistAtomsProto.EmergencyNumbersInfo();
+ emergencyNumber.isDbVersionIgnored = isDbRoutingIgnored;
+ emergencyNumber.assetVersion = assetVer;
+ emergencyNumber.otaVersion = otaVer;
+ emergencyNumber.number = dialNumber;
+ emergencyNumber.countryIso = number.getCountryIso();
+ emergencyNumber.mnc = number.getMnc();
+ emergencyNumber.route = sRoutesMap.get(number.getEmergencyCallRouting());
+ emergencyNumber.urns = number.getEmergencyUrns().toArray(new String[0]);
+ emergencyNumber.serviceCategories = getMappedServiceCategories(
+ number.getEmergencyServiceCategories());
+ emergencyNumber.sources = getMappedSources(number.getEmergencyNumberSources());
+ return emergencyNumber;
+ }
+
+ private int[] getMappedServiceCategories(List<Integer> serviceCategories) {
+ if (serviceCategories == null || serviceCategories.isEmpty()) {
+ return null;
+ }
+ return serviceCategories.stream().map(sServiceCategoriesMap::get).mapToInt(
+ Integer::intValue).toArray();
+ }
+
+ private int[] getMappedSources(List<Integer> sources) {
+ if (sources == null || sources.isEmpty()) {
+ return null;
+ }
+ return sources.stream().map(sSourcesMap::get).mapToInt(Integer::intValue).toArray();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/ImsStats.java b/src/java/com/android/internal/telephony/metrics/ImsStats.java
index fefb799..427595f 100644
--- a/src/java/com/android/internal/telephony/metrics/ImsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ImsStats.java
@@ -35,6 +35,8 @@
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ProvisioningManager;
@@ -196,7 +198,7 @@
private long mLastTimestamp;
@Nullable private ImsRegistrationStats mLastRegistrationStats;
-
+ @TransportType int mLastTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
// Available features are those reported by ImsService to be available for use.
private MmTelCapabilities mLastAvailableFeatures = new MmTelCapabilities();
@@ -265,6 +267,10 @@
boolean ratChanged = false;
@NetworkType int newRat = convertRegistrationTechToNetworkType(radioTech);
+ mLastTransportType =
+ (newRat == TelephonyManager.NETWORK_TYPE_IWLAN)
+ ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+ : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
if (mLastRegistrationStats != null && mLastRegistrationStats.rat != newRat) {
mLastRegistrationStats.rat = newRat;
ratChanged = true;
@@ -301,6 +307,7 @@
public synchronized void onImsRegistering(@TransportType int imsRadioTech) {
conclude();
+ mLastTransportType = imsRadioTech;
mLastRegistrationStats = getDefaultImsRegistrationStats();
mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
mLastRegistrationState = REGISTRATION_STATE_REGISTERING;
@@ -310,6 +317,7 @@
public synchronized void onImsRegistered(@TransportType int imsRadioTech) {
conclude();
+ mLastTransportType = imsRadioTech;
// NOTE: mLastRegistrationStats can be null (no registering phase).
if (mLastRegistrationStats == null) {
mLastRegistrationStats = getDefaultImsRegistrationStats();
@@ -347,12 +355,20 @@
mLastAvailableFeatures = new MmTelCapabilities();
}
+ /** Updates the RAT when service state changes. */
+ public synchronized void onServiceStateChanged(ServiceState state) {
+ if (mLastTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+ && mLastRegistrationStats != null) {
+ mLastRegistrationStats.rat =
+ ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_PS);
+ }
+ }
+
/**
* Returns the current RAT used for IMS voice registration, or {@link
* TelephonyManager#NETWORK_TYPE_UNKNOWN} if there isn't any.
*/
@NetworkType
- @VisibleForTesting
public synchronized int getImsVoiceRadioTech() {
if (mLastRegistrationStats == null
|| !mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VOICE)) {
@@ -380,7 +396,9 @@
@NetworkType
private int getWwanPsRat() {
- return ServiceStateStats.getDataRat(mPhone.getServiceStateTracker().getServiceState());
+ return ServiceStateStats.getRat(
+ mPhone.getServiceStateTracker().getServiceState(),
+ NetworkRegistrationInfo.DOMAIN_PS);
}
private ImsRegistrationStats getDefaultImsRegistrationStats() {
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 6c5f8d4..5e00987 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -16,15 +16,12 @@
package com.android.internal.telephony.metrics;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-
import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION;
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.DEVICE_TELEPHONY_PROPERTIES;
+import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO;
import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_LISTENER_EVENT;
@@ -33,11 +30,18 @@
import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION;
import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SHORT_CODE_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS;
import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_CONTROLLER;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_INCOMING_DATAGRAM;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_OUTGOING_DATAGRAM;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_PROVISION;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_SESSION;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_SOS_MESSAGE_RECOMMENDER;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SIP_DELEGATE_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.SIP_MESSAGE_RESPONSE;
@@ -48,20 +52,24 @@
import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
-import android.annotation.Nullable;
import android.app.StatsManager;
import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.StatsEvent;
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.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
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.EmergencyNumbersInfo;
import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
@@ -71,10 +79,17 @@
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequestsV2;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
@@ -85,6 +100,7 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.telephony.Rlog;
+import java.time.Duration;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -104,6 +120,10 @@
/** Disables various restrictions to ease debugging during development. */
private static final boolean DBG = false; // STOPSHIP if true
+ private static final long MILLIS_PER_HOUR = Duration.ofHours(1).toMillis();
+ private static final long MILLIS_PER_MINUTE = Duration.ofMinutes(1).toMillis();
+ private static final long MILLIS_PER_SECOND = Duration.ofSeconds(1).toMillis();
+
/**
* Sets atom pull cool down to 23 hours to help enforcing privacy requirement.
*
@@ -111,7 +131,7 @@
* that occur once a day.
*/
private static final long MIN_COOLDOWN_MILLIS =
- DBG ? 10L * SECOND_IN_MILLIS : 23L * HOUR_IN_MILLIS;
+ DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
/**
* Buckets with less than these many calls will be dropped.
@@ -122,59 +142,64 @@
/** Bucket size in milliseconds to round call durations into. */
private static final long DURATION_BUCKET_MILLIS =
- DBG ? 2L * SECOND_IN_MILLIS : 5L * MINUTE_IN_MILLIS;
-
- private static final StatsManager.PullAtomMetadata POLICY_PULL_DAILY =
- new StatsManager.PullAtomMetadata.Builder()
- .setCoolDownMillis(MIN_COOLDOWN_MILLIS)
- .build();
+ DBG ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
private final PersistAtomsStorage mStorage;
+ private final DeviceStateHelper mDeviceStateHelper;
private final StatsManager mStatsManager;
private final AirplaneModeStats mAirplaneModeStats;
private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet();
private static final Random sRandom = new Random();
public MetricsCollector(Context context) {
- this(context, new PersistAtomsStorage(context));
+ this(context, new PersistAtomsStorage(context), new DeviceStateHelper(context));
}
/** Allows dependency injection. Used during unit tests. */
@VisibleForTesting
- public MetricsCollector(Context context,
- PersistAtomsStorage storage) {
+ public MetricsCollector(
+ Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper) {
mStorage = storage;
+ mDeviceStateHelper = deviceStateHelper;
mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
if (mStatsManager != null) {
- registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY);
- registerAtom(CELLULAR_SERVICE_STATE, POLICY_PULL_DAILY);
- registerAtom(SIM_SLOT_STATE, null);
- registerAtom(SUPPORTED_RADIO_ACCESS_FAMILY, null);
- registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY);
- registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY);
- registerAtom(INCOMING_SMS, POLICY_PULL_DAILY);
- registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY);
- registerAtom(CARRIER_ID_TABLE_VERSION, null);
- registerAtom(DATA_CALL_SESSION, POLICY_PULL_DAILY);
- registerAtom(IMS_REGISTRATION_STATS, POLICY_PULL_DAILY);
- registerAtom(IMS_REGISTRATION_TERMINATION, POLICY_PULL_DAILY);
- registerAtom(TELEPHONY_NETWORK_REQUESTS_V2, POLICY_PULL_DAILY);
- registerAtom(IMS_REGISTRATION_FEATURE_TAG_STATS, POLICY_PULL_DAILY);
- registerAtom(RCS_CLIENT_PROVISIONING_STATS, POLICY_PULL_DAILY);
- registerAtom(RCS_ACS_PROVISIONING_STATS, POLICY_PULL_DAILY);
- registerAtom(SIP_DELEGATE_STATS, POLICY_PULL_DAILY);
- registerAtom(SIP_TRANSPORT_FEATURE_TAG_STATS, POLICY_PULL_DAILY);
- registerAtom(SIP_MESSAGE_RESPONSE, POLICY_PULL_DAILY);
- registerAtom(SIP_TRANSPORT_SESSION, POLICY_PULL_DAILY);
- registerAtom(DEVICE_TELEPHONY_PROPERTIES, null);
- registerAtom(IMS_DEDICATED_BEARER_LISTENER_EVENT, POLICY_PULL_DAILY);
- registerAtom(IMS_DEDICATED_BEARER_EVENT, POLICY_PULL_DAILY);
- registerAtom(IMS_REGISTRATION_SERVICE_DESC_STATS, POLICY_PULL_DAILY);
- registerAtom(UCE_EVENT_STATS, POLICY_PULL_DAILY);
- registerAtom(PRESENCE_NOTIFY_EVENT, POLICY_PULL_DAILY);
- registerAtom(GBA_EVENT, POLICY_PULL_DAILY);
- registerAtom(PER_SIM_STATUS, null);
-
+ // Most (but not all) of these are subject to cooldown specified by MIN_COOLDOWN_MILLIS.
+ registerAtom(CELLULAR_DATA_SERVICE_SWITCH);
+ registerAtom(CELLULAR_SERVICE_STATE);
+ registerAtom(SIM_SLOT_STATE);
+ registerAtom(SUPPORTED_RADIO_ACCESS_FAMILY);
+ registerAtom(VOICE_CALL_RAT_USAGE);
+ registerAtom(VOICE_CALL_SESSION);
+ registerAtom(INCOMING_SMS);
+ registerAtom(OUTGOING_SMS);
+ registerAtom(CARRIER_ID_TABLE_VERSION);
+ registerAtom(DATA_CALL_SESSION);
+ registerAtom(IMS_REGISTRATION_STATS);
+ registerAtom(IMS_REGISTRATION_TERMINATION);
+ registerAtom(TELEPHONY_NETWORK_REQUESTS_V2);
+ registerAtom(IMS_REGISTRATION_FEATURE_TAG_STATS);
+ registerAtom(RCS_CLIENT_PROVISIONING_STATS);
+ registerAtom(RCS_ACS_PROVISIONING_STATS);
+ registerAtom(SIP_DELEGATE_STATS);
+ registerAtom(SIP_TRANSPORT_FEATURE_TAG_STATS);
+ registerAtom(SIP_MESSAGE_RESPONSE);
+ registerAtom(SIP_TRANSPORT_SESSION);
+ registerAtom(DEVICE_TELEPHONY_PROPERTIES);
+ registerAtom(IMS_DEDICATED_BEARER_LISTENER_EVENT);
+ registerAtom(IMS_DEDICATED_BEARER_EVENT);
+ registerAtom(IMS_REGISTRATION_SERVICE_DESC_STATS);
+ registerAtom(UCE_EVENT_STATS);
+ registerAtom(PRESENCE_NOTIFY_EVENT);
+ registerAtom(GBA_EVENT);
+ registerAtom(PER_SIM_STATUS);
+ registerAtom(OUTGOING_SHORT_CODE_SMS);
+ registerAtom(SATELLITE_CONTROLLER);
+ registerAtom(SATELLITE_SESSION);
+ registerAtom(SATELLITE_INCOMING_DATAGRAM);
+ registerAtom(SATELLITE_OUTGOING_DATAGRAM);
+ registerAtom(SATELLITE_PROVISION);
+ registerAtom(SATELLITE_SOS_MESSAGE_RECOMMENDER);
+ registerAtom(EMERGENCY_NUMBERS_INFO);
Rlog.d(TAG, "registered");
} else {
Rlog.e(TAG, "could not get StatsManager, atoms not registered");
@@ -249,6 +274,22 @@
return pullGbaEvent(data);
case PER_SIM_STATUS:
return pullPerSimStatus(data);
+ case OUTGOING_SHORT_CODE_SMS:
+ return pullOutgoingShortCodeSms(data);
+ case SATELLITE_CONTROLLER:
+ return pullSatelliteController(data);
+ case SATELLITE_SESSION:
+ return pullSatelliteSession(data);
+ case SATELLITE_INCOMING_DATAGRAM:
+ return pullSatelliteIncomingDatagram(data);
+ case SATELLITE_OUTGOING_DATAGRAM:
+ return pullSatelliteOutgoingDatagram(data);
+ case SATELLITE_PROVISION:
+ return pullSatelliteProvision(data);
+ case SATELLITE_SOS_MESSAGE_RECOMMENDER:
+ return pullSatelliteSosMessageRecommender(data);
+ case EMERGENCY_NUMBERS_INFO:
+ return pullEmergencyNumbersInfo(data);
default:
Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
return StatsManager.PULL_SKIP;
@@ -260,6 +301,23 @@
return mStorage;
}
+ /** Returns the {@link DeviceStateHelper}. */
+ public DeviceStateHelper getDeviceStateHelper() {
+ return mDeviceStateHelper;
+ }
+
+ /** Updates duration segments and calls {@link PersistAtomsStorage#flushAtoms()}. */
+ public void flushAtomsStorage() {
+ concludeAll();
+ mStorage.flushAtoms();
+ }
+
+ /** Updates duration segments and calls {@link PersistAtomsStorage#clearAtoms()}. */
+ public void clearAtomsStorage() {
+ concludeAll();
+ mStorage.clearAtoms();
+ }
+
/**
* Registers a {@link DataCallSessionStats} which will be pinged for on-going data calls when
* data call atoms are pulled.
@@ -273,6 +331,44 @@
mOngoingDataCallStats.remove(call);
}
+ private void concludeDataCallSessionStats() {
+ for (DataCallSessionStats stats : mOngoingDataCallStats) {
+ stats.conclude();
+ }
+ }
+
+ private void concludeImsStats() {
+ for (Phone phone : getPhonesIfAny()) {
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.getImsStats().conclude();
+ }
+ }
+ }
+
+ private void concludeServiceStateStats() {
+ for (Phone phone : getPhonesIfAny()) {
+ phone.getServiceStateTracker().getServiceStateStats().conclude();
+ }
+ }
+
+ private void concludeRcsStats() {
+ RcsStats rcsStats = RcsStats.getInstance();
+ if (rcsStats != null) {
+ rcsStats.concludeSipTransportFeatureTagsStat();
+ rcsStats.onFlushIncompleteRcsAcsProvisioningStats();
+ rcsStats.onFlushIncompleteImsRegistrationServiceDescStats();
+ rcsStats.onFlushIncompleteImsRegistrationFeatureTagStats();
+ }
+ }
+
+ private void concludeAll() {
+ concludeDataCallSessionStats();
+ concludeImsStats();
+ concludeServiceStateStats();
+ concludeRcsStats();
+ }
+
private static int pullSimSlotState(List<StatsEvent> data) {
SimSlotState state;
try {
@@ -380,10 +476,7 @@
private int pullDataCallSession(List<StatsEvent> data) {
// Include ongoing data call segments
- for (DataCallSessionStats stats : mOngoingDataCallStats) {
- stats.conclude();
- }
-
+ concludeDataCallSessionStats();
DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
if (dataCallSessions != null) {
Arrays.stream(dataCallSessions)
@@ -411,10 +504,7 @@
private int pullCellularServiceState(List<StatsEvent> data) {
// Include the latest durations
- for (Phone phone : getPhonesIfAny()) {
- phone.getServiceStateTracker().getServiceStateStats().conclude();
- }
-
+ concludeServiceStateStats();
CellularServiceState[] persistAtoms =
mStorage.getCellularServiceStates(MIN_COOLDOWN_MILLIS);
if (persistAtoms != null) {
@@ -430,13 +520,7 @@
private int pullImsRegistrationStats(List<StatsEvent> data) {
// Include the latest durations
- for (Phone phone : getPhonesIfAny()) {
- ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
- if (imsPhone != null) {
- imsPhone.getImsStats().conclude();
- }
- }
-
+ concludeImsStats();
ImsRegistrationStats[] persistAtoms = mStorage.getImsRegistrationStats(MIN_COOLDOWN_MILLIS);
if (persistAtoms != null) {
// list is already shuffled when instances were inserted
@@ -475,14 +559,22 @@
}
}
- private static int pullDeviceTelephonyProperties(List<StatsEvent> data) {
+ private int pullDeviceTelephonyProperties(List<StatsEvent> data) {
Phone[] phones = getPhonesIfAny();
if (phones.length == 0) {
return StatsManager.PULL_SKIP;
}
+ boolean isAutoDataSwitchOn = Arrays.stream(phones)
+ .anyMatch(phone ->
+ phone.getSubId() != SubscriptionManager.getDefaultDataSubscriptionId()
+ && phone.getDataSettingsManager().isMobileDataPolicyEnabled(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH));
+ boolean hasDedicatedManagedProfileSub = Arrays.stream(phones)
+ .anyMatch(Phone::isManagedProfile);
- data.add(TelephonyStatsLog.buildStatsEvent(DEVICE_TELEPHONY_PROPERTIES,
- phones[0].isUsingNewDataStack()));
+ data.add(TelephonyStatsLog.buildStatsEvent(DEVICE_TELEPHONY_PROPERTIES, true,
+ isAutoDataSwitchOn, mStorage.getAutoDataSwitchToggleCount(),
+ hasDedicatedManagedProfileSub));
return StatsManager.PULL_SUCCESS;
}
@@ -683,16 +775,127 @@
perSimStatus.disabled2g, // is2gDisabled
perSimStatus.pin1Enabled, // isPin1Enabled
perSimStatus.minimumVoltageClass, // simVoltageClass
- perSimStatus.userModifiedApnTypes); // userModifiedApnTypeBitmask
+ perSimStatus.userModifiedApnTypes, // userModifiedApnTypeBitmask
+ perSimStatus.unmeteredNetworks, // unmeteredNetworks
+ perSimStatus.vonrEnabled); // vonrEnabled
data.add(statsEvent);
result = StatsManager.PULL_SUCCESS;
}
return result;
}
- /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
- private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
- mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
+ private int pullOutgoingShortCodeSms(List<StatsEvent> data) {
+ OutgoingShortCodeSms[] outgoingShortCodeSmsList = mStorage
+ .getOutgoingShortCodeSms(MIN_COOLDOWN_MILLIS);
+ if (outgoingShortCodeSmsList != null) {
+ // Outgoing short code SMS list is already shuffled when SMS were inserted
+ Arrays.stream(outgoingShortCodeSmsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "OUTGOING_SHORT_CODE_SMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSatelliteController(List<StatsEvent> data) {
+ SatelliteController[] controllerAtoms =
+ mStorage.getSatelliteControllerStats(MIN_COOLDOWN_MILLIS);
+ if (controllerAtoms != null) {
+ Arrays.stream(controllerAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_CONTROLLER pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSatelliteSession(List<StatsEvent> data) {
+ SatelliteSession[] sessionAtoms =
+ mStorage.getSatelliteSessionStats(MIN_COOLDOWN_MILLIS);
+ if (sessionAtoms != null) {
+ Arrays.stream(sessionAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_SESSION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSatelliteIncomingDatagram(List<StatsEvent> data) {
+ SatelliteIncomingDatagram[] incomingDatagramAtoms =
+ mStorage.getSatelliteIncomingDatagramStats(MIN_COOLDOWN_MILLIS);
+ if (incomingDatagramAtoms != null) {
+ Arrays.stream(incomingDatagramAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_INCOMING_DATAGRAM pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+
+ private int pullSatelliteOutgoingDatagram(List<StatsEvent> data) {
+ SatelliteOutgoingDatagram[] outgoingDatagramAtoms =
+ mStorage.getSatelliteOutgoingDatagramStats(MIN_COOLDOWN_MILLIS);
+ if (outgoingDatagramAtoms != null) {
+ Arrays.stream(outgoingDatagramAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_OUTGOING_DATAGRAM pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+
+ private int pullSatelliteProvision(List<StatsEvent> data) {
+ SatelliteProvision[] provisionAtoms =
+ mStorage.getSatelliteProvisionStats(MIN_COOLDOWN_MILLIS);
+ if (provisionAtoms != null) {
+ Arrays.stream(provisionAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_PROVISION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSatelliteSosMessageRecommender(List<StatsEvent> data) {
+ SatelliteSosMessageRecommender[] sosMessageRecommenderAtoms =
+ mStorage.getSatelliteSosMessageRecommenderStats(MIN_COOLDOWN_MILLIS);
+ if (sosMessageRecommenderAtoms != null) {
+ Arrays.stream(sosMessageRecommenderAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SATELLITE_SOS_MESSAGE_RECOMMENDER pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullEmergencyNumbersInfo(List<StatsEvent> data) {
+ boolean isDataLogged = false;
+ for (Phone phone : getPhonesIfAny()) {
+ if (phone != null) {
+ EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+ if (tracker != null) {
+ EmergencyNumbersInfo[] numList = tracker.getEmergencyNumbersProtoArray();
+ Arrays.stream(numList).forEach(number -> data.add(buildStatsEvent(number)));
+ isDataLogged = true;
+ }
+ }
+ }
+ return isDataLogged ? StatsManager.PULL_SUCCESS : StatsManager.PULL_SKIP;
+ }
+
+ /** Registers a pulled atom ID {@code atomId}. */
+ private void registerAtom(int atomId) {
+ mStatsManager.setPullAtomCallback(atomId, /* metadata= */ null,
+ ConcurrentUtils.DIRECT_EXECUTOR, this);
}
private static StatsEvent buildStatsEvent(CellularDataServiceSwitch serviceSwitch) {
@@ -717,8 +920,10 @@
state.simSlotIndex,
state.isMultiSim,
state.carrierId,
- (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- state.isEmergencyOnly);
+ roundAndConvertMillisToSeconds(state.totalTimeMillis),
+ state.isEmergencyOnly,
+ state.isInternetPdnUp,
+ state.foldState);
}
private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
@@ -726,7 +931,7 @@
VOICE_CALL_RAT_USAGE,
usage.carrierId,
usage.rat,
- round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS,
+ roundAndConvertMillisToSeconds(usage.totalDurationMillis),
usage.callCount);
}
@@ -736,7 +941,8 @@
session.bearerAtStart,
session.bearerAtEnd,
session.direction,
- session.setupDuration,
+ // deprecated and replaced by setupDurationMillis
+ VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN,
session.setupFailed,
session.disconnectReasonCode,
session.disconnectExtraCode,
@@ -767,7 +973,9 @@
session.videoEnabled,
session.ratAtConnected,
session.isMultiparty,
- session.callDuration);
+ session.callDuration,
+ session.lastKnownRat,
+ session.foldState);
}
private static StatsEvent buildStatsEvent(IncomingSms sms) {
@@ -786,7 +994,9 @@
sms.isMultiSim,
sms.isEsim,
sms.carrierId,
- sms.messageId);
+ sms.messageId,
+ sms.count,
+ sms.isManagedProfile);
}
private static StatsEvent buildStatsEvent(OutgoingSms sms) {
@@ -805,7 +1015,11 @@
sms.carrierId,
sms.messageId,
sms.retryId,
- sms.intervalMillis);
+ sms.intervalMillis,
+ sms.count,
+ sms.sendErrorCode,
+ sms.networkErrorCode,
+ sms.isManagedProfile);
}
private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) {
@@ -827,10 +1041,13 @@
dataCallSession.failureCause,
dataCallSession.suggestedRetryMillis,
dataCallSession.deactivateReason,
- round(dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS),
+ roundAndConvertMillisToMinutes(
+ dataCallSession.durationMinutes * MILLIS_PER_MINUTE),
dataCallSession.ongoing,
dataCallSession.bandAtEnd,
- dataCallSession.handoverFailureCauses);
+ dataCallSession.handoverFailureCauses,
+ dataCallSession.handoverFailureRat,
+ dataCallSession.isNonDds);
}
private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -839,19 +1056,15 @@
stats.carrierId,
stats.simSlotIndex,
stats.rat,
- (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int) (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int)
- (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
- / SECOND_IN_MILLIS),
- (int) (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int) (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int) (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int)
- (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
- / SECOND_IN_MILLIS),
- (int) (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
- (int) (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ roundAndConvertMillisToSeconds(stats.registeredMillis),
+ roundAndConvertMillisToSeconds(stats.voiceCapableMillis),
+ roundAndConvertMillisToSeconds(stats.voiceAvailableMillis),
+ roundAndConvertMillisToSeconds(stats.smsCapableMillis),
+ roundAndConvertMillisToSeconds(stats.smsAvailableMillis),
+ roundAndConvertMillisToSeconds(stats.videoCapableMillis),
+ roundAndConvertMillisToSeconds(stats.videoAvailableMillis),
+ roundAndConvertMillisToSeconds(stats.utCapableMillis),
+ roundAndConvertMillisToSeconds(stats.utAvailableMillis));
}
private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
@@ -882,7 +1095,7 @@
stats.slotId,
stats.featureTagName,
stats.registrationTech,
- (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ roundAndConvertMillisToSeconds(stats.registeredMillis));
}
private static StatsEvent buildStatsEvent(RcsClientProvisioningStats stats) {
@@ -903,7 +1116,7 @@
stats.responseType,
stats.isSingleRegistrationEnabled,
stats.count,
- (int) (round(stats.stateTimerMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ roundAndConvertMillisToSeconds(stats.stateTimerMillis));
}
private static StatsEvent buildStatsEvent(SipDelegateStats stats) {
@@ -912,7 +1125,7 @@
stats.dimension,
stats.carrierId,
stats.slotId,
- (int) (round(stats.uptimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ roundAndConvertMillisToSeconds(stats.uptimeMillis),
stats.destroyReason);
}
@@ -924,7 +1137,7 @@
stats.featureTagName,
stats.sipTransportDeniedReason,
stats.sipTransportDeregisteredReason,
- (int) (round(stats.associatedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ roundAndConvertMillisToSeconds(stats.associatedMillis));
}
private static StatsEvent buildStatsEvent(SipMessageResponse stats) {
@@ -984,7 +1197,7 @@
stats.serviceIdName,
stats.serviceIdVersion,
stats.registrationTech,
- (int) (round(stats.publishedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ roundAndConvertMillisToSeconds(stats.publishedMillis));
}
private static StatsEvent buildStatsEvent(UceEventStats stats) {
@@ -1022,8 +1235,97 @@
stats.count);
}
+ private static StatsEvent buildStatsEvent(OutgoingShortCodeSms shortCodeSms) {
+ return TelephonyStatsLog.buildStatsEvent(
+ OUTGOING_SHORT_CODE_SMS,
+ shortCodeSms.category,
+ shortCodeSms.xmlVersion,
+ shortCodeSms.shortCodeSmsCount);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteController satelliteController) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_CONTROLLER,
+ satelliteController.countOfSatelliteServiceEnablementsSuccess,
+ satelliteController.countOfSatelliteServiceEnablementsFail,
+ satelliteController.countOfOutgoingDatagramSuccess,
+ satelliteController.countOfOutgoingDatagramFail,
+ satelliteController.countOfIncomingDatagramSuccess,
+ satelliteController.countOfIncomingDatagramFail,
+ satelliteController.countOfDatagramTypeSosSmsSuccess,
+ satelliteController.countOfDatagramTypeSosSmsFail,
+ satelliteController.countOfDatagramTypeLocationSharingSuccess,
+ satelliteController.countOfDatagramTypeLocationSharingFail,
+ satelliteController.countOfProvisionSuccess,
+ satelliteController.countOfProvisionFail,
+ satelliteController.countOfDeprovisionSuccess,
+ satelliteController.countOfDeprovisionFail,
+ satelliteController.totalServiceUptimeSec,
+ satelliteController.totalBatteryConsumptionPercent,
+ satelliteController.totalBatteryChargedTimeSec);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteSession satelliteSession) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_SESSION,
+ satelliteSession.satelliteServiceInitializationResult,
+ satelliteSession.satelliteTechnology,
+ satelliteSession.count);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_INCOMING_DATAGRAM,
+ stats.resultCode,
+ stats.datagramSizeBytes,
+ stats.datagramTransferTimeMillis);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteOutgoingDatagram stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_OUTGOING_DATAGRAM,
+ stats.datagramType,
+ stats.resultCode,
+ stats.datagramSizeBytes,
+ stats.datagramTransferTimeMillis);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteProvision stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_PROVISION,
+ stats.resultCode,
+ stats.provisioningTimeSec,
+ stats.isProvisionRequest,
+ stats.isCanceled);
+ }
+
+ private static StatsEvent buildStatsEvent(SatelliteSosMessageRecommender stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SATELLITE_SOS_MESSAGE_RECOMMENDER,
+ stats.isDisplaySosMessageSent,
+ stats.countOfTimerStarted,
+ stats.isImsRegistered,
+ stats.cellularServiceState,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(EmergencyNumbersInfo emergencyNumber) {
+ return TelephonyStatsLog.buildStatsEvent(
+ EMERGENCY_NUMBERS_INFO,
+ emergencyNumber.isDbVersionIgnored,
+ emergencyNumber.assetVersion,
+ emergencyNumber.otaVersion,
+ emergencyNumber.number,
+ emergencyNumber.countryIso,
+ emergencyNumber.mnc,
+ emergencyNumber.route,
+ emergencyNumber.urns,
+ emergencyNumber.serviceCategories,
+ emergencyNumber.sources);
+ }
+
/** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
- private static Phone[] getPhonesIfAny() {
+ static Phone[] getPhonesIfAny() {
try {
return PhoneFactory.getPhones();
} catch (IllegalStateException e) {
@@ -1032,8 +1334,21 @@
}
}
- /** Returns the value rounded to the bucket. */
- private static long round(long value, long bucket) {
- return bucket == 0 ? value : ((value + bucket / 2) / bucket) * bucket;
+ /**
+ * Rounds the duration and converts it from milliseconds to seconds.
+ */
+ private static int roundAndConvertMillisToSeconds(long valueMillis) {
+ long roundedValueMillis = Math.round((double) valueMillis / DURATION_BUCKET_MILLIS)
+ * DURATION_BUCKET_MILLIS;
+ return (int) (roundedValueMillis / MILLIS_PER_SECOND);
+ }
+
+ /**
+ * Rounds the duration and converts it from milliseconds to minutes.
+ */
+ private static int roundAndConvertMillisToMinutes(long valueMillis) {
+ long roundedValueMillis = Math.round((double) valueMillis / DURATION_BUCKET_MILLIS)
+ * DURATION_BUCKET_MILLIS;
+ return (int) (roundedValueMillis / MILLIS_PER_MINUTE);
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
index 5e348a0..bc1edc3 100644
--- a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
+++ b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
@@ -18,9 +18,6 @@
import static android.provider.Telephony.Carriers.CONTENT_URI;
import static android.telephony.PhoneNumberUtils.areSamePhoneNumber;
-import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER;
-import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_IMS;
-import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_UICC;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_B;
@@ -35,7 +32,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.provider.Telephony;
-import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsManager;
@@ -44,12 +41,12 @@
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccSlot;
-import java.util.Optional;
-
/** Stores the per SIM status. */
public class PerSimStatus {
private static final long BITMASK_2G =
@@ -74,16 +71,21 @@
public final boolean pin1Enabled;
public final int minimumVoltageClass;
public final int userModifiedApnTypes;
+ public final long unmeteredNetworks;
+ public final boolean vonrEnabled;
/** Returns the current sim status of the given {@link Phone}. */
@Nullable
public static PerSimStatus getCurrentState(Phone phone) {
int[] numberIds = getNumberIds(phone);
if (numberIds == null) return null;
+ int carrierId = phone.getCarrierId();
ImsMmTelManager imsMmTelManager = getImsMmTelManager(phone);
IccCard iccCard = phone.getIccCard();
+ PersistAtomsStorage persistAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
return new PerSimStatus(
- phone.getCarrierId(),
+ carrierId,
numberIds[0],
numberIds[1],
numberIds[2],
@@ -101,7 +103,9 @@
is2gDisabled(phone),
iccCard == null ? false : iccCard.getIccLockEnabled(),
getMinimumVoltageClass(phone),
- getUserModifiedApnTypes(phone));
+ getUserModifiedApnTypes(phone),
+ persistAtomsStorage.getUnmeteredNetworks(phone.getPhoneId(), carrierId),
+ isVonrEnabled(phone));
}
private PerSimStatus(
@@ -119,7 +123,9 @@
boolean disabled2g,
boolean pin1Enabled,
int minimumVoltageClass,
- int userModifiedApnTypes) {
+ int userModifiedApnTypes,
+ long unmeteredNetworks,
+ boolean vonrEnabled) {
this.carrierId = carrierId;
this.phoneNumberSourceUicc = phoneNumberSourceUicc;
this.phoneNumberSourceCarrier = phoneNumberSourceCarrier;
@@ -135,6 +141,8 @@
this.pin1Enabled = pin1Enabled;
this.minimumVoltageClass = minimumVoltageClass;
this.userModifiedApnTypes = userModifiedApnTypes;
+ this.unmeteredNetworks = unmeteredNetworks;
+ this.vonrEnabled = vonrEnabled;
}
@Nullable
@@ -164,25 +172,24 @@
*/
@Nullable
private static int[] getNumberIds(Phone phone) {
- SubscriptionController subscriptionController = SubscriptionController.getInstance();
- if (subscriptionController == null) {
- return null;
+ String countryIso = "";
+ String[] numbersFromAllSources;
+
+ if (SubscriptionManagerService.getInstance() == null) return null;
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(phone.getSubId());
+ if (subInfo != null) {
+ countryIso = subInfo.getCountryIso();
}
- int subId = phone.getSubId();
- String countryIso =
- Optional.ofNullable(subscriptionController.getSubscriptionInfo(subId))
- .map(SubscriptionInfo::getCountryIso)
- .orElse("");
- // numbersFromAllSources[] - phone numbers from each sources:
- String[] numbersFromAllSources =
- new String[] {
- subscriptionController.getPhoneNumber(
- subId, PHONE_NUMBER_SOURCE_UICC, null, null), // 0
- subscriptionController.getPhoneNumber(
- subId, PHONE_NUMBER_SOURCE_CARRIER, null, null), // 1
- subscriptionController.getPhoneNumber(
- subId, PHONE_NUMBER_SOURCE_IMS, null, null), // 2
- };
+ numbersFromAllSources = new String[]{
+ SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
+ SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, null, null),
+ SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, null, null),
+ SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS, null, null)
+ };
+
int[] numberIds = new int[numbersFromAllSources.length]; // default value 0
for (int i = 0, idForNextUniqueNumber = 1; i < numberIds.length; i++) {
if (TextUtils.isEmpty(numbersFromAllSources[i])) {
@@ -269,4 +276,16 @@
return bitmask;
}
}
+
+ /** Returns true if VoNR is enabled */
+ private static boolean isVonrEnabled(Phone phone) {
+ TelephonyManager telephonyManager =
+ phone.getContext()
+ .getSystemService(TelephonyManager.class);
+ if (telephonyManager == null) {
+ return false;
+ }
+ telephonyManager = telephonyManager.createForSubscriptionId(phone.getSubId());
+ return telephonyManager.isVoNrEnabled();
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 2554463..5a21baf 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -25,6 +25,8 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.NetworkTypeBitMask;
+import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
@@ -40,16 +42,24 @@
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequestsV2;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.UnmeteredNetworks;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.util.ArrayUtils;
@@ -61,6 +71,7 @@
import java.nio.file.NoSuchFileException;
import java.security.SecureRandom;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.stream.IntStream;
/**
@@ -155,8 +166,15 @@
/** Maximum number of GBA Event to store between pulls. */
private final int mMaxNumGbaEventStats;
+ /** Maximum number of outgoing short code sms to store between pulls. */
+ private final int mMaxOutgoingShortCodeSms;
+
+ /** Maximum number of Satellite relevant stats to store between pulls. */
+ private final int mMaxNumSatelliteStats;
+ private final int mMaxNumSatelliteControllerStats = 1;
+
/** Stores persist atoms and persist states of the puller. */
- @VisibleForTesting protected final PersistAtoms mAtoms;
+ @VisibleForTesting protected PersistAtoms mAtoms;
/** Aggregates RAT duration and call count. */
private final VoiceCallRatTracker mVoiceCallRatTracker;
@@ -203,6 +221,8 @@
mMaxNumUceEventStats = 5;
mMaxNumPresenceNotifyEventStats = 10;
mMaxNumGbaEventStats = 5;
+ mMaxOutgoingShortCodeSms = 5;
+ mMaxNumSatelliteStats = 5;
} else {
mMaxNumVoiceCallSessions = 50;
mMaxNumSms = 25;
@@ -225,6 +245,8 @@
mMaxNumUceEventStats = 25;
mMaxNumPresenceNotifyEventStats = 50;
mMaxNumGbaEventStats = 10;
+ mMaxOutgoingShortCodeSms = 10;
+ mMaxNumSatelliteStats = 15;
}
mAtoms = loadAtomsFromFile();
@@ -254,6 +276,7 @@
/** Adds an incoming SMS to the storage. */
public synchronized void addIncomingSms(IncomingSms sms) {
+ sms.hashCode = SmsStats.getSmsHashCode(sms);
mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, mMaxNumSms);
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
@@ -263,6 +286,7 @@
/** Adds an outgoing SMS to the storage. */
public synchronized void addOutgoingSms(OutgoingSms sms) {
+ sms.hashCode = SmsStats.getSmsHashCode(sms);
// Update the retry id, if needed, so that it's unique and larger than all
// previous ones. (this algorithm ignores the fact that some SMS atoms might
// be dropped due to limit in size of the array).
@@ -318,6 +342,16 @@
DataCallSession existingCall = mAtoms.dataCallSession[index];
dataCall.ratSwitchCount += existingCall.ratSwitchCount;
dataCall.durationMinutes += existingCall.durationMinutes;
+
+ dataCall.handoverFailureCauses = IntStream.concat(Arrays.stream(
+ dataCall.handoverFailureCauses),
+ Arrays.stream(existingCall.handoverFailureCauses))
+ .limit(DataCallSessionStats.SIZE_LIMIT_HANDOVER_FAILURES).toArray();
+ dataCall.handoverFailureRat = IntStream.concat(Arrays.stream(
+ dataCall.handoverFailureRat),
+ Arrays.stream(existingCall.handoverFailureRat))
+ .limit(DataCallSessionStats.SIZE_LIMIT_HANDOVER_FAILURES).toArray();
+
mAtoms.dataCallSession[index] = dataCall;
} else {
mAtoms.dataCallSession =
@@ -349,9 +383,12 @@
mMaxNumCarrierIdMismatches - 1);
mAtoms.carrierIdMismatch[mMaxNumCarrierIdMismatches - 1] = carrierIdMismatch;
} else {
- int newLength = mAtoms.carrierIdMismatch.length + 1;
- mAtoms.carrierIdMismatch = Arrays.copyOf(mAtoms.carrierIdMismatch, newLength);
- mAtoms.carrierIdMismatch[newLength - 1] = carrierIdMismatch;
+ mAtoms.carrierIdMismatch =
+ ArrayUtils.appendElement(
+ CarrierIdMismatch.class,
+ mAtoms.carrierIdMismatch,
+ carrierIdMismatch,
+ true);
}
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
return true;
@@ -412,6 +449,14 @@
}
}
+ /**
+ * Store the number of times auto data switch feature is toggled.
+ */
+ public synchronized void recordToggledAutoDataSwitch() {
+ mAtoms.autoDataSwitchToggleCount++;
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
/** Adds a new {@link NetworkRequestsV2} to the storage. */
public synchronized void addNetworkRequestsV2(NetworkRequestsV2 networkRequests) {
NetworkRequestsV2 existingMetrics = find(networkRequests);
@@ -422,9 +467,9 @@
newMetrics.capability = networkRequests.capability;
newMetrics.carrierId = networkRequests.carrierId;
newMetrics.requestCount = networkRequests.requestCount;
- int newLength = mAtoms.networkRequestsV2.length + 1;
- mAtoms.networkRequestsV2 = Arrays.copyOf(mAtoms.networkRequestsV2, newLength);
- mAtoms.networkRequestsV2[newLength - 1] = newMetrics;
+ mAtoms.networkRequestsV2 =
+ ArrayUtils.appendElement(
+ NetworkRequestsV2.class, mAtoms.networkRequestsV2, newMetrics, true);
}
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
}
@@ -602,6 +647,148 @@
}
/**
+ * Sets the unmetered networks bitmask for a given phone id. If the carrier id
+ * doesn't match the existing UnmeteredNetworks' carrier id, the bitmask is
+ * first reset to 0.
+ */
+ public synchronized void addUnmeteredNetworks(
+ int phoneId, int carrierId, @NetworkTypeBitMask long bitmask) {
+ UnmeteredNetworks stats = findUnmeteredNetworks(phoneId);
+ boolean needToSave = true;
+ if (stats == null) {
+ stats = new UnmeteredNetworks();
+ stats.phoneId = phoneId;
+ stats.carrierId = carrierId;
+ stats.unmeteredNetworksBitmask = bitmask;
+ mAtoms.unmeteredNetworks =
+ ArrayUtils.appendElement(
+ UnmeteredNetworks.class, mAtoms.unmeteredNetworks, stats, true);
+ } else {
+ // Reset the bitmask to 0 if carrier id doesn't match.
+ if (stats.carrierId != carrierId) {
+ stats.carrierId = carrierId;
+ stats.unmeteredNetworksBitmask = 0;
+ }
+ if ((stats.unmeteredNetworksBitmask | bitmask) != stats.unmeteredNetworksBitmask) {
+ stats.unmeteredNetworksBitmask |= bitmask;
+ } else {
+ needToSave = false;
+ }
+ }
+ // Only save if something changes.
+ if (needToSave) {
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+ }
+
+ /** Adds an outgoing short code sms to the storage. */
+ public synchronized void addOutgoingShortCodeSms(OutgoingShortCodeSms shortCodeSms) {
+ OutgoingShortCodeSms existingOutgoingShortCodeSms = find(shortCodeSms);
+ if (existingOutgoingShortCodeSms != null) {
+ existingOutgoingShortCodeSms.shortCodeSmsCount += 1;
+ } else {
+ mAtoms.outgoingShortCodeSms = insertAtRandomPlace(mAtoms.outgoingShortCodeSms,
+ shortCodeSms, mMaxOutgoingShortCodeSms);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteController} to the storage. */
+ public synchronized void addSatelliteControllerStats(SatelliteController stats) {
+ // SatelliteController is a single data point
+ SatelliteController[] atomArray = mAtoms.satelliteController;
+ if (atomArray == null || atomArray.length == 0) {
+ atomArray = new SatelliteController[] {new SatelliteController()};
+ }
+
+ SatelliteController atom = atomArray[0];
+ atom.countOfSatelliteServiceEnablementsSuccess
+ += stats.countOfSatelliteServiceEnablementsSuccess;
+ atom.countOfSatelliteServiceEnablementsFail
+ += stats.countOfSatelliteServiceEnablementsFail;
+ atom.countOfOutgoingDatagramSuccess
+ += stats.countOfOutgoingDatagramSuccess;
+ atom.countOfOutgoingDatagramFail
+ += stats.countOfOutgoingDatagramFail;
+ atom.countOfIncomingDatagramSuccess
+ += stats.countOfIncomingDatagramSuccess;
+ atom.countOfIncomingDatagramFail
+ += stats.countOfIncomingDatagramFail;
+ atom.countOfDatagramTypeSosSmsSuccess
+ += stats.countOfDatagramTypeSosSmsSuccess;
+ atom.countOfDatagramTypeSosSmsFail
+ += stats.countOfDatagramTypeSosSmsFail;
+ atom.countOfDatagramTypeLocationSharingSuccess
+ += stats.countOfDatagramTypeLocationSharingSuccess;
+ atom.countOfDatagramTypeLocationSharingFail
+ += stats.countOfDatagramTypeLocationSharingFail;
+ atom.countOfProvisionSuccess
+ += stats.countOfProvisionSuccess;
+ atom.countOfProvisionFail
+ += stats.countOfProvisionFail;
+ atom.countOfDeprovisionSuccess
+ += stats.countOfDeprovisionSuccess;
+ atom.countOfDeprovisionFail
+ += stats.countOfDeprovisionFail;
+ atom.totalServiceUptimeSec
+ += stats.totalServiceUptimeSec;
+ atom.totalBatteryConsumptionPercent
+ += stats.totalBatteryConsumptionPercent;
+ atom.totalBatteryChargedTimeSec
+ += stats.totalBatteryChargedTimeSec;
+
+ mAtoms.satelliteController = atomArray;
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteSession} to the storage. */
+ public synchronized void addSatelliteSessionStats(SatelliteSession stats) {
+ SatelliteSession existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.satelliteSession =
+ insertAtRandomPlace(mAtoms.satelliteSession, stats, mMaxNumSatelliteStats);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteIncomingDatagram} to the storage. */
+ public synchronized void addSatelliteIncomingDatagramStats(SatelliteIncomingDatagram stats) {
+ mAtoms.satelliteIncomingDatagram =
+ insertAtRandomPlace(mAtoms.satelliteIncomingDatagram, stats, mMaxNumSatelliteStats);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteOutgoingDatagram} to the storage. */
+ public synchronized void addSatelliteOutgoingDatagramStats(SatelliteOutgoingDatagram stats) {
+ mAtoms.satelliteOutgoingDatagram =
+ insertAtRandomPlace(mAtoms.satelliteOutgoingDatagram, stats, mMaxNumSatelliteStats);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteProvision} to the storage. */
+ public synchronized void addSatelliteProvisionStats(SatelliteProvision stats) {
+ mAtoms.satelliteProvision =
+ insertAtRandomPlace(mAtoms.satelliteProvision, stats, mMaxNumSatelliteStats);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SatelliteSosMessageRecommender} to the storage. */
+ public synchronized void addSatelliteSosMessageRecommenderStats(
+ SatelliteSosMessageRecommender stats) {
+ SatelliteSosMessageRecommender existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.satelliteSosMessageRecommender =
+ insertAtRandomPlace(mAtoms.satelliteSosMessageRecommender, stats,
+ mMaxNumSatelliteStats);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /**
* Returns and clears the voice call sessions if last pulled longer than {@code
* minIntervalMillis} ago, otherwise returns {@code null}.
*/
@@ -683,7 +870,8 @@
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
for (DataCallSession dataCallSession : previousDataCallSession) {
// sort to de-correlate any potential pattern for UII concern
- Arrays.sort(dataCallSession.handoverFailureCauses);
+ sortBaseOnArray(dataCallSession.handoverFailureCauses,
+ dataCallSession.handoverFailureRat);
}
return previousDataCallSession;
} else {
@@ -692,6 +880,27 @@
}
/**
+ * Sort the other array base on the natural order of the primary array. Both arrays will be
+ * sorted in-place.
+ * @param primary The primary array to be sorted.
+ * @param other The other array to be sorted in the order of primary array.
+ */
+ private void sortBaseOnArray(int[] primary, int[] other) {
+ if (other.length != primary.length) return;
+ int[] index = IntStream.range(0, primary.length).boxed()
+ .sorted(Comparator.comparingInt(i -> primary[i]))
+ .mapToInt(Integer::intValue)
+ .toArray();
+ int[] primaryCopy = Arrays.copyOf(primary, primary.length);
+ int[] otherCopy = Arrays.copyOf(other, other.length);
+ for (int i = 0; i < index.length; i++) {
+ primary[i] = primaryCopy[index[i]];
+ other[i] = otherCopy[index[i]];
+ }
+ }
+
+
+ /**
* Returns and clears the service state durations if last pulled longer than {@code
* minIntervalMillis} ago, otherwise returns {@code null}.
*/
@@ -789,15 +998,26 @@
}
}
+ /** @return the number of times auto data switch mobile data policy is toggled. */
+ public synchronized int getAutoDataSwitchToggleCount() {
+ int count = mAtoms.autoDataSwitchToggleCount;
+ if (count > 0) {
+ mAtoms.autoDataSwitchToggleCount = 0;
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ }
+ return count;
+ }
+
/**
- * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than {@code
- * minIntervalMillis} ago, otherwise returns {@code null}.
+ * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than
+ * {@code minIntervalMillis} ago, otherwise returns {@code null}.
*/
@Nullable
public synchronized ImsRegistrationFeatureTagStats[] getImsRegistrationFeatureTagStats(
long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis
- > minIntervalMillis) {
+ long intervalMillis =
+ getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis;
+ if (intervalMillis > minIntervalMillis) {
mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis = getWallTimeMillis();
ImsRegistrationFeatureTagStats[] previousStats =
mAtoms.imsRegistrationFeatureTagStats;
@@ -829,16 +1049,26 @@
}
/**
- * Returns and clears the RcsAcsProvisioningStats if last pulled longer than {@code
- * minIntervalMillis} ago, otherwise returns {@code null}.
+ * Returns and clears the RcsAcsProvisioningStats normalized to 24h cycle if last pulled
+ * longer than {@code minIntervalMillis} ago, otherwise returns {@code null}.
*/
@Nullable
public synchronized RcsAcsProvisioningStats[] getRcsAcsProvisioningStats(
long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis
- > minIntervalMillis) {
+ long intervalMillis =
+ getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis;
+ if (intervalMillis > minIntervalMillis) {
mAtoms.rcsAcsProvisioningStatsPullTimestampMillis = getWallTimeMillis();
RcsAcsProvisioningStats[] previousStats = mAtoms.rcsAcsProvisioningStats;
+
+ for (RcsAcsProvisioningStats stat: previousStats) {
+ // in case pull interval is greater than 24H, normalize it as of one day interval
+ if (intervalMillis > DAY_IN_MILLIS) {
+ stat.stateTimerMillis = normalizeDurationTo24H(stat.stateTimerMillis,
+ intervalMillis);
+ }
+ }
+
mAtoms.rcsAcsProvisioningStats = new RcsAcsProvisioningStats[0];
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousStats;
@@ -853,10 +1083,19 @@
*/
@Nullable
public synchronized SipDelegateStats[] getSipDelegateStats(long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.sipDelegateStatsPullTimestampMillis
- > minIntervalMillis) {
+ long intervalMillis = getWallTimeMillis() - mAtoms.sipDelegateStatsPullTimestampMillis;
+ if (intervalMillis > minIntervalMillis) {
mAtoms.sipDelegateStatsPullTimestampMillis = getWallTimeMillis();
SipDelegateStats[] previousStats = mAtoms.sipDelegateStats;
+
+ for (SipDelegateStats stat: previousStats) {
+ // in case pull interval is greater than 24H, normalize it as of one day interval
+ if (intervalMillis > DAY_IN_MILLIS) {
+ stat.uptimeMillis = normalizeDurationTo24H(stat.uptimeMillis,
+ intervalMillis);
+ }
+ }
+
mAtoms.sipDelegateStats = new SipDelegateStats[0];
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousStats;
@@ -872,10 +1111,20 @@
@Nullable
public synchronized SipTransportFeatureTagStats[] getSipTransportFeatureTagStats(
long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.sipTransportFeatureTagStatsPullTimestampMillis
- > minIntervalMillis) {
+ long intervalMillis =
+ getWallTimeMillis() - mAtoms.sipTransportFeatureTagStatsPullTimestampMillis;
+ if (intervalMillis > minIntervalMillis) {
mAtoms.sipTransportFeatureTagStatsPullTimestampMillis = getWallTimeMillis();
SipTransportFeatureTagStats[] previousStats = mAtoms.sipTransportFeatureTagStats;
+
+ for (SipTransportFeatureTagStats stat: previousStats) {
+ // in case pull interval is greater than 24H, normalize it as of one day interval
+ if (intervalMillis > DAY_IN_MILLIS) {
+ stat.associatedMillis = normalizeDurationTo24H(stat.associatedMillis,
+ intervalMillis);
+ }
+ }
+
mAtoms.sipTransportFeatureTagStats = new SipTransportFeatureTagStats[0];
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousStats;
@@ -969,11 +1218,21 @@
@Nullable
public synchronized ImsRegistrationServiceDescStats[] getImsRegistrationServiceDescStats(long
minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis
- > minIntervalMillis) {
+ long intervalMillis =
+ getWallTimeMillis() - mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis;
+ if (intervalMillis > minIntervalMillis) {
mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis = getWallTimeMillis();
ImsRegistrationServiceDescStats[] previousStats =
mAtoms.imsRegistrationServiceDescStats;
+
+ for (ImsRegistrationServiceDescStats stat: previousStats) {
+ // in case pull interval is greater than 24H, normalize it as of one day interval
+ if (intervalMillis > DAY_IN_MILLIS) {
+ stat.publishedMillis = normalizeDurationTo24H(stat.publishedMillis,
+ intervalMillis);
+ }
+ }
+
mAtoms.imsRegistrationServiceDescStats = new ImsRegistrationServiceDescStats[0];
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousStats;
@@ -1034,12 +1293,168 @@
}
}
- /** Saves a pending {@link PersistAtoms} to a file in private storage immediately. */
- public void flushAtoms() {
- if (mHandler.hasCallbacks(mSaveRunnable)) {
- mHandler.removeCallbacks(mSaveRunnable);
- saveAtomsToFileNow();
+ /**
+ * Returns the unmetered networks bitmask for a given phone id. Returns 0 if there is
+ * no existing UnmeteredNetworks for the given phone id or the carrier id doesn't match.
+ * Existing UnmeteredNetworks is discarded after.
+ */
+ public synchronized @NetworkTypeBitMask long getUnmeteredNetworks(int phoneId, int carrierId) {
+ UnmeteredNetworks existingStats = findUnmeteredNetworks(phoneId);
+ if (existingStats == null) {
+ return 0L;
}
+ @NetworkTypeBitMask
+ long bitmask =
+ existingStats.carrierId != carrierId ? 0L : existingStats.unmeteredNetworksBitmask;
+ mAtoms.unmeteredNetworks =
+ sanitizeAtoms(
+ ArrayUtils.removeElement(
+ UnmeteredNetworks.class,
+ mAtoms.unmeteredNetworks,
+ existingStats),
+ UnmeteredNetworks.class);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return bitmask;
+ }
+
+ /**
+ * Returns and clears the OutgoingShortCodeSms if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized OutgoingShortCodeSms[] getOutgoingShortCodeSms(long minIntervalMillis) {
+ if ((getWallTimeMillis() - mAtoms.outgoingShortCodeSmsPullTimestampMillis)
+ > minIntervalMillis) {
+ mAtoms.outgoingShortCodeSmsPullTimestampMillis = getWallTimeMillis();
+ OutgoingShortCodeSms[] previousOutgoingShortCodeSms = mAtoms.outgoingShortCodeSms;
+ mAtoms.outgoingShortCodeSms = new OutgoingShortCodeSms[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousOutgoingShortCodeSms;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteController} stats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteController[] getSatelliteControllerStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteControllerPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteControllerPullTimestampMillis = getWallTimeMillis();
+ SatelliteController[] statsArray = mAtoms.satelliteController;
+ mAtoms.satelliteController = new SatelliteController[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteSession} stats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteSession[] getSatelliteSessionStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteSessionPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteSessionPullTimestampMillis = getWallTimeMillis();
+ SatelliteSession[] statsArray = mAtoms.satelliteSession;
+ mAtoms.satelliteSession = new SatelliteSession[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteIncomingDatagram} stats if last pulled longer than
+ * {@code minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteIncomingDatagram[] getSatelliteIncomingDatagramStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteIncomingDatagramPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteIncomingDatagramPullTimestampMillis = getWallTimeMillis();
+ SatelliteIncomingDatagram[] statsArray = mAtoms.satelliteIncomingDatagram;
+ mAtoms.satelliteIncomingDatagram = new SatelliteIncomingDatagram[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteOutgoingDatagram} stats if last pulled longer than
+ * {@code minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteOutgoingDatagram[] getSatelliteOutgoingDatagramStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteOutgoingDatagramPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteOutgoingDatagramPullTimestampMillis = getWallTimeMillis();
+ SatelliteOutgoingDatagram[] statsArray = mAtoms.satelliteOutgoingDatagram;
+ mAtoms.satelliteOutgoingDatagram = new SatelliteOutgoingDatagram[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteProvision} stats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteProvision[] getSatelliteProvisionStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteProvisionPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteProvisionPullTimestampMillis = getWallTimeMillis();
+ SatelliteProvision[] statsArray = mAtoms.satelliteProvision;
+ mAtoms.satelliteProvision = new SatelliteProvision[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the {@link SatelliteSosMessageRecommender} stats if last pulled longer
+ * than {@code minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SatelliteSosMessageRecommender[] getSatelliteSosMessageRecommenderStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.satelliteSosMessageRecommenderPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.satelliteProvisionPullTimestampMillis = getWallTimeMillis();
+ SatelliteSosMessageRecommender[] statsArray = mAtoms.satelliteSosMessageRecommender;
+ mAtoms.satelliteSosMessageRecommender = new SatelliteSosMessageRecommender[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return statsArray;
+ } else {
+ return null;
+ }
+ }
+
+ /** Saves {@link PersistAtoms} to a file in private storage immediately. */
+ public synchronized void flushAtoms() {
+ saveAtomsToFile(0);
+ }
+
+ /** Clears atoms for testing purpose. */
+ public synchronized void clearAtoms() {
+ mAtoms = makeNewPersistAtoms();
+ saveAtomsToFile(0);
}
/** Loads {@link PersistAtoms} from a file in private storage. */
@@ -1161,6 +1576,26 @@
atoms.gbaEvent,
GbaEvent.class,
mMaxNumGbaEventStats);
+ atoms.unmeteredNetworks =
+ sanitizeAtoms(
+ atoms.unmeteredNetworks,
+ UnmeteredNetworks.class
+ );
+ atoms.outgoingShortCodeSms = sanitizeAtoms(atoms.outgoingShortCodeSms,
+ OutgoingShortCodeSms.class, mMaxOutgoingShortCodeSms);
+ atoms.satelliteController = sanitizeAtoms(atoms.satelliteController,
+ SatelliteController.class, mMaxNumSatelliteControllerStats);
+ atoms.satelliteSession = sanitizeAtoms(atoms.satelliteSession,
+ SatelliteSession.class, mMaxNumSatelliteStats);
+ atoms.satelliteIncomingDatagram = sanitizeAtoms(atoms.satelliteIncomingDatagram,
+ SatelliteIncomingDatagram.class, mMaxNumSatelliteStats);
+ atoms.satelliteOutgoingDatagram = sanitizeAtoms(atoms.satelliteOutgoingDatagram,
+ SatelliteOutgoingDatagram.class, mMaxNumSatelliteStats);
+ atoms.satelliteProvision = sanitizeAtoms(atoms.satelliteProvision,
+ SatelliteProvision.class, mMaxNumSatelliteStats);
+ atoms.satelliteSosMessageRecommender = sanitizeAtoms(
+ atoms.satelliteSosMessageRecommender, SatelliteSosMessageRecommender.class,
+ mMaxNumSatelliteStats);
// out of caution, sanitize also the timestamps
atoms.voiceCallRatUsagePullTimestampMillis =
@@ -1209,7 +1644,20 @@
sanitizeTimestamp(atoms.presenceNotifyEventPullTimestampMillis);
atoms.gbaEventPullTimestampMillis =
sanitizeTimestamp(atoms.gbaEventPullTimestampMillis);
-
+ atoms.outgoingShortCodeSmsPullTimestampMillis =
+ sanitizeTimestamp(atoms.outgoingShortCodeSmsPullTimestampMillis);
+ atoms.satelliteControllerPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteControllerPullTimestampMillis);
+ atoms.satelliteSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteSessionPullTimestampMillis);
+ atoms.satelliteIncomingDatagramPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteIncomingDatagramPullTimestampMillis);
+ atoms.satelliteOutgoingDatagramPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteOutgoingDatagramPullTimestampMillis);
+ atoms.satelliteProvisionPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteProvisionPullTimestampMillis);
+ atoms.satelliteSosMessageRecommenderPullTimestampMillis =
+ sanitizeTimestamp(atoms.satelliteSosMessageRecommenderPullTimestampMillis);
return atoms;
} catch (NoSuchFileException e) {
Rlog.d(TAG, "PersistAtoms file not found");
@@ -1220,14 +1668,14 @@
}
/**
- * Posts message to save a copy of {@link PersistAtoms} to a file after a delay.
+ * Posts message to save a copy of {@link PersistAtoms} to a file after a delay or immediately.
*
* <p>The delay is introduced to avoid too frequent operations to disk, which would negatively
* impact the power consumption.
*/
- private void saveAtomsToFile(int delayMillis) {
+ private synchronized void saveAtomsToFile(int delayMillis) {
+ mHandler.removeCallbacks(mSaveRunnable);
if (delayMillis > 0 && !mSaveImmediately) {
- mHandler.removeCallbacks(mSaveRunnable);
if (mHandler.postDelayed(mSaveRunnable, delayMillis)) {
return;
}
@@ -1259,7 +1707,9 @@
&& state.simSlotIndex == key.simSlotIndex
&& state.isMultiSim == key.isMultiSim
&& state.carrierId == key.carrierId
- && state.isEmergencyOnly == key.isEmergencyOnly) {
+ && state.isEmergencyOnly == key.isEmergencyOnly
+ && state.isInternetPdnUp == key.isInternetPdnUp
+ && state.foldState == key.foldState) {
return state;
}
}
@@ -1554,7 +2004,7 @@
* the given one, or {@code null} if it does not exist.
*/
private @Nullable SipTransportFeatureTagStats find(SipTransportFeatureTagStats key) {
- for (SipTransportFeatureTagStats stat: mAtoms.sipTransportFeatureTagStats) {
+ for (SipTransportFeatureTagStats stat : mAtoms.sipTransportFeatureTagStats) {
if (stat.carrierId == key.carrierId
&& stat.slotId == key.slotId
&& stat.featureTagName == key.featureTagName
@@ -1566,9 +2016,67 @@
return null;
}
+ /** Returns the UnmeteredNetworks given a phone id. */
+ private @Nullable UnmeteredNetworks findUnmeteredNetworks(int phoneId) {
+ for (UnmeteredNetworks unmeteredNetworks : mAtoms.unmeteredNetworks) {
+ if (unmeteredNetworks.phoneId == phoneId) {
+ return unmeteredNetworks;
+ }
+ }
+ return null;
+ }
+
/**
- * Inserts a new element in a random position in an array with a maximum size, replacing the
- * least recent item if possible.
+ * Returns OutgoingShortCodeSms atom that has same category, xmlVersion as the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable OutgoingShortCodeSms find(OutgoingShortCodeSms key) {
+ for (OutgoingShortCodeSms shortCodeSms : mAtoms.outgoingShortCodeSms) {
+ if (shortCodeSms.category == key.category
+ && shortCodeSms.xmlVersion == key.xmlVersion) {
+ return shortCodeSms;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+ * if it does not exist.
+ */
+ private @Nullable SatelliteSession find(
+ SatelliteSession key) {
+ for (SatelliteSession stats : mAtoms.satelliteSession) {
+ if (stats.satelliteServiceInitializationResult
+ == key.satelliteServiceInitializationResult
+ && stats.satelliteTechnology == key.satelliteTechnology) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+ * if it does not exist.
+ */
+ private @Nullable SatelliteSosMessageRecommender find(
+ SatelliteSosMessageRecommender key) {
+ for (SatelliteSosMessageRecommender stats : mAtoms.satelliteSosMessageRecommender) {
+ if (stats.isDisplaySosMessageSent == key.isDisplaySosMessageSent
+ && stats.countOfTimerStarted == key.countOfTimerStarted
+ && stats.isImsRegistered == key.isImsRegistered
+ && stats.cellularServiceState == key.cellularServiceState) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Inserts a new element in a random position in an array with a maximum size.
+ *
+ * <p>If the array is full, merge with existing item if possible or replace one item randomly.
*/
private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) {
final int newLength = storage.length + 1;
@@ -1577,7 +2085,11 @@
if (newLength == 1) {
result[0] = instance;
} else if (arrayFull) {
- result[findItemToEvict(storage)] = instance;
+ if (instance instanceof OutgoingSms || instance instanceof IncomingSms) {
+ mergeSmsOrEvictInFullStorage(result, instance);
+ } else {
+ result[findItemToEvict(storage)] = instance;
+ }
} else {
// insert at random place (by moving the item at the random place to the end)
int insertAt = sRandom.nextInt(newLength);
@@ -1587,6 +2099,90 @@
return result;
}
+ /**
+ * Merge new sms in a full storage.
+ *
+ * <p>If new sms is similar to old sms, merge them.
+ * If not, merge 2 old similar sms and add the new sms.
+ * If not, replace old sms with the lowest count.
+ */
+ private static <T> void mergeSmsOrEvictInFullStorage(T[] storage, T instance) {
+ // key: hashCode, value: smsIndex
+ SparseIntArray map = new SparseIntArray();
+ int smsIndex1 = -1;
+ int smsIndex2 = -1;
+ int indexLowestCount = -1;
+ int minCount = Integer.MAX_VALUE;
+
+ for (int i = 0; i < storage.length; i++) {
+ // If the new SMS can be merged to an existing item, merge it and return immediately.
+ if (areSmsMergeable(storage[i], instance)) {
+ storage[i] = mergeSms(storage[i], instance);
+ return;
+ }
+
+ // Keep sms index with lowest count to evict, in case we cannot merge any 2 messages.
+ int smsCount = getSmsCount(storage[i]);
+ if (smsCount < minCount) {
+ indexLowestCount = i;
+ minCount = smsCount;
+ }
+
+ // Find any 2 messages in the storage that can be merged together.
+ if (smsIndex1 != -1) {
+ int smsHashCode = getSmsHashCode(storage[i]);
+ if (map.indexOfKey(smsHashCode) < 0) {
+ map.append(smsHashCode, i);
+ } else {
+ smsIndex1 = map.get(smsHashCode);
+ smsIndex2 = i;
+ }
+ }
+ }
+
+ // Merge 2 similar old sms and add the new sms
+ if (smsIndex1 != -1) {
+ storage[smsIndex1] = mergeSms(storage[smsIndex1], storage[smsIndex2]);
+ storage[smsIndex2] = instance;
+ return;
+ }
+
+ // Or replace old sms that has the lowest count
+ storage[indexLowestCount] = instance;
+ return;
+ }
+
+ private static <T> int getSmsHashCode(T sms) {
+ return sms instanceof OutgoingSms
+ ? ((OutgoingSms) sms).hashCode : ((IncomingSms) sms).hashCode;
+ }
+
+ private static <T> int getSmsCount(T sms) {
+ return sms instanceof OutgoingSms
+ ? ((OutgoingSms) sms).count : ((IncomingSms) sms).count;
+ }
+
+ /** Compares 2 SMS hash codes to check if they can be clubbed together in the metrics. */
+ private static <T> boolean areSmsMergeable(T instance1, T instance2) {
+ return getSmsHashCode(instance1) == getSmsHashCode(instance2);
+ }
+
+ /** Merges sms2 data on top of sms1 and returns the merged value. */
+ private static <T> T mergeSms(T sms1, T sms2) {
+ if (sms1 instanceof OutgoingSms) {
+ OutgoingSms tSms1 = (OutgoingSms) sms1;
+ OutgoingSms tSms2 = (OutgoingSms) sms2;
+ tSms1.intervalMillis = (tSms1.intervalMillis * tSms1.count
+ + tSms2.intervalMillis * tSms2.count) / (tSms1.count + tSms2.count);
+ tSms1.count += tSms2.count;
+ } else if (sms1 instanceof IncomingSms) {
+ IncomingSms tSms1 = (IncomingSms) sms1;
+ IncomingSms tSms2 = (IncomingSms) sms2;
+ tSms1.count += tSms2.count;
+ }
+ return sms1;
+ }
+
/** Returns index of the item suitable for eviction when the array is full. */
private static <T> int findItemToEvict(T[] array) {
if (array instanceof CellularServiceState[]) {
@@ -1722,6 +2318,13 @@
atoms.uceEventStatsPullTimestampMillis = currentTime;
atoms.presenceNotifyEventPullTimestampMillis = currentTime;
atoms.gbaEventPullTimestampMillis = currentTime;
+ atoms.outgoingShortCodeSmsPullTimestampMillis = currentTime;
+ atoms.satelliteControllerPullTimestampMillis = currentTime;
+ atoms.satelliteSessionPullTimestampMillis = currentTime;
+ atoms.satelliteIncomingDatagramPullTimestampMillis = currentTime;
+ atoms.satelliteOutgoingDatagramPullTimestampMillis = currentTime;
+ atoms.satelliteProvisionPullTimestampMillis = currentTime;
+ atoms.satelliteSosMessageRecommenderPullTimestampMillis = currentTime;
Rlog.d(TAG, "created new PersistAtoms");
return atoms;
diff --git a/src/java/com/android/internal/telephony/metrics/RcsStats.java b/src/java/com/android/internal/telephony/metrics/RcsStats.java
index de77b12..8d24def 100644
--- a/src/java/com/android/internal/telephony/metrics/RcsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/RcsStats.java
@@ -56,7 +56,6 @@
import android.telephony.ims.RcsContactPresenceTuple;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IRcsConfigCallback;
-import android.util.Base64;
import android.util.IndentingPrintWriter;
import com.android.ims.rcs.uce.UceStatsWriter;
@@ -83,10 +82,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -131,48 +130,51 @@
private static final Map<String, Integer> FEATURE_TAGS = new HashMap<>();
static {
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_IM);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_SESSION);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim()
+ .toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER_VIA_SMS);
FEATURE_TAGS.put(
- FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim().toLowerCase(),
+ FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim()
+ .toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING);
FEATURE_TAGS.put(
- FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(),
+ FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_POST_CALL);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_MAP);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_SKETCH);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH_VIA_SMS);
FEATURE_TAGS.put(
- FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim().toLowerCase(),
+ FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim()
+ .toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION);
String FeatureTag = FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG;
- FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG);
FEATURE_TAGS.put(
- FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(),
+ FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_VERSION_SUPPORTED);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_ROLE);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_MMTEL);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_VIDEO);
- FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(),
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_PRESENCE);
}
@@ -185,34 +187,42 @@
private static final Map<String, Integer> SERVICE_IDS = new HashMap<>();
static {
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE
);
- SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim().toLowerCase(),
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim()
+ .toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE);
}
@@ -223,33 +233,33 @@
private static final Map<String, Integer> MESSAGE_TYPE = new HashMap<>();
static {
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_INVITE);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_ACK);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_OPTIONS);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_BYE);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_CANCEL);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_REGISTER);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_PRACK);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_SUBSCRIBE);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_NOTIFY);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_PUBLISH);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_INFO);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_REFER);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_MESSAGE);
- MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(),
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_UPDATE);
}
@@ -1566,39 +1576,34 @@
@VisibleForTesting
protected int getSubId(int slotId) {
- final int[] subIds = SubscriptionManager.getSubId(slotId);
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- if (subIds != null && subIds.length >= 1) {
- subId = subIds[0];
- }
- return subId;
+ return SubscriptionManager.getSubscriptionId(slotId);
}
/** Get a enum value from pre-defined feature tag name list */
@VisibleForTesting
public int convertTagNameToValue(@NonNull String tagName) {
- return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(),
+ return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM);
}
/** Get a enum value from pre-defined service id list */
@VisibleForTesting
public int convertServiceIdToValue(@NonNull String serviceId) {
- return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(),
+ return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(Locale.ROOT),
IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM);
}
/** Get a enum value from pre-defined message type list */
@VisibleForTesting
public int convertMessageTypeToValue(@NonNull String messageType) {
- return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(),
+ return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(Locale.ROOT),
TelephonyProtoEnums.SIP_REQUEST_CUSTOM);
}
/** Get a enum value from pre-defined reason list */
@VisibleForTesting
public int convertPresenceNotifyReason(@NonNull String reason) {
- return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(),
+ return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(Locale.ROOT),
PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM);
}
@@ -1777,84 +1782,4 @@
}
pw.decreaseIndent();
}
-
- /**
- * Reset all events
- */
- public synchronized void reset() {
- if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
- return;
- }
-
- PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
-
- metricAtoms.imsRegistrationFeatureTagStats =
- PersistAtomsProto.ImsRegistrationFeatureTagStats.emptyArray();
- metricAtoms.rcsClientProvisioningStats =
- PersistAtomsProto.RcsClientProvisioningStats.emptyArray();
- metricAtoms.rcsAcsProvisioningStats =
- PersistAtomsProto.RcsAcsProvisioningStats.emptyArray();
- metricAtoms.sipDelegateStats = PersistAtomsProto.SipDelegateStats.emptyArray();
- metricAtoms.sipTransportFeatureTagStats =
- PersistAtomsProto.SipTransportFeatureTagStats.emptyArray();
- metricAtoms.sipMessageResponse = PersistAtomsProto.SipMessageResponse.emptyArray();
- metricAtoms.sipTransportSession = PersistAtomsProto.SipTransportSession.emptyArray();
- metricAtoms.imsDedicatedBearerListenerEvent =
- PersistAtomsProto.ImsDedicatedBearerListenerEvent.emptyArray();
- metricAtoms.imsDedicatedBearerEvent =
- PersistAtomsProto.ImsDedicatedBearerEvent.emptyArray();
- metricAtoms.imsRegistrationServiceDescStats =
- PersistAtomsProto.ImsRegistrationServiceDescStats.emptyArray();
- metricAtoms.uceEventStats = PersistAtomsProto.UceEventStats.emptyArray();
- metricAtoms.presenceNotifyEvent = PersistAtomsProto.PresenceNotifyEvent.emptyArray();
- metricAtoms.gbaEvent = PersistAtomsProto.GbaEvent.emptyArray();
- }
-
- /**
- * Convert the PersistAtomsProto into Base-64 encoded string
- *
- * @return Encoded string
- */
- public String buildLog() {
- PersistAtomsProto.PersistAtoms log = buildProto();
- return Base64.encodeToString(
- PersistAtomsProto.PersistAtoms.toByteArray(log), Base64.DEFAULT);
- }
-
- /**
- * Build the PersistAtomsProto
- *
- * @return PersistAtomsProto.PersistAtoms
- */
- public PersistAtomsProto.PersistAtoms buildProto() {
- PersistAtomsProto.PersistAtoms log = new PersistAtomsProto.PersistAtoms();
-
- PersistAtomsProto.PersistAtoms atoms = mAtomsStorage.mAtoms;
- log.imsRegistrationFeatureTagStats = Arrays.copyOf(atoms.imsRegistrationFeatureTagStats,
- atoms.imsRegistrationFeatureTagStats.length);
- log.rcsClientProvisioningStats = Arrays.copyOf(atoms.rcsClientProvisioningStats,
- atoms.rcsClientProvisioningStats.length);
- log.rcsAcsProvisioningStats = Arrays.copyOf(atoms.rcsAcsProvisioningStats,
- atoms.rcsAcsProvisioningStats.length);
- log.sipDelegateStats = Arrays.copyOf(atoms.sipDelegateStats, atoms.sipDelegateStats.length);
- log.sipTransportFeatureTagStats = Arrays.copyOf(atoms.sipTransportFeatureTagStats,
- atoms.sipTransportFeatureTagStats.length);
- log.sipMessageResponse = Arrays.copyOf(atoms.sipMessageResponse,
- atoms.sipMessageResponse.length);
- log.sipTransportSession = Arrays.copyOf(atoms.sipTransportSession,
- atoms.sipTransportSession.length);
- log.imsDedicatedBearerListenerEvent = Arrays.copyOf(atoms.imsDedicatedBearerListenerEvent,
- atoms.imsDedicatedBearerListenerEvent.length);
- log.imsDedicatedBearerEvent = Arrays.copyOf(atoms.imsDedicatedBearerEvent,
- atoms.imsDedicatedBearerEvent.length);
- log.imsRegistrationServiceDescStats = Arrays.copyOf(atoms.imsRegistrationServiceDescStats,
- atoms.imsRegistrationServiceDescStats.length);
- log.uceEventStats = Arrays.copyOf(atoms.uceEventStats, atoms.uceEventStats.length);
- log.presenceNotifyEvent = Arrays.copyOf(atoms.presenceNotifyEvent,
- atoms.presenceNotifyEvent.length);
- log.gbaEvent = Arrays.copyOf(atoms.gbaEvent, atoms.gbaEvent.length);
-
- return log;
- }
-
}
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
new file mode 100644
index 0000000..7ff370c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
+import com.android.telephony.Rlog;
+
+/** Tracks Satellite metrics for each phone */
+public class SatelliteStats {
+ private static final String TAG = SatelliteStats.class.getSimpleName();
+
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ private static SatelliteStats sInstance = null;
+
+ /** Gets the instance of SatelliteStats */
+ public static SatelliteStats getInstance() {
+ if (sInstance == null) {
+ Rlog.d(TAG, "SatelliteStats created.");
+ synchronized (SatelliteStats.class) {
+ sInstance = new SatelliteStats();
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteController) atom.
+ * Refer to {@link #onSatelliteControllerMetrics(SatelliteControllerParams)}.
+ */
+ public class SatelliteControllerParams {
+ private final int mCountOfSatelliteServiceEnablementsSuccess;
+ private final int mCountOfSatelliteServiceEnablementsFail;
+ private final int mCountOfOutgoingDatagramSuccess;
+ private final int mCountOfOutgoingDatagramFail;
+ private final int mCountOfIncomingDatagramSuccess;
+ private final int mCountOfIncomingDatagramFail;
+ private final int mCountOfDatagramTypeSosSmsSuccess;
+ private final int mCountOfDatagramTypeSosSmsFail;
+ private final int mCountOfDatagramTypeLocationSharingSuccess;
+ private final int mCountOfDatagramTypeLocationSharingFail;
+ private final int mCountOfProvisionSuccess;
+ private final int mCountOfProvisionFail;
+ private final int mCountOfDeprovisionSuccess;
+ private final int mCountOfDeprovisionFail;
+ private final int mTotalServiceUptimeSec;
+ private final int mTotalBatteryConsumptionPercent;
+ private final int mTotalBatteryChargedTimeSec;
+
+ private SatelliteControllerParams(Builder builder) {
+ this.mCountOfSatelliteServiceEnablementsSuccess =
+ builder.mCountOfSatelliteServiceEnablementsSuccess;
+ this.mCountOfSatelliteServiceEnablementsFail =
+ builder.mCountOfSatelliteServiceEnablementsFail;
+ this.mCountOfOutgoingDatagramSuccess = builder.mCountOfOutgoingDatagramSuccess;
+ this.mCountOfOutgoingDatagramFail = builder.mCountOfOutgoingDatagramFail;
+ this.mCountOfIncomingDatagramSuccess = builder.mCountOfIncomingDatagramSuccess;
+ this.mCountOfIncomingDatagramFail = builder.mCountOfIncomingDatagramFail;
+ this.mCountOfDatagramTypeSosSmsSuccess = builder.mCountOfDatagramTypeSosSmsSuccess;
+ this.mCountOfDatagramTypeSosSmsFail = builder.mCountOfDatagramTypeSosSmsFail;
+ this.mCountOfDatagramTypeLocationSharingSuccess =
+ builder.mCountOfDatagramTypeLocationSharingSuccess;
+ this.mCountOfDatagramTypeLocationSharingFail =
+ builder.mCountOfDatagramTypeLocationSharingFail;
+ this.mCountOfProvisionSuccess = builder.mCountOfProvisionSuccess;
+ this.mCountOfProvisionFail = builder.mCountOfProvisionFail;
+ this.mCountOfDeprovisionSuccess = builder.mCountOfDeprovisionSuccess;
+ this.mCountOfDeprovisionFail = builder.mCountOfDeprovisionFail;
+ this.mTotalServiceUptimeSec = builder.mTotalServiceUptimeSec;
+ this.mTotalBatteryConsumptionPercent = builder.mTotalBatteryConsumptionPercent;
+ this.mTotalBatteryChargedTimeSec = builder.mTotalBatteryChargedTimeSec;
+ }
+
+ public int getCountOfSatelliteServiceEnablementsSuccess() {
+ return mCountOfSatelliteServiceEnablementsSuccess;
+ }
+
+ public int getCountOfSatelliteServiceEnablementsFail() {
+ return mCountOfSatelliteServiceEnablementsFail;
+ }
+
+ public int getCountOfOutgoingDatagramSuccess() {
+ return mCountOfOutgoingDatagramSuccess;
+ }
+
+ public int getCountOfOutgoingDatagramFail() {
+ return mCountOfOutgoingDatagramFail;
+ }
+
+ public int getCountOfIncomingDatagramSuccess() {
+ return mCountOfIncomingDatagramSuccess;
+ }
+
+ public int getCountOfIncomingDatagramFail() {
+ return mCountOfIncomingDatagramFail;
+ }
+
+ public int getCountOfDatagramTypeSosSmsSuccess() {
+ return mCountOfDatagramTypeSosSmsSuccess;
+ }
+
+ public int getCountOfDatagramTypeSosSmsFail() {
+ return mCountOfDatagramTypeSosSmsFail;
+ }
+
+ public int getCountOfDatagramTypeLocationSharingSuccess() {
+ return mCountOfDatagramTypeLocationSharingSuccess;
+ }
+
+ public int getCountOfDatagramTypeLocationSharingFail() {
+ return mCountOfDatagramTypeLocationSharingFail;
+ }
+
+ public int getCountOfProvisionSuccess() {
+ return mCountOfProvisionSuccess;
+ }
+
+ public int getCountOfProvisionFail() {
+ return mCountOfProvisionFail;
+ }
+
+ public int getCountOfDeprovisionSuccess() {
+ return mCountOfDeprovisionSuccess;
+ }
+
+ public int getCountOfDeprovisionFail() {
+ return mCountOfDeprovisionFail;
+ }
+
+ public int getTotalServiceUptimeSec() {
+ return mTotalServiceUptimeSec;
+ }
+
+ public int getTotalBatteryConsumptionPercent() {
+ return mTotalBatteryConsumptionPercent;
+ }
+
+ public int getTotalBatteryChargedTimeSec() {
+ return mTotalBatteryChargedTimeSec;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteControllerParams} data structure class
+ */
+ public static class Builder {
+ private int mCountOfSatelliteServiceEnablementsSuccess = 0;
+ private int mCountOfSatelliteServiceEnablementsFail = 0;
+ private int mCountOfOutgoingDatagramSuccess = 0;
+ private int mCountOfOutgoingDatagramFail = 0;
+ private int mCountOfIncomingDatagramSuccess = 0;
+ private int mCountOfIncomingDatagramFail = 0;
+ private int mCountOfDatagramTypeSosSmsSuccess = 0;
+ private int mCountOfDatagramTypeSosSmsFail = 0;
+ private int mCountOfDatagramTypeLocationSharingSuccess = 0;
+ private int mCountOfDatagramTypeLocationSharingFail = 0;
+ private int mCountOfProvisionSuccess;
+ private int mCountOfProvisionFail;
+ private int mCountOfDeprovisionSuccess;
+ private int mCountOfDeprovisionFail;
+ private int mTotalServiceUptimeSec = 0;
+ private int mTotalBatteryConsumptionPercent = 0;
+ private int mTotalBatteryChargedTimeSec = 0;
+
+ /**
+ * Sets countOfSatelliteServiceEnablementsSuccess value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfSatelliteServiceEnablementsSuccess(
+ int countOfSatelliteServiceEnablementsSuccess) {
+ this.mCountOfSatelliteServiceEnablementsSuccess =
+ countOfSatelliteServiceEnablementsSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfSatelliteServiceEnablementsFail value of {@link SatelliteController} atom
+ * then returns Builder class
+ */
+ public Builder setCountOfSatelliteServiceEnablementsFail(
+ int countOfSatelliteServiceEnablementsFail) {
+ this.mCountOfSatelliteServiceEnablementsFail =
+ countOfSatelliteServiceEnablementsFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfOutgoingDatagramSuccess value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfOutgoingDatagramSuccess(int countOfOutgoingDatagramSuccess) {
+ this.mCountOfOutgoingDatagramSuccess = countOfOutgoingDatagramSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfOutgoingDatagramFail value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfOutgoingDatagramFail(int countOfOutgoingDatagramFail) {
+ this.mCountOfOutgoingDatagramFail = countOfOutgoingDatagramFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfIncomingDatagramSuccess value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfIncomingDatagramSuccess(int countOfIncomingDatagramSuccess) {
+ this.mCountOfIncomingDatagramSuccess = countOfIncomingDatagramSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfIncomingDatagramFail value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfIncomingDatagramFail(int countOfIncomingDatagramFail) {
+ this.mCountOfIncomingDatagramFail = countOfIncomingDatagramFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfDatagramTypeSosSmsSuccess value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfDatagramTypeSosSmsSuccess(
+ int countOfDatagramTypeSosSmsSuccess) {
+ this.mCountOfDatagramTypeSosSmsSuccess = countOfDatagramTypeSosSmsSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfDatagramTypeSosSmsFail value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setCountOfDatagramTypeSosSmsFail(int countOfDatagramTypeSosSmsFail) {
+ this.mCountOfDatagramTypeSosSmsFail = countOfDatagramTypeSosSmsFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfDatagramTypeLocationSharingSuccess value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfDatagramTypeLocationSharingSuccess(
+ int countOfDatagramTypeLocationSharingSuccess) {
+ this.mCountOfDatagramTypeLocationSharingSuccess =
+ countOfDatagramTypeLocationSharingSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfDatagramTypeLocationSharingFail value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfDatagramTypeLocationSharingFail(
+ int countOfDatagramTypeLocationSharingFail) {
+ this.mCountOfDatagramTypeLocationSharingFail =
+ countOfDatagramTypeLocationSharingFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfProvisionSuccess value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfProvisionSuccess(int countOfProvisionSuccess) {
+ this.mCountOfProvisionSuccess = countOfProvisionSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfProvisionFail value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfProvisionFail(int countOfProvisionFail) {
+ this.mCountOfProvisionFail = countOfProvisionFail;
+ return this;
+ }
+
+ /**
+ * Sets countOfDeprovisionSuccess value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfDeprovisionSuccess(int countOfDeprovisionSuccess) {
+ this.mCountOfDeprovisionSuccess = countOfDeprovisionSuccess;
+ return this;
+ }
+
+ /**
+ * Sets countOfDeprovisionSuccess value of {@link SatelliteController}
+ * atom then returns Builder class
+ */
+ public Builder setCountOfDeprovisionFail(int countOfDeprovisionFail) {
+ this.mCountOfDeprovisionFail = countOfDeprovisionFail;
+ return this;
+ }
+
+ /**
+ * Sets totalServiceUptimeSec value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setTotalServiceUptimeSec(int totalServiceUptimeSec) {
+ this.mTotalServiceUptimeSec = totalServiceUptimeSec;
+ return this;
+ }
+
+ /**
+ * Sets totalBatteryConsumptionPercent value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setTotalBatteryConsumptionPercent(int totalBatteryConsumptionPercent) {
+ this.mTotalBatteryConsumptionPercent = totalBatteryConsumptionPercent;
+ return this;
+ }
+
+ /**
+ * Sets totalBatteryChargedTimeSec value of {@link SatelliteController} atom then
+ * returns Builder class
+ */
+ public Builder setTotalBatteryChargedTimeSec(int totalBatteryChargedTimeSec) {
+ this.mTotalBatteryChargedTimeSec = totalBatteryChargedTimeSec;
+ return this;
+ }
+
+ /**
+ * Returns ControllerParams, which contains whole component of
+ * {@link SatelliteController} atom
+ */
+ public SatelliteControllerParams build() {
+ return new SatelliteStats()
+ .new SatelliteControllerParams(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ControllerParams("
+ + ", countOfSatelliteServiceEnablementsSuccess="
+ + mCountOfSatelliteServiceEnablementsSuccess
+ + ", countOfSatelliteServiceEnablementsFail="
+ + mCountOfSatelliteServiceEnablementsFail
+ + ", countOfOutgoingDatagramSuccess=" + mCountOfOutgoingDatagramSuccess
+ + ", countOfOutgoingDatagramFail=" + mCountOfOutgoingDatagramFail
+ + ", countOfIncomingDatagramSuccess=" + mCountOfIncomingDatagramSuccess
+ + ", countOfIncomingDatagramFail=" + mCountOfIncomingDatagramFail
+ + ", countOfDatagramTypeSosSms=" + mCountOfDatagramTypeSosSmsSuccess
+ + ", countOfDatagramTypeSosSms=" + mCountOfDatagramTypeSosSmsFail
+ + ", countOfDatagramTypeLocationSharing="
+ + mCountOfDatagramTypeLocationSharingSuccess
+ + ", countOfDatagramTypeLocationSharing="
+ + mCountOfDatagramTypeLocationSharingFail
+ + ", serviceUptimeSec=" + mTotalServiceUptimeSec
+ + ", batteryConsumptionPercent=" + mTotalBatteryConsumptionPercent
+ + ", batteryChargedTimeSec=" + mTotalBatteryChargedTimeSec
+ + ")";
+ }
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteSession) atom.
+ * Refer to {@link #onSatelliteSessionMetrics(SatelliteSessionParams)}.
+ */
+ public class SatelliteSessionParams {
+ private final int mSatelliteServiceInitializationResult;
+ private final int mSatelliteTechnology;
+
+ private SatelliteSessionParams(Builder builder) {
+ this.mSatelliteServiceInitializationResult =
+ builder.mSatelliteServiceInitializationResult;
+ this.mSatelliteTechnology = builder.mSatelliteTechnology;
+ }
+
+ public int getSatelliteServiceInitializationResult() {
+ return mSatelliteServiceInitializationResult;
+ }
+
+ public int getSatelliteTechnology() {
+ return mSatelliteTechnology;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteSessionParams} data structure class
+ */
+ public static class Builder {
+ private int mSatelliteServiceInitializationResult = -1;
+ private int mSatelliteTechnology = -1;
+
+ /**
+ * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
+ * atom then returns Builder class
+ */
+ public Builder setSatelliteServiceInitializationResult(
+ int satelliteServiceInitializationResult) {
+ this.mSatelliteServiceInitializationResult = satelliteServiceInitializationResult;
+ return this;
+ }
+
+ /**
+ * Sets satelliteTechnology value of {@link SatelliteSession} atoms then
+ * returns Builder class
+ */
+ public Builder setSatelliteTechnology(int satelliteTechnology) {
+ this.mSatelliteTechnology = satelliteTechnology;
+ return this;
+ }
+
+ /**
+ * Returns SessionParams, which contains whole component of
+ * {@link SatelliteSession} atom
+ */
+ public SatelliteSessionParams build() {
+ return new SatelliteStats()
+ .new SatelliteSessionParams(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SessionParams("
+ + ", satelliteServiceInitializationResult="
+ + mSatelliteServiceInitializationResult
+ + ", satelliteTechnology=" + mSatelliteTechnology
+ + ")";
+ }
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteIncomingDatagram} atom.
+ * Refer to {@link #onSatelliteIncomingDatagramMetrics(SatelliteIncomingDatagramParams)}.
+ */
+ public class SatelliteIncomingDatagramParams {
+ private final int mResultCode;
+ private final int mDatagramSizeBytes;
+ private final long mDatagramTransferTimeMillis;
+
+ private SatelliteIncomingDatagramParams(Builder builder) {
+ this.mResultCode = builder.mResultCode;
+ this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
+ this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public int getDatagramSizeBytes() {
+ return mDatagramSizeBytes;
+ }
+
+ public long getDatagramTransferTimeMillis() {
+ return mDatagramTransferTimeMillis;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteIncomingDatagramParams} data structure class
+ */
+ public static class Builder {
+ private int mResultCode = -1;
+ private int mDatagramSizeBytes = -1;
+ private long mDatagramTransferTimeMillis = -1;
+
+ /**
+ * Sets resultCode value of {@link SatelliteIncomingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setResultCode(int resultCode) {
+ this.mResultCode = resultCode;
+ return this;
+ }
+
+ /**
+ * Sets datagramSizeBytes value of {@link SatelliteIncomingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setDatagramSizeBytes(int datagramSizeBytes) {
+ this.mDatagramSizeBytes = datagramSizeBytes;
+ return this;
+ }
+
+ /**
+ * Sets datagramTransferTimeMillis value of {@link SatelliteIncomingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setDatagramTransferTimeMillis(long datagramTransferTimeMillis) {
+ this.mDatagramTransferTimeMillis = datagramTransferTimeMillis;
+ return this;
+ }
+
+ /**
+ * Returns IncomingDatagramParams, which contains whole component of
+ * {@link SatelliteIncomingDatagram} atom
+ */
+ public SatelliteIncomingDatagramParams build() {
+ return new SatelliteStats()
+ .new SatelliteIncomingDatagramParams(Builder.this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "IncomingDatagramParams("
+ + ", resultCode=" + mResultCode
+ + ", datagramSizeBytes=" + mDatagramSizeBytes
+ + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis + ")";
+ }
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteOutgoingDatagram} atom.
+ * Refer to {@link #onSatelliteOutgoingDatagramMetrics(SatelliteOutgoingDatagramParams)}.
+ */
+ public class SatelliteOutgoingDatagramParams {
+ private final int mDatagramType;
+ private final int mResultCode;
+ private final int mDatagramSizeBytes;
+ private final long mDatagramTransferTimeMillis;
+
+ private SatelliteOutgoingDatagramParams(Builder builder) {
+ this.mDatagramType = builder.mDatagramType;
+ this.mResultCode = builder.mResultCode;
+ this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
+ this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
+ }
+
+ public int getDatagramType() {
+ return mDatagramType;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public int getDatagramSizeBytes() {
+ return mDatagramSizeBytes;
+ }
+
+ public long getDatagramTransferTimeMillis() {
+ return mDatagramTransferTimeMillis;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteOutgoingDatagramParams} data structure class
+ */
+ public static class Builder {
+ private int mDatagramType = -1;
+ private int mResultCode = -1;
+ private int mDatagramSizeBytes = -1;
+ private long mDatagramTransferTimeMillis = -1;
+
+ /**
+ * Sets datagramType value of {@link SatelliteOutgoingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setDatagramType(int datagramType) {
+ this.mDatagramType = datagramType;
+ return this;
+ }
+
+ /**
+ * Sets resultCode value of {@link SatelliteOutgoingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setResultCode(int resultCode) {
+ this.mResultCode = resultCode;
+ return this;
+ }
+
+ /**
+ * Sets datagramSizeBytes value of {@link SatelliteOutgoingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setDatagramSizeBytes(int datagramSizeBytes) {
+ this.mDatagramSizeBytes = datagramSizeBytes;
+ return this;
+ }
+
+ /**
+ * Sets datagramTransferTimeMillis value of {@link SatelliteOutgoingDatagram} atom
+ * then returns Builder class
+ */
+ public Builder setDatagramTransferTimeMillis(long datagramTransferTimeMillis) {
+ this.mDatagramTransferTimeMillis = datagramTransferTimeMillis;
+ return this;
+ }
+
+ /**
+ * Returns OutgoingDatagramParams, which contains whole component of
+ * {@link SatelliteOutgoingDatagram} atom
+ */
+ public SatelliteOutgoingDatagramParams build() {
+ return new SatelliteStats()
+ .new SatelliteOutgoingDatagramParams(Builder.this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "OutgoingDatagramParams("
+ + "datagramType=" + mDatagramType
+ + ", resultCode=" + mResultCode
+ + ", datagramSizeBytes=" + mDatagramSizeBytes
+ + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis + ")";
+ }
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteProvision} atom.
+ * Refer to {@link #onSatelliteProvisionMetrics(SatelliteProvisionParams)}.
+ */
+ public class SatelliteProvisionParams {
+ private final int mResultCode;
+ private final int mProvisioningTimeSec;
+ private final boolean mIsProvisionRequest;
+ private final boolean mIsCanceled;
+
+ private SatelliteProvisionParams(Builder builder) {
+ this.mResultCode = builder.mResultCode;
+ this.mProvisioningTimeSec = builder.mProvisioningTimeSec;
+ this.mIsProvisionRequest = builder.mIsProvisionRequest;
+ this.mIsCanceled = builder.mIsCanceled;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public int getProvisioningTimeSec() {
+ return mProvisioningTimeSec;
+ }
+
+ public boolean getIsProvisionRequest() {
+ return mIsProvisionRequest;
+ }
+
+ public boolean getIsCanceled() {
+ return mIsCanceled;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteProvisionParams} data structure class
+ */
+ public static class Builder {
+ private int mResultCode = -1;
+ private int mProvisioningTimeSec = -1;
+ private boolean mIsProvisionRequest = false;
+ private boolean mIsCanceled = false;
+
+ /**
+ * Sets resultCode value of {@link SatelliteProvision} atom
+ * then returns Builder class
+ */
+ public Builder setResultCode(int resultCode) {
+ this.mResultCode = resultCode;
+ return this;
+ }
+
+ /**
+ * Sets provisioningTimeSec value of {@link SatelliteProvision} atom
+ * then returns Builder class
+ */
+ public Builder setProvisioningTimeSec(int provisioningTimeSec) {
+ this.mProvisioningTimeSec = provisioningTimeSec;
+ return this;
+ }
+
+ /**
+ * Sets isProvisionRequest value of {@link SatelliteProvision} atom
+ * then returns Builder class
+ */
+ public Builder setIsProvisionRequest(boolean isProvisionRequest) {
+ this.mIsProvisionRequest = isProvisionRequest;
+ return this;
+ }
+
+ /**
+ * Sets isCanceled value of {@link SatelliteProvision} atom
+ * then returns Builder class
+ */
+ public Builder setIsCanceled(boolean isCanceled) {
+ this.mIsCanceled = isCanceled;
+ return this;
+ }
+
+ /**
+ * Returns ProvisionParams, which contains whole component of
+ * {@link SatelliteProvision} atom
+ */
+ public SatelliteProvisionParams build() {
+ return new SatelliteStats()
+ .new SatelliteProvisionParams(Builder.this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ProvisionParams("
+ + "resultCode=" + mResultCode
+ + ", provisioningTimeSec=" + mProvisioningTimeSec
+ + ", isProvisionRequest=" + mIsProvisionRequest
+ + ", isCanceled" + mIsCanceled + ")";
+ }
+ }
+
+ /**
+ * A data class to contain whole component of {@link SatelliteSosMessageRecommender} atom.
+ * Refer to {@link #onSatelliteSosMessageRecommender(SatelliteSosMessageRecommenderParams)}.
+ */
+ public class SatelliteSosMessageRecommenderParams {
+ private final boolean mIsDisplaySosMessageSent;
+ private final int mCountOfTimerStarted;
+ private final boolean mIsImsRegistered;
+ private final int mCellularServiceState;
+
+ private SatelliteSosMessageRecommenderParams(Builder builder) {
+ this.mIsDisplaySosMessageSent = builder.mIsDisplaySosMessageSent;
+ this.mCountOfTimerStarted = builder.mCountOfTimerStarted;
+ this.mIsImsRegistered = builder.mIsImsRegistered;
+ this.mCellularServiceState = builder.mCellularServiceState;
+ }
+
+ public boolean isDisplaySosMessageSent() {
+ return mIsDisplaySosMessageSent;
+ }
+
+ public int getCountOfTimerStarted() {
+ return mCountOfTimerStarted;
+ }
+
+ public boolean isImsRegistered() {
+ return mIsImsRegistered;
+ }
+
+ public int getCellularServiceState() {
+ return mCellularServiceState;
+ }
+
+ /**
+ * A builder class to create {@link SatelliteProvisionParams} data structure class
+ */
+ public static class Builder {
+ private boolean mIsDisplaySosMessageSent = false;
+ private int mCountOfTimerStarted = -1;
+ private boolean mIsImsRegistered = false;
+ private int mCellularServiceState = -1;
+
+ /**
+ * Sets resultCode value of {@link SatelliteSosMessageRecommender} atom
+ * then returns Builder class
+ */
+ public Builder setDisplaySosMessageSent(
+ boolean isDisplaySosMessageSent) {
+ this.mIsDisplaySosMessageSent = isDisplaySosMessageSent;
+ return this;
+ }
+
+ /**
+ * Sets countOfTimerIsStarted value of {@link SatelliteSosMessageRecommender} atom
+ * then returns Builder class
+ */
+ public Builder setCountOfTimerStarted(int countOfTimerStarted) {
+ this.mCountOfTimerStarted = countOfTimerStarted;
+ return this;
+ }
+
+ /**
+ * Sets isImsRegistered value of {@link SatelliteSosMessageRecommender} atom
+ * then returns Builder class
+ */
+ public Builder setImsRegistered(boolean isImsRegistered) {
+ this.mIsImsRegistered = isImsRegistered;
+ return this;
+ }
+
+ /**
+ * Sets cellularServiceState value of {@link SatelliteSosMessageRecommender} atom
+ * then returns Builder class
+ */
+ public Builder setCellularServiceState(int cellularServiceState) {
+ this.mCellularServiceState = cellularServiceState;
+ return this;
+ }
+
+ /**
+ * Returns SosMessageRecommenderParams, which contains whole component of
+ * {@link SatelliteSosMessageRecommenderParams} atom
+ */
+ public SatelliteSosMessageRecommenderParams build() {
+ return new SatelliteStats()
+ .new SatelliteSosMessageRecommenderParams(Builder.this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SosMessageRecommenderParams("
+ + "isDisplaySosMessageSent=" + mIsDisplaySosMessageSent
+ + ", countOfTimerStarted=" + mCountOfTimerStarted
+ + ", isImsRegistered=" + mIsImsRegistered
+ + ", cellularServiceState=" + mCellularServiceState + ")";
+ }
+ }
+
+ /** Create a new atom or update an existing atom for SatelliteController metrics */
+ public synchronized void onSatelliteControllerMetrics(SatelliteControllerParams param) {
+ SatelliteController proto = new SatelliteController();
+ proto.countOfSatelliteServiceEnablementsSuccess =
+ param.getCountOfSatelliteServiceEnablementsSuccess();
+ proto.countOfSatelliteServiceEnablementsFail =
+ param.getCountOfSatelliteServiceEnablementsFail();
+ proto.countOfOutgoingDatagramSuccess = param.getCountOfOutgoingDatagramSuccess();
+ proto.countOfOutgoingDatagramFail = param.getCountOfOutgoingDatagramFail();
+ proto.countOfIncomingDatagramSuccess = param.getCountOfIncomingDatagramSuccess();
+ proto.countOfIncomingDatagramFail = param.getCountOfIncomingDatagramFail();
+ proto.countOfDatagramTypeSosSmsSuccess = param.getCountOfDatagramTypeSosSmsSuccess();
+ proto.countOfDatagramTypeSosSmsFail = param.getCountOfDatagramTypeSosSmsFail();
+ proto.countOfDatagramTypeLocationSharingSuccess =
+ param.getCountOfDatagramTypeLocationSharingSuccess();
+ proto.countOfDatagramTypeLocationSharingFail =
+ param.getCountOfDatagramTypeLocationSharingFail();
+ proto.countOfProvisionSuccess = param.getCountOfProvisionSuccess();
+ proto.countOfProvisionFail = param.getCountOfProvisionFail();
+ proto.countOfDeprovisionSuccess = param.getCountOfDeprovisionSuccess();
+ proto.countOfDeprovisionFail = param.getCountOfDeprovisionFail();
+ proto.totalServiceUptimeSec = param.getTotalServiceUptimeSec();
+ proto.totalBatteryConsumptionPercent = param.getTotalBatteryConsumptionPercent();
+ proto.totalBatteryChargedTimeSec = param.getTotalBatteryChargedTimeSec();
+
+ mAtomsStorage.addSatelliteControllerStats(proto);
+ }
+
+ /** Create a new atom or update an existing atom for SatelliteSession metrics */
+ public synchronized void onSatelliteSessionMetrics(SatelliteSessionParams param) {
+ SatelliteSession proto = new SatelliteSession();
+ proto.satelliteServiceInitializationResult =
+ param.getSatelliteServiceInitializationResult();
+ proto.satelliteTechnology = param.getSatelliteTechnology();
+ proto.count = 1;
+ mAtomsStorage.addSatelliteSessionStats(proto);
+ }
+
+ /** Create a new atom for SatelliteIncomingDatagram metrics */
+ public synchronized void onSatelliteIncomingDatagramMetrics(
+ SatelliteIncomingDatagramParams param) {
+ SatelliteIncomingDatagram proto = new SatelliteIncomingDatagram();
+ proto.resultCode = param.getResultCode();
+ proto.datagramSizeBytes = param.getDatagramSizeBytes();
+ proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
+ mAtomsStorage.addSatelliteIncomingDatagramStats(proto);
+ }
+
+ /** Create a new atom for SatelliteOutgoingDatagram metrics */
+ public synchronized void onSatelliteOutgoingDatagramMetrics(
+ SatelliteOutgoingDatagramParams param) {
+ SatelliteOutgoingDatagram proto = new SatelliteOutgoingDatagram();
+ proto.datagramType = param.getDatagramType();
+ proto.resultCode = param.getResultCode();
+ proto.datagramSizeBytes = param.getDatagramSizeBytes();
+ proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
+ mAtomsStorage.addSatelliteOutgoingDatagramStats(proto);
+ }
+
+ /** Create a new atom for SatelliteProvision metrics */
+ public synchronized void onSatelliteProvisionMetrics(SatelliteProvisionParams param) {
+ SatelliteProvision proto = new SatelliteProvision();
+ proto.resultCode = param.getResultCode();
+ proto.provisioningTimeSec = param.getProvisioningTimeSec();
+ proto.isProvisionRequest = param.getIsProvisionRequest();
+ proto.isCanceled = param.getIsCanceled();
+ mAtomsStorage.addSatelliteProvisionStats(proto);
+ }
+
+ /** Create a new atom or update an existing atom for SatelliteSosMessageRecommender metrics */
+ public synchronized void onSatelliteSosMessageRecommender(
+ SatelliteSosMessageRecommenderParams param) {
+ SatelliteSosMessageRecommender proto = new SatelliteSosMessageRecommender();
+ proto.isDisplaySosMessageSent = param.isDisplaySosMessageSent();
+ proto.countOfTimerStarted = param.getCountOfTimerStarted();
+ proto.isImsRegistered = param.isImsRegistered();
+ proto.cellularServiceState = param.getCellularServiceState();
+ proto.count = 1;
+ mAtomsStorage.addSatelliteSosMessageRecommenderStats(proto);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index df4db73..b830cd0 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -15,7 +15,13 @@
*/
package com.android.internal.telephony.metrics;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
+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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.telephony.AccessNetworkConstants;
@@ -29,25 +35,32 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.data.DataNetwork;
+import com.android.internal.telephony.data.DataNetworkController;
+import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
import com.android.telephony.Rlog;
+import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/** Tracks service state duration and switch metrics for each phone. */
-public class ServiceStateStats {
+public class ServiceStateStats extends DataNetworkControllerCallback {
private static final String TAG = ServiceStateStats.class.getSimpleName();
private final AtomicReference<TimestampedServiceState> mLastState =
new AtomicReference<>(new TimestampedServiceState(null, 0L));
private final Phone mPhone;
private final PersistAtomsStorage mStorage;
+ private final DeviceStateHelper mDeviceStateHelper;
public ServiceStateStats(Phone phone) {
+ super(Runnable::run);
mPhone = phone;
mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ mDeviceStateHelper = PhoneFactory.getMetricsCollector().getDeviceStateHelper();
}
/** Finalizes the durations of the current service state segment. */
@@ -76,6 +89,21 @@
addServiceState(lastState, now);
}
+ /** Registers for internet pdn connected callback. */
+ public void registerDataNetworkControllerCallback() {
+ mPhone.getDataNetworkController().registerDataNetworkControllerCallback(this);
+ }
+
+ /** Updates service state when internet pdn gets connected. */
+ public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {
+ onInternetDataNetworkChanged(true);
+ }
+
+ /** Updates service state when internet pdn gets disconnected. */
+ public void onInternetDataNetworkDisconnected() {
+ onInternetDataNetworkChanged(false);
+ }
+
/** Updates the current service state. */
public void onServiceStateChanged(ServiceState serviceState) {
final long now = getTimeMillis();
@@ -85,7 +113,7 @@
} else {
CellularServiceState newState = new CellularServiceState();
newState.voiceRat = getVoiceRat(mPhone, serviceState);
- newState.dataRat = getDataRat(serviceState);
+ newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
newState.voiceRoamingType = serviceState.getVoiceRoamingType();
newState.dataRoamingType = serviceState.getDataRoamingType();
newState.isEndc = isEndc(serviceState);
@@ -93,7 +121,8 @@
newState.isMultiSim = SimSlotState.isMultiSim();
newState.carrierId = mPhone.getCarrierId();
newState.isEmergencyOnly = isEmergencyOnly(serviceState);
-
+ newState.isInternetPdnUp = isInternetPdnUp(mPhone);
+ newState.foldState = mDeviceStateHelper.getFoldState();
TimestampedServiceState prevState =
mLastState.getAndSet(new TimestampedServiceState(newState, now));
addServiceStateAndSwitch(
@@ -101,6 +130,26 @@
}
}
+ /** Updates the fold state of the device for the current service state. */
+ public void onFoldStateChanged(int foldState) {
+ final long now = getTimeMillis();
+ CellularServiceState lastServiceState = mLastState.get().mServiceState;
+ if (lastServiceState == null || lastServiceState.foldState == foldState) {
+ // Not need to update the fold state if modem is off or if is the
+ // same fold state
+ return;
+ } else {
+ TimestampedServiceState lastState =
+ mLastState.getAndUpdate(
+ state -> {
+ CellularServiceState newServiceState = copyOf(state.mServiceState);
+ newServiceState.foldState = foldState;
+ return new TimestampedServiceState(newServiceState, now);
+ });
+ addServiceState(lastState, now);
+ }
+ }
+
private void addServiceState(TimestampedServiceState prevState, long now) {
addServiceStateAndSwitch(prevState, now, null);
}
@@ -170,7 +219,10 @@
}
int chNumber = serviceState.getChannelNumber();
int band;
- @NetworkType int rat = getRat(serviceState);
+ @NetworkType int rat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
+ if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ rat = serviceState.getVoiceNetworkType();
+ }
switch (rat) {
case TelephonyManager.NETWORK_TYPE_GSM:
case TelephonyManager.NETWORK_TYPE_GPRS:
@@ -188,6 +240,9 @@
case TelephonyManager.NETWORK_TYPE_LTE_CA:
band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
break;
+ case TelephonyManager.NETWORK_TYPE_NR:
+ band = AccessNetworkUtils.getOperatingBandForNrarfcn(chNumber);
+ break;
default:
Rlog.w(TAG, "getBand: unknown WWAN RAT " + rat);
band = 0;
@@ -214,6 +269,8 @@
copy.carrierId = state.carrierId;
copy.totalTimeMillis = state.totalTimeMillis;
copy.isEmergencyOnly = state.isEmergencyOnly;
+ copy.isInternetPdnUp = state.isInternetPdnUp;
+ copy.foldState = state.foldState;
return copy;
}
@@ -231,52 +288,53 @@
/**
* Returns the current voice RAT from IMS registration if present, otherwise from the service
* state.
+ *
+ * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
+ * despite that the device may have emergency service over a certain RAT.
*/
static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state) {
+ return getVoiceRat(phone, state, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN);
+ }
+
+ /**
+ * Returns the current voice RAT according to the bearer.
+ *
+ * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned
+ * despite that the device may have emergency service over a certain RAT.
+ */
+ @VisibleForTesting public
+ static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer) {
if (state == null) {
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
- if (imsPhone != null) {
+ if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) {
@NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech();
if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
- // If IMS is over WWAN but WWAN PS is not in-service, then IMS RAT is invalid
+ // If IMS is registered over WWAN but WWAN PS is not in service,
+ // fallback to WWAN CS RAT
boolean isImsVoiceRatValid =
(imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
- || getDataRat(state) != TelephonyManager.NETWORK_TYPE_UNKNOWN);
- return isImsVoiceRatValid ? imsVoiceRat : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ || getRat(state, NetworkRegistrationInfo.DOMAIN_PS)
+ != TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ if (isImsVoiceRatValid) {
+ return imsVoiceRat;
+ }
}
}
-
- // If WWAN CS is not in-service, we should return NETWORK_TYPE_UNKNOWN
- final NetworkRegistrationInfo wwanRegInfo =
- state.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_CS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- return wwanRegInfo != null && wwanRegInfo.isInService()
- ? wwanRegInfo.getAccessNetworkTechnology()
- : TelephonyManager.NETWORK_TYPE_UNKNOWN;
- }
-
- /**
- * Returns RAT used by WWAN.
- *
- * <p>Returns PS WWAN RAT, or CS WWAN RAT if PS WWAN RAT is unavailable.
- */
- private static @NetworkType int getRat(ServiceState state) {
- @NetworkType int rat = getDataRat(state);
- if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
- rat = state.getVoiceNetworkType();
+ if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ } else {
+ return getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
}
- return rat;
}
- /** Returns PS (data) RAT used by WWAN. */
- static @NetworkType int getDataRat(ServiceState state) {
+ /** Returns RAT used by WWAN if WWAN is in service. */
+ public static @NetworkType int getRat(
+ ServiceState state, @NetworkRegistrationInfo.Domain int domain) {
final NetworkRegistrationInfo wwanRegInfo =
state.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
return wwanRegInfo != null && wwanRegInfo.isInService()
? wwanRegInfo.getAccessNetworkTechnology()
: TelephonyManager.NETWORK_TYPE_UNKNOWN;
@@ -291,7 +349,7 @@
}
private static boolean isEndc(ServiceState state) {
- if (getDataRat(state) != TelephonyManager.NETWORK_TYPE_LTE) {
+ if (getRat(state, NetworkRegistrationInfo.DOMAIN_PS) != TelephonyManager.NETWORK_TYPE_LTE) {
return false;
}
int nrState = state.getNrState();
@@ -299,6 +357,29 @@
|| nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
}
+ private static boolean isInternetPdnUp(Phone phone) {
+ DataNetworkController dataNetworkController = phone.getDataNetworkController();
+ if (dataNetworkController != null) {
+ return dataNetworkController.getInternetDataNetworkState() == DATA_CONNECTED;
+ }
+ return false;
+ }
+
+ private void onInternetDataNetworkChanged(boolean internetPdnUp) {
+ final long now = getTimeMillis();
+ TimestampedServiceState lastState =
+ mLastState.getAndUpdate(
+ state -> {
+ if (state.mServiceState == null) {
+ return new TimestampedServiceState(null, now);
+ }
+ CellularServiceState newServiceState = copyOf(state.mServiceState);
+ newServiceState.isInternetPdnUp = internetPdnUp;
+ return new TimestampedServiceState(newServiceState, now);
+ });
+ addServiceState(lastState, now);
+ }
+
@VisibleForTesting
protected long getTimeMillis() {
return SystemClock.elapsedRealtime();
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
index af7e23e..2f1e6a7 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -58,9 +58,11 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.telephony.Rlog;
+import java.util.Objects;
import java.util.Random;
/** Collects sms events per phone ID for the pulled atom. */
@@ -154,46 +156,62 @@
/** Create a new atom when an outgoing SMS is sent. */
public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
- @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp,
+ @SmsManager.Result int sendErrorCode, long messageId, boolean isFromDefaultApp,
long intervalMillis) {
- onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, errorCode, NO_ERROR_CODE,
+ onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, sendErrorCode, NO_ERROR_CODE,
messageId, isFromDefaultApp, intervalMillis);
}
/** Create a new atom when an outgoing SMS is sent. */
public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
- @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId,
+ @SmsManager.Result int sendErrorCode, int networkErrorCode, long messageId,
boolean isFromDefaultApp, long intervalMillis) {
OutgoingSms proto =
getOutgoingDefaultProto(is3gpp2, isOverIms, messageId, isFromDefaultApp,
intervalMillis);
+ // The field errorCode is used for up-to-Android-13 devices. From Android 14, sendErrorCode
+ // and networkErrorCode will be used. The field errorCode will be deprecated when most
+ // devices use Android 14 or higher versions.
if (isOverIms) {
// Populate error code and result for IMS case
- proto.errorCode = errorCode;
+ proto.errorCode = sendErrorCode;
if (fallbackToCs) {
proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
- } else if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ } else if (sendErrorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
- } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ } else if (sendErrorCode != SmsManager.RESULT_ERROR_NONE) {
proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
}
} else {
// Populate error code and result for CS case
- if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ if (sendErrorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
- } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ } else if (sendErrorCode != SmsManager.RESULT_ERROR_NONE) {
proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
}
- proto.errorCode = radioSpecificErrorCode;
- if (errorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
- && radioSpecificErrorCode == NO_ERROR_CODE) {
+ proto.errorCode = networkErrorCode;
+ if (sendErrorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
+ && networkErrorCode == NO_ERROR_CODE) {
proto.errorCode = is3gpp2 ? NO_NETWORK_ERROR_3GPP2 : NO_NETWORK_ERROR_3GPP;
}
}
+
+ proto.sendErrorCode = sendErrorCode;
+ proto.networkErrorCode = networkErrorCode;
+
mAtomsStorage.addOutgoingSms(proto);
}
+ /** Create a new atom when user attempted to send an outgoing short code sms. */
+ public void onOutgoingShortCodeSms(int category, int xmlVersion) {
+ OutgoingShortCodeSms proto = new OutgoingShortCodeSms();
+ proto.category = category;
+ proto.xmlVersion = xmlVersion;
+ proto.shortCodeSmsCount = 1;
+ mAtomsStorage.addOutgoingShortCodeSms(proto);
+ }
+
/** Creates a proto for a normal single-part {@code IncomingSms} with default values. */
private IncomingSms getIncomingDefaultProto(boolean is3gpp2,
@InboundSmsHandler.SmsSource int smsSource) {
@@ -214,6 +232,8 @@
// Message ID is initialized with random number, as it is not available for all incoming
// SMS messages (e.g. those handled by OS or error cases).
proto.messageId = RANDOM.nextLong();
+ proto.count = 1;
+ proto.isManagedProfile = mPhone.isManagedProfile();
return proto;
}
@@ -238,6 +258,8 @@
// in the persistent storage.
proto.retryId = 0;
proto.intervalMillis = intervalMillis;
+ proto.count = 1;
+ proto.isManagedProfile = mPhone.isManagedProfile();
return proto;
}
@@ -304,6 +326,26 @@
}
}
+ /**
+ * Returns a hash value to identify messages that are identical for the purpose of merging them
+ * together when storage is full.
+ */
+ static int getSmsHashCode(OutgoingSms sms) {
+ return Objects.hash(sms.smsFormat, sms.smsTech, sms.rat, sms.sendResult, sms.errorCode,
+ sms.isRoaming, sms.isFromDefaultApp, sms.simSlotIndex, sms.isMultiSim,
+ sms.isEsim, sms.carrierId);
+ }
+
+ /**
+ * Returns a hash value to identify messages that are identical for the purpose of merging them
+ * together when storage is full.
+ */
+ static int getSmsHashCode(IncomingSms sms) {
+ return Objects.hash(sms.smsFormat, sms.smsTech, sms.rat, sms.smsType,
+ sms.totalParts, sms.receivedParts, sms.blocked, sms.error,
+ sms.isRoaming, sms.simSlotIndex, sms.isMultiSim, sms.isEsim, sms.carrierId);
+ }
+
private int getPhoneId() {
Phone phone = mPhone;
if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 5b47ae5..3fdbfe0 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -290,14 +290,12 @@
break;
case "--metricsproto":
pw.println(convertProtoToBase64String(buildProto()));
- pw.println(RcsStats.getInstance().buildLog());
if (reset) {
reset();
}
break;
case "--metricsprototext":
pw.println(buildProto().toString());
- pw.println(RcsStats.getInstance().buildProto().toString());
break;
}
}
@@ -749,8 +747,6 @@
.setRadioState(mLastRadioState.get(key)).build();
addTelephonyEvent(event);
}
-
- RcsStats.getInstance().reset();
}
/**
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 5f9f275..ba07fa0 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -33,16 +33,6 @@
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
@@ -54,7 +44,9 @@
import android.telecom.VideoProfile;
import android.telecom.VideoProfile.VideoState;
import android.telephony.Annotation.NetworkType;
+import android.telephony.AnomalyReporter;
import android.telephony.DisconnectCause;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
@@ -72,6 +64,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
@@ -83,6 +76,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
/** Collects voice call events per phone ID for the pulled atom. */
@@ -125,12 +119,16 @@
/** Holds the audio codec value for IMS calls. */
private static final SparseIntArray IMS_CODEC_MAP = buildImsCodecMap();
- /** Holds setup duration buckets with values as their upper bounds in milliseconds. */
- private static final SparseIntArray CALL_SETUP_DURATION_MAP = buildCallSetupDurationMap();
-
/** Holds call duration buckets with values as their upper bounds in milliseconds. */
private static final SparseIntArray CALL_DURATION_MAP = buildCallDurationMap();
+ /** UUID for reporting concurrent call anomaly */
+ private static final UUID CONCURRENT_CALL_ANOMALY_UUID =
+ UUID.fromString("76780b5a-623e-48a4-be3f-925e05177c9c");
+
+ /** If the number of concurrent calls exceeds this number, report anomaly*/
+ private static final int MAX_NORMAL_CONCURRENT_CALLS = 3;
+
/**
* Tracks statistics for each call connection, indexed with ID returned by {@link
* #getConnectionId}.
@@ -159,6 +157,8 @@
private final PersistAtomsStorage mAtomsStorage =
PhoneFactory.getMetricsCollector().getAtomsStorage();
private final UiccController mUiccController = UiccController.getInstance();
+ private final DeviceStateHelper mDeviceStateHelper =
+ PhoneFactory.getMetricsCollector().getDeviceStateHelper();
public VoiceCallSessionStats(int phoneId, Phone phone) {
mPhoneId = phoneId;
@@ -240,6 +240,12 @@
}
}
+ /** Updates internal states when an IMS fails to start. */
+ public synchronized void onImsCallStartFailed(
+ @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) {
+ onImsCallTerminated(conn, reasonInfo);
+ }
+
/** Updates internal states when an IMS call is terminated. */
public synchronized void onImsCallTerminated(
@Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) {
@@ -342,9 +348,9 @@
public synchronized void onRilSrvccStateChanged(int state) {
List<Connection> handoverConnections = null;
if (mPhone.getImsPhone() != null) {
- loge("onRilSrvccStateChanged: ImsPhone is null");
- } else {
handoverConnections = mPhone.getImsPhone().getHandoverConnection();
+ } else {
+ loge("onRilSrvccStateChanged: ImsPhone is null");
}
List<Integer> imsConnIds;
if (handoverConnections == null) {
@@ -363,6 +369,9 @@
VoiceCallSession proto = mCallProtos.get(id);
proto.srvccCompleted = true;
proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
+ // Call RAT may have changed (e.g. IWLAN -> UMTS) due to bearer change
+ updateRatAtEnd(proto, getVoiceRatWithVoNRFix(
+ mPhone, mPhone.getServiceState(), proto.bearerAtEnd));
}
break;
case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
@@ -395,7 +404,6 @@
logd("acceptCall: resetting setup info, connectionId=%d", id);
VoiceCallSession proto = mCallProtos.get(id);
proto.setupBeginMillis = getTimeMillis();
- proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
} else {
loge("acceptCall: untracked connection, connectionId=%d", id);
}
@@ -417,14 +425,12 @@
}
int bearer = getBearer(conn);
ServiceState serviceState = getServiceState();
- @NetworkType int rat = ServiceStateStats.getVoiceRat(mPhone, serviceState);
-
+ @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, bearer);
VoiceCallSession proto = new VoiceCallSession();
proto.bearerAtStart = bearer;
proto.bearerAtEnd = bearer;
proto.direction = getDirection(conn);
- proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
proto.setupFailed = true;
proto.disconnectReasonCode = conn.getDisconnectCause();
proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
@@ -442,12 +448,18 @@
proto.srvccFailureCount = 0L;
proto.srvccCancellationCount = 0L;
proto.rttEnabled = false;
- proto.isEmergency = conn.isEmergencyCall();
+ proto.isEmergency = conn.isEmergencyCall() || conn.isNetworkIdentifiedEmergencyCall();
proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false;
proto.isMultiparty = conn.isMultiparty();
+ proto.lastKnownRat = rat;
// internal fields for tracking
- proto.setupBeginMillis = getTimeMillis();
+ if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) {
+ // MT call setup hasn't begun hence set to 0
+ proto.setupBeginMillis = 0L;
+ } else {
+ proto.setupBeginMillis = getTimeMillis();
+ }
// audio codec might have already been set
int codec = audioQualityToCodec(bearer, conn.getAudioCodec());
@@ -456,6 +468,10 @@
}
proto.concurrentCallCountAtStart = mCallProtos.size();
+ if (proto.concurrentCallCountAtStart > MAX_NORMAL_CONCURRENT_CALLS) {
+ AnomalyReporter.reportAnomaly(
+ CONCURRENT_CALL_ANOMALY_UUID, "Anomalous number of concurrent calls");
+ }
mCallProtos.put(id, proto);
// RAT call count needs to be updated
@@ -469,6 +485,12 @@
loge("finishCall: could not find call to be removed, connectionId=%d", connectionId);
return;
}
+
+ // Compute time it took to fail setup (except for MT calls that have never been picked up)
+ if (proto.setupFailed && proto.setupBeginMillis != 0L && proto.setupDurationMillis == 0) {
+ proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
+ }
+
mCallProtos.delete(connectionId);
proto.concurrentCallCountAtEnd = mCallProtos.size();
@@ -491,6 +513,12 @@
proto.carrierId = mPhone.getCarrierId();
}
+ // Update end RAT
+ updateRatAtEnd(proto, getVoiceRatWithVoNRFix(mPhone, getServiceState(), proto.bearerAtEnd));
+
+ // Set device fold state
+ proto.foldState = mDeviceStateHelper.getFoldState();
+
mAtomsStorage.addVoiceCallSession(proto);
// merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls)
@@ -542,7 +570,6 @@
private void checkCallSetup(Connection conn, VoiceCallSession proto) {
if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) {
proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
- proto.setupDuration = classifySetupDuration(proto.setupDurationMillis);
proto.setupBeginMillis = 0L;
}
// Clear setupFailed if call now active, but otherwise leave it unchanged
@@ -551,7 +578,7 @@
proto.setupFailed = false;
// Track RAT when voice call is connected.
ServiceState serviceState = getServiceState();
- proto.ratAtConnected = ServiceStateStats.getVoiceRat(mPhone, serviceState);
+ proto.ratAtConnected = getVoiceRatWithVoNRFix(mPhone, serviceState, proto.bearerAtEnd);
// Reset list of codecs with the last codec at the present time. In this way, we
// track codec quality only after call is connected and not while ringing.
resetCodecList(conn);
@@ -559,22 +586,34 @@
}
private void updateRatTracker(ServiceState state) {
- @NetworkType int rat = ServiceStateStats.getVoiceRat(mPhone, state);
- int band =
- (rat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(state);
-
+ // RAT usage is not broken down by bearer. In case a CS call is made while there is IMS
+ // voice registration, this may be inaccurate (i.e. there could be multiple RAT in use, but
+ // we only pick the most feasible one).
+ @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, state,
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN);
mRatUsage.add(mPhone.getCarrierId(), rat, getTimeMillis(), getConnectionIds());
+
for (int i = 0; i < mCallProtos.size(); i++) {
VoiceCallSession proto = mCallProtos.valueAt(i);
- if (proto.ratAtEnd != rat) {
- proto.ratSwitchCount++;
- proto.ratAtEnd = rat;
- }
- proto.bandAtEnd = band;
+ rat = getVoiceRatWithVoNRFix(mPhone, state, proto.bearerAtEnd);
+ updateRatAtEnd(proto, rat);
+ proto.bandAtEnd = (rat == TelephonyManager.NETWORK_TYPE_IWLAN)
+ ? 0
+ : ServiceStateStats.getBand(state);
// assuming that SIM carrier ID does not change during the call
}
}
+ private void updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat) {
+ if (proto.ratAtEnd != rat) {
+ proto.ratSwitchCount++;
+ proto.ratAtEnd = rat;
+ if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ proto.lastKnownRat = rat;
+ }
+ }
+ }
+
private void finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis) {
VoiceCallSession proto = mCallProtos.get(id);
proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
@@ -641,6 +680,43 @@
return mPhone.getSignalStrength().getLevel();
}
+ /**
+ * This is a copy of ServiceStateStats.getVoiceRat(Phone, ServiceState, int) with minimum fix
+ * required for tracking EPSFB correctly.
+ */
+ @VisibleForTesting private static @NetworkType int getVoiceRatWithVoNRFix(
+ Phone phone, @Nullable ServiceState state, int bearer) {
+ if (state == null) {
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) {
+ @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech();
+ @NetworkType int wwanPsRat =
+ ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_PS);
+ if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ // If IMS is registered over WWAN but WWAN PS is not in service,
+ // fallback to WWAN CS RAT
+ boolean isImsVoiceRatValid =
+ (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
+ || wwanPsRat != TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ if (isImsVoiceRatValid) {
+ // Fix for VoNR and EPSFB, b/277906557
+ @NetworkType int oldRat = ServiceStateStats.getVoiceRat(phone, state, bearer),
+ rat = imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
+ ? imsVoiceRat : wwanPsRat;
+ logd("getVoiceRatWithVoNRFix: oldRat=%d, newRat=%d", oldRat, rat);
+ return rat;
+ }
+ }
+ }
+ if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ } else {
+ return ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
+ }
+ }
+
/** Resets the list of codecs used for the connection with only the codec currently in use. */
private void resetCodecList(Connection conn) {
int id = getConnectionId(conn);
@@ -746,16 +822,6 @@
}
}
- private static int classifySetupDuration(int durationMillis) {
- // keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order
- for (int i = 0; i < CALL_SETUP_DURATION_MAP.size(); i++) {
- if (durationMillis < CALL_SETUP_DURATION_MAP.keyAt(i)) {
- return CALL_SETUP_DURATION_MAP.valueAt(i);
- }
- }
- return VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW;
- }
-
private static int classifyCallDuration(long durationMillis) {
if (durationMillis == 0L) {
return VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
@@ -834,41 +900,6 @@
return map;
}
- private static SparseIntArray buildCallSetupDurationMap() {
- SparseIntArray map = new SparseIntArray();
-
- map.put(
- CALL_SETUP_DURATION_UNKNOWN,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN);
- map.put(
- CALL_SETUP_DURATION_EXTREMELY_FAST,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST);
- map.put(
- CALL_SETUP_DURATION_ULTRA_FAST,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST);
- map.put(
- CALL_SETUP_DURATION_VERY_FAST,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST);
- map.put(
- CALL_SETUP_DURATION_FAST,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST);
- map.put(
- CALL_SETUP_DURATION_NORMAL,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL);
- map.put(
- CALL_SETUP_DURATION_SLOW,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW);
- map.put(
- CALL_SETUP_DURATION_VERY_SLOW,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW);
- map.put(
- CALL_SETUP_DURATION_ULTRA_SLOW,
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW);
- // anything above would be CALL_SETUP_DURATION_EXTREMELY_SLOW
-
- return map;
- }
-
private static SparseIntArray buildCallDurationMap() {
SparseIntArray map = new SparseIntArray();
diff --git a/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java b/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
index 0190776..8b34933 100644
--- a/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.content.Context;
@@ -178,8 +179,11 @@
@Override
public void handleNetworkUnavailable() {
- String reason = "handleNetworkUnavailable";
- clearNetworkStateAndRerunDetection(reason, false /* fullyClearNitz */);
+ boolean networkStateChanged = clearNetworkState(false /* fullyClearNitz */);
+ if (networkStateChanged) {
+ String reason = "handleNetworkUnavailable";
+ runDetection(reason);
+ }
}
@Override
@@ -246,15 +250,20 @@
// while in airplane mode from influencing behavior afterwards.
//
// After clearing detection state, the time zone detection should work out from first
- // principles what the time / time zone is. This assumes calls like handleNetworkAvailable()
- // will be made after airplane mode is re-enabled as the device re-establishes network
+ // principles what the time zone is. This assumes calls like handleNetworkAvailable() will
+ // be made after airplane mode is re-enabled as the device re-establishes network
// connectivity.
// Clear country detection state.
+ boolean countryStateChanged = mCountryIsoCode != null;
mCountryIsoCode = null;
- String reason = "handleAirplaneModeChanged(" + on + ")";
- clearNetworkStateAndRerunDetection(reason, true /* fullyClearNitz */);
+ boolean networkStateChanged = clearNetworkState(true /* fullyClearNitz */);
+
+ if (countryStateChanged || networkStateChanged) {
+ String reason = "handleAirplaneModeChanged(" + on + ")";
+ runDetection(reason);
+ }
}
private void restoreNetworkStateAndRerunDetection(String reason) {
@@ -289,28 +298,17 @@
}
}
- private void clearNetworkStateAndRerunDetection(String reason, boolean fullyClearNitz) {
- if (mLatestNitzSignal == null) {
- if (fullyClearNitz) {
- mLastNitzSignalCleared = null;
- }
-
- // The network state is already empty so there's no need to do anything.
- if (DBG) {
- Rlog.d(LOG_TAG, reason + ": mLatestNitzSignal was already null. Nothing to do.");
- }
- return;
- }
-
+ private boolean clearNetworkState(boolean fullyClearNitz) {
if (fullyClearNitz) {
mLastNitzSignalCleared = null;
} else {
mLastNitzSignalCleared = new TimestampedValue<>(
mDeviceState.elapsedRealtimeMillis(), mLatestNitzSignal);
}
- mLatestNitzSignal = null;
- runDetection(reason);
+ boolean networkStateChanged = mLatestNitzSignal != null;
+ mLatestNitzSignal = null;
+ return networkStateChanged;
}
private void runDetection(String reason) {
@@ -375,7 +373,7 @@
builder.addDebugInfo("Clearing time suggestion"
+ " reason=" + reason);
} else {
- TimestampedValue<Long> newNitzTime = nitzSignal.createTimeSignal();
+ UnixEpochTime newNitzTime = nitzSignal.createTimeSignal();
builder.setUnixEpochTime(newNitzTime);
builder.addDebugInfo("Sending new time suggestion"
+ " nitzSignal=" + nitzSignal
diff --git a/src/java/com/android/internal/telephony/nitz/OWNERS b/src/java/com/android/internal/telephony/nitz/OWNERS
new file mode 100644
index 0000000..be0513d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 847766
+include platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java b/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
index 9c7aac9..74b30f8 100644
--- a/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
@@ -18,13 +18,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.app.timedetector.TimeDetector;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneDetector;
import android.content.Context;
import android.os.SystemClock;
-import android.os.TimestampedValue;
import android.util.LocalLog;
import com.android.internal.telephony.Phone;
@@ -69,8 +69,9 @@
Objects.requireNonNull(timeSuggestion);
if (timeSuggestion.getUnixEpochTime() != null) {
- TimestampedValue<Long> unixEpochTime = timeSuggestion.getUnixEpochTime();
- TelephonyMetrics.getInstance().writeNITZEvent(mSlotIndex, unixEpochTime.getValue());
+ UnixEpochTime unixEpochTime = timeSuggestion.getUnixEpochTime();
+ TelephonyMetrics.getInstance().writeNITZEvent(
+ mSlotIndex, unixEpochTime.getUnixEpochTimeMillis());
}
mTimeDetector.suggestTelephonyTime(timeSuggestion);
}
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
new file mode 100644
index 0000000..e7f09c2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Build;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.telephony.Rlog;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Datagram controller used for sending and receiving satellite datagrams.
+ */
+public class DatagramController {
+ private static final String TAG = "DatagramController";
+
+ @NonNull private static DatagramController sInstance;
+ @NonNull private final Context mContext;
+ @NonNull private final PointingAppController mPointingAppController;
+ @NonNull private final DatagramDispatcher mDatagramDispatcher;
+ @NonNull private final DatagramReceiver mDatagramReceiver;
+ public static final long MAX_DATAGRAM_ID = (long) Math.pow(2, 16);
+ public static final int ROUNDING_UNIT = 10;
+ public static final long SATELLITE_ALIGN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final boolean DEBUG = !"user".equals(Build.TYPE);
+
+ /** Variables used to update onSendDatagramStateChanged(). */
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private int mSendSubId;
+ @GuardedBy("mLock")
+ private @SatelliteManager.SatelliteDatagramTransferState int mSendDatagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ @GuardedBy("mLock")
+ private int mSendPendingCount = 0;
+ @GuardedBy("mLock")
+ private int mSendErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+ /** Variables used to update onReceiveDatagramStateChanged(). */
+ @GuardedBy("mLock")
+ private int mReceiveSubId;
+ @GuardedBy("mLock")
+ private @SatelliteManager.SatelliteDatagramTransferState int mReceiveDatagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ @GuardedBy("mLock")
+ private int mReceivePendingCount = 0;
+ @GuardedBy("mLock")
+ private int mReceiveErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+
+ private SatelliteDatagram mDemoModeDatagram;
+ private boolean mIsDemoMode = false;
+ private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
+
+ /**
+ * @return The singleton instance of DatagramController.
+ */
+ public static DatagramController getInstance() {
+ if (sInstance == null) {
+ loge("DatagramController was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the DatagramController singleton instance.
+ * @param context The Context to use to create the DatagramController.
+ * @param looper The looper for the handler.
+ * @param pointingAppController PointingAppController is used to update
+ * PointingApp about datagram transfer state changes.
+ * @return The singleton instance of DatagramController.
+ */
+ public static DatagramController make(@NonNull Context context, @NonNull Looper looper,
+ @NonNull PointingAppController pointingAppController) {
+ if (sInstance == null) {
+ sInstance = new DatagramController(context, looper, pointingAppController);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DatagramController to send and receive satellite datagrams.
+ *
+ * @param context The Context for the DatagramController.
+ * @param looper The looper for the handler
+ * @param pointingAppController PointingAppController is used to update PointingApp
+ * about datagram transfer state changes.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected DatagramController(@NonNull Context context, @NonNull Looper looper,
+ @NonNull PointingAppController pointingAppController) {
+ mContext = context;
+ mPointingAppController = pointingAppController;
+
+ // Create the DatagramDispatcher singleton,
+ // which is used to send satellite datagrams.
+ mDatagramDispatcher = DatagramDispatcher.make(mContext, looper, this);
+
+ // Create the DatagramReceiver singleton,
+ // which is used to receive satellite datagrams.
+ mDatagramReceiver = DatagramReceiver.make(mContext, looper, this);
+ }
+
+ /**
+ * Register to receive incoming datagrams over satellite.
+ *
+ * @param subId The subId of the subscription to register for incoming satellite datagrams.
+ * @param callback The callback to handle incoming datagrams over satellite.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ */
+ @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ return mDatagramReceiver.registerForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Unregister to stop receiving incoming datagrams over satellite.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+ */
+ public void unregisterForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ mDatagramReceiver.unregisterForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Poll pending satellite datagrams over satellite.
+ *
+ * This method requests modem to check if there are any pending datagrams to be received over
+ * satellite. If there are any incoming datagrams, they will be received via
+ * {@link android.telephony.satellite.SatelliteDatagramCallback#onSatelliteDatagramReceived(
+ * long, SatelliteDatagram, int, Consumer)}
+ *
+ * @param subId The subId of the subscription used for receiving datagrams.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
+ mDatagramReceiver.pollPendingSatelliteDatagrams(subId, callback);
+ }
+
+ /**
+ * Send datagram over satellite.
+ *
+ * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+ * input to this method. Datagram received here will be passed down to modem without any
+ * encoding or encryption.
+ *
+ * When demo mode is on, save the sent datagram and this datagram will be used as a received
+ * datagram.
+ *
+ * @param subId The subId of the subscription to send satellite datagrams for.
+ * @param datagramType datagram type indicating whether the datagram is of type
+ * SOS_SMS or LOCATION_SHARING.
+ * @param datagram encoded gateway datagram which is encrypted by the caller.
+ * Datagram will be passed down to modem without any encoding or encryption.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+ @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+ @NonNull Consumer<Integer> callback) {
+ setDemoModeDatagram(datagramType, datagram);
+ mDatagramDispatcher.sendSatelliteDatagram(subId, datagramType, datagram,
+ needFullScreenPointingUI, callback);
+ }
+
+ /**
+ * Update send status to {@link PointingAppController}.
+ *
+ * @param subId The subId of the subscription to send satellite datagrams for
+ * @param datagramTransferState The new send datagram transfer state.
+ * @param sendPendingCount number of datagrams that are currently being sent
+ * @param errorCode If datagram transfer failed, the reason for failure.
+ */
+ public void updateSendStatus(int subId,
+ @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
+ int sendPendingCount, int errorCode) {
+ synchronized (mLock) {
+ logd("updateSendStatus"
+ + " subId: " + subId
+ + " datagramTransferState: " + datagramTransferState
+ + " sendPendingCount: " + sendPendingCount + " errorCode: " + errorCode);
+
+ mSendSubId = subId;
+ mSendDatagramTransferState = datagramTransferState;
+ mSendPendingCount = sendPendingCount;
+ mSendErrorCode = errorCode;
+
+ notifyDatagramTransferStateChangedToSessionController();
+ mPointingAppController.updateSendDatagramTransferState(mSendSubId,
+ mSendDatagramTransferState, mSendPendingCount, mSendErrorCode);
+ }
+ }
+
+ /**
+ * Update receive status to {@link PointingAppController}.
+ *
+ * @param subId The subId of the subscription used to receive datagrams
+ * @param datagramTransferState The new receive datagram transfer state.
+ * @param receivePendingCount The number of datagrams that are currently pending to be received.
+ * @param errorCode If datagram transfer failed, the reason for failure.
+ */
+ public void updateReceiveStatus(int subId,
+ @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
+ int receivePendingCount, int errorCode) {
+ synchronized (mLock) {
+ logd("updateReceiveStatus"
+ + " subId: " + subId
+ + " datagramTransferState: " + datagramTransferState
+ + " receivePendingCount: " + receivePendingCount + " errorCode: " + errorCode);
+
+ mReceiveSubId = subId;
+ mReceiveDatagramTransferState = datagramTransferState;
+ mReceivePendingCount = receivePendingCount;
+ mReceiveErrorCode = errorCode;
+
+ notifyDatagramTransferStateChangedToSessionController();
+ mPointingAppController.updateReceiveDatagramTransferState(mReceiveSubId,
+ mReceiveDatagramTransferState, mReceivePendingCount, mReceiveErrorCode);
+ }
+
+ if (isPollingInIdleState()) {
+ mDatagramDispatcher.retrySendingDatagrams();
+ }
+ }
+
+ /**
+ * Return receive pending datagram count
+ * @return receive pending datagram count.
+ */
+ public int getReceivePendingCount() {
+ return mReceivePendingCount;
+ }
+
+ /**
+ * This function is used by {@link SatelliteController} to notify {@link DatagramController}
+ * that satellite modem state has changed.
+ *
+ * @param state Current satellite modem state.
+ */
+ public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
+ mDatagramDispatcher.onSatelliteModemStateChanged(state);
+ mDatagramReceiver.onSatelliteModemStateChanged(state);
+ }
+
+ void onDeviceAlignedWithSatellite(boolean isAligned) {
+ mDatagramDispatcher.onDeviceAlignedWithSatellite(isAligned);
+ mDatagramReceiver.onDeviceAlignedWithSatellite(isAligned);
+ }
+
+ @VisibleForTesting
+ public boolean isReceivingDatagrams() {
+ synchronized (mLock) {
+ return (mReceiveDatagramTransferState
+ == SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ }
+ }
+
+ public boolean isSendingInIdleState() {
+ synchronized (mLock) {
+ return mSendDatagramTransferState ==
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ }
+ }
+
+ public boolean isPollingInIdleState() {
+ synchronized (mLock) {
+ return mReceiveDatagramTransferState ==
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ }
+ }
+
+ /**
+ * Set variables for {@link DatagramDispatcher} and {@link DatagramReceiver} to run demo mode
+ * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setDemoMode(boolean isDemoMode) {
+ mIsDemoMode = isDemoMode;
+ mDatagramDispatcher.setDemoMode(isDemoMode);
+ mDatagramReceiver.setDemoMode(isDemoMode);
+
+ if (!isDemoMode) {
+ mDemoModeDatagram = null;
+ }
+ }
+
+ /** Get the last sent datagram for demo mode */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public SatelliteDatagram getDemoModeDatagram() {
+ return mDemoModeDatagram;
+ }
+
+ /**
+ * Set last sent datagram for demo mode
+ * @param datagramType datagram type, only DATAGRAM_TYPE_SOS_MESSAGE will be saved
+ * @param datagram datagram The last datagram saved when sendSatelliteDatagramForDemo is called
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void setDemoModeDatagram(@SatelliteManager.DatagramType int datagramType,
+ SatelliteDatagram datagram) {
+ if (mIsDemoMode && datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ mDemoModeDatagram = datagram;
+ }
+ }
+
+ long getSatelliteAlignedTimeoutDuration() {
+ return mAlignTimeoutDuration;
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds whether
+ * the device is aligned with the satellite for demo mode
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
+ if (!isMockModemAllowed()) {
+ loge("Updating align timeout duration is not allowed");
+ return false;
+ }
+
+ logd("setSatelliteDeviceAlignedTimeoutDuration: timeoutMillis=" + timeoutMillis);
+ mAlignTimeoutDuration = timeoutMillis;
+ return true;
+ }
+
+ private boolean isMockModemAllowed() {
+ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ private void notifyDatagramTransferStateChangedToSessionController() {
+ SatelliteSessionController sessionController = SatelliteSessionController.getInstance();
+ if (sessionController == null) {
+ loge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController"
+ + " is not initialized yet");
+ } else {
+ sessionController.onDatagramTransferStateChanged(
+ mSendDatagramTransferState, mReceiveDatagramTransferState);
+ }
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
new file mode 100644
index 0000000..77b410d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/**
+ * Datagram dispatcher used to send satellite datagrams.
+ */
+public class DatagramDispatcher extends Handler {
+ private static final String TAG = "DatagramDispatcher";
+
+ private static final int CMD_SEND_SATELLITE_DATAGRAM = 1;
+ private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2;
+ private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
+
+ @NonNull private static DatagramDispatcher sInstance;
+ @NonNull private final Context mContext;
+ @NonNull private final DatagramController mDatagramController;
+ @NonNull private final ControllerMetricsStats mControllerMetricsStats;
+
+ private boolean mIsDemoMode = false;
+ private boolean mIsAligned = false;
+ private DatagramDispatcherHandlerRequest mSendSatelliteDatagramRequest = null;
+
+ private static AtomicLong mNextDatagramId = new AtomicLong(0);
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mSendingDatagramInProgress;
+
+ /**
+ * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency
+ * datagrams.
+ */
+ @GuardedBy("mLock")
+ private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
+ mPendingEmergencyDatagramsMap = new LinkedHashMap<>();
+
+ /**
+ * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending non-emergency
+ * datagrams.
+ */
+ @GuardedBy("mLock")
+ private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
+ mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>();
+
+ /**
+ * Create the DatagramDispatcher singleton instance.
+ * @param context The Context to use to create the DatagramDispatcher.
+ * @param looper The looper for the handler.
+ * @param datagramController DatagramController which is used to update datagram transfer state.
+ * @return The singleton instance of DatagramDispatcher.
+ */
+ public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ if (sInstance == null) {
+ sInstance = new DatagramDispatcher(context, looper, datagramController);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DatagramDispatcher to send satellite datagrams.
+ *
+ * @param context The Context for the DatagramDispatcher.
+ * @param looper The looper for the handler.
+ * @param datagramController DatagramController which is used to update datagram transfer state.
+ */
+ @VisibleForTesting
+ protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ super(looper);
+ mContext = context;
+ mDatagramController = datagramController;
+ mControllerMetricsStats = ControllerMetricsStats.getInstance();
+
+ synchronized (mLock) {
+ mSendingDatagramInProgress = false;
+ }
+ }
+
+ private static final class DatagramDispatcherHandlerRequest {
+ /** The argument to use for the request */
+ public @NonNull Object argument;
+ /** The caller needs to specify the phone to be used for the request */
+ public @NonNull Phone phone;
+ /** The result of the request that is run on the main thread */
+ public @Nullable Object result;
+
+ DatagramDispatcherHandlerRequest(Object argument, Phone phone) {
+ this.argument = argument;
+ this.phone = phone;
+ }
+ }
+
+ private static final class SendSatelliteDatagramArgument {
+ public int subId;
+ public long datagramId;
+ public @SatelliteManager.DatagramType int datagramType;
+ public @NonNull SatelliteDatagram datagram;
+ public boolean needFullScreenPointingUI;
+ public @NonNull Consumer<Integer> callback;
+ public long datagramStartTime;
+ public boolean skipCheckingSatelliteAligned = false;
+
+ SendSatelliteDatagramArgument(int subId, long datagramId,
+ @SatelliteManager.DatagramType int datagramType,
+ @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+ @NonNull Consumer<Integer> callback) {
+ this.subId = subId;
+ this.datagramId = datagramId;
+ this.datagramType = datagramType;
+ this.datagram = datagram;
+ this.needFullScreenPointingUI = needFullScreenPointingUI;
+ this.callback = callback;
+ }
+
+ /** returns the size of outgoing SMS, rounded by 10 bytes */
+ public int getDatagramRoundedSizeBytes() {
+ if (datagram.getSatelliteDatagram() != null) {
+ int sizeBytes = datagram.getSatelliteDatagram().length;
+ // rounded by ROUNDING_UNIT
+ return (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT);
+ } else {
+ return 0;
+ }
+ }
+
+ /** sets the start time at datagram is sent out */
+ public void setDatagramStartTime() {
+ datagramStartTime =
+ datagramStartTime == 0 ? System.currentTimeMillis() : datagramStartTime;
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ DatagramDispatcherHandlerRequest request;
+ Message onCompleted;
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case CMD_SEND_SATELLITE_DATAGRAM: {
+ logd("CMD_SEND_SATELLITE_DATAGRAM");
+ request = (DatagramDispatcherHandlerRequest) msg.obj;
+ SendSatelliteDatagramArgument argument =
+ (SendSatelliteDatagramArgument) request.argument;
+ onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
+
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().sendSatelliteDatagram(argument.datagram,
+ argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+ argument.needFullScreenPointingUI, onCompleted);
+ break;
+ }
+
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.sendSatelliteDatagram(onCompleted, argument.datagram,
+ argument.needFullScreenPointingUI);
+ } else {
+ loge("sendSatelliteDatagram: No phone object");
+ synchronized (mLock) {
+ // Remove current datagram from pending map
+ if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ mPendingEmergencyDatagramsMap.remove(argument.datagramId);
+ } else {
+ mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
+ }
+
+ // Update send status
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ getPendingDatagramCount(),
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ 0, SatelliteManager.SATELLITE_ERROR_NONE);
+
+ // report phone == null case
+ reportSendDatagramCompleted(argument,
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ argument.callback.accept(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+
+ // Abort sending all the pending datagrams
+ abortSendingPendingDatagrams(argument.subId,
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+ }
+ break;
+ }
+
+ case EVENT_SEND_SATELLITE_DATAGRAM_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (DatagramDispatcherHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar, "sendSatelliteDatagram");
+ SendSatelliteDatagramArgument argument =
+ (SendSatelliteDatagramArgument) request.argument;
+
+ synchronized (mLock) {
+ if (mIsDemoMode && (error == SatelliteManager.SATELLITE_ERROR_NONE)) {
+ if (argument.skipCheckingSatelliteAligned) {
+ logd("Satellite was already aligned. No need to check alignment again");
+ } else if (!mIsAligned) {
+ logd("Satellite is not aligned in demo mode, wait for the alignment.");
+ startSatelliteAlignedTimer(request);
+ break;
+ }
+ }
+
+ logd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error);
+ // log metrics about the outgoing datagram
+ reportSendDatagramCompleted(argument, error);
+
+ mSendingDatagramInProgress = false;
+
+ // Remove current datagram from pending map.
+ if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ mPendingEmergencyDatagramsMap.remove(argument.datagramId);
+ } else {
+ mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
+ }
+
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ // Update send status for current datagram
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+ getPendingDatagramCount(), error);
+ mControllerMetricsStats.reportOutgoingDatagramSuccessCount(
+ argument.datagramType);
+
+ if (getPendingDatagramCount() > 0) {
+ // Send response for current datagram
+ argument.callback.accept(error);
+ // Send pending datagrams
+ sendPendingDatagrams();
+ } else {
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ 0, SatelliteManager.SATELLITE_ERROR_NONE);
+ // Send response for current datagram
+ argument.callback.accept(error);
+ }
+ } else {
+ // Update send status
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ getPendingDatagramCount(), error);
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ 0, SatelliteManager.SATELLITE_ERROR_NONE);
+ // Send response for current datagram
+ // after updating datagram transfer state internally.
+ argument.callback.accept(error);
+ // Abort sending all the pending datagrams
+ mControllerMetricsStats.reportOutgoingDatagramFailCount(
+ argument.datagramType);
+ abortSendingPendingDatagrams(argument.subId,
+ SatelliteManager.SATELLITE_REQUEST_ABORTED);
+ }
+ }
+ break;
+ }
+
+ case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: {
+ handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj);
+ break;
+ }
+
+ default:
+ logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
+ break;
+ }
+ }
+
+ /**
+ * Send datagram over satellite.
+ *
+ * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+ * input to this method. Datagram received here will be passed down to modem without any
+ * encoding or encryption.
+ *
+ * @param subId The subId of the subscription to send satellite datagrams for.
+ * @param datagramType datagram type indicating whether the datagram is of type
+ * SOS_SMS or LOCATION_SHARING.
+ * @param datagram encoded gateway datagram which is encrypted by the caller.
+ * Datagram will be passed down to modem without any encoding or encryption.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+ @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+ @NonNull Consumer<Integer> callback) {
+ Phone phone = SatelliteServiceUtils.getPhone();
+
+ long datagramId = mNextDatagramId.getAndUpdate(
+ n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID));
+
+ SendSatelliteDatagramArgument datagramArgs =
+ new SendSatelliteDatagramArgument(subId, datagramId, datagramType, datagram,
+ needFullScreenPointingUI, callback);
+
+ synchronized (mLock) {
+ // Add datagram to pending datagram map
+ if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ mPendingEmergencyDatagramsMap.put(datagramId, datagramArgs);
+ } else {
+ mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs);
+ }
+
+ // Modem can be busy receiving datagrams, so send datagram only when modem is not busy.
+ if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) {
+ mSendingDatagramInProgress = true;
+ datagramArgs.setDatagramStartTime();
+ mDatagramController.updateSendStatus(subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
+ sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
+ }
+ }
+ }
+
+ public void retrySendingDatagrams() {
+ synchronized (mLock) {
+ sendPendingDatagrams();
+ }
+ }
+
+ /** Set demo mode
+ *
+ * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected void setDemoMode(boolean isDemoMode) {
+ mIsDemoMode = isDemoMode;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+ if (mIsDemoMode) {
+ synchronized (mLock) {
+ mIsAligned = isAligned;
+ if (isAligned) handleEventSatelliteAligned();
+ }
+ }
+ }
+
+ private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) {
+ if (isSatelliteAlignedTimerStarted()) {
+ logd("Satellite aligned timer was already started");
+ return;
+ }
+ mSendSatelliteDatagramRequest = request;
+ sendMessageDelayed(
+ obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request),
+ getSatelliteAlignedTimeoutDuration());
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected long getSatelliteAlignedTimeoutDuration() {
+ return mDatagramController.getSatelliteAlignedTimeoutDuration();
+ }
+
+ private void handleEventSatelliteAligned() {
+ if (isSatelliteAlignedTimerStarted()) {
+ stopSatelliteAlignedTimer();
+
+ if (mSendSatelliteDatagramRequest == null) {
+ loge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null");
+ } else {
+ SendSatelliteDatagramArgument argument =
+ (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument;
+ argument.skipCheckingSatelliteAligned = true;
+ Message message = obtainMessage(
+ EVENT_SEND_SATELLITE_DATAGRAM_DONE, mSendSatelliteDatagramRequest);
+ mSendSatelliteDatagramRequest = null;
+ AsyncResult.forMessage(message, null, null);
+ message.sendToTarget();
+ }
+ }
+ }
+
+ private void handleEventSatelliteAlignedTimeout(
+ @NonNull DatagramDispatcherHandlerRequest request) {
+ SatelliteManager.SatelliteException exception =
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_NOT_REACHABLE);
+ Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ }
+
+ private boolean isSatelliteAlignedTimerStarted() {
+ return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
+ }
+
+ private void stopSatelliteAlignedTimer() {
+ removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
+ }
+
+ /**
+ * Send pending satellite datagrams. Emergency datagrams are given priority over
+ * non-emergency datagrams.
+ */
+ @GuardedBy("mLock")
+ private void sendPendingDatagrams() {
+ logd("sendPendingDatagrams()");
+ if (!mDatagramController.isPollingInIdleState()) {
+ // Datagram should be sent to satellite modem when modem is free.
+ logd("sendPendingDatagrams: modem is receiving datagrams");
+ return;
+ }
+
+ if (getPendingDatagramCount() <= 0) {
+ logd("sendPendingDatagrams: no pending datagrams to send");
+ return;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null;
+ if (!mSendingDatagramInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) {
+ pendingDatagram = mPendingEmergencyDatagramsMap.entrySet();
+ } else if (!mSendingDatagramInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) {
+ pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet();
+ }
+
+ if ((pendingDatagram != null) && pendingDatagram.iterator().hasNext()) {
+ mSendingDatagramInProgress = true;
+ SendSatelliteDatagramArgument datagramArg =
+ pendingDatagram.iterator().next().getValue();
+ // Sets the trigger time for getting pending datagrams
+ datagramArg.setDatagramStartTime();
+ mDatagramController.updateSendStatus(datagramArg.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
+ sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone);
+ }
+ }
+
+ /**
+ * Send error code to all the pending datagrams
+ *
+ * @param pendingDatagramsMap The pending datagrams map to be cleaned up.
+ * @param errorCode error code to be returned.
+ */
+ @GuardedBy("mLock")
+ private void sendErrorCodeAndCleanupPendingDatagrams(
+ LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap,
+ @SatelliteManager.SatelliteError int errorCode) {
+ if (pendingDatagramsMap.size() == 0) {
+ return;
+ }
+ loge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources");
+
+ // Send error code to all the pending datagrams
+ for (Entry<Long, SendSatelliteDatagramArgument> entry :
+ pendingDatagramsMap.entrySet()) {
+ SendSatelliteDatagramArgument argument = entry.getValue();
+ reportSendDatagramCompleted(argument, errorCode);
+ mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType);
+ argument.callback.accept(errorCode);
+ }
+
+ // Clear pending datagram maps
+ pendingDatagramsMap.clear();
+ }
+
+ /**
+ * Abort sending all the pending datagrams.
+ *
+ * @param subId The subId of the subscription used to send datagram
+ * @param errorCode The error code that resulted in abort.
+ */
+ @GuardedBy("mLock")
+ private void abortSendingPendingDatagrams(int subId,
+ @SatelliteManager.SatelliteError int errorCode) {
+ logd("abortSendingPendingDatagrams()");
+ sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode);
+ sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode);
+ }
+
+ /**
+ * Return pending datagram count
+ * @return pending datagram count
+ */
+ @GuardedBy("mLock")
+ private int getPendingDatagramCount() {
+ return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size();
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread and returns immediately.
+ *
+ * @param command command to be executed on the main thread
+ * @param argument additional parameters required to perform of the operation
+ * @param phone phone object used to perform the operation.
+ */
+ private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) {
+ DatagramDispatcherHandlerRequest request = new DatagramDispatcherHandlerRequest(
+ argument, phone);
+ Message msg = this.obtainMessage(command, request);
+ msg.sendToTarget();
+ }
+
+ private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument,
+ @NonNull @SatelliteManager.SatelliteError int resultCode) {
+ SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics(
+ new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
+ .setDatagramType(argument.datagramType)
+ .setResultCode(resultCode)
+ .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes())
+ .setDatagramTransferTimeMillis(
+ System.currentTimeMillis() - argument.datagramStartTime)
+ .build());
+ }
+
+ /**
+ * Destroys this DatagramDispatcher. Used for tearing down static resources during testing.
+ */
+ @VisibleForTesting
+ public void destroy() {
+ sInstance = null;
+ }
+
+ /**
+ * This function is used by {@link DatagramController} to notify {@link DatagramDispatcher}
+ * that satellite modem state has changed.
+ *
+ * @param state Current satellite modem state.
+ */
+ public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
+ synchronized (mLock) {
+ if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
+ || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
+ logd("onSatelliteModemStateChanged: cleaning up resources");
+ cleanUpResources();
+ } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) {
+ sendPendingDatagrams();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void cleanUpResources() {
+ mSendingDatagramInProgress = false;
+ if (getPendingDatagramCount() > 0) {
+ mDatagramController.updateSendStatus(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ getPendingDatagramCount(), SatelliteManager.SATELLITE_REQUEST_ABORTED);
+ }
+ mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ 0, SatelliteManager.SATELLITE_ERROR_NONE);
+ abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SatelliteManager.SATELLITE_REQUEST_ABORTED);
+
+ stopSatelliteAlignedTimer();
+ mIsDemoMode = false;
+ mSendSatelliteDatagramRequest = null;
+ mIsAligned = false;
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+
+ private static void logw(@NonNull String log) { Rlog.w(TAG, log); }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
new file mode 100644
index 0000000..06eede1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -0,0 +1,805 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Telephony;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Pair;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+import com.android.internal.util.FunctionalUtils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/**
+ * Datagram receiver used to receive satellite datagrams and then,
+ * deliver received datagrams to messaging apps.
+ */
+public class DatagramReceiver extends Handler {
+ private static final String TAG = "DatagramReceiver";
+
+ private static final int CMD_POLL_PENDING_SATELLITE_DATAGRAMS = 1;
+ private static final int EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE = 2;
+ private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
+
+ /** Key used to read/write satellite datagramId in shared preferences. */
+ private static final String SATELLITE_DATAGRAM_ID_KEY = "satellite_datagram_id_key";
+ private static AtomicLong mNextDatagramId = new AtomicLong(0);
+
+ @NonNull private static DatagramReceiver sInstance;
+ @NonNull private final Context mContext;
+ @NonNull private final ContentResolver mContentResolver;
+ @NonNull private SharedPreferences mSharedPreferences = null;
+ @NonNull private final DatagramController mDatagramController;
+ @NonNull private final ControllerMetricsStats mControllerMetricsStats;
+ @NonNull private final Looper mLooper;
+
+ private long mDatagramTransferStartTime = 0;
+ private boolean mIsDemoMode = false;
+ @GuardedBy("mLock")
+ private boolean mIsAligned = false;
+ private DatagramReceiverHandlerRequest mPollPendingSatelliteDatagramsRequest = null;
+ private final Object mLock = new Object();
+
+ /**
+ * Map key: subId, value: SatelliteDatagramListenerHandler to notify registrants.
+ */
+ private final ConcurrentHashMap<Integer, SatelliteDatagramListenerHandler>
+ mSatelliteDatagramListenerHandlers = new ConcurrentHashMap<>();
+
+ /**
+ * Map key: DatagramId, value: pendingAckCount
+ * This map is used to track number of listeners that are yet to send ack for a particular
+ * datagram.
+ */
+ private final ConcurrentHashMap<Long, Integer>
+ mPendingAckCountHashMap = new ConcurrentHashMap<>();
+
+ /**
+ * Create the DatagramReceiver singleton instance.
+ * @param context The Context to use to create the DatagramReceiver.
+ * @param looper The looper for the handler.
+ * @param datagramController DatagramController which is used to update datagram transfer state.
+ * @return The singleton instance of DatagramReceiver.
+ */
+ public static DatagramReceiver make(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ if (sInstance == null) {
+ sInstance = new DatagramReceiver(context, looper, datagramController);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DatagramReceiver to received satellite datagrams.
+ * The received datagrams will be delivered to respective messaging apps.
+ *
+ * @param context The Context for the DatagramReceiver.
+ * @param looper The looper for the handler.
+ * @param datagramController DatagramController which is used to update datagram transfer state.
+ */
+ @VisibleForTesting
+ protected DatagramReceiver(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ super(looper);
+ mContext = context;
+ mLooper = looper;
+ mContentResolver = context.getContentResolver();
+ mDatagramController = datagramController;
+ mControllerMetricsStats = ControllerMetricsStats.getInstance();
+
+ try {
+ mSharedPreferences =
+ mContext.getSharedPreferences(SatelliteController.SATELLITE_SHARED_PREF,
+ Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ loge("Cannot get default shared preferences: " + e);
+ }
+
+ if ((mSharedPreferences != null) &&
+ (!mSharedPreferences.contains(SATELLITE_DATAGRAM_ID_KEY))) {
+ mSharedPreferences.edit().putLong(SATELLITE_DATAGRAM_ID_KEY, mNextDatagramId.get())
+ .commit();
+ }
+ }
+
+ private static final class DatagramReceiverHandlerRequest {
+ /** The argument to use for the request */
+ public @NonNull Object argument;
+ /** The caller needs to specify the phone to be used for the request */
+ public @NonNull Phone phone;
+ /** The subId of the subscription used for the request. */
+ public int subId;
+ /** The result of the request that is run on the main thread */
+ public @Nullable Object result;
+
+ DatagramReceiverHandlerRequest(Object argument, Phone phone, int subId) {
+ this.argument = argument;
+ this.phone = phone;
+ this.subId = subId;
+ }
+ }
+
+ /**
+ * Listeners are updated about incoming datagrams using a backgroundThread.
+ */
+ @VisibleForTesting
+ public static final class SatelliteDatagramListenerHandler extends Handler {
+ public static final int EVENT_SATELLITE_DATAGRAM_RECEIVED = 1;
+ public static final int EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM = 2;
+ public static final int EVENT_RECEIVED_ACK = 3;
+
+ @NonNull private final ConcurrentHashMap<IBinder, ISatelliteDatagramCallback> mListeners;
+ private final int mSubId;
+
+ private static final class DatagramRetryArgument {
+ public long datagramId;
+ @NonNull public SatelliteDatagram datagram;
+ public int pendingCount;
+ @NonNull public ISatelliteDatagramCallback listener;
+
+ DatagramRetryArgument(long datagramId, @NonNull SatelliteDatagram datagram,
+ int pendingCount, @NonNull ISatelliteDatagramCallback listener) {
+ this.datagramId = datagramId;
+ this.datagram = datagram;
+ this.pendingCount = pendingCount;
+ this.listener = listener;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other == null || getClass() != other.getClass()) return false;
+ DatagramRetryArgument that = (DatagramRetryArgument) other;
+ return datagramId == that.datagramId
+ && datagram.equals(that.datagram)
+ && pendingCount == that.pendingCount
+ && listener.equals(that.listener);
+ }
+ }
+
+ @VisibleForTesting
+ public SatelliteDatagramListenerHandler(@NonNull Looper looper, int subId) {
+ super(looper);
+ mSubId = subId;
+ mListeners = new ConcurrentHashMap<>();
+ }
+
+ public void addListener(@NonNull ISatelliteDatagramCallback listener) {
+ mListeners.put(listener.asBinder(), listener);
+ }
+
+ public void removeListener(@NonNull ISatelliteDatagramCallback listener) {
+ mListeners.remove(listener.asBinder());
+ }
+
+ public boolean hasListeners() {
+ return !mListeners.isEmpty();
+ }
+
+ public int getNumOfListeners() {
+ return mListeners.size();
+ }
+
+ private int getTimeoutToReceiveAck() {
+ return sInstance.mContext.getResources().getInteger(
+ R.integer.config_timeout_to_receive_delivered_ack_millis);
+ }
+
+ private long getDatagramId() {
+ long datagramId = 0;
+ if (sInstance.mSharedPreferences == null) {
+ try {
+ // Try to recreate if it is null
+ sInstance.mSharedPreferences = sInstance.mContext
+ .getSharedPreferences(SatelliteController.SATELLITE_SHARED_PREF,
+ Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ loge("Cannot get default shared preferences: " + e);
+ }
+ }
+
+ if (sInstance.mSharedPreferences != null) {
+ long prevDatagramId = sInstance.mSharedPreferences
+ .getLong(SATELLITE_DATAGRAM_ID_KEY, mNextDatagramId.get());
+ datagramId = (prevDatagramId + 1) % DatagramController.MAX_DATAGRAM_ID;
+ mNextDatagramId.set(datagramId);
+ sInstance.mSharedPreferences.edit().putLong(SATELLITE_DATAGRAM_ID_KEY, datagramId)
+ .commit();
+ } else {
+ loge("Shared preferences is null - returning default datagramId");
+ datagramId = mNextDatagramId.getAndUpdate(
+ n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID));
+ }
+
+ return datagramId;
+ }
+
+ private void insertDatagram(long datagramId, @NonNull SatelliteDatagram datagram) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(
+ Telephony.SatelliteDatagrams.COLUMN_UNIQUE_KEY_DATAGRAM_ID, datagramId);
+ contentValues.put(
+ Telephony.SatelliteDatagrams.COLUMN_DATAGRAM, datagram.getSatelliteDatagram());
+ Uri uri = sInstance.mContentResolver.insert(Telephony.SatelliteDatagrams.CONTENT_URI,
+ contentValues);
+ if (uri == null) {
+ loge("Cannot insert datagram with datagramId: " + datagramId);
+ } else {
+ logd("Inserted datagram with datagramId: " + datagramId);
+ }
+ }
+
+ private void deleteDatagram(long datagramId) {
+ String whereClause = (Telephony.SatelliteDatagrams.COLUMN_UNIQUE_KEY_DATAGRAM_ID
+ + "=" + datagramId);
+ try (Cursor cursor = sInstance.mContentResolver.query(
+ Telephony.SatelliteDatagrams.CONTENT_URI,
+ null, whereClause, null, null)) {
+ if ((cursor != null) && (cursor.getCount() == 1)) {
+ int numRowsDeleted = sInstance.mContentResolver.delete(
+ Telephony.SatelliteDatagrams.CONTENT_URI, whereClause, null);
+ if (numRowsDeleted == 0) {
+ loge("Cannot delete datagram with datagramId: " + datagramId);
+ } else {
+ logd("Deleted datagram with datagramId: " + datagramId);
+ }
+ } else {
+ loge("Datagram with datagramId: " + datagramId + " is not present in DB.");
+ }
+ } catch(SQLException e) {
+ loge("deleteDatagram SQLException e:" + e);
+ }
+ }
+
+ private void onSatelliteDatagramReceived(@NonNull DatagramRetryArgument argument) {
+ try {
+ IVoidConsumer internalAck = new IVoidConsumer.Stub() {
+ /**
+ * This callback will be used by datagram receiver app
+ * to send ack back to Telephony. If the callback is not
+ * received within five minutes, then Telephony will
+ * resend the datagram again.
+ */
+ @Override
+ public void accept() {
+ logd("acknowledgeSatelliteDatagramReceived: "
+ + "datagramId=" + argument.datagramId);
+ sendMessage(obtainMessage(EVENT_RECEIVED_ACK, argument));
+ }
+ };
+
+ argument.listener.onSatelliteDatagramReceived(argument.datagramId,
+ argument.datagram, argument.pendingCount, internalAck);
+ } catch (RemoteException e) {
+ logd("EVENT_SATELLITE_DATAGRAM_RECEIVED RemoteException: " + e);
+ }
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_SATELLITE_DATAGRAM_RECEIVED: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Pair<SatelliteDatagram, Integer> result =
+ (Pair<SatelliteDatagram, Integer>) ar.result;
+ SatelliteDatagram satelliteDatagram = result.first;
+ int pendingCount = result.second;
+ logd("Received EVENT_SATELLITE_DATAGRAM_RECEIVED for subId=" + mSubId
+ + " pendingCount:" + pendingCount);
+
+ if (pendingCount <= 0 && satelliteDatagram == null) {
+ sInstance.mDatagramController.updateReceiveStatus(mSubId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
+ pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+ } else if (satelliteDatagram != null) {
+ sInstance.mDatagramController.updateReceiveStatus(mSubId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
+ pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+
+ long datagramId = getDatagramId();
+ sInstance.mPendingAckCountHashMap.put(datagramId, getNumOfListeners());
+ insertDatagram(datagramId, satelliteDatagram);
+
+ mListeners.values().forEach(listener -> {
+ DatagramRetryArgument argument = new DatagramRetryArgument(datagramId,
+ satelliteDatagram, pendingCount, listener);
+ onSatelliteDatagramReceived(argument);
+ // wait for ack and retry after the timeout specified.
+ sendMessageDelayed(
+ obtainMessage(EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM,
+ argument), getTimeoutToReceiveAck());
+ });
+
+ sInstance.mControllerMetricsStats.reportIncomingDatagramCount(
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+
+ if (pendingCount <= 0) {
+ sInstance.mDatagramController.updateReceiveStatus(mSubId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+ } else {
+ // Poll for pending datagrams
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("pollPendingSatelliteDatagram result: " + result);
+ }
+ };
+ Consumer<Integer> callback = FunctionalUtils.ignoreRemoteException(
+ internalCallback::accept);
+ sInstance.pollPendingSatelliteDatagramsInternal(mSubId, callback);
+ }
+
+ // Send the captured data about incoming datagram to metric
+ sInstance.reportMetrics(
+ satelliteDatagram, SatelliteManager.SATELLITE_ERROR_NONE);
+ break;
+ }
+
+ case EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM: {
+ DatagramRetryArgument argument = (DatagramRetryArgument) msg.obj;
+ logd("Received EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM datagramId:"
+ + argument.datagramId);
+ onSatelliteDatagramReceived(argument);
+ break;
+ }
+
+ case EVENT_RECEIVED_ACK: {
+ DatagramRetryArgument argument = (DatagramRetryArgument) msg.obj;
+ int pendingAckCount = sInstance.mPendingAckCountHashMap
+ .get(argument.datagramId);
+ pendingAckCount -= 1;
+ sInstance.mPendingAckCountHashMap.put(argument.datagramId, pendingAckCount);
+ logd("Received EVENT_RECEIVED_ACK datagramId:" + argument.datagramId);
+ removeMessages(EVENT_RETRY_DELIVERING_RECEIVED_DATAGRAM, argument);
+
+ if (pendingAckCount <= 0) {
+ // Delete datagram from DB after receiving ack from all listeners
+ deleteDatagram(argument.datagramId);
+ sInstance.mPendingAckCountHashMap.remove(argument.datagramId);
+ }
+ break;
+ }
+
+ default:
+ loge("SatelliteDatagramListenerHandler unknown event: " + msg.what);
+ }
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ DatagramReceiverHandlerRequest request;
+ Message onCompleted;
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case CMD_POLL_PENDING_SATELLITE_DATAGRAMS: {
+ request = (DatagramReceiverHandlerRequest) msg.obj;
+ onCompleted =
+ obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE, request);
+
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance()
+ .pollPendingSatelliteDatagrams(onCompleted);
+ break;
+ }
+
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.pollPendingSatelliteDatagrams(onCompleted);
+ } else {
+ loge("pollPendingSatelliteDatagrams: No phone object");
+ mDatagramController.updateReceiveStatus(request.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+ mDatagramController.getReceivePendingCount(),
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+
+ mDatagramController.updateReceiveStatus(request.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ mDatagramController.getReceivePendingCount(),
+ SatelliteManager.SATELLITE_ERROR_NONE);
+
+ reportMetrics(null, SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ mControllerMetricsStats.reportIncomingDatagramCount(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ // Send response for current request
+ ((Consumer<Integer>) request.argument)
+ .accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+ break;
+ }
+
+ case EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (DatagramReceiverHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "pollPendingSatelliteDatagrams");
+
+ if (mIsDemoMode && error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ SatelliteDatagram datagram = mDatagramController.getDemoModeDatagram();
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(
+ request.subId, mContext);
+ SatelliteDatagramListenerHandler listenerHandler =
+ mSatelliteDatagramListenerHandlers.get(validSubId);
+ if (listenerHandler != null) {
+ Pair<SatelliteDatagram, Integer> pair = new Pair<>(datagram, 0);
+ ar = new AsyncResult(null, pair, null);
+ Message message = listenerHandler.obtainMessage(
+ SatelliteDatagramListenerHandler.EVENT_SATELLITE_DATAGRAM_RECEIVED,
+ ar);
+ listenerHandler.sendMessage(message);
+ } else {
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ }
+ }
+
+ logd("EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE error: " + error);
+ if (error != SatelliteManager.SATELLITE_ERROR_NONE) {
+ mDatagramController.updateReceiveStatus(request.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+ mDatagramController.getReceivePendingCount(), error);
+
+ mDatagramController.updateReceiveStatus(request.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ mDatagramController.getReceivePendingCount(),
+ SatelliteManager.SATELLITE_ERROR_NONE);
+
+ reportMetrics(null, error);
+ mControllerMetricsStats.reportIncomingDatagramCount(error);
+ }
+ // Send response for current request
+ ((Consumer<Integer>) request.argument).accept(error);
+ break;
+ }
+
+ case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: {
+ handleEventSatelliteAlignedTimeout((DatagramReceiverHandlerRequest) msg.obj);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Register to receive incoming datagrams over satellite.
+ *
+ * @param subId The subId of the subscription to register for incoming satellite datagrams.
+ * @param callback The callback to handle incoming datagrams over satellite.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ */
+ @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ if (!SatelliteController.getInstance().isSatelliteSupported()) {
+ return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+ }
+
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ SatelliteDatagramListenerHandler satelliteDatagramListenerHandler =
+ mSatelliteDatagramListenerHandlers.get(validSubId);
+ if (satelliteDatagramListenerHandler == null) {
+ satelliteDatagramListenerHandler = new SatelliteDatagramListenerHandler(
+ mLooper, validSubId);
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().registerForSatelliteDatagramsReceived(
+ satelliteDatagramListenerHandler,
+ SatelliteDatagramListenerHandler.EVENT_SATELLITE_DATAGRAM_RECEIVED, null);
+ } else {
+ Phone phone = SatelliteServiceUtils.getPhone();
+ phone.registerForSatelliteDatagramsReceived(satelliteDatagramListenerHandler,
+ SatelliteDatagramListenerHandler.EVENT_SATELLITE_DATAGRAM_RECEIVED, null);
+ }
+ }
+
+ satelliteDatagramListenerHandler.addListener(callback);
+ mSatelliteDatagramListenerHandlers.put(validSubId, satelliteDatagramListenerHandler);
+ return SatelliteManager.SATELLITE_ERROR_NONE;
+ }
+
+ /**
+ * Unregister to stop receiving incoming datagrams over satellite.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+ */
+ public void unregisterForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ SatelliteDatagramListenerHandler handler =
+ mSatelliteDatagramListenerHandlers.get(validSubId);
+ if (handler != null) {
+ handler.removeListener(callback);
+
+ if (!handler.hasListeners()) {
+ mSatelliteDatagramListenerHandlers.remove(validSubId);
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance()
+ .unregisterForSatelliteDatagramsReceived(handler);
+ } else {
+ Phone phone = SatelliteServiceUtils.getPhone();
+ if (phone != null) {
+ phone.unregisterForSatelliteDatagramsReceived(handler);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Poll pending satellite datagrams over satellite.
+ *
+ * This method requests modem to check if there are any pending datagrams to be received over
+ * satellite. If there are any incoming datagrams, they will be received via
+ * {@link android.telephony.satellite.SatelliteDatagramCallback
+ * #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)}
+ *
+ * @param subId The subId of the subscription used for receiving datagrams.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
+ if (!mDatagramController.isPollingInIdleState()) {
+ // Poll request should be sent to satellite modem only when it is free.
+ logd("pollPendingSatelliteDatagrams: satellite modem is busy receiving datagrams.");
+ callback.accept(SatelliteManager.SATELLITE_MODEM_BUSY);
+ return;
+ }
+
+ pollPendingSatelliteDatagramsInternal(subId, callback);
+ }
+
+ private void pollPendingSatelliteDatagramsInternal(int subId,
+ @NonNull Consumer<Integer> callback) {
+ if (!mDatagramController.isSendingInIdleState()) {
+ // Poll request should be sent to satellite modem only when it is free.
+ logd("pollPendingSatelliteDatagrams: satellite modem is busy sending datagrams.");
+ callback.accept(SatelliteManager.SATELLITE_MODEM_BUSY);
+ return;
+ }
+
+ mDatagramController.updateReceiveStatus(subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING,
+ mDatagramController.getReceivePendingCount(),
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ mDatagramTransferStartTime = System.currentTimeMillis();
+ Phone phone = SatelliteServiceUtils.getPhone();
+
+ if (mIsDemoMode) {
+ DatagramReceiverHandlerRequest request = new DatagramReceiverHandlerRequest(
+ callback, phone, subId);
+ synchronized (mLock) {
+ if (mIsAligned) {
+ Message msg = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
+ request);
+ AsyncResult.forMessage(msg, null, null);
+ msg.sendToTarget();
+ } else {
+ startSatelliteAlignedTimer(request);
+ }
+ }
+ } else {
+ sendRequestAsync(CMD_POLL_PENDING_SATELLITE_DATAGRAMS, callback, phone, subId);
+ }
+ }
+
+ /**
+ * This function is used by {@link DatagramController} to notify {@link DatagramReceiver}
+ * that satellite modem state has changed.
+ *
+ * @param state Current satellite modem state.
+ */
+ public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
+ synchronized (mLock) {
+ if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
+ || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
+ logd("onSatelliteModemStateChanged: cleaning up resources");
+ cleanUpResources();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void cleanupDemoModeResources() {
+ if (isSatelliteAlignedTimerStarted()) {
+ stopSatelliteAlignedTimer();
+ if (mPollPendingSatelliteDatagramsRequest == null) {
+ loge("Satellite aligned timer was started "
+ + "but mPollPendingSatelliteDatagramsRequest is null");
+ } else {
+ Consumer<Integer> callback =
+ (Consumer<Integer>) mPollPendingSatelliteDatagramsRequest.argument;
+ callback.accept(SatelliteManager.SATELLITE_REQUEST_ABORTED);
+ }
+ }
+ mIsDemoMode = false;
+ mPollPendingSatelliteDatagramsRequest = null;
+ mIsAligned = false;
+ }
+
+ @GuardedBy("mLock")
+ private void cleanUpResources() {
+ if (mDatagramController.isReceivingDatagrams()) {
+ mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+ mDatagramController.getReceivePendingCount(),
+ SatelliteManager.SATELLITE_REQUEST_ABORTED);
+ }
+ mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ cleanupDemoModeResources();
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread and returns immediately.
+ *
+ * @param command command to be executed on the main thread
+ * @param argument additional parameters required to perform of the operation
+ * @param phone phone object used to perform the operation
+ * @param subId The subId of the subscription used for the request.
+ */
+ private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone,
+ int subId) {
+ DatagramReceiverHandlerRequest request = new DatagramReceiverHandlerRequest(
+ argument, phone, subId);
+ Message msg = this.obtainMessage(command, request);
+ msg.sendToTarget();
+ }
+
+ /** Report incoming datagram related metrics */
+ private void reportMetrics(@Nullable SatelliteDatagram satelliteDatagram,
+ @NonNull @SatelliteManager.SatelliteError int resultCode) {
+ int datagramSizeRoundedBytes = -1;
+ int datagramTransferTime = 0;
+
+ if (satelliteDatagram != null) {
+ if (satelliteDatagram.getSatelliteDatagram() != null) {
+ int sizeBytes = satelliteDatagram.getSatelliteDatagram().length;
+ // rounded by 10 bytes
+ datagramSizeRoundedBytes =
+ (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT);
+ }
+ datagramTransferTime = (int) (System.currentTimeMillis() - mDatagramTransferStartTime);
+ mDatagramTransferStartTime = 0;
+ }
+
+ SatelliteStats.getInstance().onSatelliteIncomingDatagramMetrics(
+ new SatelliteStats.SatelliteIncomingDatagramParams.Builder()
+ .setResultCode(resultCode)
+ .setDatagramSizeBytes(datagramSizeRoundedBytes)
+ .setDatagramTransferTimeMillis(datagramTransferTime)
+ .build());
+ }
+
+ /** Set demo mode
+ *
+ * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected void setDemoMode(boolean isDemoMode) {
+ mIsDemoMode = isDemoMode;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+ if (mIsDemoMode) {
+ synchronized (mLock) {
+ mIsAligned = isAligned;
+ if (isAligned) handleEventSatelliteAligned();
+ }
+ }
+ }
+
+ private void startSatelliteAlignedTimer(DatagramReceiverHandlerRequest request) {
+ if (isSatelliteAlignedTimerStarted()) {
+ logd("Satellite aligned timer was already started");
+ return;
+ }
+ mPollPendingSatelliteDatagramsRequest = request;
+ sendMessageDelayed(
+ obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request),
+ getSatelliteAlignedTimeoutDuration());
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected long getSatelliteAlignedTimeoutDuration() {
+ return mDatagramController.getSatelliteAlignedTimeoutDuration();
+ }
+
+ private void handleEventSatelliteAligned() {
+ if (isSatelliteAlignedTimerStarted()) {
+ stopSatelliteAlignedTimer();
+
+ if (mPollPendingSatelliteDatagramsRequest == null) {
+ loge("handleSatelliteAlignedTimer: mPollPendingSatelliteDatagramsRequest is null");
+ } else {
+ Message message = obtainMessage(
+ EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
+ mPollPendingSatelliteDatagramsRequest);
+ mPollPendingSatelliteDatagramsRequest = null;
+ AsyncResult.forMessage(message, null, null);
+ message.sendToTarget();
+ }
+ }
+ }
+
+ private void handleEventSatelliteAlignedTimeout(DatagramReceiverHandlerRequest request) {
+ SatelliteManager.SatelliteException exception =
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_NOT_REACHABLE);
+ Message message = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE, request);
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ }
+
+ private boolean isSatelliteAlignedTimerStarted() {
+ return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
+ }
+
+ private void stopSatelliteAlignedTimer() {
+ removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
+ }
+
+ /**
+ * Destroys this DatagramDispatcher. Used for tearing down static resources during testing.
+ */
+ @VisibleForTesting
+ public void destroy() {
+ sInstance = null;
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
new file mode 100644
index 0000000..f7f93cf
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.telephony.Rlog;
+import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteManager;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.internal.telephony.Phone;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * PointingApp controller to manage interactions with PointingUI app.
+ */
+public class PointingAppController {
+ private static final String TAG = "PointingAppController";
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final boolean DEBUG = !"user".equals(Build.TYPE);
+
+ @NonNull
+ private static PointingAppController sInstance;
+ @NonNull private final Context mContext;
+ private boolean mStartedSatelliteTransmissionUpdates;
+ @NonNull private String mPointingUiPackageName = "";
+ @NonNull private String mPointingUiClassName = "";
+
+ /**
+ * Map key: subId, value: SatelliteTransmissionUpdateHandler to notify registrants.
+ */
+ private final ConcurrentHashMap<Integer, SatelliteTransmissionUpdateHandler>
+ mSatelliteTransmissionUpdateHandlers = new ConcurrentHashMap<>();
+
+ /**
+ * @return The singleton instance of PointingAppController.
+ */
+ public static PointingAppController getInstance() {
+ if (sInstance == null) {
+ loge("PointingAppController was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the PointingAppController singleton instance.
+ * @param context The Context to use to create the PointingAppController.
+ * @return The singleton instance of PointingAppController.
+ */
+ public static PointingAppController make(@NonNull Context context) {
+ if (sInstance == null) {
+ sInstance = new PointingAppController(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a PointingAppController to manage interactions with PointingUI app.
+ *
+ * @param context The Context for the PointingUIController.
+ */
+ private PointingAppController(@NonNull Context context) {
+ mContext = context;
+ mStartedSatelliteTransmissionUpdates = false;
+ }
+
+ /**
+ * Set the flag mStartedSatelliteTransmissionUpdates to true or false based on the state of
+ * transmission updates
+ * @param startedSatelliteTransmissionUpdates boolean to set the flag
+ */
+ public void setStartedSatelliteTransmissionUpdates(
+ boolean startedSatelliteTransmissionUpdates) {
+ mStartedSatelliteTransmissionUpdates = startedSatelliteTransmissionUpdates;
+ }
+
+ private static final class DatagramTransferStateHandlerRequest {
+ public int datagramTransferState;
+ public int pendingCount;
+ public int errorCode;
+
+ DatagramTransferStateHandlerRequest(int datagramTransferState, int pendingCount,
+ int errorCode) {
+ this.datagramTransferState = datagramTransferState;
+ this.pendingCount = pendingCount;
+ this.errorCode = errorCode;
+ }
+ }
+
+
+ private static final class SatelliteTransmissionUpdateHandler extends Handler {
+ public static final int EVENT_POSITION_INFO_CHANGED = 1;
+ public static final int EVENT_SEND_DATAGRAM_STATE_CHANGED = 2;
+ public static final int EVENT_RECEIVE_DATAGRAM_STATE_CHANGED = 3;
+ public static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 4;
+
+ private final ConcurrentHashMap<IBinder, ISatelliteTransmissionUpdateCallback> mListeners;
+ SatelliteTransmissionUpdateHandler(Looper looper) {
+ super(looper);
+ mListeners = new ConcurrentHashMap<>();
+ }
+
+ public void addListener(ISatelliteTransmissionUpdateCallback listener) {
+ mListeners.put(listener.asBinder(), listener);
+ }
+
+ public void removeListener(ISatelliteTransmissionUpdateCallback listener) {
+ mListeners.remove(listener.asBinder());
+ }
+
+ public boolean hasListeners() {
+ return !mListeners.isEmpty();
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_POSITION_INFO_CHANGED: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ PointingInfo pointingInfo = (PointingInfo) ar.result;
+ List<IBinder> toBeRemoved = new ArrayList<>();
+ mListeners.values().forEach(listener -> {
+ try {
+ listener.onSatellitePositionChanged(pointingInfo);
+ } catch (RemoteException e) {
+ logd("EVENT_POSITION_INFO_CHANGED RemoteException: " + e);
+ toBeRemoved.add(listener.asBinder());
+ }
+ });
+ toBeRemoved.forEach(listener -> {
+ mListeners.remove(listener);
+ });
+ break;
+ }
+
+ case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ logd("Receive EVENT_DATAGRAM_TRANSFER_STATE_CHANGED state=" + (int) ar.result);
+ break;
+ }
+
+ case EVENT_SEND_DATAGRAM_STATE_CHANGED: {
+ logd("Received EVENT_SEND_DATAGRAM_STATE_CHANGED");
+ DatagramTransferStateHandlerRequest request =
+ (DatagramTransferStateHandlerRequest) msg.obj;
+ List<IBinder> toBeRemoved = new ArrayList<>();
+ mListeners.values().forEach(listener -> {
+ try {
+ listener.onSendDatagramStateChanged(request.datagramTransferState,
+ request.pendingCount, request.errorCode);
+ } catch (RemoteException e) {
+ logd("EVENT_SEND_DATAGRAM_STATE_CHANGED RemoteException: " + e);
+ toBeRemoved.add(listener.asBinder());
+ }
+ });
+ toBeRemoved.forEach(listener -> {
+ mListeners.remove(listener);
+ });
+ break;
+ }
+
+ case EVENT_RECEIVE_DATAGRAM_STATE_CHANGED: {
+ logd("Received EVENT_RECEIVE_DATAGRAM_STATE_CHANGED");
+ DatagramTransferStateHandlerRequest request =
+ (DatagramTransferStateHandlerRequest) msg.obj;
+ List<IBinder> toBeRemoved = new ArrayList<>();
+ mListeners.values().forEach(listener -> {
+ try {
+ listener.onReceiveDatagramStateChanged(request.datagramTransferState,
+ request.pendingCount, request.errorCode);
+ } catch (RemoteException e) {
+ logd("EVENT_RECEIVE_DATAGRAM_STATE_CHANGED RemoteException: " + e);
+ toBeRemoved.add(listener.asBinder());
+ }
+ });
+ toBeRemoved.forEach(listener -> {
+ mListeners.remove(listener);
+ });
+ break;
+ }
+
+ default:
+ loge("SatelliteTransmissionUpdateHandler unknown event: " + msg.what);
+ }
+ }
+ }
+
+ /**
+ * Register to start receiving updates for satellite position and datagram transfer state
+ * @param subId The subId of the subscription to register for receiving the updates.
+ * @param callback The callback to notify of satellite transmission updates.
+ * @param phone The Phone object to unregister for receiving the updates.
+ */
+ public void registerForSatelliteTransmissionUpdates(int subId,
+ ISatelliteTransmissionUpdateCallback callback, Phone phone) {
+ SatelliteTransmissionUpdateHandler handler =
+ mSatelliteTransmissionUpdateHandlers.get(subId);
+ if (handler != null) {
+ handler.addListener(callback);
+ return;
+ } else {
+ handler = new SatelliteTransmissionUpdateHandler(Looper.getMainLooper());
+ handler.addListener(callback);
+ mSatelliteTransmissionUpdateHandlers.put(subId, handler);
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().registerForSatellitePositionInfoChanged(
+ handler, SatelliteTransmissionUpdateHandler.EVENT_POSITION_INFO_CHANGED,
+ null);
+ SatelliteModemInterface.getInstance().registerForDatagramTransferStateChanged(
+ handler,
+ SatelliteTransmissionUpdateHandler.EVENT_DATAGRAM_TRANSFER_STATE_CHANGED,
+ null);
+ } else {
+ phone.registerForSatellitePositionInfoChanged(handler,
+ SatelliteTransmissionUpdateHandler.EVENT_POSITION_INFO_CHANGED, null);
+ }
+ }
+ }
+
+ /**
+ * Unregister to stop receiving updates on satellite position and datagram transfer state
+ * If the callback was not registered before, it is ignored
+ * @param subId The subId of the subscription to unregister for receiving the updates.
+ * @param result The callback to get the error code in case of failure
+ * @param callback The callback that was passed to {@link
+ * #registerForSatelliteTransmissionUpdates(int, ISatelliteTransmissionUpdateCallback, Phone)}.
+ * @param phone The Phone object to unregister for receiving the updates
+ */
+ public void unregisterForSatelliteTransmissionUpdates(int subId, Consumer<Integer> result,
+ ISatelliteTransmissionUpdateCallback callback, Phone phone) {
+ SatelliteTransmissionUpdateHandler handler =
+ mSatelliteTransmissionUpdateHandlers.get(subId);
+ if (handler != null) {
+ handler.removeListener(callback);
+
+ if (handler.hasListeners()) {
+ result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+ return;
+ }
+
+ mSatelliteTransmissionUpdateHandlers.remove(subId);
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().unregisterForSatellitePositionInfoChanged(
+ handler);
+ SatelliteModemInterface.getInstance().unregisterForDatagramTransferStateChanged(
+ handler);
+ } else {
+ if (phone == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ phone.unregisterForSatellitePositionInfoChanged(handler);
+ }
+ }
+ }
+
+ /**
+ * Start receiving satellite trasmission updates.
+ * This can be called by the pointing UI when the user starts pointing to the satellite.
+ * Modem should continue to report the pointing input as the device or satellite moves.
+ * The transmission updates will be received via
+ * {@link android.telephony.satellite.SatelliteTransmissionUpdateCallback
+ * #onSatellitePositionChanged(pointingInfo)}.
+ */
+ public void startSatelliteTransmissionUpdates(@NonNull Message message, @Nullable Phone phone) {
+ if (mStartedSatelliteTransmissionUpdates) {
+ logd("startSatelliteTransmissionUpdates: already started");
+ AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_ERROR_NONE));
+ message.sendToTarget();
+ return;
+ }
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().startSendingSatellitePointingInfo(message);
+ mStartedSatelliteTransmissionUpdates = true;
+ return;
+ }
+ if (phone != null) {
+ phone.startSatellitePositionUpdates(message);
+ mStartedSatelliteTransmissionUpdates = true;
+ } else {
+ loge("startSatelliteTransmissionUpdates: No phone object");
+ AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+ message.sendToTarget();
+ }
+ }
+
+ /**
+ * Stop receiving satellite transmission updates.
+ * This can be called by the pointing UI when the user stops pointing to the satellite.
+ */
+ public void stopSatelliteTransmissionUpdates(@NonNull Message message, @Nullable Phone phone) {
+ if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
+ SatelliteModemInterface.getInstance().stopSendingSatellitePointingInfo(message);
+ return;
+ }
+ if (phone != null) {
+ phone.stopSatellitePositionUpdates(message);
+ } else {
+ loge("startSatelliteTransmissionUpdates: No phone object");
+ AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+ message.sendToTarget();
+ }
+ }
+
+ /**
+ * Check if Pointing is needed and Launch Pointing UI
+ * @param needFullScreenPointingUI if pointing UI has to be launchd with Full screen
+ */
+ public void startPointingUI(boolean needFullScreenPointingUI) {
+ String packageName = getPointingUiPackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ logd("startPointingUI: config_pointing_ui_package is not set. Ignore the request");
+ return;
+ }
+
+ Intent launchIntent;
+ String className = getPointingUiClassName();
+ if (!TextUtils.isEmpty(className)) {
+ launchIntent = new Intent()
+ .setComponent(new ComponentName(packageName, className))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ } else {
+ launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
+ }
+ if (launchIntent == null) {
+ loge("startPointingUI: launchIntent is null");
+ return;
+ }
+ launchIntent.putExtra("needFullScreen", needFullScreenPointingUI);
+
+ try {
+ mContext.startActivity(launchIntent);
+ } catch (ActivityNotFoundException ex) {
+ loge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
+ }
+ }
+
+ public void updateSendDatagramTransferState(int subId,
+ @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
+ int sendPendingCount, int errorCode) {
+ DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
+ datagramTransferState, sendPendingCount, errorCode);
+ SatelliteTransmissionUpdateHandler handler =
+ mSatelliteTransmissionUpdateHandlers.get(subId);
+
+ if (handler != null) {
+ Message msg = handler.obtainMessage(
+ SatelliteTransmissionUpdateHandler.EVENT_SEND_DATAGRAM_STATE_CHANGED,
+ request);
+ msg.sendToTarget();
+ } else {
+ loge("SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+ }
+ }
+
+ public void updateReceiveDatagramTransferState(int subId,
+ @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
+ int receivePendingCount, int errorCode) {
+ DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
+ datagramTransferState, receivePendingCount, errorCode);
+ SatelliteTransmissionUpdateHandler handler =
+ mSatelliteTransmissionUpdateHandlers.get(subId);
+
+ if (handler != null) {
+ Message msg = handler.obtainMessage(
+ SatelliteTransmissionUpdateHandler.EVENT_RECEIVE_DATAGRAM_STATE_CHANGED,
+ request);
+ msg.sendToTarget();
+ } else {
+ loge(" SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+ }
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite pointing UI app package and class names.
+ *
+ * @param packageName The package name of the satellite pointing UI app.
+ * @param className The class name of the satellite pointing UI app.
+ * @return {@code true} if the satellite pointing UI app package and class is set successfully,
+ * {@code false} otherwise.
+ */
+ boolean setSatellitePointingUiClassName(
+ @Nullable String packageName, @Nullable String className) {
+ if (!isMockModemAllowed()) {
+ loge("setSatellitePointingUiClassName: modifying satellite pointing UI package and "
+ + "class name is not allowed");
+ return false;
+ }
+
+ logd("setSatellitePointingUiClassName: config_pointing_ui_package is updated, new "
+ + "packageName=" + packageName
+ + ", config_pointing_ui_class new className=" + className);
+
+ if (packageName == null || packageName.equals("null")) {
+ mPointingUiPackageName = "";
+ mPointingUiClassName = "";
+ } else {
+ mPointingUiPackageName = packageName;
+ if (className == null || className.equals("null")) {
+ mPointingUiClassName = "";
+ } else {
+ mPointingUiClassName = className;
+ }
+ }
+
+ return true;
+ }
+
+ @NonNull private String getPointingUiPackageName() {
+ if (!TextUtils.isEmpty(mPointingUiPackageName)) {
+ return mPointingUiPackageName;
+ }
+ return TextUtils.emptyIfNull(mContext.getResources().getString(
+ R.string.config_pointing_ui_package));
+ }
+
+ @NonNull private String getPointingUiClassName() {
+ if (!TextUtils.isEmpty(mPointingUiClassName)) {
+ return mPointingUiClassName;
+ }
+ return TextUtils.emptyIfNull(mContext.getResources().getString(
+ R.string.config_pointing_ui_class));
+ }
+
+ private boolean isMockModemAllowed() {
+ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+ /**
+ * TODO: The following needs to be added in this class:
+ * - check if pointingUI crashes - then restart it
+ */
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
new file mode 100644
index 0000000..5cd8444
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -0,0 +1,2280 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.net.wifi.WifiManager;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.provider.Settings;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+import android.uwb.UwbManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
+import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+import com.android.internal.util.FunctionalUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * Satellite controller is the backend service of
+ * {@link android.telephony.satellite.SatelliteManager}.
+ */
+public class SatelliteController extends Handler {
+ private static final String TAG = "SatelliteController";
+ /** Whether enabling verbose debugging message or not. */
+ private static final boolean DBG = false;
+ /** File used to store shared preferences related to satellite. */
+ public static final String SATELLITE_SHARED_PREF = "satellite_shared_pref";
+ /** Value to pass for the setting key SATELLITE_MODE_ENABLED, enabled = 1, disabled = 0 */
+ public static final int SATELLITE_MODE_ENABLED_TRUE = 1;
+ public static final int SATELLITE_MODE_ENABLED_FALSE = 0;
+
+ /** Message codes used in handleMessage() */
+ //TODO: Move the Commands and events related to position updates to PointingAppController
+ private static final int CMD_START_SATELLITE_TRANSMISSION_UPDATES = 1;
+ private static final int EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE = 2;
+ private static final int CMD_STOP_SATELLITE_TRANSMISSION_UPDATES = 3;
+ private static final int EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE = 4;
+ private static final int CMD_PROVISION_SATELLITE_SERVICE = 7;
+ private static final int EVENT_PROVISION_SATELLITE_SERVICE_DONE = 8;
+ private static final int CMD_DEPROVISION_SATELLITE_SERVICE = 9;
+ private static final int EVENT_DEPROVISION_SATELLITE_SERVICE_DONE = 10;
+ private static final int CMD_SET_SATELLITE_ENABLED = 11;
+ private static final int EVENT_SET_SATELLITE_ENABLED_DONE = 12;
+ private static final int CMD_IS_SATELLITE_ENABLED = 13;
+ private static final int EVENT_IS_SATELLITE_ENABLED_DONE = 14;
+ private static final int CMD_IS_SATELLITE_SUPPORTED = 15;
+ private static final int EVENT_IS_SATELLITE_SUPPORTED_DONE = 16;
+ private static final int CMD_GET_SATELLITE_CAPABILITIES = 17;
+ private static final int EVENT_GET_SATELLITE_CAPABILITIES_DONE = 18;
+ private static final int CMD_IS_SATELLITE_COMMUNICATION_ALLOWED = 19;
+ private static final int EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE = 20;
+ private static final int CMD_GET_TIME_SATELLITE_NEXT_VISIBLE = 21;
+ private static final int EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE = 22;
+ private static final int EVENT_RADIO_STATE_CHANGED = 23;
+ private static final int CMD_IS_SATELLITE_PROVISIONED = 24;
+ private static final int EVENT_IS_SATELLITE_PROVISIONED_DONE = 25;
+ private static final int EVENT_SATELLITE_PROVISION_STATE_CHANGED = 26;
+ private static final int EVENT_PENDING_DATAGRAMS = 27;
+ private static final int EVENT_SATELLITE_MODEM_STATE_CHANGED = 28;
+
+ @NonNull private static SatelliteController sInstance;
+ @NonNull private final Context mContext;
+ @NonNull private final SatelliteModemInterface mSatelliteModemInterface;
+ @NonNull private SatelliteSessionController mSatelliteSessionController;
+ @NonNull private final PointingAppController mPointingAppController;
+ @NonNull private final DatagramController mDatagramController;
+ @NonNull private final ControllerMetricsStats mControllerMetricsStats;
+ @NonNull private final ProvisionMetricsStats mProvisionMetricsStats;
+ private SharedPreferences mSharedPreferences = null;
+ private final CommandsInterface mCi;
+ private ContentResolver mContentResolver = null;
+
+ private final Object mRadioStateLock = new Object();
+
+ /** Flags to indicate whether the resepective radio is enabled */
+ @GuardedBy("mRadioStateLock")
+ private boolean mBTStateEnabled = false;
+ @GuardedBy("mRadioStateLock")
+ private boolean mNfcStateEnabled = false;
+ @GuardedBy("mRadioStateLock")
+ private boolean mUwbStateEnabled = false;
+ @GuardedBy("mRadioStateLock")
+ private boolean mWifiStateEnabled = false;
+
+ // Flags to indicate that respective radios need to be disabled when satellite is enabled
+ private boolean mDisableBTOnSatelliteEnabled = false;
+ private boolean mDisableNFCOnSatelliteEnabled = false;
+ private boolean mDisableUWBOnSatelliteEnabled = false;
+ private boolean mDisableWifiOnSatelliteEnabled = false;
+
+ private final Object mSatelliteEnabledRequestLock = new Object();
+ @GuardedBy("mSatelliteEnabledRequestLock")
+ private RequestSatelliteEnabledArgument mSatelliteEnabledRequest = null;
+ /** Flag to indicate that satellite is enabled successfully
+ * and waiting for all the radios to be disabled so that success can be sent to callback
+ */
+ @GuardedBy("mSatelliteEnabledRequestLock")
+ private boolean mWaitingForRadioDisabled = false;
+
+ private final AtomicBoolean mRegisteredForProvisionStateChangedWithSatelliteService =
+ new AtomicBoolean(false);
+ private final AtomicBoolean mRegisteredForProvisionStateChangedWithPhone =
+ new AtomicBoolean(false);
+ private final AtomicBoolean mRegisteredForPendingDatagramCountWithSatelliteService =
+ new AtomicBoolean(false);
+ private final AtomicBoolean mRegisteredForPendingDatagramCountWithPhone =
+ new AtomicBoolean(false);
+ private final AtomicBoolean mRegisteredForSatelliteModemStateChangedWithSatelliteService =
+ new AtomicBoolean(false);
+ private final AtomicBoolean mRegisteredForSatelliteModemStateChangedWithPhone =
+ new AtomicBoolean(false);
+ /**
+ * Map key: subId, value: callback to get error code of the provision request.
+ */
+ private final ConcurrentHashMap<Integer, Consumer<Integer>> mSatelliteProvisionCallbacks =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Map key: binder of the callback, value: callback to receive provision state changed events.
+ */
+ private final ConcurrentHashMap<IBinder, ISatelliteProvisionStateCallback>
+ mSatelliteProvisionStateChangedListeners = new ConcurrentHashMap<>();
+ private final Object mIsSatelliteSupportedLock = new Object();
+ @GuardedBy("mIsSatelliteSupportedLock")
+ private Boolean mIsSatelliteSupported = null;
+ private boolean mIsDemoModeEnabled = false;
+ private final Object mIsSatelliteEnabledLock = new Object();
+ @GuardedBy("mIsSatelliteEnabledLock")
+ private Boolean mIsSatelliteEnabled = null;
+ private boolean mIsRadioOn = false;
+ private final Object mIsSatelliteProvisionedLock = new Object();
+ @GuardedBy("mIsSatelliteProvisionedLock")
+ private Boolean mIsSatelliteProvisioned = null;
+ private final Object mSatelliteCapabilitiesLock = new Object();
+ @GuardedBy("mSatelliteCapabilitiesLock")
+ private SatelliteCapabilities mSatelliteCapabilities;
+ private final Object mNeedsSatellitePointingLock = new Object();
+ @GuardedBy("mNeedsSatellitePointingLock")
+ private boolean mNeedsSatellitePointing = false;
+
+ /**
+ * @return The singleton instance of SatelliteController.
+ */
+ public static SatelliteController getInstance() {
+ if (sInstance == null) {
+ loge("SatelliteController was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the SatelliteController singleton instance.
+ * @param context The Context to use to create the SatelliteController.
+ */
+ public static void make(@NonNull Context context) {
+ if (sInstance == null) {
+ HandlerThread satelliteThread = new HandlerThread(TAG);
+ satelliteThread.start();
+ sInstance = new SatelliteController(context, satelliteThread.getLooper());
+ }
+ }
+
+ /**
+ * Create a SatelliteController to act as a backend service of
+ * {@link android.telephony.satellite.SatelliteManager}
+ *
+ * @param context The Context for the SatelliteController.
+ * @param looper The looper for the handler. It does not run on main thread.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public SatelliteController(@NonNull Context context, @NonNull Looper looper) {
+ super(looper);
+
+ mContext = context;
+ Phone phone = SatelliteServiceUtils.getPhone();
+ mCi = phone.mCi;
+ // Create the SatelliteModemInterface singleton, which is used to manage connections
+ // to the satellite service and HAL interface.
+ mSatelliteModemInterface = SatelliteModemInterface.make(mContext, this);
+
+ // Create the PointingUIController singleton,
+ // which is used to manage interactions with PointingUI app.
+ mPointingAppController = PointingAppController.make(mContext);
+
+ // Create the SatelliteControllerMetrics to report controller metrics
+ // should be called before making DatagramController
+ mControllerMetricsStats = ControllerMetricsStats.make(mContext);
+ mProvisionMetricsStats = ProvisionMetricsStats.getOrCreateInstance();
+
+ // Create the DatagramController singleton,
+ // which is used to send and receive satellite datagrams.
+ mDatagramController = DatagramController.make(mContext, looper, mPointingAppController);
+
+ requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteSupported: resultCode=" + resultCode);
+ }
+ });
+ mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+ mIsRadioOn = phone.isRadioOn();
+ registerForSatelliteProvisionStateChanged();
+ registerForPendingDatagramCount();
+ registerForSatelliteModemStateChanged();
+ mContentResolver = mContext.getContentResolver();
+
+ try {
+ mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
+ Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ loge("Cannot get default shared preferences: " + e);
+ }
+
+ initializeSatelliteModeRadios();
+
+ ContentObserver satelliteModeRadiosContentObserver = new ContentObserver(this) {
+ @Override
+ public void onChange(boolean selfChange) {
+ initializeSatelliteModeRadios();
+ }
+ };
+ if (mContentResolver != null) {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.SATELLITE_MODE_RADIOS),
+ false, satelliteModeRadiosContentObserver);
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void initializeSatelliteModeRadios() {
+ if (mContentResolver != null) {
+ BTWifiNFCStateReceiver bTWifiNFCSateReceiver = new BTWifiNFCStateReceiver();
+ UwbAdapterStateCallback uwbAdapterStateCallback = new UwbAdapterStateCallback();
+ IntentFilter radioStateIntentFilter = new IntentFilter();
+
+ synchronized (mRadioStateLock) {
+ // Initialize radio states to default value
+ mDisableBTOnSatelliteEnabled = false;
+ mDisableNFCOnSatelliteEnabled = false;
+ mDisableWifiOnSatelliteEnabled = false;
+ mDisableUWBOnSatelliteEnabled = false;
+
+ mBTStateEnabled = false;
+ mNfcStateEnabled = false;
+ mWifiStateEnabled = false;
+ mUwbStateEnabled = false;
+
+ // Read satellite mode radios from settings
+ String satelliteModeRadios = Settings.Global.getString(mContentResolver,
+ Settings.Global.SATELLITE_MODE_RADIOS);
+ if (satelliteModeRadios == null) {
+ loge("initializeSatelliteModeRadios: satelliteModeRadios is null");
+ return;
+ }
+ logd("Radios To be checked when satellite is on: " + satelliteModeRadios);
+
+ if (satelliteModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (bluetoothAdapter != null) {
+ mDisableBTOnSatelliteEnabled = true;
+ mBTStateEnabled = bluetoothAdapter.isEnabled();
+ radioStateIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ }
+ }
+
+ if (satelliteModeRadios.contains(Settings.Global.RADIO_NFC)) {
+ Context applicationContext = mContext.getApplicationContext();
+ NfcAdapter nfcAdapter = null;
+ if (applicationContext != null) {
+ nfcAdapter = NfcAdapter.getDefaultAdapter(mContext.getApplicationContext());
+ }
+ if (nfcAdapter != null) {
+ mDisableNFCOnSatelliteEnabled = true;
+ mNfcStateEnabled = nfcAdapter.isEnabled();
+ radioStateIntentFilter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+ }
+ }
+
+ if (satelliteModeRadios.contains(Settings.Global.RADIO_WIFI)) {
+ WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+ if (wifiManager != null) {
+ mDisableWifiOnSatelliteEnabled = true;
+ mWifiStateEnabled = wifiManager.isWifiEnabled();
+ radioStateIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ }
+ }
+ mContext.registerReceiver(bTWifiNFCSateReceiver, radioStateIntentFilter);
+
+ if (satelliteModeRadios.contains(Settings.Global.RADIO_UWB)) {
+ UwbManager uwbManager = mContext.getSystemService(UwbManager.class);
+ if (uwbManager != null) {
+ mDisableUWBOnSatelliteEnabled = true;
+ mUwbStateEnabled = uwbManager.isUwbEnabled();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ uwbManager.registerAdapterStateCallback(mContext.getMainExecutor(),
+ uwbAdapterStateCallback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ logd("mDisableBTOnSatelliteEnabled: " + mDisableBTOnSatelliteEnabled
+ + " mDisableNFCOnSatelliteEnabled: " + mDisableNFCOnSatelliteEnabled
+ + " mDisableWifiOnSatelliteEnabled: " + mDisableWifiOnSatelliteEnabled
+ + " mDisableUWBOnSatelliteEnabled: " + mDisableUWBOnSatelliteEnabled);
+
+ logd("mBTStateEnabled: " + mBTStateEnabled
+ + " mNfcStateEnabled: " + mNfcStateEnabled
+ + " mWifiStateEnabled: " + mWifiStateEnabled
+ + " mUwbStateEnabled: " + mUwbStateEnabled);
+ }
+ }
+ }
+
+ protected class UwbAdapterStateCallback implements UwbManager.AdapterStateCallback {
+
+ public String toString(int state) {
+ switch (state) {
+ case UwbManager.AdapterStateCallback.STATE_DISABLED:
+ return "Disabled";
+
+ case UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE:
+ return "Inactive";
+
+ case UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE:
+ return "Active";
+
+ default:
+ return "";
+ }
+ }
+
+ @Override
+ public void onStateChanged(int state, int reason) {
+ logd("UwbAdapterStateCallback#onStateChanged() called, state = " + toString(state));
+ logd("Adapter state changed reason " + String.valueOf(reason));
+ synchronized (mRadioStateLock) {
+ if (state == UwbManager.AdapterStateCallback.STATE_DISABLED) {
+ mUwbStateEnabled = false;
+ evaluateToSendSatelliteEnabledSuccess();
+ } else {
+ mUwbStateEnabled = true;
+ }
+ logd("mUwbStateEnabled: " + mUwbStateEnabled);
+ }
+ }
+ }
+
+ protected class BTWifiNFCStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ logd("BTWifiNFCStateReceiver NULL action for intent " + intent);
+ return;
+ }
+
+ switch (action) {
+ case BluetoothAdapter.ACTION_STATE_CHANGED:
+ int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ logd("Bluetooth state updated to " + btState);
+ synchronized (mRadioStateLock) {
+ if (btState == BluetoothAdapter.STATE_OFF) {
+ mBTStateEnabled = false;
+ evaluateToSendSatelliteEnabledSuccess();
+ } else if (btState == BluetoothAdapter.STATE_ON) {
+ mBTStateEnabled = true;
+ }
+ logd("mBTStateEnabled: " + mBTStateEnabled);
+ }
+ break;
+
+ case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED:
+ int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1);
+ logd("Nfc state updated to " + nfcState);
+ synchronized (mRadioStateLock) {
+ if (nfcState == NfcAdapter.STATE_ON) {
+ mNfcStateEnabled = true;
+ } else if (nfcState == NfcAdapter.STATE_OFF) {
+ mNfcStateEnabled = false;
+ evaluateToSendSatelliteEnabledSuccess();
+ }
+ logd("mNfcStateEnabled: " + mNfcStateEnabled);
+ }
+ break;
+
+ case WifiManager.WIFI_STATE_CHANGED_ACTION:
+ int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN);
+ logd("Wifi state updated to " + wifiState);
+ synchronized (mRadioStateLock) {
+ if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
+ mWifiStateEnabled = true;
+ } else if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
+ mWifiStateEnabled = false;
+ evaluateToSendSatelliteEnabledSuccess();
+ }
+ logd("mWifiStateEnabled: " + mWifiStateEnabled);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private static final class SatelliteControllerHandlerRequest {
+ /** The argument to use for the request */
+ public @NonNull Object argument;
+ /** The caller needs to specify the phone to be used for the request */
+ public @NonNull Phone phone;
+ /** The result of the request that is run on the main thread */
+ public @Nullable Object result;
+
+ SatelliteControllerHandlerRequest(Object argument, Phone phone) {
+ this.argument = argument;
+ this.phone = phone;
+ }
+ }
+
+ private static final class RequestSatelliteEnabledArgument {
+ public boolean enableSatellite;
+ public boolean enableDemoMode;
+ @NonNull public Consumer<Integer> callback;
+
+ RequestSatelliteEnabledArgument(boolean enableSatellite, boolean enableDemoMode,
+ Consumer<Integer> callback) {
+ this.enableSatellite = enableSatellite;
+ this.enableDemoMode = enableDemoMode;
+ this.callback = callback;
+ }
+ }
+
+ private static final class ProvisionSatelliteServiceArgument {
+ @NonNull public String token;
+ @NonNull public byte[] provisionData;
+ @NonNull public Consumer<Integer> callback;
+ public int subId;
+
+ ProvisionSatelliteServiceArgument(String token, byte[] provisionData,
+ Consumer<Integer> callback, int subId) {
+ this.token = token;
+ this.provisionData = provisionData;
+ this.callback = callback;
+ this.subId = subId;
+ }
+ }
+
+ /**
+ * Arguments to send to SatelliteTransmissionUpdate registrants
+ */
+ public static final class SatelliteTransmissionUpdateArgument {
+ @NonNull public Consumer<Integer> errorCallback;
+ @NonNull public ISatelliteTransmissionUpdateCallback callback;
+ public int subId;
+
+ SatelliteTransmissionUpdateArgument(Consumer<Integer> errorCallback,
+ ISatelliteTransmissionUpdateCallback callback, int subId) {
+ this.errorCallback = errorCallback;
+ this.callback = callback;
+ this.subId = subId;
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ SatelliteControllerHandlerRequest request;
+ Message onCompleted;
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case CMD_START_SATELLITE_TRANSMISSION_UPDATES: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted =
+ obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, request);
+ mPointingAppController.startSatelliteTransmissionUpdates(onCompleted,
+ request.phone);
+ break;
+ }
+
+ case EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE: {
+ handleStartSatelliteTransmissionUpdatesDone((AsyncResult) msg.obj);
+ break;
+ }
+
+ case CMD_STOP_SATELLITE_TRANSMISSION_UPDATES: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted =
+ obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, request);
+ mPointingAppController.stopSatelliteTransmissionUpdates(onCompleted, request.phone);
+ break;
+ }
+
+ case EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "stopSatelliteTransmissionUpdates");
+ ((Consumer<Integer>) request.argument).accept(error);
+ break;
+ }
+
+ case CMD_PROVISION_SATELLITE_SERVICE: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ ProvisionSatelliteServiceArgument argument =
+ (ProvisionSatelliteServiceArgument) request.argument;
+ if (mSatelliteProvisionCallbacks.containsKey(argument.subId)) {
+ argument.callback.accept(
+ SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
+ notifyRequester(request);
+ break;
+ }
+ mSatelliteProvisionCallbacks.put(argument.subId, argument.callback);
+ onCompleted = obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
+ // Log the current time for provision triggered
+ mProvisionMetricsStats.setProvisioningStartTime();
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.provisionSatelliteService(argument.token,
+ argument.provisionData, onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.provisionSatelliteService(onCompleted, argument.token);
+ } else {
+ loge("provisionSatelliteService: No phone object");
+ argument.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ notifyRequester(request);
+ mProvisionMetricsStats
+ .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+ .reportProvisionMetrics();
+ mControllerMetricsStats.reportProvisionCount(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+ break;
+ }
+
+ case EVENT_PROVISION_SATELLITE_SERVICE_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int errorCode = SatelliteServiceUtils.getSatelliteError(ar,
+ "provisionSatelliteService");
+ handleEventProvisionSatelliteServiceDone(
+ (ProvisionSatelliteServiceArgument) request.argument, errorCode);
+ notifyRequester(request);
+ break;
+ }
+
+ case CMD_DEPROVISION_SATELLITE_SERVICE: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ ProvisionSatelliteServiceArgument argument =
+ (ProvisionSatelliteServiceArgument) request.argument;
+ onCompleted = obtainMessage(EVENT_DEPROVISION_SATELLITE_SERVICE_DONE, request);
+ if (argument.callback != null) {
+ mProvisionMetricsStats.setProvisioningStartTime();
+ }
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface
+ .deprovisionSatelliteService(argument.token, onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.deprovisionSatelliteService(onCompleted, argument.token);
+ } else {
+ loge("deprovisionSatelliteService: No phone object");
+ if (argument.callback != null) {
+ argument.callback.accept(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ mProvisionMetricsStats
+ .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+ .reportProvisionMetrics();
+ mControllerMetricsStats.reportDeprovisionCount(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+ }
+ break;
+ }
+
+ case EVENT_DEPROVISION_SATELLITE_SERVICE_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int errorCode = SatelliteServiceUtils.getSatelliteError(ar,
+ "deprovisionSatelliteService");
+ handleEventDeprovisionSatelliteServiceDone(
+ (ProvisionSatelliteServiceArgument) request.argument, errorCode);
+ break;
+ }
+
+ case CMD_SET_SATELLITE_ENABLED: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ handleSatelliteEnabled(request);
+ break;
+ }
+
+ case EVENT_SET_SATELLITE_ENABLED_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ RequestSatelliteEnabledArgument argument =
+ (RequestSatelliteEnabledArgument) request.argument;
+ int error = SatelliteServiceUtils.getSatelliteError(ar, "setSatelliteEnabled");
+ logd("EVENT_SET_SATELLITE_ENABLED_DONE = " + error);
+
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (argument.enableSatellite) {
+ synchronized (mSatelliteEnabledRequestLock) {
+ mWaitingForRadioDisabled = true;
+ }
+ setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_TRUE);
+
+ /**
+ * TODO for NTN-based satellites: Check if satellite is acquired.
+ */
+ if (mNeedsSatellitePointing) {
+ mPointingAppController.startPointingUI(false);
+ }
+ evaluateToSendSatelliteEnabledSuccess();
+ } else {
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest != null &&
+ mSatelliteEnabledRequest.enableSatellite == true &&
+ argument.enableSatellite == false && mWaitingForRadioDisabled) {
+ // Previous mSatelliteEnabledRequest is successful but waiting for
+ // all radios to be turned off.
+ mSatelliteEnabledRequest.callback.accept(
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+ }
+ resetSatelliteEnabledRequest();
+
+ setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
+ setDemoModeEnabled(argument.enableDemoMode);
+ synchronized (mIsSatelliteEnabledLock) {
+ mIsSatelliteEnabled = argument.enableSatellite;
+ }
+ // If satellite is disabled, send success to callback immediately
+ argument.callback.accept(error);
+ updateSatelliteEnabledState(
+ argument.enableSatellite, "EVENT_SET_SATELLITE_ENABLED_DONE");
+ }
+ } else {
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest != null &&
+ mSatelliteEnabledRequest.enableSatellite == true &&
+ argument.enableSatellite == false && mWaitingForRadioDisabled) {
+ // Previous mSatelliteEnabledRequest is successful but waiting for
+ // all radios to be turned off.
+ mSatelliteEnabledRequest.callback.accept(
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+ }
+ resetSatelliteEnabledRequest();
+
+ // If Satellite enable/disable request returned Error, no need to wait for radio
+ argument.callback.accept(error);
+ }
+
+ if (argument.enableSatellite) {
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ mControllerMetricsStats.onSatelliteEnabled();
+ mControllerMetricsStats.reportServiceEnablementSuccessCount();
+ } else {
+ mControllerMetricsStats.reportServiceEnablementFailCount();
+ }
+ SessionMetricsStats.getInstance()
+ .setInitializationResult(error)
+ .setRadioTechnology(SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY)
+ .reportSessionMetrics();
+ } else {
+ mControllerMetricsStats.onSatelliteDisabled();
+ }
+ break;
+ }
+
+ case CMD_IS_SATELLITE_ENABLED: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_IS_SATELLITE_ENABLED_DONE, request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.requestIsSatelliteEnabled(onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.isSatellitePowerOn(onCompleted);
+ } else {
+ loge("isSatelliteEnabled: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_IS_SATELLITE_ENABLED_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "isSatelliteEnabled");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("isSatelliteEnabled: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ boolean enabled = ((int[]) ar.result)[0] == 1;
+ if (DBG) logd("isSatelliteEnabled: " + enabled);
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, enabled);
+ updateSatelliteEnabledState(enabled, "EVENT_IS_SATELLITE_ENABLED_DONE");
+ }
+ } else if (error == SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED) {
+ updateSatelliteSupportedStateWhenSatelliteServiceConnected(false);
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case CMD_IS_SATELLITE_SUPPORTED: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_IS_SATELLITE_SUPPORTED_DONE, request);
+
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.requestIsSatelliteSupported(onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.isSatelliteSupported(onCompleted);
+ } else {
+ loge("isSatelliteSupported: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_IS_SATELLITE_SUPPORTED_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar, "isSatelliteSupported");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("isSatelliteSupported: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ boolean supported = (boolean) ar.result;
+ if (DBG) logd("isSatelliteSupported: " + supported);
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, supported);
+ updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
+ }
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case CMD_GET_SATELLITE_CAPABILITIES: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_SATELLITE_CAPABILITIES_DONE, request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.requestSatelliteCapabilities(onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.getSatelliteCapabilities(onCompleted);
+ } else {
+ loge("getSatelliteCapabilities: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_GET_SATELLITE_CAPABILITIES_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "getSatelliteCapabilities");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("getSatelliteCapabilities: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ SatelliteCapabilities capabilities = (SatelliteCapabilities) ar.result;
+ synchronized (mNeedsSatellitePointingLock) {
+ mNeedsSatellitePointing = capabilities.isPointingRequired();
+ }
+ if (DBG) logd("getSatelliteCapabilities: " + capabilities);
+ bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
+ capabilities);
+ synchronized (mSatelliteCapabilitiesLock) {
+ mSatelliteCapabilities = capabilities;
+ }
+ }
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case CMD_IS_SATELLITE_COMMUNICATION_ALLOWED: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted =
+ obtainMessage(EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE, request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface
+ .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+ onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.isSatelliteCommunicationAllowedForCurrentLocation(onCompleted);
+ } else {
+ loge("isSatelliteCommunicationAllowedForCurrentLocation: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "isSatelliteCommunicationAllowedForCurrentLocation");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("isSatelliteCommunicationAllowedForCurrentLocation: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ boolean communicationAllowed = (boolean) ar.result;
+ if (DBG) {
+ logd("isSatelliteCommunicationAllowedForCurrentLocation: "
+ + communicationAllowed);
+ }
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
+ communicationAllowed);
+ }
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case CMD_GET_TIME_SATELLITE_NEXT_VISIBLE: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE,
+ request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface
+ .requestTimeForNextSatelliteVisibility(onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.requestTimeForNextSatelliteVisibility(onCompleted);
+ } else {
+ loge("requestTimeForNextSatelliteVisibility: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "requestTimeForNextSatelliteVisibility");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("requestTimeForNextSatelliteVisibility: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ int nextVisibilityDuration = ((int[]) ar.result)[0];
+ if (DBG) {
+ logd("requestTimeForNextSatelliteVisibility: " +
+ nextVisibilityDuration);
+ }
+ bundle.putInt(SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY,
+ nextVisibilityDuration);
+ }
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case EVENT_RADIO_STATE_CHANGED: {
+ if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF
+ || mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+ mIsRadioOn = false;
+ logd("Radio State Changed to " + mCi.getRadioState());
+ IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("RequestSatelliteEnabled: result=" + result);
+ }
+ };
+ Phone phone = SatelliteServiceUtils.getPhone();
+ Consumer<Integer> result = FunctionalUtils
+ .ignoreRemoteException(errorCallback::accept);
+ RequestSatelliteEnabledArgument message =
+ new RequestSatelliteEnabledArgument(false, false, result);
+ request = new SatelliteControllerHandlerRequest(message, phone);
+ handleSatelliteEnabled(request);
+ } else {
+ mIsRadioOn = true;
+ if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ synchronized (mIsSatelliteSupportedLock) {
+ if (mIsSatelliteSupported == null) {
+ ResultReceiver receiver = new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(
+ int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteSupported: resultCode="
+ + resultCode);
+ }
+ };
+ requestIsSatelliteSupported(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
+ }
+ }
+ } else {
+ logd("EVENT_RADIO_STATE_CHANGED: Satellite vendor service is supported."
+ + " Ignored the event");
+ }
+ }
+ break;
+ }
+
+ case CMD_IS_SATELLITE_PROVISIONED: {
+ request = (SatelliteControllerHandlerRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_IS_SATELLITE_PROVISIONED_DONE, request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.requestIsSatelliteProvisioned(onCompleted);
+ break;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.isSatelliteProvisioned(onCompleted);
+ } else {
+ loge("isSatelliteProvisioned: No phone object");
+ ((ResultReceiver) request.argument).send(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ }
+ break;
+ }
+
+ case EVENT_IS_SATELLITE_PROVISIONED_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (SatelliteControllerHandlerRequest) ar.userObj;
+ int error = SatelliteServiceUtils.getSatelliteError(ar,
+ "isSatelliteProvisioned");
+ Bundle bundle = new Bundle();
+ if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+ if (ar.result == null) {
+ loge("isSatelliteProvisioned: result is null");
+ error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ } else {
+ boolean provisioned = ((int[]) ar.result)[0] == 1;
+ if (DBG) logd("isSatelliteProvisioned: " + provisioned);
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, provisioned);
+ synchronized (mIsSatelliteProvisionedLock) {
+ mIsSatelliteProvisioned = provisioned;
+ }
+ }
+ }
+ ((ResultReceiver) request.argument).send(error, bundle);
+ break;
+ }
+
+ case EVENT_SATELLITE_PROVISION_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.result == null) {
+ loge("EVENT_SATELLITE_PROVISION_STATE_CHANGED: result is null");
+ } else {
+ handleEventSatelliteProvisionStateChanged((boolean) ar.result);
+ }
+ break;
+
+ case EVENT_PENDING_DATAGRAMS:
+ logd("Received EVENT_PENDING_DATAGRAMS");
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("pollPendingSatelliteDatagram result: " + result);
+ }
+ };
+ pollPendingSatelliteDatagrams(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, internalCallback);
+ break;
+
+ case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.result == null) {
+ loge("EVENT_SATELLITE_MODEM_STATE_CHANGED: result is null");
+ } else {
+ handleEventSatelliteModemStateChanged((int) ar.result);
+ }
+ break;
+
+ default:
+ Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
+ msg.what);
+ break;
+ }
+ }
+
+ private void notifyRequester(SatelliteControllerHandlerRequest request) {
+ synchronized (request) {
+ request.notifyAll();
+ }
+ }
+
+ /**
+ * Request to enable or disable the satellite modem and demo mode. If the satellite modem is
+ * enabled, this will also disable the cellular modem, and if the satellite modem is disabled,
+ * this will also re-enable the cellular modem.
+ *
+ * @param subId The subId of the subscription to set satellite enabled for.
+ * @param enableSatellite {@code true} to enable the satellite modem and
+ * {@code false} to disable.
+ * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
+ * @param callback The callback to get the error code of the request.
+ */
+ public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+ @NonNull IIntegerConsumer callback) {
+ logd("requestSatelliteEnabled subId: " + subId + " enableSatellite: " + enableSatellite
+ + " enableDemoMode: " + enableDemoMode);
+
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+ return;
+ }
+
+ if (enableSatellite) {
+ if (!mIsRadioOn) {
+ loge("Radio is not on, can not enable satellite");
+ result.accept(SatelliteManager.SATELLITE_INVALID_MODEM_STATE);
+ return;
+ }
+ } else {
+ /* if disable satellite, always assume demo is also disabled */
+ enableDemoMode = false;
+ }
+
+ synchronized (mIsSatelliteEnabledLock) {
+ if (mIsSatelliteEnabled != null) {
+ if (mIsSatelliteEnabled == enableSatellite) {
+ if (enableDemoMode != mIsDemoModeEnabled) {
+ loge("Received invalid demo mode while satellite session is enabled"
+ + " enableDemoMode = " + enableDemoMode);
+ result.accept(SatelliteManager.SATELLITE_INVALID_ARGUMENTS);
+ return;
+ } else {
+ logd("Enable request matches with current state"
+ + " enableSatellite = " + enableSatellite);
+ result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+ return;
+ }
+ }
+ }
+ }
+
+ RequestSatelliteEnabledArgument request =
+ new RequestSatelliteEnabledArgument(enableSatellite, enableDemoMode, result);
+ /**
+ * Multiple satellite enabled requests are handled as below:
+ * 1. If there are no ongoing requests, store current request in mSatelliteEnabledRequest
+ * 2. If there is a ongoing request, then:
+ * 1. ongoing request = enable, current request = enable: return IN_PROGRESS error
+ * 2. ongoing request = disable, current request = disable: return IN_PROGRESS error
+ * 3. ongoing request = disable, current request = enable: return SATELLITE_ERROR error
+ * 4. ongoing request = enable, current request = disable: send request to modem
+ */
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest == null) {
+ mSatelliteEnabledRequest = request;
+ } else if (mSatelliteEnabledRequest.enableSatellite == request.enableSatellite) {
+ logd("requestSatelliteEnabled enableSatellite: " + enableSatellite
+ + " is already in progress.");
+ result.accept(SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS);
+ return;
+ } else if (mSatelliteEnabledRequest.enableSatellite == false
+ && request.enableSatellite == true) {
+ logd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
+ + "processed. Disable satellite is already in progress.");
+ result.accept(SatelliteManager.SATELLITE_ERROR);
+ return;
+ }
+ }
+
+ sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Request to get whether the satellite modem is enabled.
+ *
+ * @param subId The subId of the subscription to check whether satellite is enabled for.
+ * @param result The result receiver that returns whether the satellite modem is enabled
+ * if the request is successful or an error code if the request failed.
+ */
+ public void requestIsSatelliteEnabled(int subId, @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ synchronized (mIsSatelliteEnabledLock) {
+ if (mIsSatelliteEnabled != null) {
+ /* We have already successfully queried the satellite modem. */
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, mIsSatelliteEnabled);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ return;
+ }
+ }
+
+ sendRequestAsync(CMD_IS_SATELLITE_ENABLED, result, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Get whether the satellite modem is enabled.
+ * This will return the cached value instead of querying the satellite modem.
+ *
+ * @return {@code true} if the satellite modem is enabled and {@code false} otherwise.
+ */
+ public boolean isSatelliteEnabled() {
+ if (mIsSatelliteEnabled == null) return false;
+ return mIsSatelliteEnabled;
+ }
+
+ /**
+ * Request to get whether the satellite service demo mode is enabled.
+ *
+ * @param subId The subId of the subscription to check whether the satellite demo mode
+ * is enabled for.
+ * @param result The result receiver that returns whether the satellite demo mode is enabled
+ * if the request is successful or an error code if the request failed.
+ */
+ public void requestIsDemoModeEnabled(int subId, @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+ return;
+ }
+
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean(SatelliteManager.KEY_DEMO_MODE_ENABLED, mIsDemoModeEnabled);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ }
+
+ /**
+ * Get whether the satellite service demo mode is enabled.
+ *
+ * @return {@code true} if the satellite demo mode is enabled and {@code false} otherwise.
+ */
+ public boolean isDemoModeEnabled() {
+ return mIsDemoModeEnabled;
+ }
+
+ /**
+ * Request to get whether the satellite service is supported on the device.
+ *
+ * @param subId The subId of the subscription to check satellite service support for.
+ * @param result The result receiver that returns whether the satellite service is supported on
+ * the device if the request is successful or an error code if the request failed.
+ */
+ public void requestIsSatelliteSupported(int subId, @NonNull ResultReceiver result) {
+ synchronized (mIsSatelliteSupportedLock) {
+ if (mIsSatelliteSupported != null) {
+ /* We have already successfully queried the satellite modem. */
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ return;
+ }
+ }
+
+ sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, result, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Request to get the {@link SatelliteCapabilities} of the satellite service.
+ *
+ * @param subId The subId of the subscription to get the satellite capabilities for.
+ * @param result The result receiver that returns the {@link SatelliteCapabilities}
+ * if the request is successful or an error code if the request failed.
+ */
+ public void requestSatelliteCapabilities(int subId, @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ synchronized (mSatelliteCapabilitiesLock) {
+ if (mSatelliteCapabilities != null) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
+ mSatelliteCapabilities);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ return;
+ }
+ }
+
+ sendRequestAsync(CMD_GET_SATELLITE_CAPABILITIES, result, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Start receiving satellite transmission updates.
+ * This can be called by the pointing UI when the user starts pointing to the satellite.
+ * Modem should continue to report the pointing input as the device or satellite moves.
+ *
+ * @param subId The subId of the subscription to start satellite transmission updates for.
+ * @param errorCallback The callback to get the error code of the request.
+ * @param callback The callback to notify of satellite transmission updates.
+ */
+ public void startSatelliteTransmissionUpdates(int subId,
+ @NonNull IIntegerConsumer errorCallback,
+ @NonNull ISatelliteTransmissionUpdateCallback callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+ return;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ mPointingAppController.registerForSatelliteTransmissionUpdates(validSubId, callback, phone);
+ sendRequestAsync(CMD_START_SATELLITE_TRANSMISSION_UPDATES,
+ new SatelliteTransmissionUpdateArgument(result, callback, validSubId), phone);
+ }
+
+ /**
+ * Stop receiving satellite transmission updates.
+ * This can be called by the pointing UI when the user stops pointing to the satellite.
+ *
+ * @param subId The subId of the subscription to stop satellite transmission updates for.
+ * @param errorCallback The callback to get the error code of the request.
+ * @param callback The callback that was passed to {@link #startSatelliteTransmissionUpdates(
+ * int, IIntegerConsumer, ISatelliteTransmissionUpdateCallback)}.
+ */
+ public void stopSatelliteTransmissionUpdates(int subId, @NonNull IIntegerConsumer errorCallback,
+ @NonNull ISatelliteTransmissionUpdateCallback callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+ return;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(
+ validSubId, result, callback, phone);
+
+ // Even if handler is null - which means there are no listeners, the modem command to stop
+ // satellite transmission updates might have failed. The callers might want to retry
+ // sending the command. Thus, we always need to send this command to the modem.
+ sendRequestAsync(CMD_STOP_SATELLITE_TRANSMISSION_UPDATES, result, phone);
+ }
+
+ /**
+ * Register the subscription with a satellite provider.
+ * This is needed to register the subscription if the provider allows dynamic registration.
+ *
+ * @param subId The subId of the subscription to be provisioned.
+ * @param token The token to be used as a unique identifier for provisioning with satellite
+ * gateway.
+ * @param provisionData Data from the provisioning app that can be used by provisioning server
+ * @param callback The callback to get the error code of the request.
+ *
+ * @return The signal transport used by the caller to cancel the provision request,
+ * or {@code null} if the request failed.
+ */
+ @Nullable public ICancellationSignal provisionSatelliteService(int subId,
+ @NonNull String token, @NonNull byte[] provisionData,
+ @NonNull IIntegerConsumer callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return null;
+ }
+ if (!satelliteSupported) {
+ result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ return null;
+ }
+
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ if (mSatelliteProvisionCallbacks.containsKey(validSubId)) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
+ return null;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned != null && satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+ return null;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ sendRequestAsync(CMD_PROVISION_SATELLITE_SERVICE,
+ new ProvisionSatelliteServiceArgument(token, provisionData, result, validSubId),
+ phone);
+
+ ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+ CancellationSignal.fromTransport(cancelTransport).setOnCancelListener(() -> {
+ sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE,
+ new ProvisionSatelliteServiceArgument(token, provisionData, null,
+ validSubId), phone);
+ mProvisionMetricsStats.setIsCanceled(true);
+ });
+ return cancelTransport;
+ }
+
+ /**
+ * Unregister the device/subscription with the satellite provider.
+ * This is needed if the provider allows dynamic registration. Once deprovisioned,
+ * {@link android.telephony.satellite.SatelliteProvisionStateCallback
+ * #onSatelliteProvisionStateChanged(boolean)}
+ * should report as deprovisioned.
+ *
+ * @param subId The subId of the subscription to be deprovisioned.
+ * @param token The token of the device/subscription to be deprovisioned.
+ * @param callback The callback to get the error code of the request.
+ */
+ public void deprovisionSatelliteService(int subId,
+ @NonNull String token, @NonNull IIntegerConsumer callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+ return;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE,
+ new ProvisionSatelliteServiceArgument(token, null, result, validSubId),
+ phone);
+ }
+
+ /**
+ * Registers for the satellite provision state changed.
+ *
+ * @param subId The subId of the subscription to register for provision state changed.
+ * @param callback The callback to handle the satellite provision state changed event.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ */
+ @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(int subId,
+ @NonNull ISatelliteProvisionStateCallback callback) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ }
+ if (!satelliteSupported) {
+ return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+ }
+
+ mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback);
+ return SatelliteManager.SATELLITE_ERROR_NONE;
+ }
+
+ /**
+ * Unregisters for the satellite provision state changed.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for provision state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteProvisionStateChanged(int, ISatelliteProvisionStateCallback)}.
+ */
+ public void unregisterForSatelliteProvisionStateChanged(
+ int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+ mSatelliteProvisionStateChangedListeners.remove(callback.asBinder());
+ }
+
+ /**
+ * Request to get whether the device is provisioned with a satellite provider.
+ *
+ * @param subId The subId of the subscription to get whether the device is provisioned for.
+ * @param result The result receiver that returns whether the device is provisioned with a
+ * satellite provider if the request is successful or an error code if the
+ * request failed.
+ */
+ public void requestIsSatelliteProvisioned(int subId, @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ synchronized (mIsSatelliteProvisionedLock) {
+ if (mIsSatelliteProvisioned != null) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
+ mIsSatelliteProvisioned);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ return;
+ }
+ }
+
+ sendRequestAsync(CMD_IS_SATELLITE_PROVISIONED, result, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Registers for modem state changed from satellite modem.
+ *
+ * @param subId The subId of the subscription to register for satellite modem state changed.
+ * @param callback The callback to handle the satellite modem state changed event.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ */
+ @SatelliteManager.SatelliteError public int registerForSatelliteModemStateChanged(int subId,
+ @NonNull ISatelliteStateCallback callback) {
+ if (mSatelliteSessionController != null) {
+ mSatelliteSessionController.registerForSatelliteModemStateChanged(callback);
+ } else {
+ loge("registerForSatelliteModemStateChanged: mSatelliteSessionController"
+ + " is not initialized yet");
+ return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ }
+ return SatelliteManager.SATELLITE_ERROR_NONE;
+ }
+
+ /**
+ * Unregisters for modem state changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for satellite modem state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteModemStateChanged(int, ISatelliteStateCallback)}.
+ */
+ public void unregisterForSatelliteModemStateChanged(int subId,
+ @NonNull ISatelliteStateCallback callback) {
+ if (mSatelliteSessionController != null) {
+ mSatelliteSessionController.unregisterForSatelliteModemStateChanged(callback);
+ } else {
+ loge("registerForSatelliteModemStateChanged: mSatelliteSessionController"
+ + " is not initialized yet");
+ }
+ }
+
+ /**
+ * Register to receive incoming datagrams over satellite.
+ *
+ * @param subId The subId of the subscription to register for incoming satellite datagrams.
+ * @param callback The callback to handle incoming datagrams over satellite.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+ */
+ @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ return mDatagramController.registerForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Unregister to stop receiving incoming datagrams over satellite.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+ */
+ public void unregisterForSatelliteDatagram(int subId,
+ @NonNull ISatelliteDatagramCallback callback) {
+ mDatagramController.unregisterForSatelliteDatagram(subId, callback);
+ }
+
+ /**
+ * Poll pending satellite datagrams over satellite.
+ *
+ * This method requests modem to check if there are any pending datagrams to be received over
+ * satellite. If there are any incoming datagrams, they will be received via
+ * {@link android.telephony.satellite.SatelliteDatagramCallback#onSatelliteDatagramReceived(
+ * long, SatelliteDatagram, int, Consumer)}
+ *
+ * @param subId The subId of the subscription used for receiving datagrams.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void pollPendingSatelliteDatagrams(int subId, @NonNull IIntegerConsumer callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+ return;
+ }
+
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ mDatagramController.pollPendingSatelliteDatagrams(validSubId, result);
+ }
+
+ /**
+ * Send datagram over satellite.
+ *
+ * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+ * input to this method. Datagram received here will be passed down to modem without any
+ * encoding or encryption.
+ *
+ * @param subId The subId of the subscription to send satellite datagrams for.
+ * @param datagramType datagram type indicating whether the datagram is of type
+ * SOS_SMS or LOCATION_SHARING.
+ * @param datagram encoded gateway datagram which is encrypted by the caller.
+ * Datagram will be passed down to modem without any encoding or encryption.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+ */
+ public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+ SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+ @NonNull IIntegerConsumer callback) {
+ Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+ return;
+ }
+
+ /**
+ * TODO for NTN-based satellites: Check if satellite is acquired.
+ */
+ if (mNeedsSatellitePointing) {
+ mPointingAppController.startPointingUI(needFullScreenPointingUI);
+ }
+
+ final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+ mDatagramController.sendSatelliteDatagram(validSubId, datagramType, datagram,
+ needFullScreenPointingUI, result);
+ }
+
+ /**
+ * Request to get whether satellite communication is allowed for the current location.
+ *
+ * @param subId The subId of the subscription to check whether satellite communication is
+ * allowed for the current location for.
+ * @param result The result receiver that returns whether satellite communication is allowed
+ * for the current location if the request is successful or an error code
+ * if the request failed.
+ */
+ public void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId,
+ @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ sendRequestAsync(
+ CMD_IS_SATELLITE_COMMUNICATION_ALLOWED, result, SatelliteServiceUtils.getPhone());
+ }
+
+ /**
+ * Request to get the time after which the satellite will be visible.
+ *
+ * @param subId The subId to get the time after which the satellite will be visible for.
+ * @param result The result receiver that returns the time after which the satellite will
+ * be visible if the request is successful or an error code if the request failed.
+ */
+ public void requestTimeForNextSatelliteVisibility(int subId, @NonNull ResultReceiver result) {
+ Boolean satelliteSupported = isSatelliteSupportedInternal();
+ if (satelliteSupported == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteSupported) {
+ result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+ return;
+ }
+
+ Boolean satelliteProvisioned = isSatelliteProvisioned();
+ if (satelliteProvisioned == null) {
+ result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+ return;
+ }
+ if (!satelliteProvisioned) {
+ result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+ return;
+ }
+
+ Phone phone = SatelliteServiceUtils.getPhone();
+ sendRequestAsync(CMD_GET_TIME_SATELLITE_NEXT_VISIBLE, result, phone);
+ }
+
+ /**
+ * Inform whether the device is aligned with satellite for demo mode.
+ *
+ * @param subId The subId of the subscription.
+ * @param isAligned {@true} means device is aligned with the satellite, otherwise {@false}.
+ */
+ public void onDeviceAlignedWithSatellite(@NonNull int subId, @NonNull boolean isAligned) {
+ mDatagramController.onDeviceAlignedWithSatellite(isAligned);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite vendor service package name.
+ *
+ * @param servicePackageName The package name of the satellite vendor service.
+ * @return {@code true} if the satellite vendor service is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
+ boolean result = mSatelliteModemInterface.setSatelliteServicePackageName(
+ servicePackageName);
+ if (result) {
+ logd("setSatelliteServicePackageName: Resetting cached states");
+
+ // Cached states need to be cleared whenever switching satellite vendor services.
+ synchronized (mIsSatelliteSupportedLock) {
+ mIsSatelliteSupported = null;
+ }
+ synchronized (mIsSatelliteProvisionedLock) {
+ mIsSatelliteProvisioned = null;
+ }
+ synchronized (mIsSatelliteEnabledLock) {
+ mIsSatelliteEnabled = null;
+ }
+ synchronized (mSatelliteCapabilitiesLock) {
+ mSatelliteCapabilities = null;
+ }
+ ResultReceiver receiver = new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteSupported: resultCode=" + resultCode);
+ }
+ };
+ requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
+ }
+ return result;
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds that
+ * satellite should stay at listening mode to wait for the next incoming page before disabling
+ * listening mode.
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteListeningTimeoutDuration(long timeoutMillis) {
+ if (mSatelliteSessionController == null) {
+ loge("mSatelliteSessionController is not initialized yet");
+ return false;
+ }
+ return mSatelliteSessionController.setSatelliteListeningTimeoutDuration(timeoutMillis);
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds whether
+ * the device is aligned with the satellite for demo mode
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
+ return mDatagramController.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite gateway service package name.
+ *
+ * @param servicePackageName The package name of the satellite gateway service.
+ * @return {@code true} if the satellite gateway service is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatelliteGatewayServicePackageName(@Nullable String servicePackageName) {
+ if (mSatelliteSessionController == null) {
+ loge("mSatelliteSessionController is not initialized yet");
+ return false;
+ }
+ return mSatelliteSessionController.setSatelliteGatewayServicePackageName(
+ servicePackageName);
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite pointing UI app package and class names.
+ *
+ * @param packageName The package name of the satellite pointing UI app.
+ * @param className The class name of the satellite pointing UI app.
+ * @return {@code true} if the satellite pointing UI app package and class is set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setSatellitePointingUiClassName(
+ @Nullable String packageName, @Nullable String className) {
+ return mPointingAppController.setSatellitePointingUiClassName(packageName, className);
+ }
+
+ /**
+ * This function is used by {@link SatelliteModemInterface} to notify
+ * {@link SatelliteController} that the satellite vendor service was just connected.
+ * <p>
+ * {@link SatelliteController} will send requests to satellite modem to check whether it support
+ * satellite and whether it is provisioned. {@link SatelliteController} will use these cached
+ * values to serve requests from its clients.
+ * <p>
+ * Because satellite vendor service might have just come back from a crash, we need to disable
+ * the satellite modem so that resources will be cleaned up and internal states will be reset.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onSatelliteServiceConnected() {
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ synchronized (mIsSatelliteSupportedLock) {
+ if (mIsSatelliteSupported == null) {
+ ResultReceiver receiver = new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(
+ int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteSupported: resultCode="
+ + resultCode);
+ }
+ };
+ requestIsSatelliteSupported(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
+ }
+ }
+ } else {
+ logd("onSatelliteServiceConnected: Satellite vendor service is not supported."
+ + " Ignored the event");
+ }
+ }
+
+ /**
+ * @return {@code true} is satellite is supported on the device, {@code false} otherwise.
+ */
+ public boolean isSatelliteSupported() {
+ Boolean supported = isSatelliteSupportedInternal();
+ return (supported != null ? supported : false);
+ }
+
+ /**
+ * If we have not successfully queried the satellite modem for its satellite service support,
+ * we will retry the query one more time. Otherwise, we will return the cached result.
+ */
+ private Boolean isSatelliteSupportedInternal() {
+ synchronized (mIsSatelliteSupportedLock) {
+ if (mIsSatelliteSupported != null) {
+ /* We have already successfully queried the satellite modem. */
+ return mIsSatelliteSupported;
+ }
+ }
+ /**
+ * We have not successfully checked whether the modem supports satellite service.
+ * Thus, we need to retry it now.
+ */
+ requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteSupported: resultCode=" + resultCode);
+ }
+ });
+ return null;
+ }
+
+ private void handleEventProvisionSatelliteServiceDone(
+ @NonNull ProvisionSatelliteServiceArgument arg,
+ @SatelliteManager.SatelliteError int result) {
+ logd("handleEventProvisionSatelliteServiceDone: result="
+ + result + ", subId=" + arg.subId);
+
+ Consumer<Integer> callback = mSatelliteProvisionCallbacks.remove(arg.subId);
+ if (callback == null) {
+ loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
+ + arg.subId);
+ mProvisionMetricsStats
+ .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+ .setIsProvisionRequest(true)
+ .reportProvisionMetrics();
+ mControllerMetricsStats.reportProvisionCount(
+ SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ return;
+ }
+ callback.accept(result);
+ }
+
+ private void handleEventDeprovisionSatelliteServiceDone(
+ @NonNull ProvisionSatelliteServiceArgument arg,
+ @SatelliteManager.SatelliteError int result) {
+ if (arg == null) {
+ loge("handleEventDeprovisionSatelliteServiceDone: arg is null");
+ return;
+ }
+ logd("handleEventDeprovisionSatelliteServiceDone: result="
+ + result + ", subId=" + arg.subId);
+
+ if (arg.callback != null) {
+ arg.callback.accept(result);
+ mProvisionMetricsStats.setResultCode(result)
+ .setIsProvisionRequest(false)
+ .reportProvisionMetrics();
+ mControllerMetricsStats.reportDeprovisionCount(result);
+ }
+ }
+
+ private void handleStartSatelliteTransmissionUpdatesDone(@NonNull AsyncResult ar) {
+ SatelliteControllerHandlerRequest request = (SatelliteControllerHandlerRequest) ar.userObj;
+ SatelliteTransmissionUpdateArgument arg =
+ (SatelliteTransmissionUpdateArgument) request.argument;
+ int errorCode = SatelliteServiceUtils.getSatelliteError(ar,
+ "handleStartSatelliteTransmissionUpdatesDone");
+ arg.errorCallback.accept(errorCode);
+
+ if (errorCode != SatelliteManager.SATELLITE_ERROR_NONE) {
+ mPointingAppController.setStartedSatelliteTransmissionUpdates(false);
+ // We need to remove the callback from our listener list since the caller might not call
+ // stopSatelliteTransmissionUpdates to unregister the callback in case of failure.
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(arg.subId,
+ arg.errorCallback, arg.callback, request.phone);
+ } else {
+ mPointingAppController.setStartedSatelliteTransmissionUpdates(true);
+ }
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread and returns immediately.
+ *
+ * @param command command to be executed on the main thread
+ * @param argument additional parameters required to perform of the operation
+ * @param phone phone object used to perform the operation.
+ */
+ private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) {
+ SatelliteControllerHandlerRequest request = new SatelliteControllerHandlerRequest(
+ argument, phone);
+ Message msg = this.obtainMessage(command, request);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread. As this is a synchronous
+ * request, it waits until the request is complete and then return the result.
+ *
+ * @param command command to be executed on the main thread
+ * @param argument additional parameters required to perform of the operation
+ * @param phone phone object used to perform the operation.
+ * @return result of the operation
+ */
+ private @Nullable Object sendRequest(int command, @NonNull Object argument,
+ @Nullable Phone phone) {
+ if (Looper.myLooper() == this.getLooper()) {
+ throw new RuntimeException("This method will deadlock if called from the main thread");
+ }
+
+ SatelliteControllerHandlerRequest request = new SatelliteControllerHandlerRequest(
+ argument, phone);
+ Message msg = this.obtainMessage(command, request);
+ msg.sendToTarget();
+
+ synchronized (request) {
+ while(request.result == null) {
+ try {
+ request.wait();
+ } catch (InterruptedException e) {
+ // Do nothing, go back and wait until the request is complete.
+ }
+ }
+ }
+ return request.result;
+ }
+
+ /**
+ * Check if satellite is provisioned for a subscription on the device.
+ * @return true if satellite is provisioned on the given subscription else return false.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected Boolean isSatelliteProvisioned() {
+ synchronized (mIsSatelliteProvisionedLock) {
+ if (mIsSatelliteProvisioned != null) {
+ return mIsSatelliteProvisioned;
+ }
+ }
+
+ requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteProvisioned: resultCode=" + resultCode);
+ }
+ });
+ return null;
+ }
+
+ private void handleSatelliteEnabled(SatelliteControllerHandlerRequest request) {
+ RequestSatelliteEnabledArgument argument =
+ (RequestSatelliteEnabledArgument) request.argument;
+ Message onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request);
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ mSatelliteModemInterface.requestSatelliteEnabled(argument.enableSatellite,
+ argument.enableDemoMode, onCompleted);
+ return;
+ }
+ Phone phone = request.phone;
+ if (phone != null) {
+ phone.setSatellitePower(onCompleted, argument.enableSatellite);
+ } else {
+ loge("requestSatelliteEnabled: No phone object");
+ argument.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+ }
+
+ private void updateSatelliteSupportedStateWhenSatelliteServiceConnected(boolean supported) {
+ synchronized (mIsSatelliteSupportedLock) {
+ mIsSatelliteSupported = supported;
+ }
+ mSatelliteSessionController = SatelliteSessionController.make(
+ mContext, getLooper(), supported);
+ if (supported) {
+ registerForSatelliteProvisionStateChanged();
+ registerForPendingDatagramCount();
+ registerForSatelliteModemStateChanged();
+
+ requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestIsSatelliteProvisioned: resultCode=" + resultCode);
+ requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ false, false,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("requestSatelliteEnabled: result=" + result);
+ }
+ });
+ }
+ });
+ requestSatelliteCapabilities(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("requestSatelliteCapabilities: resultCode=" + resultCode);
+ }
+ });
+ }
+ }
+
+ private void updateSatelliteEnabledState(boolean enabled, String caller) {
+ synchronized (mIsSatelliteEnabledLock) {
+ mIsSatelliteEnabled = enabled;
+ }
+ if (mSatelliteSessionController != null) {
+ mSatelliteSessionController.onSatelliteEnabledStateChanged(enabled);
+ mSatelliteSessionController.setDemoMode(mIsDemoModeEnabled);
+ } else {
+ loge(caller + ": mSatelliteSessionController is not initialized yet");
+ }
+ }
+
+ private void registerForSatelliteProvisionStateChanged() {
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ if (!mRegisteredForProvisionStateChangedWithSatelliteService.get()) {
+ mSatelliteModemInterface.registerForSatelliteProvisionStateChanged(
+ this, EVENT_SATELLITE_PROVISION_STATE_CHANGED, null);
+ mRegisteredForProvisionStateChangedWithSatelliteService.set(true);
+ }
+ } else {
+ Phone phone = SatelliteServiceUtils.getPhone();
+ if (phone == null) {
+ loge("registerForSatelliteProvisionStateChanged: phone is null");
+ } else if (!mRegisteredForProvisionStateChangedWithPhone.get()) {
+ phone.registerForSatelliteProvisionStateChanged(
+ this, EVENT_SATELLITE_PROVISION_STATE_CHANGED, null);
+ mRegisteredForProvisionStateChangedWithPhone.set(true);
+ }
+ }
+ }
+
+ private void registerForPendingDatagramCount() {
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ if (!mRegisteredForPendingDatagramCountWithSatelliteService.get()) {
+ mSatelliteModemInterface.registerForPendingDatagrams(
+ this, EVENT_PENDING_DATAGRAMS, null);
+ mRegisteredForPendingDatagramCountWithSatelliteService.set(true);
+ }
+ } else {
+ Phone phone = SatelliteServiceUtils.getPhone();
+ if (phone == null) {
+ loge("registerForPendingDatagramCount: satellite phone is "
+ + "not initialized yet");
+ } else if (!mRegisteredForPendingDatagramCountWithPhone.get()) {
+ phone.registerForPendingDatagramCount(this, EVENT_PENDING_DATAGRAMS, null);
+ mRegisteredForPendingDatagramCountWithPhone.set(true);
+ }
+ }
+ }
+
+ private void registerForSatelliteModemStateChanged() {
+ if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+ if (!mRegisteredForSatelliteModemStateChangedWithSatelliteService.get()) {
+ mSatelliteModemInterface.registerForSatelliteModemStateChanged(
+ this, EVENT_SATELLITE_MODEM_STATE_CHANGED, null);
+ mRegisteredForSatelliteModemStateChangedWithSatelliteService.set(true);
+ }
+ } else {
+ Phone phone = SatelliteServiceUtils.getPhone();
+ if (phone == null) {
+ loge("registerForSatelliteModemStateChanged: satellite phone is "
+ + "not initialized yet");
+ } else if (!mRegisteredForSatelliteModemStateChangedWithPhone.get()) {
+ phone.registerForSatelliteModemStateChanged(
+ this, EVENT_SATELLITE_MODEM_STATE_CHANGED, null);
+ mRegisteredForSatelliteModemStateChangedWithPhone.set(true);
+ }
+ }
+ }
+
+ private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
+ logd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
+
+ synchronized (mIsSatelliteProvisionedLock) {
+ mIsSatelliteProvisioned = provisioned;
+ }
+
+ List<ISatelliteProvisionStateCallback> toBeRemoved = new ArrayList<>();
+ mSatelliteProvisionStateChangedListeners.values().forEach(listener -> {
+ try {
+ listener.onSatelliteProvisionStateChanged(provisioned);
+ } catch (RemoteException e) {
+ logd("handleSatelliteProvisionStateChangedEvent RemoteException: " + e);
+ toBeRemoved.add(listener);
+ }
+ });
+ toBeRemoved.forEach(listener -> {
+ mSatelliteProvisionStateChangedListeners.remove(listener.asBinder());
+ });
+ }
+
+ private void handleEventSatelliteModemStateChanged(
+ @SatelliteManager.SatelliteModemState int state) {
+ logd("handleEventSatelliteModemStateChanged: state=" + state);
+ if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
+ || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
+ setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
+ setDemoModeEnabled(false);
+ updateSatelliteEnabledState(
+ false, "handleEventSatelliteModemStateChanged");
+ cleanUpResources(state);
+ }
+
+ mDatagramController.onSatelliteModemStateChanged(state);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void setSettingsKeyForSatelliteMode(int val) {
+ logd("setSettingsKeyForSatelliteMode val: " + val);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.SATELLITE_MODE_ENABLED, val);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected boolean areAllRadiosDisabled() {
+ synchronized (mRadioStateLock) {
+ if ((mDisableBTOnSatelliteEnabled && mBTStateEnabled)
+ || (mDisableNFCOnSatelliteEnabled && mNfcStateEnabled)
+ || (mDisableWifiOnSatelliteEnabled && mWifiStateEnabled)
+ || (mDisableUWBOnSatelliteEnabled && mUwbStateEnabled)) {
+ logd("All radios are not disabled yet.");
+ return false;
+ }
+ logd("All radios are disabled.");
+ return true;
+ }
+ }
+
+ private void evaluateToSendSatelliteEnabledSuccess() {
+ logd("evaluateToSendSatelliteEnabledSuccess");
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (areAllRadiosDisabled() && (mSatelliteEnabledRequest != null)
+ && mWaitingForRadioDisabled) {
+ logd("Sending success to callback that sent enable satellite request");
+ setDemoModeEnabled(mSatelliteEnabledRequest.enableDemoMode);
+ synchronized (mIsSatelliteEnabledLock) {
+ mIsSatelliteEnabled = mSatelliteEnabledRequest.enableSatellite;
+ }
+ mSatelliteEnabledRequest.callback.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+ updateSatelliteEnabledState(
+ mSatelliteEnabledRequest.enableSatellite,
+ "EVENT_SET_SATELLITE_ENABLED_DONE");
+ mSatelliteEnabledRequest = null;
+ mWaitingForRadioDisabled = false;
+ }
+ }
+ }
+
+ private void resetSatelliteEnabledRequest() {
+ logd("resetSatelliteEnabledRequest");
+ synchronized (mSatelliteEnabledRequestLock) {
+ mSatelliteEnabledRequest = null;
+ mWaitingForRadioDisabled = false;
+ }
+ }
+
+ private void cleanUpResources(@SatelliteManager.SatelliteModemState int state) {
+ logd("cleanUpResources");
+ if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest != null) {
+ mSatelliteEnabledRequest.callback.accept(
+ SatelliteManager.SATELLITE_INVALID_MODEM_STATE);
+ }
+ }
+ resetSatelliteEnabledRequest();
+ }
+ }
+
+ private void setDemoModeEnabled(boolean enabled) {
+ mIsDemoModeEnabled = enabled;
+ mDatagramController.setDemoMode(mIsDemoModeEnabled);
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
new file mode 100644
index 0000000..80c67b3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.telephony.Rlog;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.telephony.satellite.stub.ISatellite;
+import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.SatelliteService;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.Arrays;
+
+/**
+ * Satellite modem interface to manage connections with the satellite service and HAL interface.
+ */
+public class SatelliteModemInterface {
+ private static final String TAG = "SatelliteModemInterface";
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final boolean DEBUG = !"user".equals(Build.TYPE);
+ private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
+ private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
+ private static final int REBIND_MULTIPLIER = 2;
+
+ @NonNull private static SatelliteModemInterface sInstance;
+ @NonNull private final Context mContext;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull protected final ExponentialBackoff mExponentialBackoff;
+ @NonNull private final Object mLock = new Object();
+ @NonNull private final SatelliteController mSatelliteController;
+ /**
+ * {@code true} to use the vendor satellite service and {@code false} to use the HAL.
+ */
+ private boolean mIsSatelliteServiceSupported;
+ @Nullable private ISatellite mSatelliteService;
+ @Nullable private SatelliteServiceConnection mSatelliteServiceConnection;
+ @NonNull private String mVendorSatellitePackageName = "";
+ private boolean mIsBound;
+ private boolean mIsBinding;
+
+ @NonNull private final RegistrantList mSatelliteProvisionStateChangedRegistrants =
+ new RegistrantList();
+ @NonNull private final RegistrantList mSatellitePositionInfoChangedRegistrants =
+ new RegistrantList();
+ @NonNull private final RegistrantList mDatagramTransferStateChangedRegistrants =
+ new RegistrantList();
+ @NonNull private final RegistrantList mSatelliteModemStateChangedRegistrants =
+ new RegistrantList();
+ @NonNull private final RegistrantList mPendingDatagramsRegistrants = new RegistrantList();
+ @NonNull private final RegistrantList mSatelliteDatagramsReceivedRegistrants =
+ new RegistrantList();
+
+ @NonNull private final ISatelliteListener mListener = new ISatelliteListener.Stub() {
+ @Override
+ public void onSatelliteProvisionStateChanged(boolean provisioned) {
+ mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
+ }
+
+ @Override
+ public void onSatelliteDatagramReceived(
+ android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
+ mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
+ SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+ }
+
+ @Override
+ public void onPendingDatagrams() {
+ mPendingDatagramsRegistrants.notifyResult(null);
+ }
+
+ @Override
+ public void onSatellitePositionChanged(
+ android.telephony.satellite.stub.PointingInfo pointingInfo) {
+ mSatellitePositionInfoChangedRegistrants.notifyResult(
+ SatelliteServiceUtils.fromPointingInfo(pointingInfo));
+ }
+
+ @Override
+ public void onSatelliteModemStateChanged(int state) {
+ mSatelliteModemStateChangedRegistrants.notifyResult(
+ SatelliteServiceUtils.fromSatelliteModemState(state));
+ int datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
+ switch (state) {
+ case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
+ datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
+ datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+ datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
+ // keep previous state as this could be retrying sending or receiving
+ break;
+ }
+ mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
+ }
+ };
+
+ /**
+ * @return The singleton instance of SatelliteModemInterface.
+ */
+ public static SatelliteModemInterface getInstance() {
+ if (sInstance == null) {
+ loge("SatelliteModemInterface was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the SatelliteModemInterface singleton instance.
+ * @param context The Context to use to create the SatelliteModemInterface.
+ * @param satelliteController The singleton instance of SatelliteController.
+ * @return The singleton instance of SatelliteModemInterface.
+ */
+ public static SatelliteModemInterface make(@NonNull Context context,
+ SatelliteController satelliteController) {
+ if (sInstance == null) {
+ sInstance = new SatelliteModemInterface(
+ context, satelliteController, Looper.getMainLooper());
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a SatelliteModemInterface to manage connections to the SatelliteService.
+ *
+ * @param context The Context for the SatelliteModemInterface.
+ * @param looper The Looper to run binding retry on.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected SatelliteModemInterface(@NonNull Context context,
+ SatelliteController satelliteController, @NonNull Looper looper) {
+ mContext = context;
+ mIsSatelliteServiceSupported = getSatelliteServiceSupport();
+ mSatelliteController = satelliteController;
+ mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
+ REBIND_MULTIPLIER, looper, () -> {
+ synchronized (mLock) {
+ if ((mIsBound && mSatelliteService != null) || mIsBinding) {
+ return;
+ }
+ }
+ if (mSatelliteServiceConnection != null) {
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ }
+ bindService();
+ });
+ mExponentialBackoff.start();
+ logd("Created SatelliteModemInterface. Attempting to bind to SatelliteService.");
+ bindService();
+ }
+
+ /**
+ * Get the SatelliteService interface, if it exists.
+ *
+ * @return The bound ISatellite, or {@code null} if it is not yet connected.
+ */
+ @Nullable public ISatellite getService() {
+ return mSatelliteService;
+ }
+
+ @NonNull private String getSatellitePackageName() {
+ if (!TextUtils.isEmpty(mVendorSatellitePackageName)) {
+ return mVendorSatellitePackageName;
+ }
+ return TextUtils.emptyIfNull(mContext.getResources().getString(
+ R.string.config_satellite_service_package));
+ }
+
+ private boolean getSatelliteServiceSupport() {
+ return !TextUtils.isEmpty(getSatellitePackageName());
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void bindService() {
+ synchronized (mLock) {
+ if (mIsBinding || mIsBound) return;
+ mIsBinding = true;
+ }
+ String packageName = getSatellitePackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ loge("Unable to bind to the satellite service because the package is undefined.");
+ // Since the package name comes from static device configs, stop retry because
+ // rebind will continue to fail without a valid package name.
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.stop();
+ return;
+ }
+ Intent intent = new Intent(SatelliteService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+
+ mSatelliteServiceConnection = new SatelliteServiceConnection();
+ logd("Binding to " + packageName);
+ try {
+ boolean success = mContext.bindService(
+ intent, mSatelliteServiceConnection, Context.BIND_AUTO_CREATE);
+ if (success) {
+ logd("Successfully bound to the satellite service.");
+ } else {
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.notifyFailed();
+ loge("Error binding to the satellite service. Retrying in "
+ + mExponentialBackoff.getCurrentDelay() + " ms.");
+ }
+ } catch (Exception e) {
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.notifyFailed();
+ loge("Exception binding to the satellite service. Retrying in "
+ + mExponentialBackoff.getCurrentDelay() + " ms. Exception: " + e);
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void unbindService() {
+ disconnectSatelliteService();
+ mContext.unbindService(mSatelliteServiceConnection);
+ mSatelliteServiceConnection = null;
+ }
+
+ private void disconnectSatelliteService() {
+ // TODO: clean up any listeners and return failed for pending callbacks
+ mSatelliteService = null;
+ }
+
+ private class SatelliteServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logd("onServiceConnected: ComponentName=" + name);
+ synchronized (mLock) {
+ mIsBound = true;
+ mIsBinding = false;
+ }
+ mSatelliteService = ISatellite.Stub.asInterface(service);
+ mExponentialBackoff.stop();
+ try {
+ mSatelliteService.setSatelliteListener(mListener);
+ } catch (RemoteException e) {
+ // TODO: Retry setSatelliteListener
+ logd("setSatelliteListener: RemoteException " + e);
+ }
+ mSatelliteController.onSatelliteServiceConnected();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ loge("onServiceDisconnected: Waiting for reconnect.");
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ // Since we are still technically bound, clear the service and wait for reconnect.
+ disconnectSatelliteService();
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ loge("onBindingDied: Unbinding and rebinding service.");
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ mExponentialBackoff.start();
+ }
+ }
+
+ /**
+ * Registers for the satellite provision state changed.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteProvisionStateChanged(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mSatelliteProvisionStateChangedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for the satellite provision state changed.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {
+ mSatelliteProvisionStateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Registers for satellite position info changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatellitePositionInfoChanged(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mSatellitePositionInfoChangedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for satellite position info changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatellitePositionInfoChanged(@NonNull Handler h) {
+ mSatellitePositionInfoChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Registers for datagram transfer state changed.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForDatagramTransferStateChanged(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mDatagramTransferStateChangedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for datagram transfer state changed.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForDatagramTransferStateChanged(@NonNull Handler h) {
+ mDatagramTransferStateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Registers for modem state changed from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteModemStateChanged(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mSatelliteModemStateChangedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for modem state changed from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatelliteModemStateChanged(@NonNull Handler h) {
+ mSatelliteModemStateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Registers for pending datagrams indication from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForPendingDatagrams(@NonNull Handler h, int what, @Nullable Object obj) {
+ mPendingDatagramsRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for pending datagrams indication from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForPendingDatagrams(@NonNull Handler h) {
+ mPendingDatagramsRegistrants.remove(h);
+ }
+
+ /**
+ * Registers for new datagrams received from satellite modem.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSatelliteDatagramsReceived(
+ @NonNull Handler h, int what, @Nullable Object obj) {
+ mSatelliteDatagramsReceivedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new datagrams received from satellite modem.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSatelliteDatagramsReceived(@NonNull Handler h) {
+ mSatelliteDatagramsReceivedRegistrants.remove(h);
+ }
+
+ /**
+ * Request to enable or disable the satellite service listening mode.
+ * Listening mode allows the satellite service to listen for incoming pages.
+ *
+ * @param enable True to enable satellite listening mode and false to disable.
+ * @param timeout How long the satellite modem should wait for the next incoming page before
+ * disabling listening mode.
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestSatelliteListeningEnabled(boolean enable, int timeout,
+ @Nullable Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestSatelliteListeningEnabled(enable, timeout,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestSatelliteListeningEnabled: " + error);
+ Binder.withCleanCallingIdentity(() -> {
+ if (message != null) {
+ sendMessageWithResult(message, null, error);
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestSatelliteListeningEnabled: RemoteException " + e);
+ if (message != null) {
+ sendMessageWithResult(
+ message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ }
+ } else {
+ loge("requestSatelliteListeningEnabled: Satellite service is unavailable.");
+ if (message != null) {
+ sendMessageWithResult(message, null,
+ SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * Allow cellular modem scanning while satellite mode is on.
+ * @param enabled {@code true} to enable cellular modem while satellite mode is on
+ * and {@code false} to disable
+ * @param message The Message to send to result of the operation to.
+ */
+ public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+ @Nullable Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(enabled,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
+ Binder.withCleanCallingIdentity(() -> {
+ if (message != null) {
+ sendMessageWithResult(message, null, error);
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
+ if (message != null) {
+ sendMessageWithResult(
+ message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ }
+ } else {
+ loge("enableCellularModemWhileSatelliteModeIsOn: Satellite service is unavailable.");
+ if (message != null) {
+ sendMessageWithResult(message, null,
+ SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+ }
+ /**
+ * Request to enable or disable the satellite modem and demo mode. If the satellite modem
+ * is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
+ * this may also re-enable the cellular modem.
+ *
+ * @param enableSatellite True to enable the satellite modem and false to disable.
+ * @param enableDemoMode True to enable demo mode and false to disable.
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
+ @NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestSatelliteEnabled(enableSatellite, enableDemoMode,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("setSatelliteEnabled: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("setSatelliteEnabled: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("setSatelliteEnabled: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get whether the satellite modem is enabled.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestIsSatelliteEnabled(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestIsSatelliteEnabled(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestIsSatelliteEnabled: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IBooleanConsumer.Stub() {
+ @Override
+ public void accept(boolean result) {
+ // Convert for compatibility with SatelliteResponse
+ // TODO: This should just report result instead.
+ int[] enabled = new int[] {result ? 1 : 0};
+ logd("requestIsSatelliteEnabled: " + Arrays.toString(enabled));
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, enabled, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestIsSatelliteEnabled: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestIsSatelliteEnabled: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get whether the satellite service is supported on the device.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestIsSatelliteSupported(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestIsSatelliteSupported(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestIsSatelliteSupported: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IBooleanConsumer.Stub() {
+ @Override
+ public void accept(boolean result) {
+ logd("requestIsSatelliteSupported: " + result);
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, result, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestIsSatelliteSupported: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestIsSatelliteSupported: Satellite service is unavailable.");
+ sendMessageWithResult(
+ message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get the SatelliteCapabilities of the satellite service.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestSatelliteCapabilities(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestSatelliteCapabilities(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestSatelliteCapabilities: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new ISatelliteCapabilitiesConsumer.Stub() {
+ @Override
+ public void accept(android.telephony.satellite.stub.SatelliteCapabilities
+ result) {
+ SatelliteCapabilities capabilities =
+ SatelliteServiceUtils.fromSatelliteCapabilities(result);
+ logd("requestSatelliteCapabilities: " + capabilities);
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, capabilities, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestSatelliteCapabilities: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestSatelliteCapabilities: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * User started pointing to the satellite.
+ * The satellite service should report the satellite pointing info via
+ * ISatelliteListener#onSatellitePositionChanged as the user device/satellite moves.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void startSendingSatellitePointingInfo(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.startSendingSatellitePointingInfo(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("startSendingSatellitePointingInfo: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("startSendingSatellitePointingInfo: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("startSendingSatellitePointingInfo: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * User stopped pointing to the satellite.
+ * The satellite service should stop reporting satellite pointing info to the framework.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void stopSendingSatellitePointingInfo(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.stopSendingSatellitePointingInfo(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("stopSendingSatellitePointingInfo: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("stopSendingSatellitePointingInfo: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("stopSendingSatellitePointingInfo: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Provision the device with a satellite provider.
+ * This is needed if the provider allows dynamic registration.
+ * Once provisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report true.
+ *
+ * @param token The token to be used as a unique identifier for provisioning with satellite
+ * gateway.
+ * @param provisionData Data from the provisioning app that can be used by provisioning server
+ * @param message The Message to send to result of the operation to.
+ */
+ public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
+ @NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.provisionSatelliteService(token, provisionData,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("provisionSatelliteService: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("provisionSatelliteService: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("provisionSatelliteService: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Deprovision the device with the satellite provider.
+ * This is needed if the provider allows dynamic registration.
+ * Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false.
+ *
+ * @param token The token of the device/subscription to be deprovisioned.
+ * @param message The Message to send to result of the operation to.
+ */
+ public void deprovisionSatelliteService(@NonNull String token, @NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.deprovisionSatelliteService(token, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("deprovisionSatelliteService: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("deprovisionSatelliteService: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("deprovisionSatelliteService: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get whether this device is provisioned with a satellite provider.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestIsSatelliteProvisioned(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestIsSatelliteProvisioned(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestIsSatelliteProvisioned: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IBooleanConsumer.Stub() {
+ @Override
+ public void accept(boolean result) {
+ // Convert for compatibility with SatelliteResponse
+ // TODO: This should just report result instead.
+ int[] provisioned = new int[] {result ? 1 : 0};
+ logd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, provisioned, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestIsSatelliteProvisioned: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Poll the pending datagrams to be received over satellite.
+ * The satellite service should check if there are any pending datagrams to be received over
+ * satellite and report them via ISatelliteListener#onSatelliteDatagramsReceived.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void pollPendingSatelliteDatagrams(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.pollPendingSatelliteDatagrams(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("pollPendingSatelliteDatagrams: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("pollPendingSatelliteDatagrams: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("pollPendingSatelliteDatagrams: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Send datagram over satellite.
+ *
+ * @param datagram Datagram to send in byte format.
+ * @param isEmergency Whether this is an emergency datagram.
+ * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+ * full screen mode.
+ * @param message The Message to send to result of the operation to.
+ */
+ public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency,
+ boolean needFullScreenPointingUI, @NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.sendSatelliteDatagram(
+ SatelliteServiceUtils.toSatelliteDatagram(datagram), isEmergency,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("sendSatelliteDatagram: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("sendSatelliteDatagram: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("sendSatelliteDatagram: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request the current satellite modem state.
+ * The satellite service should report the current satellite modem state via
+ * ISatelliteListener#onSatelliteModemStateChanged.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestSatelliteModemState(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestSatelliteModemState(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestSatelliteModemState: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ // Convert SatelliteModemState from service to frameworks definition.
+ int modemState = SatelliteServiceUtils.fromSatelliteModemState(result);
+ logd("requestSatelliteModemState: " + modemState);
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, modemState, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestSatelliteModemState: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestSatelliteModemState: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get whether satellite communication is allowed for the current location.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+ + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IBooleanConsumer.Stub() {
+ @Override
+ public void accept(boolean result) {
+ logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+ + result);
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, result, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: RemoteException "
+ + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+ + "Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Request to get the time after which the satellite will be visible. This is an int
+ * representing the duration in seconds after which the satellite will be visible.
+ * This will return 0 if the satellite is currently visible.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void requestTimeForNextSatelliteVisibility(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.requestTimeForNextSatelliteVisibility(
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("requestTimeForNextSatelliteVisibility: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ }, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ // Convert for compatibility with SatelliteResponse
+ // TODO: This should just report result instead.
+ int[] time = new int[] {result};
+ logd("requestTimeForNextSatelliteVisibility: "
+ + Arrays.toString(time));
+ Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+ message, time, SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("requestTimeForNextSatelliteVisibility: RemoteException " + e);
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+ } else {
+ loge("requestTimeForNextSatelliteVisibility: Satellite service is unavailable.");
+ sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+ }
+ }
+
+ public boolean isSatelliteServiceSupported() {
+ return mIsSatelliteServiceSupported;
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite vendor service package name.
+ *
+ * @param servicePackageName The package name of the satellite vendor service.
+ * @return {@code true} if the satellite vendor service is set successfully,
+ * {@code false} otherwise.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
+ if (!shouldAllowModifyingSatelliteServicePackageName()) {
+ loge("setSatelliteServicePackageName: modifying satellite service package name "
+ + "is not allowed");
+ return false;
+ }
+
+ logd("setSatelliteServicePackageName: config_satellite_service_package is "
+ + "updated, new packageName=" + servicePackageName);
+ mExponentialBackoff.stop();
+ if (mSatelliteServiceConnection != null) {
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ }
+
+ if (servicePackageName == null || servicePackageName.equals("null")) {
+ mVendorSatellitePackageName = "";
+ } else {
+ mVendorSatellitePackageName = servicePackageName;
+ }
+ mIsSatelliteServiceSupported = getSatelliteServiceSupport();
+ bindService();
+ mExponentialBackoff.start();
+
+ return true;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected static void sendMessageWithResult(@NonNull Message message, @Nullable Object result,
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = error == SatelliteManager.SATELLITE_ERROR_NONE
+ ? null : new SatelliteException(error);
+ AsyncResult.forMessage(message, result, exception);
+ message.sendToTarget();
+ }
+
+ private boolean shouldAllowModifyingSatelliteServicePackageName() {
+ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
new file mode 100644
index 0000000..25657b3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE;
+
+import android.annotation.NonNull;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ResultReceiver;
+import android.provider.DeviceConfig;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.util.Pair;
+
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.metrics.SatelliteStats;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * This module is responsible for monitoring the cellular service state and IMS registration state
+ * during an emergency call and notify Dialer when Telephony is not able to find any network and
+ * the call likely will not get connected so that Dialer will prompt the user if they would like to
+ * switch to satellite messaging.
+ */
+public class SatelliteSOSMessageRecommender extends Handler {
+ private static final String TAG = "SatelliteSOSMessageRecommender";
+
+ /**
+ * Device config for the timeout duration in milliseconds to determine whether to recommend
+ * Dialer to show the SOS button to users.
+ * <p>
+ * The timer is started when there is an ongoing emergency call, and the IMS is not registered,
+ * and cellular service is not available. When the timer expires, SatelliteSOSMessageRecommender
+ * will send the event EVENT_DISPLAY_SOS_MESSAGE to Dialer, which will then prompt user to
+ * switch to using satellite SOS messaging.
+ */
+ public static final String EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS =
+ "emergency_call_to_sos_msg_hysteresis_timeout_millis";
+ /**
+ * The default value of {@link #EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS} when it is
+ * not provided in the device config.
+ */
+ public static final long DEFAULT_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS = 20000;
+
+ private static final int EVENT_EMERGENCY_CALL_STARTED = 1;
+ protected static final int EVENT_CELLULAR_SERVICE_STATE_CHANGED = 2;
+ private static final int EVENT_IMS_REGISTRATION_STATE_CHANGED = 3;
+ protected static final int EVENT_TIME_OUT = 4;
+ private static final int EVENT_SATELLITE_PROVISIONED_STATE_CHANGED = 5;
+ private static final int EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED = 6;
+
+ @NonNull
+ private final SatelliteController mSatelliteController;
+ private ImsManager mImsManager;
+
+ private Connection mEmergencyConnection = null;
+ /* The phone used for emergency call */
+ private Phone mPhone = null;
+ private final ISatelliteProvisionStateCallback mISatelliteProvisionStateCallback;
+ @ServiceState.RegState
+ private AtomicInteger mCellularServiceState = new AtomicInteger();
+ private AtomicBoolean mIsImsRegistered = new AtomicBoolean();
+ private AtomicBoolean mIsSatelliteAllowedInCurrentLocation = new AtomicBoolean();
+ private final ResultReceiver mReceiverForRequestIsSatelliteAllowedForCurrentLocation;
+ private final long mTimeoutMillis;
+ protected int mCountOfTimerStarted = 0;
+
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(ImsRegistrationAttributes attributes) {
+ sendMessage(obtainMessage(EVENT_IMS_REGISTRATION_STATE_CHANGED, true));
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ sendMessage(obtainMessage(EVENT_IMS_REGISTRATION_STATE_CHANGED, false));
+ }
+ };
+
+ /**
+ * Create an instance of SatelliteSOSMessageRecommender.
+ *
+ * @param looper The looper used with the handler of this class.
+ */
+ public SatelliteSOSMessageRecommender(@NonNull Looper looper) {
+ this(looper, SatelliteController.getInstance(), null,
+ getEmergencyCallToSosMsgHysteresisTimeoutMillis());
+ }
+
+ /**
+ * Create an instance of SatelliteSOSMessageRecommender. This constructor should be used in
+ * only unit tests.
+ *
+ * @param looper The looper used with the handler of this class.
+ * @param satelliteController The SatelliteController singleton instance.
+ * @param imsManager The ImsManager instance associated with the phone, which is used for making
+ * the emergency call. This argument is not null only in unit tests.
+ * @param timeoutMillis The timeout duration of the timer.
+ */
+ @VisibleForTesting
+ protected SatelliteSOSMessageRecommender(@NonNull Looper looper,
+ @NonNull SatelliteController satelliteController, ImsManager imsManager,
+ long timeoutMillis) {
+ super(looper);
+ mSatelliteController = satelliteController;
+ mImsManager = imsManager;
+ mTimeoutMillis = timeoutMillis;
+ mISatelliteProvisionStateCallback = new ISatelliteProvisionStateCallback.Stub() {
+ @Override
+ public void onSatelliteProvisionStateChanged(boolean provisioned) {
+ logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
+ sendMessage(obtainMessage(EVENT_SATELLITE_PROVISIONED_STATE_CHANGED, provisioned));
+ }
+ };
+ mReceiverForRequestIsSatelliteAllowedForCurrentLocation = new ResultReceiver(this) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+ boolean isSatelliteCommunicationAllowed =
+ resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+ mIsSatelliteAllowedInCurrentLocation.set(isSatelliteCommunicationAllowed);
+ if (!isSatelliteCommunicationAllowed) {
+ logd("Satellite is not allowed for current location.");
+ cleanUpResources();
+ }
+ } else {
+ loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+ mIsSatelliteAllowedInCurrentLocation.set(false);
+ cleanUpResources();
+ }
+ } else {
+ loge("requestIsSatelliteCommunicationAllowedForCurrentLocation() resultCode="
+ + resultCode);
+ mIsSatelliteAllowedInCurrentLocation.set(false);
+ cleanUpResources();
+ }
+ }
+ };
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_EMERGENCY_CALL_STARTED:
+ handleEmergencyCallStartedEvent((Pair<Connection, Phone>) msg.obj);
+ break;
+ case EVENT_TIME_OUT:
+ handleTimeoutEvent();
+ break;
+ case EVENT_SATELLITE_PROVISIONED_STATE_CHANGED:
+ handleSatelliteProvisionStateChangedEvent((boolean) msg.obj);
+ break;
+ case EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED:
+ handleEmergencyCallConnectionStateChangedEvent((Pair<String, Integer>) msg.obj);
+ break;
+ case EVENT_IMS_REGISTRATION_STATE_CHANGED:
+ handleImsRegistrationStateChangedEvent((boolean) msg.obj);
+ break;
+ case EVENT_CELLULAR_SERVICE_STATE_CHANGED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ handleCellularServiceStateChangedEvent((ServiceState) ar.result);
+ break;
+ default:
+ logd("handleMessage: unexpected message code: " + msg.what);
+ break;
+ }
+ }
+
+ /**
+ * Inform SatelliteSOSMessageRecommender that an emergency call has just started.
+ *
+ * @param connection The connection created by TelephonyConnectionService for the emergency
+ * call.
+ * @param phone The phone used for the emergency call.
+ */
+ public void onEmergencyCallStarted(@NonNull Connection connection, @NonNull Phone phone) {
+ if (!mSatelliteController.isSatelliteSupported()) {
+ logd("onEmergencyCallStarted: satellite is not supported");
+ return;
+ }
+ Pair<Connection, Phone> argument = new Pair<>(connection, phone);
+ sendMessage(obtainMessage(EVENT_EMERGENCY_CALL_STARTED, argument));
+ }
+
+ /**
+ * Inform SatelliteSOSMessageRecommender that the state of the emergency call connection has
+ * changed.
+ *
+ * @param callId The ID of the emergency call.
+ * @param state The connection state of the emergency call.
+ */
+ public void onEmergencyCallConnectionStateChanged(
+ String callId, @Connection.ConnectionState int state) {
+ Pair<String, Integer> argument = new Pair<>(callId, state);
+ sendMessage(obtainMessage(EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED, argument));
+ }
+
+ private void handleEmergencyCallStartedEvent(@NonNull Pair<Connection, Phone> arg) {
+ mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ mReceiverForRequestIsSatelliteAllowedForCurrentLocation);
+ if (mPhone != null) {
+ logd("handleEmergencyCallStartedEvent: new emergency call started while there is "
+ + " an ongoing call");
+ unregisterForInterestedStateChangedEvents(mPhone);
+ }
+ mPhone = arg.second;
+ mEmergencyConnection = arg.first;
+ mCellularServiceState.set(mPhone.getServiceState().getState());
+ mIsImsRegistered.set(mPhone.isImsRegistered());
+ handleStateChangedEventForHysteresisTimer();
+ registerForInterestedStateChangedEvents(mPhone);
+ }
+
+ private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) {
+ if (!provisioned) {
+ cleanUpResources();
+ }
+ }
+
+ private void handleTimeoutEvent() {
+ boolean isDialerNotified = false;
+ if (!mIsImsRegistered.get() && !isCellularAvailable()
+ && mIsSatelliteAllowedInCurrentLocation.get()
+ && mSatelliteController.isSatelliteProvisioned()
+ && shouldTrackCall(mEmergencyConnection.getState())) {
+ logd("handleTimeoutEvent: Sending EVENT_DISPLAY_SOS_MESSAGE to Dialer...");
+ mEmergencyConnection.sendConnectionEvent(Call.EVENT_DISPLAY_SOS_MESSAGE, null);
+ isDialerNotified = true;
+ }
+ reportEsosRecommenderDecision(isDialerNotified);
+ cleanUpResources();
+ }
+
+ private void handleEmergencyCallConnectionStateChangedEvent(
+ @NonNull Pair<String, Integer> arg) {
+ if (mEmergencyConnection == null) {
+ // Either the call was not created or the timer already timed out.
+ return;
+ }
+
+ String callId = arg.first;
+ int state = arg.second;
+ if (!mEmergencyConnection.getTelecomCallId().equals(callId)) {
+ loge("handleEmergencyCallConnectionStateChangedEvent: unexpected state changed event "
+ + ", mEmergencyConnection=" + mEmergencyConnection + ", callId=" + callId
+ + ", state=" + state);
+ /**
+ * TelephonyConnectionService sent us a connection state changed event for a call that
+ * we're not tracking. There must be some unexpected things happened in
+ * TelephonyConnectionService. Thus, we need to clean up the resources.
+ */
+ cleanUpResources();
+ return;
+ }
+
+ if (!shouldTrackCall(state)) {
+ reportEsosRecommenderDecision(false);
+ cleanUpResources();
+ }
+ }
+
+ private void handleImsRegistrationStateChangedEvent(boolean registered) {
+ if (registered != mIsImsRegistered.get()) {
+ mIsImsRegistered.set(registered);
+ handleStateChangedEventForHysteresisTimer();
+ }
+ }
+
+ private void handleCellularServiceStateChangedEvent(@NonNull ServiceState serviceState) {
+ int state = serviceState.getState();
+ if (mCellularServiceState.get() != state) {
+ mCellularServiceState.set(state);
+ handleStateChangedEventForHysteresisTimer();
+ }
+ }
+
+ private void reportEsosRecommenderDecision(boolean isDialerNotified) {
+ SatelliteStats.getInstance().onSatelliteSosMessageRecommender(
+ new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
+ .setDisplaySosMessageSent(isDialerNotified)
+ .setCountOfTimerStarted(mCountOfTimerStarted)
+ .setImsRegistered(mIsImsRegistered.get())
+ .setCellularServiceState(mCellularServiceState.get())
+ .build());
+ }
+
+ private void cleanUpResources() {
+ stopTimer();
+ if (mPhone != null) {
+ unregisterForInterestedStateChangedEvents(mPhone);
+ mPhone = null;
+ }
+ mEmergencyConnection = null;
+ mCountOfTimerStarted = 0;
+ }
+
+ private void registerForInterestedStateChangedEvents(@NonNull Phone phone) {
+ mSatelliteController.registerForSatelliteProvisionStateChanged(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
+ phone.registerForServiceStateChanged(this, EVENT_CELLULAR_SERVICE_STATE_CHANGED, null);
+ registerForImsRegistrationStateChanged(phone);
+ }
+
+ private void registerForImsRegistrationStateChanged(@NonNull Phone phone) {
+ ImsManager imsManager = (mImsManager != null) ? mImsManager : ImsManager.getInstance(
+ phone.getContext(), phone.getPhoneId());
+ try {
+ imsManager.addRegistrationCallback(mImsRegistrationCallback, this::post);
+ } catch (ImsException ex) {
+ loge("registerForImsRegistrationStateChanged: ex=" + ex);
+ }
+ }
+
+ private void unregisterForInterestedStateChangedEvents(@NonNull Phone phone) {
+ mSatelliteController.unregisterForSatelliteProvisionStateChanged(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
+ phone.unregisterForServiceStateChanged(this);
+ unregisterForImsRegistrationStateChanged(phone);
+ }
+
+ private void unregisterForImsRegistrationStateChanged(@NonNull Phone phone) {
+ ImsManager imsManager = (mImsManager != null) ? mImsManager : ImsManager.getInstance(
+ phone.getContext(), phone.getPhoneId());
+ imsManager.removeRegistrationListener(mImsRegistrationCallback);
+ }
+
+ private boolean isCellularAvailable() {
+ return (mCellularServiceState.get() == ServiceState.STATE_IN_SERVICE
+ || mCellularServiceState.get() == ServiceState.STATE_EMERGENCY_ONLY);
+ }
+
+ private void handleStateChangedEventForHysteresisTimer() {
+ if (!mIsImsRegistered.get() && !isCellularAvailable()) {
+ startTimer();
+ } else {
+ stopTimer();
+ }
+ }
+
+ private void startTimer() {
+ if (hasMessages(EVENT_TIME_OUT)) {
+ return;
+ }
+ sendMessageDelayed(obtainMessage(EVENT_TIME_OUT), mTimeoutMillis);
+ mCountOfTimerStarted++;
+ }
+
+ private void stopTimer() {
+ removeMessages(EVENT_TIME_OUT);
+ }
+
+ private static long getEmergencyCallToSosMsgHysteresisTimeoutMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
+ DEFAULT_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ }
+
+ private boolean shouldTrackCall(int connectionState) {
+ /**
+ * An active connection state means both parties are connected to the call and can actively
+ * communicate. A disconnected connection state means the emergency call has ended. In both
+ * cases, we don't need to track the call anymore.
+ */
+ return (connectionState != Connection.STATE_ACTIVE
+ && connectionState != Connection.STATE_DISCONNECTED);
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
new file mode 100644
index 0000000..f11ca66
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.stub.NTRadioTechnology;
+import android.telephony.satellite.stub.SatelliteError;
+import android.telephony.satellite.stub.SatelliteModemState;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RILUtils;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Utils class for satellite service <-> framework conversions
+ */
+public class SatelliteServiceUtils {
+ private static final String TAG = "SatelliteServiceUtils";
+
+ /**
+ * Convert radio technology from service definition to framework definition.
+ * @param radioTechnology The NTRadioTechnology from the satellite service.
+ * @return The converted NTRadioTechnology for the framework.
+ */
+ @SatelliteManager.NTRadioTechnology
+ public static int fromSatelliteRadioTechnology(int radioTechnology) {
+ switch (radioTechnology) {
+ case NTRadioTechnology.NB_IOT_NTN:
+ return SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
+ case NTRadioTechnology.NR_NTN:
+ return SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN;
+ case NTRadioTechnology.EMTC_NTN:
+ return SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN;
+ case NTRadioTechnology.PROPRIETARY:
+ return SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY;
+ default:
+ loge("Received invalid radio technology: " + radioTechnology);
+ return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
+ }
+ }
+
+ /**
+ * Convert satellite error from service definition to framework definition.
+ * @param error The SatelliteError from the satellite service.
+ * @return The converted SatelliteError for the framework.
+ */
+ @SatelliteManager.SatelliteError public static int fromSatelliteError(int error) {
+ switch (error) {
+ case SatelliteError.ERROR_NONE:
+ return SatelliteManager.SATELLITE_ERROR_NONE;
+ case SatelliteError.SATELLITE_ERROR:
+ return SatelliteManager.SATELLITE_ERROR;
+ case SatelliteError.SERVER_ERROR:
+ return SatelliteManager.SATELLITE_SERVER_ERROR;
+ case SatelliteError.SERVICE_ERROR:
+ return SatelliteManager.SATELLITE_SERVICE_ERROR;
+ case SatelliteError.MODEM_ERROR:
+ return SatelliteManager.SATELLITE_MODEM_ERROR;
+ case SatelliteError.NETWORK_ERROR:
+ return SatelliteManager.SATELLITE_NETWORK_ERROR;
+ case SatelliteError.INVALID_TELEPHONY_STATE:
+ return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ case SatelliteError.INVALID_MODEM_STATE:
+ return SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+ case SatelliteError.INVALID_ARGUMENTS:
+ return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+ case SatelliteError.REQUEST_FAILED:
+ return SatelliteManager.SATELLITE_REQUEST_FAILED;
+ case SatelliteError.RADIO_NOT_AVAILABLE:
+ return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
+ case SatelliteError.REQUEST_NOT_SUPPORTED:
+ return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED;
+ case SatelliteError.NO_RESOURCES:
+ return SatelliteManager.SATELLITE_NO_RESOURCES;
+ case SatelliteError.SERVICE_NOT_PROVISIONED:
+ return SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED;
+ case SatelliteError.SERVICE_PROVISION_IN_PROGRESS:
+ return SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS;
+ case SatelliteError.REQUEST_ABORTED:
+ return SatelliteManager.SATELLITE_REQUEST_ABORTED;
+ case SatelliteError.SATELLITE_ACCESS_BARRED:
+ return SatelliteManager.SATELLITE_ACCESS_BARRED;
+ case SatelliteError.NETWORK_TIMEOUT:
+ return SatelliteManager.SATELLITE_NETWORK_TIMEOUT;
+ case SatelliteError.SATELLITE_NOT_REACHABLE:
+ return SatelliteManager.SATELLITE_NOT_REACHABLE;
+ case SatelliteError.NOT_AUTHORIZED:
+ return SatelliteManager.SATELLITE_NOT_AUTHORIZED;
+ }
+ loge("Received invalid satellite service error: " + error);
+ return SatelliteManager.SATELLITE_SERVICE_ERROR;
+ }
+
+ /**
+ * Convert satellite modem state from service definition to framework definition.
+ * @param modemState The SatelliteModemState from the satellite service.
+ * @return The converted SatelliteModemState for the framework.
+ */
+ @SatelliteManager.SatelliteModemState
+ public static int fromSatelliteModemState(int modemState) {
+ switch (modemState) {
+ case SatelliteModemState.SATELLITE_MODEM_STATE_IDLE:
+ return SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
+ case SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING:
+ return SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
+ case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+ return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+ case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
+ return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING;
+ case SatelliteModemState.SATELLITE_MODEM_STATE_OFF:
+ return SatelliteManager.SATELLITE_MODEM_STATE_OFF;
+ case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE:
+ return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
+ default:
+ loge("Received invalid modem state: " + modemState);
+ return SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Convert SatelliteCapabilities from service definition to framework definition.
+ * @param capabilities The SatelliteCapabilities from the satellite service.
+ * @return The converted SatelliteCapabilities for the framework.
+ */
+ @Nullable public static SatelliteCapabilities fromSatelliteCapabilities(
+ @Nullable android.telephony.satellite.stub.SatelliteCapabilities capabilities) {
+ if (capabilities == null) return null;
+ int[] radioTechnologies = capabilities.supportedRadioTechnologies == null
+ ? new int[0] : capabilities.supportedRadioTechnologies;
+
+ Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>();
+ int[] antennaPositionKeys = capabilities.antennaPositionKeys;
+ AntennaPosition[] antennaPositionValues = capabilities.antennaPositionValues;
+ if (antennaPositionKeys != null && antennaPositionValues != null &&
+ antennaPositionKeys.length == antennaPositionValues.length) {
+ for(int i = 0; i < antennaPositionKeys.length; i++) {
+ antennaPositionMap.put(antennaPositionKeys[i], antennaPositionValues[i]);
+ }
+ }
+
+ return new SatelliteCapabilities(
+ Arrays.stream(radioTechnologies)
+ .map(SatelliteServiceUtils::fromSatelliteRadioTechnology)
+ .boxed().collect(Collectors.toSet()),
+ capabilities.isPointingRequired, capabilities.maxBytesPerOutgoingDatagram,
+ antennaPositionMap);
+ }
+
+ /**
+ * Convert PointingInfo from service definition to framework definition.
+ * @param pointingInfo The PointingInfo from the satellite service.
+ * @return The converted PointingInfo for the framework.
+ */
+ @Nullable public static PointingInfo fromPointingInfo(
+ android.telephony.satellite.stub.PointingInfo pointingInfo) {
+ if (pointingInfo == null) return null;
+ return new PointingInfo(pointingInfo.satelliteAzimuth, pointingInfo.satelliteElevation);
+ }
+
+ /**
+ * Convert SatelliteDatagram from service definition to framework definition.
+ * @param datagram The SatelliteDatagram from the satellite service.
+ * @return The converted SatelliteDatagram for the framework.
+ */
+ @Nullable public static SatelliteDatagram fromSatelliteDatagram(
+ android.telephony.satellite.stub.SatelliteDatagram datagram) {
+ if (datagram == null) return null;
+ byte[] data = datagram.data == null ? new byte[0] : datagram.data;
+ return new SatelliteDatagram(data);
+ }
+
+ /**
+ * Convert SatelliteDatagram from framework definition to service definition.
+ * @param datagram The SatelliteDatagram from the framework.
+ * @return The converted SatelliteDatagram for the satellite service.
+ */
+ @Nullable public static android.telephony.satellite.stub.SatelliteDatagram toSatelliteDatagram(
+ @Nullable SatelliteDatagram datagram) {
+ android.telephony.satellite.stub.SatelliteDatagram converted =
+ new android.telephony.satellite.stub.SatelliteDatagram();
+ converted.data = datagram.getSatelliteDatagram();
+ return converted;
+ }
+
+ /**
+ * Get the {@link SatelliteManager.SatelliteError} from the provided result.
+ *
+ * @param ar AsyncResult used to determine the error code.
+ * @param caller The satellite request.
+ *
+ * @return The {@link SatelliteManager.SatelliteError} error code from the request.
+ */
+ @SatelliteManager.SatelliteError public static int getSatelliteError(@NonNull AsyncResult ar,
+ @NonNull String caller) {
+ int errorCode;
+ if (ar.exception == null) {
+ errorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+ } else {
+ errorCode = SatelliteManager.SATELLITE_ERROR;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error = ((CommandException) ar.exception).getCommandError();
+ errorCode = RILUtils.convertToSatelliteError(error);
+ loge(caller + " CommandException: " + ar.exception);
+ } else if (ar.exception instanceof SatelliteManager.SatelliteException) {
+ errorCode = ((SatelliteManager.SatelliteException) ar.exception).getErrorCode();
+ loge(caller + " SatelliteException: " + ar.exception);
+ } else {
+ loge(caller + " unknown exception: " + ar.exception);
+ }
+ }
+ logd(caller + " error: " + errorCode);
+ return errorCode;
+ }
+
+ /**
+ * Get valid subscription id for satellite communication.
+ *
+ * @param subId The subscription id.
+ * @return input subId if the subscription is active else return default subscription id.
+ */
+ public static int getValidSatelliteSubId(int subId, @NonNull Context context) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ boolean isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId,
+ context.getOpPackageName(), context.getAttributionTag());
+
+ if (isActive) {
+ return subId;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ logd("getValidSatelliteSubId: use DEFAULT_SUBSCRIPTION_ID for subId=" + subId);
+ return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ }
+
+ /**
+ * Return phone associated with phoneId 0.
+ *
+ * @return phone associated with phoneId 0 or {@code null} if it doesn't exist.
+ */
+ public static @Nullable Phone getPhone() {
+ return PhoneFactory.getPhone(0);
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(@NonNull String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
new file mode 100644
index 0000000..36ad250
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.telephony.Rlog;
+import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.stub.ISatelliteGateway;
+import android.telephony.satellite.stub.SatelliteGatewayService;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This module is responsible for managing session state transition and inform listeners of modem
+ * state changed events accordingly.
+ */
+public class SatelliteSessionController extends StateMachine {
+ private static final String TAG = "SatelliteSessionController";
+ private static final boolean DBG = true;
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final boolean DEBUG = !"user".equals(Build.TYPE);
+
+ /**
+ * The time duration in millis that the satellite will stay at listening mode to wait for the
+ * next incoming page before disabling listening mode when transitioning from sending mode.
+ */
+ public static final String SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS =
+ "satellite_stay_at_listening_from_sending_millis";
+ /**
+ * The default value of {@link #SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS}.
+ */
+ public static final long DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS = 180000;
+ /**
+ * The time duration in millis that the satellite will stay at listening mode to wait for the
+ * next incoming page before disabling listening mode when transitioning from receiving mode.
+ */
+ public static final String SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS =
+ "satellite_stay_at_listening_from_receiving_millis";
+ /**
+ * The default value of {@link #SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS}
+ */
+ public static final long DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS = 30000;
+ /**
+ * The default value of {@link #SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS},
+ * and {@link #SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS} for demo mode
+ */
+ public static final long DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS = 3000;
+
+ private static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 1;
+ private static final int EVENT_LISTENING_TIMER_TIMEOUT = 2;
+ private static final int EVENT_SATELLITE_ENABLED_STATE_CHANGED = 3;
+
+ private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
+ private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
+ private static final int REBIND_MULTIPLIER = 2;
+ @NonNull private final ExponentialBackoff mExponentialBackoff;
+ @NonNull private final Object mLock = new Object();
+ @Nullable
+ private ISatelliteGateway mSatelliteGatewayService;
+ private String mSatelliteGatewayServicePackageName = "";
+ @Nullable private SatelliteGatewayServiceConnection mSatelliteGatewayServiceConnection;
+ private boolean mIsBound;
+ private boolean mIsBinding;
+
+ @NonNull private static SatelliteSessionController sInstance;
+
+ @NonNull private final Context mContext;
+ @NonNull private final SatelliteModemInterface mSatelliteModemInterface;
+ @NonNull private final UnavailableState mUnavailableState = new UnavailableState();
+ @NonNull private final PowerOffState mPowerOffState = new PowerOffState();
+ @NonNull private final IdleState mIdleState = new IdleState();
+ @NonNull private final TransferringState mTransferringState = new TransferringState();
+ @NonNull private final ListeningState mListeningState = new ListeningState();
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected AtomicBoolean mIsSendingTriggeredDuringTransferringState;
+ private long mSatelliteStayAtListeningFromSendingMillis;
+ private long mSatelliteStayAtListeningFromReceivingMillis;
+ private final ConcurrentHashMap<IBinder, ISatelliteStateCallback> mListeners;
+ @SatelliteManager.SatelliteModemState private int mCurrentState;
+ final boolean mIsSatelliteSupported;
+ private boolean mIsDemoMode = false;
+
+ /**
+ * @return The singleton instance of SatelliteSessionController.
+ */
+ public static SatelliteSessionController getInstance() {
+ if (sInstance == null) {
+ Log.e(TAG, "SatelliteSessionController was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the SatelliteSessionController singleton instance.
+ *
+ * @param context The Context for the SatelliteSessionController.
+ * @param looper The looper associated with the handler of this class.
+ * @param isSatelliteSupported Whether satellite is supported on the device.
+ * @return The singleton instance of SatelliteSessionController.
+ */
+ public static SatelliteSessionController make(
+ @NonNull Context context, @NonNull Looper looper, boolean isSatelliteSupported) {
+ if (sInstance == null) {
+ sInstance = new SatelliteSessionController(context, looper, isSatelliteSupported,
+ SatelliteModemInterface.getInstance(),
+ getSatelliteStayAtListeningFromSendingMillis(),
+ getSatelliteStayAtListeningFromReceivingMillis());
+ } else {
+ if (isSatelliteSupported != sInstance.mIsSatelliteSupported) {
+ Rlog.e(TAG, "New satellite support state " + isSatelliteSupported
+ + " is different from existing state " + sInstance.mIsSatelliteSupported
+ + ". Ignore the new state.");
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a SatelliteSessionController to manage satellite session.
+ *
+ * @param context The Context for the SatelliteSessionController.
+ * @param looper The looper associated with the handler of this class.
+ * @param isSatelliteSupported Whether satellite is supported on the device.
+ * @param satelliteModemInterface The singleton of SatelliteModemInterface.
+ * @param satelliteStayAtListeningFromSendingMillis The duration to stay at listening mode when
+ * transitioning from sending mode.
+ * @param satelliteStayAtListeningFromReceivingMillis The duration to stay at listening mode
+ * when transitioning from receiving mode.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected SatelliteSessionController(@NonNull Context context, @NonNull Looper looper,
+ boolean isSatelliteSupported,
+ @NonNull SatelliteModemInterface satelliteModemInterface,
+ long satelliteStayAtListeningFromSendingMillis,
+ long satelliteStayAtListeningFromReceivingMillis) {
+ super(TAG, looper);
+
+ mContext = context;
+ mSatelliteModemInterface = satelliteModemInterface;
+ mSatelliteStayAtListeningFromSendingMillis = satelliteStayAtListeningFromSendingMillis;
+ mSatelliteStayAtListeningFromReceivingMillis = satelliteStayAtListeningFromReceivingMillis;
+ mListeners = new ConcurrentHashMap<>();
+ mIsSendingTriggeredDuringTransferringState = new AtomicBoolean(false);
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+ mIsSatelliteSupported = isSatelliteSupported;
+ mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
+ REBIND_MULTIPLIER, looper, () -> {
+ synchronized (mLock) {
+ if ((mIsBound && mSatelliteGatewayService != null) || mIsBinding) {
+ return;
+ }
+ }
+ if (mSatelliteGatewayServiceConnection != null) {
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ }
+ bindService();
+ });
+
+ addState(mUnavailableState);
+ addState(mPowerOffState);
+ addState(mIdleState);
+ addState(mTransferringState);
+ addState(mListeningState, mTransferringState);
+ setInitialState(isSatelliteSupported);
+ start();
+ }
+
+ /**
+ * {@link DatagramController} uses this function to notify {@link SatelliteSessionController}
+ * that its datagram transfer state has changed.
+ *
+ * @param sendState The current datagram send state of {@link DatagramController}.
+ * @param receiveState The current datagram receive state of {@link DatagramController}.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onDatagramTransferStateChanged(
+ @SatelliteManager.SatelliteDatagramTransferState int sendState,
+ @SatelliteManager.SatelliteDatagramTransferState int receiveState) {
+ sendMessage(EVENT_DATAGRAM_TRANSFER_STATE_CHANGED,
+ new DatagramTransferState(sendState, receiveState));
+ if (sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING) {
+ mIsSendingTriggeredDuringTransferringState.set(true);
+ }
+ }
+
+ /**
+ * {@link SatelliteController} uses this function to notify {@link SatelliteSessionController}
+ * that the satellite enabled state has changed.
+ *
+ * @param enabled {@code true} means enabled and {@code false} means disabled.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onSatelliteEnabledStateChanged(boolean enabled) {
+ sendMessage(EVENT_SATELLITE_ENABLED_STATE_CHANGED, enabled);
+ }
+
+ /**
+ * Registers for modem state changed from satellite modem.
+ *
+ * @param callback The callback to handle the satellite modem state changed event.
+ */
+ public void registerForSatelliteModemStateChanged(@NonNull ISatelliteStateCallback callback) {
+ try {
+ callback.onSatelliteModemStateChanged(mCurrentState);
+ mListeners.put(callback.asBinder(), callback);
+ } catch (RemoteException ex) {
+ loge("registerForSatelliteModemStateChanged: Got RemoteException ex=" + ex);
+ }
+ }
+
+ /**
+ * Unregisters for modem state changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteModemStateChanged(ISatelliteStateCallback)}.
+ */
+ public void unregisterForSatelliteModemStateChanged(@NonNull ISatelliteStateCallback callback) {
+ mListeners.remove(callback.asBinder());
+ }
+
+ /**
+ * This API can be used by only CTS to update the timeout duration in milliseconds that
+ * satellite should stay at listening mode to wait for the next incoming page before disabling
+ * listening mode.
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ boolean setSatelliteListeningTimeoutDuration(long timeoutMillis) {
+ if (!isMockModemAllowed()) {
+ loge("Updating listening timeout duration is not allowed");
+ return false;
+ }
+
+ logd("setSatelliteListeningTimeoutDuration: timeoutMillis=" + timeoutMillis);
+ if (timeoutMillis == 0) {
+ mSatelliteStayAtListeningFromSendingMillis =
+ getSatelliteStayAtListeningFromSendingMillis();
+ mSatelliteStayAtListeningFromReceivingMillis =
+ getSatelliteStayAtListeningFromReceivingMillis();
+ } else {
+ mSatelliteStayAtListeningFromSendingMillis = timeoutMillis;
+ mSatelliteStayAtListeningFromReceivingMillis = timeoutMillis;
+ }
+
+ return true;
+ }
+
+ /**
+ * This API can be used by only CTS to update satellite gateway service package name.
+ *
+ * @param servicePackageName The package name of the satellite gateway service.
+ * @return {@code true} if the satellite gateway service is set successfully,
+ * {@code false} otherwise.
+ */
+ boolean setSatelliteGatewayServicePackageName(@Nullable String servicePackageName) {
+ if (!isMockModemAllowed()) {
+ loge("setSatelliteGatewayServicePackageName: modifying satellite gateway service "
+ + "package name is not allowed");
+ return false;
+ }
+
+ logd("setSatelliteGatewayServicePackageName: config_satellite_gateway_service_package is "
+ + "updated, new packageName=" + servicePackageName);
+
+ if (servicePackageName == null || servicePackageName.equals("null")) {
+ mSatelliteGatewayServicePackageName = "";
+ } else {
+ mSatelliteGatewayServicePackageName = servicePackageName;
+ }
+
+ if (mSatelliteGatewayServiceConnection != null) {
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ bindService();
+ }
+ return true;
+ }
+ /**
+ * Adjusts listening timeout duration when demo mode is on
+ *
+ * @param isDemoMode {@code true} : The listening timeout durations will be set to
+ * {@link #DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS}
+ * {@code false} : The listening timeout durations will be restored to
+ * production mode
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setDemoMode(boolean isDemoMode) {
+ mIsDemoMode = isDemoMode;
+ }
+
+ private boolean isDemoMode() {
+ return mIsDemoMode;
+ }
+
+ private static class DatagramTransferState {
+ @SatelliteManager.SatelliteDatagramTransferState public int sendState;
+ @SatelliteManager.SatelliteDatagramTransferState public int receiveState;
+
+ DatagramTransferState(@SatelliteManager.SatelliteDatagramTransferState int sendState,
+ @SatelliteManager.SatelliteDatagramTransferState int receiveState) {
+ this.sendState = sendState;
+ this.receiveState = receiveState;
+ }
+ }
+
+ private class UnavailableState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd("Entering UnavailableState");
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ loge("UnavailableState: receive msg " + getWhatToString(msg.what) + " unexpectedly");
+ return HANDLED;
+ }
+ }
+
+ private class PowerOffState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd("Entering PowerOffState");
+
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
+ mIsSendingTriggeredDuringTransferringState.set(false);
+ unbindService();
+ notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+ }
+
+ @Override
+ public void exit() {
+ if (DBG) logd("Exiting PowerOffState");
+ logd("Attempting to bind to SatelliteGatewayService.");
+ bindService();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("PowerOffState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+ handleSatelliteEnabledStateChanged((boolean) msg.obj);
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private void handleSatelliteEnabledStateChanged(boolean on) {
+ if (on) {
+ transitionTo(mIdleState);
+ }
+ }
+ }
+
+ private class IdleState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd("Entering IdleState");
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
+ mIsSendingTriggeredDuringTransferringState.set(false);
+ //Enable Cellular Modem scanning
+ mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, null);
+ notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("IdleState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
+ handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
+ break;
+ case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+ handleSatelliteEnabledStateChanged(!(boolean) msg.obj, "IdleState");
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private void handleEventDatagramTransferStateChanged(
+ @NonNull DatagramTransferState datagramTransferState) {
+ if ((datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING)
+ || (datagramTransferState.receiveState
+ == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING)) {
+ transitionTo(mTransferringState);
+ }
+ }
+
+ @Override
+ public void exit() {
+ if (DBG) logd("Exiting IdleState");
+ //Disable Cellular Modem Scanning
+ mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
+ }
+ }
+
+ private class TransferringState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd("Entering TransferringState");
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+ notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("TransferringState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
+ handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
+ return HANDLED;
+ case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+ handleSatelliteEnabledStateChanged(!(boolean) msg.obj, "TransferringState");
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private void handleEventDatagramTransferStateChanged(
+ @NonNull DatagramTransferState datagramTransferState) {
+ if (isSending(datagramTransferState.sendState) || isReceiving(
+ datagramTransferState.receiveState)) {
+ // Stay at transferring state.
+ } else if ((datagramTransferState.sendState
+ == SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED)
+ || (datagramTransferState.receiveState
+ == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED)) {
+ transitionTo(mIdleState);
+ } else {
+ transitionTo(mListeningState);
+ }
+ }
+ }
+
+ private class ListeningState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd("Entering ListeningState");
+
+ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
+ long timeoutMillis = updateListeningMode(true);
+ sendMessageDelayed(EVENT_LISTENING_TIMER_TIMEOUT, timeoutMillis);
+ mIsSendingTriggeredDuringTransferringState.set(false);
+ notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+ }
+
+ @Override
+ public void exit() {
+ removeMessages(EVENT_LISTENING_TIMER_TIMEOUT);
+ updateListeningMode(false);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("ListeningState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_LISTENING_TIMER_TIMEOUT:
+ transitionTo(mIdleState);
+ break;
+ case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
+ handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
+ break;
+ case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+ handleSatelliteEnabledStateChanged(!(boolean) msg.obj, "ListeningState");
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private long updateListeningMode(boolean enabled) {
+ long timeoutMillis;
+ if (mIsSendingTriggeredDuringTransferringState.get()) {
+ timeoutMillis = mSatelliteStayAtListeningFromSendingMillis;
+ } else {
+ timeoutMillis = mSatelliteStayAtListeningFromReceivingMillis;
+ }
+ mSatelliteModemInterface.requestSatelliteListeningEnabled(
+ enabled, (int) timeoutMillis, null);
+ return timeoutMillis;
+ }
+
+ private void handleEventDatagramTransferStateChanged(
+ @NonNull DatagramTransferState datagramTransferState) {
+ if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING
+ || datagramTransferState.receiveState
+ == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING) {
+ transitionTo(mTransferringState);
+ }
+ }
+ }
+
+ /**
+ * @return the string for msg.what
+ */
+ @Override
+ protected String getWhatToString(int what) {
+ String whatString;
+ switch (what) {
+ case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
+ whatString = "EVENT_DATAGRAM_TRANSFER_STATE_CHANGED";
+ break;
+ case EVENT_LISTENING_TIMER_TIMEOUT:
+ whatString = "EVENT_LISTENING_TIMER_TIMEOUT";
+ break;
+ case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+ whatString = "EVENT_SATELLITE_ENABLED_STATE_CHANGED";
+ break;
+ default:
+ whatString = "UNKNOWN EVENT " + what;
+ }
+ return whatString;
+ }
+
+ private void setInitialState(boolean isSatelliteSupported) {
+ if (isSatelliteSupported) {
+ setInitialState(mPowerOffState);
+ } else {
+ setInitialState(mUnavailableState);
+ }
+ }
+
+ private void notifyStateChangedEvent(@SatelliteManager.SatelliteModemState int state) {
+ List<ISatelliteStateCallback> toBeRemoved = new ArrayList<>();
+ mListeners.values().forEach(listener -> {
+ try {
+ listener.onSatelliteModemStateChanged(state);
+ } catch (RemoteException e) {
+ logd("notifyStateChangedEvent RemoteException: " + e);
+ toBeRemoved.add(listener);
+ }
+ });
+
+ toBeRemoved.forEach(listener -> {
+ mListeners.remove(listener.asBinder());
+ });
+ }
+
+ private void handleSatelliteEnabledStateChanged(boolean off, String caller) {
+ if (off) {
+ transitionTo(mPowerOffState);
+ } else {
+ loge(caller + ": Unexpected satellite radio powered-on state changed event");
+ }
+ }
+
+ private boolean isSending(@SatelliteManager.SatelliteDatagramTransferState int sendState) {
+ return (sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING
+ || sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS);
+ }
+
+ private boolean isReceiving(@SatelliteManager.SatelliteDatagramTransferState int receiveState) {
+ return (receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING
+ || receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS
+ || receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE);
+ }
+
+ @NonNull
+ private String getSatelliteGatewayPackageName() {
+ if (!TextUtils.isEmpty(mSatelliteGatewayServicePackageName)) {
+ return mSatelliteGatewayServicePackageName;
+ }
+ return TextUtils.emptyIfNull(mContext.getResources().getString(
+ R.string.config_satellite_gateway_service_package));
+ }
+
+ private void bindService() {
+ synchronized (mLock) {
+ if (mIsBinding || mIsBound) return;
+ mIsBinding = true;
+ }
+ mExponentialBackoff.start();
+
+ String packageName = getSatelliteGatewayPackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ loge("Unable to bind to the satellite gateway service because the package is"
+ + " undefined.");
+ // Since the package name comes from static device configs, stop retry because
+ // rebind will continue to fail without a valid package name.
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.stop();
+ return;
+ }
+ Intent intent = new Intent(SatelliteGatewayService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+
+ mSatelliteGatewayServiceConnection = new SatelliteGatewayServiceConnection();
+ try {
+ boolean success = mContext.bindService(
+ intent, mSatelliteGatewayServiceConnection, Context.BIND_AUTO_CREATE);
+ if (success) {
+ logd("Successfully bound to the satellite gateway service.");
+ } else {
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.notifyFailed();
+ loge("Error binding to the satellite gateway service. Retrying in "
+ + mExponentialBackoff.getCurrentDelay() + " ms.");
+ }
+ } catch (Exception e) {
+ synchronized (mLock) {
+ mIsBinding = false;
+ }
+ mExponentialBackoff.notifyFailed();
+ loge("Exception binding to the satellite gateway service. Retrying in "
+ + mExponentialBackoff.getCurrentDelay() + " ms. Exception: " + e);
+ }
+ }
+
+ private void unbindService() {
+ logd("unbindService");
+ mExponentialBackoff.stop();
+ mSatelliteGatewayService = null;
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
+ }
+ if (mSatelliteGatewayServiceConnection != null) {
+ mContext.unbindService(mSatelliteGatewayServiceConnection);
+ mSatelliteGatewayServiceConnection = null;
+ }
+ }
+ private class SatelliteGatewayServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logd("onServiceConnected: ComponentName=" + name);
+ synchronized (mLock) {
+ mIsBound = true;
+ mIsBinding = false;
+ }
+ mSatelliteGatewayService = ISatelliteGateway.Stub.asInterface(service);
+ mExponentialBackoff.stop();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ loge("onServiceDisconnected: Waiting for reconnect.");
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
+ }
+ mSatelliteGatewayService = null;
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ loge("onBindingDied: Unbinding and rebinding service.");
+ synchronized (mLock) {
+ mIsBound = false;
+ mIsBinding = false;
+ }
+ unbindService();
+ mExponentialBackoff.start();
+ }
+ }
+
+ private boolean isMockModemAllowed() {
+ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ private static long getSatelliteStayAtListeningFromSendingMillis() {
+ if (sInstance != null && sInstance.isDemoMode()) {
+ return DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS;
+ } else {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS,
+ DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS);
+ }
+ }
+
+ private static long getSatelliteStayAtListeningFromReceivingMillis() {
+ if (sInstance != null && sInstance.isDemoMode()) {
+ return DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS;
+ } else {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+ SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS,
+ DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
new file mode 100644
index 0000000..7a1de7c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.SatelliteStats;
+
+/**
+ * Stats to log to satellite metrics
+ */
+public class ControllerMetricsStats {
+ private static final int ADD_COUNT = 1;
+ private static final String TAG = ControllerMetricsStats.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static ControllerMetricsStats sInstance;
+
+ private final Context mContext;
+ private SatelliteStats mSatelliteStats;
+
+ private long mSatelliteOnTimeMillis;
+ private int mBatteryLevelWhenServiceOn;
+ private boolean mIsSatelliteModemOn;
+ private Boolean mIsBatteryCharged = null;
+ private int mBatteryChargedStartTimeSec;
+ private int mTotalBatteryChargeTimeSec;
+
+ /**
+ * @return The singleton instance of ControllerMetricsStats.
+ */
+ public static ControllerMetricsStats getInstance() {
+ if (sInstance == null) {
+ loge("ControllerMetricsStats was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the ControllerMetricsStats singleton instance.
+ *
+ * @param context The Context for the ControllerMetricsStats.
+ * @return The singleton instance of ControllerMetricsStats.
+ */
+ public static ControllerMetricsStats make(@NonNull Context context) {
+ if (sInstance == null) {
+ sInstance = new ControllerMetricsStats(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the ControllerMetricsStats singleton instance, testing purpose only.
+ *
+ * @param context The Context for the ControllerMetricsStats.
+ * @param satelliteStats SatelliteStats instance to test
+ * @return The singleton instance of ControllerMetricsStats.
+ */
+ @VisibleForTesting
+ public static ControllerMetricsStats make(@NonNull Context context,
+ @NonNull SatelliteStats satelliteStats) {
+ if (sInstance == null) {
+ sInstance = new ControllerMetricsStats(context, satelliteStats);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the ControllerMetricsStats to manage metrics report for
+ * {@link SatelliteStats.SatelliteControllerParams}
+ * @param context The Context for the ControllerMetricsStats.
+ */
+ ControllerMetricsStats(@NonNull Context context) {
+ mContext = context;
+ mSatelliteStats = SatelliteStats.getInstance();
+ }
+
+ /**
+ * Create the ControllerMetricsStats to manage metrics report for
+ * {@link SatelliteStats.SatelliteControllerParams}
+ *
+ * @param context The Context for the ControllerMetricsStats.
+ * @param satelliteStats SatelliteStats object used for testing purpose
+ */
+ @VisibleForTesting
+ protected ControllerMetricsStats(@NonNull Context context,
+ @NonNull SatelliteStats satelliteStats) {
+ mContext = context;
+ mSatelliteStats = satelliteStats;
+ }
+
+
+ /** Report a counter when an attempt for satellite service on is successfully done */
+ public void reportServiceEnablementSuccessCount() {
+ logd("reportServiceEnablementSuccessCount()");
+ mSatelliteStats.onSatelliteControllerMetrics(
+ new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfSatelliteServiceEnablementsSuccess(ADD_COUNT)
+ .build());
+ }
+
+ /** Report a counter when an attempt for satellite service on is failed */
+ public void reportServiceEnablementFailCount() {
+ logd("reportServiceEnablementSuccessCount()");
+ mSatelliteStats.onSatelliteControllerMetrics(
+ new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfSatelliteServiceEnablementsFail(ADD_COUNT)
+ .build());
+ }
+
+ /** Report a counter when an attempt for outgoing datagram is successfully done */
+ public void reportOutgoingDatagramSuccessCount(
+ @NonNull @SatelliteManager.DatagramType int datagramType) {
+ SatelliteStats.SatelliteControllerParams controllerParam;
+ if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramSuccess(ADD_COUNT)
+ .setCountOfDatagramTypeSosSmsSuccess(ADD_COUNT)
+ .build();
+ } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramSuccess(ADD_COUNT)
+ .setCountOfDatagramTypeLocationSharingSuccess(ADD_COUNT)
+ .build();
+ } else { // datagramType == SatelliteManager.DATAGRAM_TYPE_UNKNOWN
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramSuccess(ADD_COUNT)
+ .build();
+ }
+ logd("reportServiceEnablementSuccessCount(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+
+ /** Report a counter when an attempt for outgoing datagram is failed */
+ public void reportOutgoingDatagramFailCount(
+ @NonNull @SatelliteManager.DatagramType int datagramType) {
+ SatelliteStats.SatelliteControllerParams controllerParam;
+ if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramFail(ADD_COUNT)
+ .setCountOfDatagramTypeSosSmsFail(ADD_COUNT)
+ .build();
+ } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramFail(ADD_COUNT)
+ .setCountOfDatagramTypeLocationSharingFail(ADD_COUNT)
+ .build();
+ } else { // datagramType == SatelliteManager.DATAGRAM_TYPE_UNKNOWN
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfOutgoingDatagramFail(ADD_COUNT)
+ .build();
+ }
+ logd("reportOutgoingDatagramFailCount(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+
+ /** Report a counter when an attempt for incoming datagram is failed */
+ public void reportIncomingDatagramCount(
+ @NonNull @SatelliteManager.SatelliteError int result) {
+ SatelliteStats.SatelliteControllerParams controllerParam;
+ if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfIncomingDatagramSuccess(ADD_COUNT)
+ .build();
+ } else {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfIncomingDatagramFail(ADD_COUNT)
+ .build();
+ }
+ logd("reportIncomingDatagramCount(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+
+ /** Report a counter when an attempt for de-provision is success or not */
+ public void reportProvisionCount(@NonNull @SatelliteManager.SatelliteError int result) {
+ SatelliteStats.SatelliteControllerParams controllerParam;
+ if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfProvisionSuccess(ADD_COUNT)
+ .build();
+ } else {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfProvisionFail(ADD_COUNT)
+ .build();
+ }
+ logd("reportProvisionCount(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+
+ /** Report a counter when an attempt for de-provision is success or not */
+ public void reportDeprovisionCount(@NonNull @SatelliteManager.SatelliteError int result) {
+ SatelliteStats.SatelliteControllerParams controllerParam;
+ if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfDeprovisionSuccess(ADD_COUNT)
+ .build();
+ } else {
+ controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfDeprovisionFail(ADD_COUNT)
+ .build();
+ }
+ logd("reportDeprovisionCount(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+
+ /** Return the total service up time for satellite service */
+ @VisibleForTesting
+ public int captureTotalServiceUpTimeSec() {
+ long totalTimeMillis = getCurrentTime() - mSatelliteOnTimeMillis;
+ mSatelliteOnTimeMillis = 0;
+ return (int) (totalTimeMillis / 1000);
+ }
+
+ /** Return the total battery charge time while satellite service is on */
+ @VisibleForTesting
+ public int captureTotalBatteryChargeTimeSec() {
+ int totalTime = mTotalBatteryChargeTimeSec;
+ mTotalBatteryChargeTimeSec = 0;
+ return totalTime;
+ }
+
+ /** Capture the satellite service on time and register battery monitor */
+ public void onSatelliteEnabled() {
+ if (!isSatelliteModemOn()) {
+ mIsSatelliteModemOn = true;
+
+ startCaptureBatteryLevel();
+
+ // log the timestamp of the satellite modem power on
+ mSatelliteOnTimeMillis = getCurrentTime();
+
+ // register broadcast receiver for monitoring battery status change
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+
+ logd("register BatteryStatusReceiver");
+ mContext.registerReceiver(mBatteryStatusReceiver, filter);
+ }
+ }
+
+ /** Capture the satellite service off time and de-register battery monitor */
+ public void onSatelliteDisabled() {
+ if (isSatelliteModemOn()) {
+ mIsSatelliteModemOn = false;
+
+ logd("unregister BatteryStatusReceiver");
+ mContext.unregisterReceiver(mBatteryStatusReceiver);
+
+ int totalServiceUpTime = captureTotalServiceUpTimeSec();
+ int batteryConsumptionPercent = captureTotalBatteryConsumptionPercent(mContext);
+ int totalBatteryChargeTime = captureTotalBatteryChargeTimeSec();
+
+ // report metrics about service up time and battery
+ SatelliteStats.SatelliteControllerParams controllerParam =
+ new SatelliteStats.SatelliteControllerParams.Builder()
+ .setTotalServiceUptimeSec(totalServiceUpTime)
+ .setTotalBatteryConsumptionPercent(batteryConsumptionPercent)
+ .setTotalBatteryChargedTimeSec(totalBatteryChargeTime)
+ .build();
+ logd("onSatelliteDisabled(): " + controllerParam);
+ mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+ }
+ }
+
+ /** Log the total battery charging time when satellite service is on */
+ private void updateSatelliteBatteryChargeTime(boolean isCharged) {
+ logd("updateSatelliteBatteryChargeTime(" + isCharged + ")");
+ // update only when the charge state has changed
+ if (mIsBatteryCharged == null || isCharged != mIsBatteryCharged) {
+ mIsBatteryCharged = isCharged;
+
+ // When charged, log the start time of battery charging
+ if (isCharged) {
+ mBatteryChargedStartTimeSec = (int) (getCurrentTime() / 1000);
+ // When discharged, log the accumulated total battery charging time.
+ } else {
+ mTotalBatteryChargeTimeSec +=
+ (int) (getCurrentTime() / 1000)
+ - mBatteryChargedStartTimeSec;
+ mBatteryChargedStartTimeSec = 0;
+ }
+ }
+ }
+
+ /** Capture the battery level when satellite service is on */
+ @VisibleForTesting
+ public void startCaptureBatteryLevel() {
+ try {
+ BatteryManager batteryManager = mContext.getSystemService(BatteryManager.class);
+ mBatteryLevelWhenServiceOn =
+ batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ logd("sBatteryLevelWhenServiceOn = " + mBatteryLevelWhenServiceOn);
+ } catch (NullPointerException e) {
+ loge("BatteryManager is null");
+ }
+ }
+
+ /** Capture the total consumption level when service is off */
+ @VisibleForTesting
+ public int captureTotalBatteryConsumptionPercent(Context context) {
+ try {
+ BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ int currentLevel =
+ batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ return Math.max((mBatteryLevelWhenServiceOn - currentLevel), 0);
+ } catch (NullPointerException e) {
+ loge("BatteryManager is null");
+ return 0;
+ }
+ }
+
+ /** Receives the battery status whether it is in charging or not, update interval is 60 sec. */
+ private final BroadcastReceiver mBatteryStatusReceiver = new BroadcastReceiver() {
+ private long mLastUpdatedTime = 0;
+ private static final long UPDATE_INTERVAL = 60 * 1000;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ long currentTime = getCurrentTime();
+ if (currentTime - mLastUpdatedTime > UPDATE_INTERVAL) {
+ mLastUpdatedTime = currentTime;
+ int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ boolean isCharged = (status == BatteryManager.BATTERY_STATUS_CHARGING);
+ logd("Battery is charged(" + isCharged + ")");
+ updateSatelliteBatteryChargeTime(isCharged);
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public boolean isSatelliteModemOn() {
+ return mIsSatelliteModemOn;
+ }
+
+ @VisibleForTesting
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+
+ private static void logd(@NonNull String log) {
+ if (DBG) {
+ Log.d(TAG, log);
+ }
+ }
+
+ private static void loge(@NonNull String log) {
+ Log.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
new file mode 100644
index 0000000..38696aa
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.SatelliteStats;
+
+/**
+ * Stats to log to satellite metrics
+ */
+public class ProvisionMetricsStats {
+ private static final String TAG = ProvisionMetricsStats.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static ProvisionMetricsStats sInstance = null;
+
+ public static final int INVALID_TIME = -1;
+
+ private int mResultCode;
+ private int mProvisioningStartTimeSec;
+ private boolean mIsProvisionRequest;
+ private boolean mIsCanceled;
+
+ private ProvisionMetricsStats() {
+ initializeProvisionParams();
+ }
+
+ /**
+ * Returns the Singleton instance of ProvisionMetricsStats class.
+ * If an instance of the Singleton class has not been created,
+ * it creates a new instance and returns it. Otherwise, it returns
+ * the existing instance.
+ * @return the Singleton instance of ProvisionMetricsStats
+ */
+ public static ProvisionMetricsStats getOrCreateInstance() {
+ if (sInstance == null) {
+ logd("Create new ProvisionMetricsStats.");
+ sInstance = new ProvisionMetricsStats();
+ }
+ return sInstance;
+ }
+
+ /** Sets the resultCode for provision metrics */
+ public ProvisionMetricsStats setResultCode(@SatelliteManager.SatelliteError int error) {
+ mResultCode = error;
+ return this;
+ }
+
+ /** Sets the start time of provisioning */
+ public void setProvisioningStartTime() {
+ mProvisioningStartTimeSec = (int) (System.currentTimeMillis() / 1000);
+ }
+
+ /** Sets the isProvisionRequest to indicate whether provision or de-provision */
+ public ProvisionMetricsStats setIsProvisionRequest(boolean isProvisionRequest) {
+ mIsProvisionRequest = isProvisionRequest;
+ return this;
+ }
+
+ /** Sets the isCanceled to know whether the provision is canceled */
+ public ProvisionMetricsStats setIsCanceled(boolean isCanceled) {
+ mIsCanceled = isCanceled;
+ return this;
+ }
+
+ /** Report the provision metrics atoms to PersistAtomsStorage in telephony */
+ public void reportProvisionMetrics() {
+ SatelliteStats.SatelliteProvisionParams provisionParams =
+ new SatelliteStats.SatelliteProvisionParams.Builder()
+ .setResultCode(mResultCode)
+ .setProvisioningTimeSec((int)
+ (System.currentTimeMillis() / 1000) - mProvisioningStartTimeSec)
+ .setIsProvisionRequest(mIsProvisionRequest)
+ .setIsCanceled(mIsCanceled)
+ .build();
+ SatelliteStats.getInstance().onSatelliteProvisionMetrics(provisionParams);
+ logd(provisionParams.toString());
+ initializeProvisionParams();
+ }
+
+ private void initializeProvisionParams() {
+ mResultCode = -1;
+ mProvisioningStartTimeSec = INVALID_TIME;
+ mIsProvisionRequest = false;
+ mIsCanceled = false;
+ }
+
+ private static void logd(@NonNull String log) {
+ if (DBG) {
+ Log.d(TAG, log);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
new file mode 100644
index 0000000..776ba64
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.SatelliteStats;
+
+/**
+ * Stats to log to satellite session metrics
+ */
+public class SessionMetricsStats {
+ private static final String TAG = SessionMetricsStats.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static SessionMetricsStats sInstance = null;
+ private @SatelliteManager.SatelliteError int mInitializationResult;
+ private @SatelliteManager.NTRadioTechnology int mRadioTechnology;
+
+ private SessionMetricsStats() {
+ initializeSessionMetricsParam();
+ }
+
+ /**
+ * Returns the Singleton instance of SessionMetricsStats class.
+ * If an instance of the Singleton class has not been created,
+ * it creates a new instance and returns it. Otherwise, it returns
+ * the existing instance.
+ * @return the Singleton instance of SessionMetricsStats
+ */
+ public static SessionMetricsStats getInstance() {
+ if (sInstance == null) {
+ loge("create new SessionMetricsStats.");
+ sInstance = new SessionMetricsStats();
+ }
+ return sInstance;
+ }
+
+ /** Sets the satellite initialization result */
+ public SessionMetricsStats setInitializationResult(
+ @SatelliteManager.SatelliteError int result) {
+ logd("setInitializationResult(" + result + ")");
+ mInitializationResult = result;
+ return this;
+ }
+
+ /** Sets the satellite ratio technology */
+ public SessionMetricsStats setRadioTechnology(
+ @SatelliteManager.NTRadioTechnology int radioTechnology) {
+ logd("setRadioTechnology(" + radioTechnology + ")");
+ mRadioTechnology = radioTechnology;
+ return this;
+ }
+
+ /** Report the session metrics atoms to PersistAtomsStorage in telephony */
+ public void reportSessionMetrics() {
+ SatelliteStats.SatelliteSessionParams sessionParams =
+ new SatelliteStats.SatelliteSessionParams.Builder()
+ .setSatelliteServiceInitializationResult(mInitializationResult)
+ .setSatelliteTechnology(mRadioTechnology)
+ .build();
+ logd(sessionParams.toString());
+ SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
+ initializeSessionMetricsParam();
+ }
+
+ private void initializeSessionMetricsParam() {
+ mInitializationResult = SatelliteManager.SATELLITE_ERROR_NONE;
+ mRadioTechnology = SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
+ }
+
+ private static void logd(@NonNull String log) {
+ if (DBG) {
+ Log.d(TAG, log);
+ }
+ }
+
+ private static void loge(@NonNull String log) {
+ Log.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
new file mode 100644
index 0000000..b90dc5e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -0,0 +1,2401 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.provider.Telephony;
+import android.provider.Telephony.SimInfo;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.DataRoamingMode;
+import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.util.function.TriConsumer;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * The subscription database manager is the wrapper of {@link SimInfo}
+ * table. It's a full memory cache of the entire subscription database, and the update can be
+ * asynchronously or synchronously. The database's cache allows multi threads to read
+ * simultaneously, if no write is ongoing.
+ *
+ * Note that from Android 14, directly writing into the subscription database through content
+ * resolver with {@link SimInfo#CONTENT_URI} will cause cache/db out of sync. All the read/write
+ * to the database should go through {@link SubscriptionManagerService}.
+ */
+public class SubscriptionDatabaseManager extends Handler {
+ private static final String LOG_TAG = "SDMGR";
+
+ /** Whether enabling verbose debugging message or not. */
+ private static final boolean VDBG = false;
+
+ /** Invalid database row index. */
+ private static final int INVALID_ROW_INDEX = -1;
+
+ /** The mapping from {@link SimInfo} table to {@link SubscriptionInfoInternal} get methods. */
+ private static final Map<String, Function<SubscriptionInfoInternal, ?>>
+ SUBSCRIPTION_GET_METHOD_MAP = Map.ofEntries(
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionInfoInternal::getSubscriptionId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ICC_ID,
+ SubscriptionInfoInternal::getIccId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SIM_SLOT_INDEX,
+ SubscriptionInfoInternal::getSimSlotIndex),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_DISPLAY_NAME,
+ SubscriptionInfoInternal::getDisplayName),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARRIER_NAME,
+ SubscriptionInfoInternal::getCarrierName),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NAME_SOURCE,
+ SubscriptionInfoInternal::getDisplayNameSource),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_COLOR,
+ SubscriptionInfoInternal::getIconTint),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NUMBER,
+ SubscriptionInfoInternal::getNumber),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_DATA_ROAMING,
+ SubscriptionInfoInternal::getDataRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_MCC_STRING,
+ SubscriptionInfoInternal::getMcc),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_MNC_STRING,
+ SubscriptionInfoInternal::getMnc),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_EHPLMNS,
+ SubscriptionInfoInternal::getEhplmns),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_HPLMNS,
+ SubscriptionInfoInternal::getHplmns),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_EMBEDDED,
+ SubscriptionInfoInternal::getEmbedded),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARD_ID,
+ SubscriptionInfoInternal::getCardString),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ACCESS_RULES,
+ SubscriptionInfoInternal::getNativeAccessRules),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ SubscriptionInfoInternal::getCarrierConfigAccessRules),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_REMOVABLE,
+ SubscriptionInfoInternal::getRemovableEmbedded),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastExtremeThreatAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastSevereThreatAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_AMBER_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastAmberAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_EMERGENCY_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastEmergencyAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
+ SubscriptionInfoInternal::getCellBroadcastAlertSoundDuration),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
+ SubscriptionInfoInternal::getCellBroadcastAlertReminderInterval),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_VIBRATE,
+ SubscriptionInfoInternal::getCellBroadcastAlertVibrationEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_SPEECH,
+ SubscriptionInfoInternal::getCellBroadcastAlertSpeechEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastEtwsTestAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastAreaInfoMessageEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_CMAS_TEST_ALERT,
+ SubscriptionInfoInternal::getCellBroadcastTestAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_OPT_OUT_DIALOG,
+ SubscriptionInfoInternal::getCellBroadcastOptOutDialogEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+ SubscriptionInfoInternal::getEnhanced4GModeEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_VT_IMS_ENABLED,
+ SubscriptionInfoInternal::getVideoTelephonyEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ENABLED,
+ SubscriptionInfoInternal::getWifiCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_MODE,
+ SubscriptionInfoInternal::getWifiCallingMode),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ SubscriptionInfoInternal::getWifiCallingModeForRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+ SubscriptionInfoInternal::getWifiCallingEnabledForRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_OPPORTUNISTIC,
+ SubscriptionInfoInternal::getOpportunistic),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_GROUP_UUID,
+ SubscriptionInfoInternal::getGroupUuid),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ISO_COUNTRY_CODE,
+ SubscriptionInfoInternal::getCountryIso),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARRIER_ID,
+ SubscriptionInfoInternal::getCarrierId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PROFILE_CLASS,
+ SubscriptionInfoInternal::getProfileClass),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SUBSCRIPTION_TYPE,
+ SubscriptionInfoInternal::getSubscriptionType),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_GROUP_OWNER,
+ SubscriptionInfoInternal::getGroupOwner),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ SubscriptionInfoInternal::getEnabledMobileDataPolicies),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IMSI,
+ SubscriptionInfoInternal::getImsi),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+ SubscriptionInfoInternal::getUiccApplicationsEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+ SubscriptionInfoInternal::getRcsUceEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+ SubscriptionInfoInternal::getCrossSimCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_RCS_CONFIG,
+ SubscriptionInfoInternal::getRcsConfig),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ SubscriptionInfoInternal::getAllowedNetworkTypesForReasons),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_D2D_STATUS_SHARING,
+ SubscriptionInfoInternal::getDeviceToDeviceStatusSharingPreference),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+ SubscriptionInfoInternal::getVoImsOptInEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ SubscriptionInfoInternal::getDeviceToDeviceStatusSharingContacts),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ SubscriptionInfoInternal::getNrAdvancedCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+ SubscriptionInfoInternal::getNumberFromCarrier),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
+ SubscriptionInfoInternal::getNumberFromIms),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PORT_INDEX,
+ SubscriptionInfoInternal::getPortIndex),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_USAGE_SETTING,
+ SubscriptionInfoInternal::getUsageSetting),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_TP_MESSAGE_REF,
+ SubscriptionInfoInternal::getLastUsedTPMessageReference),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_USER_HANDLE,
+ SubscriptionInfoInternal::getUserId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENABLED,
+ SubscriptionInfoInternal::getSatelliteEnabled)
+ );
+
+ /**
+ * The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
+ * {@link SubscriptionDatabaseManager} setting integer methods.
+ */
+ private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, Integer>>
+ SUBSCRIPTION_SET_INTEGER_METHOD_MAP = Map.ofEntries(
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SIM_SLOT_INDEX,
+ SubscriptionDatabaseManager::setSimSlotIndex),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NAME_SOURCE,
+ SubscriptionDatabaseManager::setDisplayNameSource),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_COLOR,
+ SubscriptionDatabaseManager::setIconTint),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_DATA_ROAMING,
+ SubscriptionDatabaseManager::setDataRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_EMBEDDED,
+ SubscriptionDatabaseManager::setEmbedded),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_REMOVABLE,
+ SubscriptionDatabaseManager::setRemovableEmbedded),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastExtremeThreatAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastSevereThreatAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_AMBER_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastAmberAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_EMERGENCY_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastEmergencyAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
+ SubscriptionDatabaseManager::setCellBroadcastAlertSoundDuration),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
+ SubscriptionDatabaseManager::setCellBroadcastAlertReminderInterval),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_VIBRATE,
+ SubscriptionDatabaseManager::setCellBroadcastAlertVibrationEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ALERT_SPEECH,
+ SubscriptionDatabaseManager::setCellBroadcastAlertSpeechEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastEtwsTestAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastAreaInfoMessageEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_CMAS_TEST_ALERT,
+ SubscriptionDatabaseManager::setCellBroadcastTestAlertEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CB_OPT_OUT_DIALOG,
+ SubscriptionDatabaseManager::setCellBroadcastOptOutDialogEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+ SubscriptionDatabaseManager::setEnhanced4GModeEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_VT_IMS_ENABLED,
+ SubscriptionDatabaseManager::setVideoTelephonyEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ENABLED,
+ SubscriptionDatabaseManager::setWifiCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_MODE,
+ SubscriptionDatabaseManager::setWifiCallingMode),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ SubscriptionDatabaseManager::setWifiCallingModeForRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+ SubscriptionDatabaseManager::setWifiCallingEnabledForRoaming),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IS_OPPORTUNISTIC,
+ SubscriptionDatabaseManager::setOpportunistic),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARRIER_ID,
+ SubscriptionDatabaseManager::setCarrierId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PROFILE_CLASS,
+ SubscriptionDatabaseManager::setProfileClass),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SUBSCRIPTION_TYPE,
+ SubscriptionDatabaseManager::setSubscriptionType),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+ SubscriptionDatabaseManager::setUiccApplicationsEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+ SubscriptionDatabaseManager::setRcsUceEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+ SubscriptionDatabaseManager::setCrossSimCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_D2D_STATUS_SHARING,
+ SubscriptionDatabaseManager::setDeviceToDeviceStatusSharingPreference),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+ SubscriptionDatabaseManager::setVoImsOptInEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ SubscriptionDatabaseManager::setNrAdvancedCallingEnabled),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PORT_INDEX,
+ SubscriptionDatabaseManager::setPortIndex),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_USAGE_SETTING,
+ SubscriptionDatabaseManager::setUsageSetting),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_TP_MESSAGE_REF,
+ SubscriptionDatabaseManager::setLastUsedTPMessageReference),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_USER_HANDLE,
+ SubscriptionDatabaseManager::setUserId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENABLED,
+ SubscriptionDatabaseManager::setSatelliteEnabled)
+ );
+
+ /**
+ * The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
+ * {@link SubscriptionDatabaseManager} setting string methods.
+ */
+ private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, String>>
+ SUBSCRIPTION_SET_STRING_METHOD_MAP = Map.ofEntries(
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ICC_ID,
+ SubscriptionDatabaseManager::setIccId),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_DISPLAY_NAME,
+ SubscriptionDatabaseManager::setDisplayName),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARRIER_NAME,
+ SubscriptionDatabaseManager::setCarrierName),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_NUMBER,
+ SubscriptionDatabaseManager::setNumber),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_MCC_STRING,
+ SubscriptionDatabaseManager::setMcc),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_MNC_STRING,
+ SubscriptionDatabaseManager::setMnc),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_EHPLMNS,
+ SubscriptionDatabaseManager::setEhplmns),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_HPLMNS,
+ SubscriptionDatabaseManager::setHplmns),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_CARD_ID,
+ SubscriptionDatabaseManager::setCardString),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_GROUP_UUID,
+ SubscriptionDatabaseManager::setGroupUuid),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ISO_COUNTRY_CODE,
+ SubscriptionDatabaseManager::setCountryIso),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_GROUP_OWNER,
+ SubscriptionDatabaseManager::setGroupOwner),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ SubscriptionDatabaseManager::setEnabledMobileDataPolicies),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_IMSI,
+ SubscriptionDatabaseManager::setImsi),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ SubscriptionDatabaseManager::setAllowedNetworkTypesForReasons),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ SubscriptionDatabaseManager::setDeviceToDeviceStatusSharingContacts),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+ SubscriptionDatabaseManager::setNumberFromCarrier),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
+ SubscriptionDatabaseManager::setNumberFromIms)
+ );
+
+ /**
+ * The mapping from columns in {@link android.provider.Telephony.SimInfo} table to
+ * {@link SubscriptionDatabaseManager} setting byte array methods.
+ */
+ private static final Map<String, TriConsumer<SubscriptionDatabaseManager, Integer, byte[]>>
+ SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP = Map.ofEntries(
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ACCESS_RULES,
+ SubscriptionDatabaseManager::setNativeAccessRules),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ SubscriptionDatabaseManager::setCarrierConfigAccessRules),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_RCS_CONFIG,
+ SubscriptionDatabaseManager::setRcsConfig)
+ );
+
+ /**
+ * The columns that should be in-sync between the subscriptions in the same group. Changing
+ * the value in those fields will automatically apply to the rest of the subscriptions in the
+ * group.
+ *
+ * @see SubscriptionManager#getSubscriptionsInGroup(ParcelUuid)
+ */
+ private static final Set<String> GROUP_SHARING_COLUMNS = Set.of(
+ SimInfo.COLUMN_DISPLAY_NAME,
+ SimInfo.COLUMN_NAME_SOURCE,
+ SimInfo.COLUMN_COLOR,
+ SimInfo.COLUMN_DATA_ROAMING,
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+ SimInfo.COLUMN_VT_IMS_ENABLED,
+ SimInfo.COLUMN_WFC_IMS_ENABLED,
+ SimInfo.COLUMN_WFC_IMS_MODE,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+ SimInfo.COLUMN_RCS_CONFIG,
+ SimInfo.COLUMN_D2D_STATUS_SHARING,
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ SimInfo.COLUMN_USER_HANDLE
+ );
+
+ /**
+ * The deprecated columns that do not have corresponding set methods in
+ * {@link SubscriptionDatabaseManager}.
+ */
+ private static final Set<String> DEPRECATED_DATABASE_COLUMNS = Set.of(
+ SimInfo.COLUMN_DISPLAY_NUMBER_FORMAT,
+ SimInfo.COLUMN_MCC,
+ SimInfo.COLUMN_MNC,
+ SimInfo.COLUMN_SIM_PROVISIONING_STATUS,
+ SimInfo.COLUMN_IS_METERED,
+ SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES,
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES
+ );
+
+ /** The context */
+ @NonNull
+ private final Context mContext;
+
+ /** The callback used for passing events back to {@link SubscriptionManagerService}. */
+ @NonNull
+ private final SubscriptionDatabaseManagerCallback mCallback;
+
+ /** UICC controller */
+ private final UiccController mUiccController;
+
+ /**
+ * The read/write lock to protect the entire database access. Using a Re-entrant read lock as
+ * much more read requests are expected than the write requests. All the access to
+ * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock.
+ */
+ @NonNull
+ private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
+
+ /** Indicating whether access the database asynchronously or not. */
+ private final boolean mAsyncMode;
+
+ /** Local log for most important debug messages. */
+ @NonNull
+ private final LocalLog mLocalLog = new LocalLog(128);
+
+ /**
+ * The entire subscription database, including subscriptions from inserted, previously inserted
+ * SIMs. This is the full memory cache of the subscription database. The key is the subscription
+ * id. Note all the access to this map needs to be protected by the re-entrant lock
+ * {@link #mReadWriteLock}.
+ *
+ * @see SimInfo
+ */
+ @GuardedBy("mReadWriteLock")
+ @NonNull
+ private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache =
+ new HashMap<>(16);
+
+ /** Whether database has been initialized after boot up. */
+ @GuardedBy("this")
+ private boolean mDatabaseInitialized = false;
+
+ /**
+ * This is the callback used for listening events from {@link SubscriptionDatabaseManager}.
+ */
+ public abstract static class SubscriptionDatabaseManagerCallback {
+ /** The executor of the callback. */
+ private final @NonNull Executor mExecutor;
+
+ /**
+ * Constructor
+ *
+ * @param executor The executor of the callback.
+ */
+ public SubscriptionDatabaseManagerCallback(@NonNull @CallbackExecutor Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * @return The executor of the callback.
+ */
+ @VisibleForTesting
+ public @NonNull Executor getExecutor() {
+ return mExecutor;
+ }
+
+ /**
+ * Invoke the callback from executor.
+ *
+ * @param runnable The callback method to invoke.
+ */
+ public void invokeFromExecutor(@NonNull Runnable runnable) {
+ mExecutor.execute(runnable);
+ }
+
+ /**
+ * Called when database has been initialized.
+ */
+ public abstract void onInitialized();
+
+ /**
+ * Called when subscription changed.
+ *
+ * @param subId The subscription id.
+ */
+ public abstract void onSubscriptionChanged(int subId);
+ }
+
+ /**
+ * The constructor.
+ *
+ * @param context The context.
+ * @param looper Looper for the handler.
+ * @param callback Subscription database callback.
+ */
+ public SubscriptionDatabaseManager(@NonNull Context context, @NonNull Looper looper,
+ @NonNull SubscriptionDatabaseManagerCallback callback) {
+ super(looper);
+ log("Created SubscriptionDatabaseManager.");
+ mContext = context;
+ mCallback = callback;
+ mUiccController = UiccController.getInstance();
+ mAsyncMode = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_subscription_database_async_update);
+ initializeDatabase();
+ }
+
+ /**
+ * Helper method to get specific field from {@link SubscriptionInfoInternal} by the database
+ * column name. {@link SubscriptionInfoInternal} represent one single record in the
+ * {@link SimInfo} table. So every column has a corresponding get method in
+ * {@link SubscriptionInfoInternal} (except for unused or deprecated columns).
+ *
+ * @param subInfo The subscription info.
+ * @param columnName The database column name.
+ *
+ * @return The corresponding value from {@link SubscriptionInfoInternal}.
+ *
+ * @throws IllegalArgumentException if {@code columnName} is invalid.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ */
+ @NonNull
+ private static Object getSubscriptionInfoFieldByColumnName(
+ @NonNull SubscriptionInfoInternal subInfo, @NonNull String columnName) {
+ if (SUBSCRIPTION_GET_METHOD_MAP.containsKey(columnName)) {
+ return SUBSCRIPTION_GET_METHOD_MAP.get(columnName).apply(subInfo);
+ }
+ throw new IllegalArgumentException("Invalid column name " + columnName);
+ }
+
+ /**
+ * Get a specific field from the subscription database by {@code subId} and {@code columnName}.
+ *
+ * @param subId The subscription id.
+ * @param columnName The database column name.
+ *
+ * @return The value from subscription database.
+ *
+ * @throws IllegalArgumentException if {@code subId} or {@code columnName} is invalid.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ */
+ @NonNull
+ public Object getSubscriptionProperty(int subId, @NonNull String columnName) {
+ SubscriptionInfoInternal subInfo = getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ throw new IllegalArgumentException("getSubscriptionProperty: Invalid subId " + subId
+ + ", columnName=" + columnName);
+ }
+
+ return getSubscriptionInfoFieldByColumnName(subInfo, columnName);
+ }
+
+ /**
+ * Set a field in the subscription database. Note not all fields are supported.
+ *
+ * @param subId Subscription Id of Subscription.
+ * @param columnName Column name in the database. Note not all fields are supported.
+ * @param value Value to store in the database.
+ *
+ * @throws IllegalArgumentException if {@code subId} or {@code columnName} is invalid, or
+ * {@code value} cannot be converted to the corresponding database column format.
+ * @throws NumberFormatException if a string value cannot be converted to integer.
+ * @throws ClassCastException if {@code value} cannot be casted to the required type.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ */
+ public void setSubscriptionProperty(int subId, @NonNull String columnName,
+ @NonNull Object value) {
+ if (SUBSCRIPTION_SET_INTEGER_METHOD_MAP.containsKey(columnName)) {
+ // For integer type columns, accepting both integer and string that can be converted to
+ // integer.
+ int intValue;
+ if (value instanceof String) {
+ intValue = Integer.parseInt((String) value);
+ } else if (value instanceof Integer) {
+ intValue = (int) value;
+ } else {
+ throw new ClassCastException("columnName=" + columnName + ", cannot cast "
+ + value.getClass() + " to integer.");
+ }
+ SUBSCRIPTION_SET_INTEGER_METHOD_MAP.get(columnName).accept(this, subId, intValue);
+ } else if (SUBSCRIPTION_SET_STRING_METHOD_MAP.containsKey(columnName)) {
+ // For string type columns. Will throw exception if value is not string type.
+ SUBSCRIPTION_SET_STRING_METHOD_MAP.get(columnName).accept(this, subId, (String) value);
+ } else if (SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP.containsKey(columnName)) {
+ // For byte array type columns, accepting both byte[] and string that can be converted
+ // to byte[] using base 64 encoding/decoding.
+ byte[] byteArrayValue;
+ if (value instanceof String) {
+ byteArrayValue = Base64.decode((String) value, Base64.DEFAULT);
+ } else if (value instanceof byte[]) {
+ byteArrayValue = (byte[]) value;
+ } else {
+ throw new ClassCastException("columnName=" + columnName + ", cannot cast "
+ + value.getClass() + " to byte[].");
+ }
+ SUBSCRIPTION_SET_BYTE_ARRAY_METHOD_MAP.get(columnName).accept(
+ this, subId, byteArrayValue);
+ } else {
+ throw new IllegalArgumentException("Does not support set " + columnName + ".");
+ }
+ }
+
+ /**
+ * Comparing the old/new {@link SubscriptionInfoInternal} and create content values for database
+ * update. If any field in the new subscription info is different from the old one, then each
+ * delta will be added into the {@link ContentValues}.
+ *
+ * @param oldSubInfo The old {@link SubscriptionInfoInternal}.
+ * @param newSubInfo The new {@link SubscriptionInfoInternal}.
+ *
+ * @return The delta content values for database update.
+ */
+ @NonNull
+ private ContentValues createDeltaContentValues(@Nullable SubscriptionInfoInternal oldSubInfo,
+ @NonNull SubscriptionInfoInternal newSubInfo) {
+ ContentValues deltaContentValues = new ContentValues();
+
+ for (String columnName : Telephony.SimInfo.getAllColumns()) {
+ if (DEPRECATED_DATABASE_COLUMNS.contains(columnName)) continue;
+ // subId is generated by the database. Cannot be updated.
+ if (columnName.equals(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID)) continue;
+ Object newValue = getSubscriptionInfoFieldByColumnName(newSubInfo, columnName);
+ if (newValue != null) {
+ Object oldValue = null;
+ if (oldSubInfo != null) {
+ oldValue = getSubscriptionInfoFieldByColumnName(oldSubInfo, columnName);
+ }
+ // Some columns need special handling. We need to convert them into a format that
+ // is accepted by the database.
+ if (!Objects.equals(oldValue, newValue)) {
+ deltaContentValues.putObject(columnName, newValue);
+ }
+ }
+ }
+ return deltaContentValues;
+ }
+
+ /**
+ * Synchronously insert a new record into the database. This operation is synchronous because
+ * we need to convert the inserted row index into the subscription id.
+ *
+ * @param contentValues The fields of the subscription to be inserted into the database.
+ *
+ * @return The row index of the new record. {@link #INVALID_ROW_INDEX} if insertion failed.
+ */
+ private int insertNewRecordIntoDatabaseSync(@NonNull ContentValues contentValues) {
+ Objects.requireNonNull(contentValues);
+ Uri uri = mContext.getContentResolver().insert(SimInfo.CONTENT_URI, contentValues);
+ if (uri != null && uri.getLastPathSegment() != null) {
+ int subId = Integer.parseInt(uri.getLastPathSegment());
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ logv("insertNewRecordIntoDatabaseSync: contentValues=" + contentValues);
+ logl("insertNewRecordIntoDatabaseSync: Successfully added subscription. subId="
+ + uri.getLastPathSegment());
+ return subId;
+ }
+ }
+
+ logel("insertNewRecordIntoDatabaseSync: Failed to insert subscription into database. "
+ + "contentValues=" + contentValues);
+ return INVALID_ROW_INDEX;
+ }
+
+ /**
+ * Insert a new subscription info. The subscription info must have subscription id
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. Note this is a slow method, so be
+ * cautious to call this method.
+ *
+ * @param subInfo The subscription info to update.
+ *
+ * @return The new subscription id. {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} (-1) if
+ * insertion fails.
+ */
+ public int insertSubscriptionInfo(@NonNull SubscriptionInfoInternal subInfo) {
+ Objects.requireNonNull(subInfo);
+ // A new subscription to be inserted must have invalid subscription id.
+ if (SubscriptionManager.isValidSubscriptionId(subInfo.getSubscriptionId())) {
+ throw new RuntimeException("insertSubscriptionInfo: Not a new subscription to "
+ + "insert. subInfo=" + subInfo);
+ }
+
+ synchronized (this) {
+ if (!mDatabaseInitialized) {
+ throw new IllegalStateException(
+ "Database has not been initialized. Can't insert new "
+ + "record at this point.");
+ }
+ }
+
+ int subId;
+ // Grab the write lock so no other threads can read or write the cache.
+ mReadWriteLock.writeLock().lock();
+ try {
+ // Synchronously insert into the database. Note this should be the only synchronous
+ // write operation performed by the subscription database manager. The reason is that
+ // we need to get the sub id for cache update.
+ subId = insertNewRecordIntoDatabaseSync(createDeltaContentValues(null, subInfo));
+ if (subId > 0) {
+ mAllSubscriptionInfoInternalCache.put(subId, new SubscriptionInfoInternal
+ .Builder(subInfo)
+ .setId(subId).build());
+ } else {
+ logel("insertSubscriptionInfo: Failed to insert a new subscription. subInfo="
+ + subInfo);
+ }
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+
+ mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
+ return subId;
+ }
+
+ /**
+ * Remove a subscription record from the database.
+ *
+ * @param subId The subscription id of the subscription to be deleted.
+ *
+ * @throws IllegalArgumentException If {@code subId} is invalid.
+ */
+ public void removeSubscriptionInfo(int subId) {
+ if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) {
+ throw new IllegalArgumentException("subId " + subId + " is invalid.");
+ }
+
+ mReadWriteLock.writeLock().lock();
+ try {
+ if (mContext.getContentResolver().delete(SimInfo.CONTENT_URI,
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+ new String[]{Integer.toString(subId)}) > 0) {
+ mAllSubscriptionInfoInternalCache.remove(subId);
+ } else {
+ logel("Failed to remove subscription with subId=" + subId);
+ }
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+
+ mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
+ }
+
+ /**
+ * Update a subscription in the database (synchronously or asynchronously).
+ *
+ * @param subId The subscription id of the subscription to be updated.
+ * @param contentValues The fields to be update.
+ *
+ * @return The number of rows updated. Note if the database is configured as asynchronously
+ * update, then this will be always 1.
+ */
+ private int updateDatabase(int subId, @NonNull ContentValues contentValues) {
+ logv("updateDatabase: prepare to update sub " + subId);
+
+ synchronized (this) {
+ if (!mDatabaseInitialized) {
+ logel("updateDatabase: Database has not been initialized. Can't update database at "
+ + "this point. contentValues=" + contentValues);
+ return 0;
+ }
+ }
+
+ if (mAsyncMode) {
+ // Perform the update in the handler thread asynchronously.
+ post(() -> {
+ mContext.getContentResolver().update(Uri.withAppendedPath(
+ SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null);
+ logv("updateDatabase: async updated subscription in the database."
+ + " subId=" + subId + ", contentValues= " + contentValues.getValues());
+ });
+ return 1;
+ } else {
+ logv("updateDatabase: sync updated subscription in the database."
+ + " subId=" + subId + ", contentValues= " + contentValues.getValues());
+
+ return mContext.getContentResolver().update(Uri.withAppendedPath(
+ SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null);
+ }
+ }
+
+ /**
+ * Update a certain field of subscription in the database. Also update the subscription cache
+ * {@link #mAllSubscriptionInfoInternalCache}.
+ *
+ * @param subId The subscription id.
+ * @param columnName The database column name from the database table {@link SimInfo}.
+ * @param newValue The new value to update the subscription info cache
+ * {@link #mAllSubscriptionInfoInternalCache}.
+ * @param builderSetMethod The {@link SubscriptionInfo.Builder} method to set a specific field
+ * when constructing the new {@link SubscriptionInfo}. This should be one of the
+ * SubscriptionInfoInternal.Builder.setXxxx method.
+ * @param <T> The type of newValue for subscription cache update.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ private <T> void writeDatabaseAndCacheHelper(int subId, @NonNull String columnName,
+ @Nullable T newValue,
+ BiFunction<SubscriptionInfoInternal.Builder, T, SubscriptionInfoInternal.Builder>
+ builderSetMethod) {
+ ContentValues contentValues = new ContentValues();
+
+ // Grab the write lock so no other threads can read or write the cache.
+ mReadWriteLock.writeLock().lock();
+ try {
+ final SubscriptionInfoInternal oldSubInfo =
+ mAllSubscriptionInfoInternalCache.get(subId);
+ if (oldSubInfo == null) {
+ logel("Subscription doesn't exist. subId=" + subId + ", columnName=" + columnName);
+ throw new IllegalArgumentException("Subscription doesn't exist. subId=" + subId
+ + ", columnName=" + columnName);
+ }
+
+ // Check if writing this field should automatically write to the rest of subscriptions
+ // in the same group.
+ final boolean syncToGroup = GROUP_SHARING_COLUMNS.contains(columnName);
+
+ mAllSubscriptionInfoInternalCache.forEach((id, subInfo) -> {
+ if (id == subId || (syncToGroup && !oldSubInfo.getGroupUuid().isEmpty()
+ && oldSubInfo.getGroupUuid().equals(subInfo.getGroupUuid()))) {
+ // Check if the new value is different from the old value in the cache.
+ if (!Objects.equals(getSubscriptionInfoFieldByColumnName(subInfo, columnName),
+ newValue)) {
+ logv("writeDatabaseAndCacheHelper: subId=" + subId + ",columnName="
+ + columnName + ", newValue=" + newValue);
+ // If the value is different, then we need to update the cache. Since all
+ // fields in SubscriptionInfo are final, we need to create a new
+ // SubscriptionInfo.
+ SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal
+ .Builder(subInfo);
+
+ // Apply the new value to the builder. This line is equivalent to
+ // builder.setXxxxxx(newValue);
+ builder = builderSetMethod.apply(builder, newValue);
+
+ // Prepare the content value for update.
+ contentValues.putObject(columnName, newValue);
+ if (updateDatabase(id, contentValues) > 0) {
+ // Update the subscription database cache.
+ mAllSubscriptionInfoInternalCache.put(id, builder.build());
+ mCallback.invokeFromExecutor(()
+ -> mCallback.onSubscriptionChanged(subId));
+ }
+ }
+ }
+ });
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Update the database with the {@link SubscriptionInfoInternal}, and also update the cache.
+ *
+ * @param newSubInfo The new {@link SubscriptionInfoInternal}.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void updateSubscription(@NonNull SubscriptionInfoInternal newSubInfo) {
+ Objects.requireNonNull(newSubInfo);
+
+ // Grab the write lock so no other threads can read or write the cache.
+ mReadWriteLock.writeLock().lock();
+ try {
+ int subId = newSubInfo.getSubscriptionId();
+ SubscriptionInfoInternal oldSubInfo = mAllSubscriptionInfoInternalCache.get(
+ newSubInfo.getSubscriptionId());
+ if (oldSubInfo == null) {
+ throw new IllegalArgumentException("updateSubscription: subscription does not "
+ + "exist. subId=" + subId);
+ }
+ if (oldSubInfo.equals(newSubInfo)) return;
+
+ if (updateDatabase(subId, createDeltaContentValues(oldSubInfo, newSubInfo)) > 0) {
+ mAllSubscriptionInfoInternalCache.put(subId, newSubInfo);
+ mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId));
+ }
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Set the ICCID of the SIM that is associated with the subscription.
+ *
+ * @param subId Subscription id.
+ * @param iccId The ICCID of the SIM that is associated with this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setIccId(int subId, @NonNull String iccId) {
+ Objects.requireNonNull(iccId);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ICC_ID, iccId,
+ SubscriptionInfoInternal.Builder::setIccId);
+ }
+
+ /**
+ * Set the SIM index of the slot that currently contains the subscription. Set to
+ * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+ *
+ * @param subId Subscription id.
+ * @param simSlotIndex The SIM slot index.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSimSlotIndex(int subId, int simSlotIndex) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SIM_SLOT_INDEX, simSlotIndex,
+ SubscriptionInfoInternal.Builder::setSimSlotIndex);
+ }
+
+ /**
+ * Set the name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ *
+ * @param subId Subscription id.
+ * @param displayName The display name.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setDisplayName(int subId, @NonNull String displayName) {
+ Objects.requireNonNull(displayName);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DISPLAY_NAME, displayName,
+ SubscriptionInfoInternal.Builder::setDisplayName);
+ }
+
+ /**
+ * Set the name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @param subId Subscription id.
+ * @param carrierName The carrier name.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCarrierName(int subId, @NonNull String carrierName) {
+ Objects.requireNonNull(carrierName);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_NAME, carrierName,
+ SubscriptionInfoInternal.Builder::setCarrierName);
+ }
+
+ /**
+ * Set the source of the display name.
+ *
+ * @param subId Subscription id.
+ * @param displayNameSource The source of the display name.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ *
+ * @see SubscriptionInfo#getDisplayName()
+ */
+ public void setDisplayNameSource(int subId, @SimDisplayNameSource int displayNameSource) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NAME_SOURCE, displayNameSource,
+ SubscriptionInfoInternal.Builder::setDisplayNameSource);
+ }
+
+ /**
+ * Set the color to be used for tinting the icon when displaying to the user.
+ *
+ * @param subId Subscription id.
+ * @param iconTint The color to be used for tinting the icon when displaying to the user.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setIconTint(int subId, @ColorInt int iconTint) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_COLOR, iconTint,
+ SubscriptionInfoInternal.Builder::setIconTint);
+ }
+
+ /**
+ * Set the number presented to the user identify this subscription.
+ *
+ * @param subId Subscription id.
+ * @param number the number presented to the user identify this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setNumber(int subId, @NonNull String number) {
+ Objects.requireNonNull(number);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NUMBER, number,
+ SubscriptionInfoInternal.Builder::setNumber);
+ }
+
+ /**
+ * Set whether user enables data roaming for this subscription or not.
+ *
+ * @param subId Subscription id.
+ * @param dataRoaming Data roaming mode. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setDataRoaming(int subId, @DataRoamingMode int dataRoaming) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DATA_ROAMING, dataRoaming,
+ SubscriptionInfoInternal.Builder::setDataRoaming);
+ }
+
+ /**
+ * Set the mobile country code.
+ *
+ * @param subId Subscription id.
+ * @param mcc The mobile country code.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setMcc(int subId, @NonNull String mcc) {
+ Objects.requireNonNull(mcc);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MCC_STRING, mcc,
+ SubscriptionInfoInternal.Builder::setMcc);
+ }
+
+ /**
+ * Set the mobile network code.
+ *
+ * @param subId Subscription id.
+ * @param mnc Mobile network code.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setMnc(int subId, @NonNull String mnc) {
+ Objects.requireNonNull(mnc);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MNC_STRING, mnc,
+ SubscriptionInfoInternal.Builder::setMnc);
+ }
+
+ /**
+ * Set EHPLMNs associated with the subscription.
+ *
+ * @param subId Subscription id.
+ * @param ehplmns EHPLMNs associated with the subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEhplmns(int subId, @NonNull String[] ehplmns) {
+ Objects.requireNonNull(ehplmns);
+ setEhplmns(subId, Arrays.stream(ehplmns)
+ .filter(Predicate.not(TextUtils::isEmpty))
+ .collect(Collectors.joining(",")));
+ }
+
+ /**
+ * Set EHPLMNs associated with the subscription.
+ *
+ * @param subId Subscription id.
+ * @param ehplmns EHPLMNs associated with the subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEhplmns(int subId, @NonNull String ehplmns) {
+ Objects.requireNonNull(ehplmns);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_EHPLMNS, ehplmns,
+ SubscriptionInfoInternal.Builder::setEhplmns);
+ }
+
+ /**
+ * Set HPLMNs associated with the subscription.
+ *
+ * @param subId Subscription id.
+ * @param hplmns HPLMNs associated with the subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setHplmns(int subId, @NonNull String[] hplmns) {
+ Objects.requireNonNull(hplmns);
+ setHplmns(subId, Arrays.stream(hplmns)
+ .filter(Predicate.not(TextUtils::isEmpty))
+ .collect(Collectors.joining(",")));
+ }
+
+ /**
+ * Set HPLMNs associated with the subscription.
+ *
+ * @param subId Subscription id.
+ * @param hplmns HPLMNs associated with the subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setHplmns(int subId, @NonNull String hplmns) {
+ Objects.requireNonNull(hplmns);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_HPLMNS, hplmns,
+ SubscriptionInfoInternal.Builder::setHplmns);
+ }
+
+ /**
+ * Set whether the subscription is from eSIM or not.
+ *
+ * @param subId Subscription id.
+ * @param isEmbedded if the subscription is from eSIM.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEmbedded(int subId, int isEmbedded) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_EMBEDDED, isEmbedded,
+ SubscriptionInfoInternal.Builder::setEmbedded);
+ }
+
+ /**
+ * Set whether the subscription is from eSIM or not.
+ *
+ * @param subId Subscription id.
+ * @param isEmbedded {@code true} if the subscription is from eSIM.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEmbedded(int subId, boolean isEmbedded) {
+ setEmbedded(subId, isEmbedded ? 1 : 0);
+ }
+
+ /**
+ * Set the card string of the SIM card. This is usually the ICCID or EID.
+ *
+ * @param subId Subscription id.
+ * @param cardString The card string of the SIM card.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ *
+ * @see SubscriptionInfo#getCardString()
+ */
+ public void setCardString(int subId, @NonNull String cardString) {
+ Objects.requireNonNull(cardString);
+ // Also update the public card id.
+ setCardId(subId, mUiccController.convertToPublicCardId(cardString));
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARD_ID, cardString,
+ SubscriptionInfoInternal.Builder::setCardString);
+ }
+
+ /**
+ * Set the card id. This is the non-PII card id converted from
+ * {@link SubscriptionInfoInternal#getCardString()}. This field only exists in
+ * {@link SubscriptionInfo}, but not the database.
+ *
+ * @param subId Subscription id.
+ * @param cardId The card id.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCardId(int subId, int cardId) {
+ // card id does not have a corresponding SimInfo column. So we only update the cache.
+
+ // Grab the write lock so no other threads can read or write the cache.
+ mReadWriteLock.writeLock().lock();
+ try {
+ SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
+ if (subInfoCache == null) {
+ throw new IllegalArgumentException("setCardId: Subscription doesn't exist. subId="
+ + subId);
+ }
+ mAllSubscriptionInfoInternalCache.put(subId,
+ new SubscriptionInfoInternal.Builder(subInfoCache)
+ .setCardId(cardId).build());
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Set the native access rules for this subscription, if it is embedded and defines any.
+ * This does not include access rules for non-embedded subscriptions.
+ *
+ * @param subId Subscription id.
+ * @param nativeAccessRules The native access rules for this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setNativeAccessRules(int subId, @NonNull byte[] nativeAccessRules) {
+ Objects.requireNonNull(nativeAccessRules);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES, nativeAccessRules,
+ SubscriptionInfoInternal.Builder::setNativeAccessRules);
+ }
+
+ /**
+ * Set the carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ *
+ * @param subId Subscription id.
+ * @param carrierConfigAccessRules The carrier certificates for this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCarrierConfigAccessRules(int subId, @NonNull byte[] carrierConfigAccessRules) {
+ Objects.requireNonNull(carrierConfigAccessRules);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ carrierConfigAccessRules,
+ SubscriptionInfoInternal.Builder::setCarrierConfigAccessRules);
+ }
+
+ /**
+ * Set the carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ *
+ * @param subId Subscription id.
+ * @param carrierConfigAccessRules The carrier certificates for this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCarrierConfigAccessRules(int subId,
+ @NonNull UiccAccessRule[] carrierConfigAccessRules) {
+ Objects.requireNonNull(carrierConfigAccessRules);
+ byte[] carrierConfigAccessRulesBytes = UiccAccessRule.encodeRules(carrierConfigAccessRules);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ carrierConfigAccessRulesBytes,
+ SubscriptionInfoInternal.Builder::setCarrierConfigAccessRules);
+ }
+
+ /**
+ * Set whether an embedded subscription is on a removable card. Such subscriptions are
+ * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful for embedded subscription.
+ *
+ * @param subId Subscription id.
+ * @param isRemovableEmbedded if the subscription is from the removable embedded SIM.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setRemovableEmbedded(int subId, int isRemovableEmbedded) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_REMOVABLE, isRemovableEmbedded,
+ SubscriptionInfoInternal.Builder::setRemovableEmbedded);
+ }
+
+ /**
+ * Set whether cell broadcast extreme threat alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isExtremeThreatAlertEnabled whether cell broadcast extreme threat alert is enabled by
+ * the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastExtremeThreatAlertEnabled(int subId,
+ int isExtremeThreatAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
+ isExtremeThreatAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastExtremeThreatAlertEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast severe threat alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isSevereThreatAlertEnabled whether cell broadcast severe threat alert is enabled by
+ * the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastSevereThreatAlertEnabled(int subId,
+ int isSevereThreatAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
+ isSevereThreatAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastSevereThreatAlertEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast amber alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isAmberAlertEnabled whether cell broadcast amber alert is enabled by
+ * the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAmberAlertEnabled(int subId, int isAmberAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_AMBER_ALERT, isAmberAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAmberAlertEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast emergency alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isEmergencyAlertEnabled whether cell broadcast emergency alert is enabled by
+ * the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastEmergencyAlertEnabled(int subId,
+ int isEmergencyAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_EMERGENCY_ALERT,
+ isEmergencyAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastEmergencyAlertEnabled);
+ }
+
+ /**
+ * Set cell broadcast alert sound duration.
+ *
+ * @param subId Subscription id.
+ * @param alertSoundDuration Alert sound duration in seconds.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAlertSoundDuration(int subId, int alertSoundDuration) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
+ alertSoundDuration,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAlertSoundDuration);
+ }
+
+ /**
+ * Set cell broadcast alert reminder interval.
+ *
+ * @param subId Subscription id.
+ * @param reminderInterval Alert reminder interval in milliseconds.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAlertReminderInterval(int subId, int reminderInterval) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
+ reminderInterval,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAlertReminderInterval);
+ }
+
+ /**
+ * Set whether cell broadcast alert vibration is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isAlertVibrationEnabled whether cell broadcast alert vibration is enabled by the user
+ * or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAlertVibrationEnabled(int subId, int isAlertVibrationEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_VIBRATE, isAlertVibrationEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAlertVibrationEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast alert speech is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isAlertSpeechEnabled whether cell broadcast alert speech is enabled by the user or
+ * not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAlertSpeechEnabled(int subId, int isAlertSpeechEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ALERT_SPEECH, isAlertSpeechEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAlertSpeechEnabled);
+ }
+
+ /**
+ * Set whether ETWS test alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isEtwsTestAlertEnabled whether cell broadcast ETWS test alert is enabled by the user
+ * or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastEtwsTestAlertEnabled(int subId, int isEtwsTestAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
+ isEtwsTestAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastEtwsTestAlertEnabled);
+ }
+
+ /**
+ * Set whether area info message is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isAreaInfoMessageEnabled whether cell broadcast area info message is enabled by the
+ * user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastAreaInfoMessageEnabled(int subId, int isAreaInfoMessageEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
+ isAreaInfoMessageEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastAreaInfoMessageEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast test alert is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isTestAlertEnabled whether cell broadcast test alert is enabled by the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastTestAlertEnabled(int subId, int isTestAlertEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_CMAS_TEST_ALERT, isTestAlertEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastTestAlertEnabled);
+ }
+
+ /**
+ * Set whether cell broadcast opt-out dialog should be shown or not.
+ *
+ * @param subId Subscription id.
+ * @param isOptOutDialogEnabled whether cell broadcast opt-out dialog should be shown or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCellBroadcastOptOutDialogEnabled(int subId, int isOptOutDialogEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CB_OPT_OUT_DIALOG, isOptOutDialogEnabled,
+ SubscriptionInfoInternal.Builder::setCellBroadcastOptOutDialogEnabled);
+ }
+
+ /**
+ * Set whether enhanced 4G mode is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isEnhanced4GModeEnabled whether enhanced 4G mode is enabled by the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEnhanced4GModeEnabled(int subId, int isEnhanced4GModeEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+ isEnhanced4GModeEnabled,
+ SubscriptionInfoInternal.Builder::setEnhanced4GModeEnabled);
+ }
+
+ /**
+ * Set whether video telephony is enabled by the user or not.
+ *
+ * @param subId Subscription id.
+ * @param isVideoTelephonyEnabled whether video telephony is enabled by the user or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setVideoTelephonyEnabled(int subId, int isVideoTelephonyEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VT_IMS_ENABLED, isVideoTelephonyEnabled,
+ SubscriptionInfoInternal.Builder::setVideoTelephonyEnabled);
+ }
+
+ /**
+ * Set whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+ *
+ * @param subId Subscription id.
+ * @param isWifiCallingEnabled whether Wi-Fi calling is enabled by the user or not when the
+ * device is not roaming.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setWifiCallingEnabled(int subId, int isWifiCallingEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ENABLED, isWifiCallingEnabled,
+ SubscriptionInfoInternal.Builder::setWifiCallingEnabled);
+ }
+
+ /**
+ * Set Wi-Fi calling mode when the device is not roaming.
+ *
+ * @param subId Subscription id.
+ * @param wifiCallingMode Wi-Fi calling mode when the device is not roaming.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setWifiCallingMode(int subId,
+ @ImsMmTelManager.WiFiCallingMode int wifiCallingMode) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_MODE, wifiCallingMode,
+ SubscriptionInfoInternal.Builder::setWifiCallingMode);
+ }
+
+ /**
+ * Set Wi-Fi calling mode when the device is roaming.
+ *
+ * @param subId Subscription id.
+ * @param wifiCallingModeForRoaming Wi-Fi calling mode when the device is roaming.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setWifiCallingModeForRoaming(int subId,
+ @ImsMmTelManager.WiFiCallingMode int wifiCallingModeForRoaming) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ wifiCallingModeForRoaming,
+ SubscriptionInfoInternal.Builder::setWifiCallingModeForRoaming);
+ }
+
+ /**
+ * Set whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+ *
+ * @param subId Subscription id.
+ * @param isWifiCallingEnabledForRoaming whether Wi-Fi calling is enabled by the user or not
+ * when the device is roaming.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setWifiCallingEnabledForRoaming(int subId, int isWifiCallingEnabledForRoaming) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+ isWifiCallingEnabledForRoaming,
+ SubscriptionInfoInternal.Builder::setWifiCallingEnabledForRoaming);
+ }
+
+ /**
+ * Set whether the subscription is opportunistic or not.
+ *
+ * @param subId Subscription id.
+ * @param isOpportunistic if the subscription is opportunistic.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setOpportunistic(int subId, boolean isOpportunistic) {
+ setOpportunistic(subId, isOpportunistic ? 1 : 0);
+ }
+
+ /**
+ * Set whether the subscription is opportunistic or not.
+ *
+ * @param subId Subscription id.
+ * @param isOpportunistic if the subscription is opportunistic.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setOpportunistic(int subId, int isOpportunistic) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_OPPORTUNISTIC, isOpportunistic,
+ SubscriptionInfoInternal.Builder::setOpportunistic);
+ }
+
+ /**
+ * Set the group UUID of the subscription group.
+ *
+ * @param subId Subscription id.
+ * @param groupUuid The group UUID.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ *
+ * @see SubscriptionInfo#getGroupUuid()
+ */
+ public void setGroupUuid(int subId, @NonNull String groupUuid) {
+ Objects.requireNonNull(groupUuid);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_UUID, groupUuid,
+ SubscriptionInfoInternal.Builder::setGroupUuid);
+ }
+
+ /**
+ * Set the ISO Country code for the subscription's provider.
+ *
+ * @param subId Subscription id.
+ * @param countryIso The ISO country code for the subscription's provider.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCountryIso(int subId, @NonNull String countryIso) {
+ Objects.requireNonNull(countryIso);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ISO_COUNTRY_CODE, countryIso,
+ SubscriptionInfoInternal.Builder::setCountryIso);
+ }
+
+ /**
+ * Set the subscription carrier id.
+ *
+ * @param subId Subscription id.
+ * @param carrierId The carrier id.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ public void setCarrierId(int subId, int carrierId) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_ID, carrierId,
+ SubscriptionInfoInternal.Builder::setCarrierId);
+ }
+
+ /**
+ * Set the profile class populated from the profile metadata if present.
+ *
+ * @param subId Subscription id.
+ * @param profileClass the profile class populated from the profile metadata if present.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ *
+ * @see SubscriptionInfo#getProfileClass()
+ */
+ public void setProfileClass(int subId, @ProfileClass int profileClass) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PROFILE_CLASS, profileClass,
+ SubscriptionInfoInternal.Builder::setProfileClass);
+ }
+
+ /**
+ * Set the subscription type.
+ *
+ * @param subId Subscription id.
+ * @param type Subscription type.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSubscriptionType(int subId, @SubscriptionType int type) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SUBSCRIPTION_TYPE, type,
+ SubscriptionInfoInternal.Builder::setType);
+ }
+
+ /**
+ * Set the owner package of group the subscription belongs to.
+ *
+ * @param subId Subscription id.
+ * @param groupOwner Owner package of group the subscription belongs to.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setGroupOwner(int subId, @NonNull String groupOwner) {
+ Objects.requireNonNull(groupOwner);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_OWNER, groupOwner,
+ SubscriptionInfoInternal.Builder::setGroupOwner);
+ }
+
+ /**
+ * Set the enabled mobile data policies.
+ *
+ * @param subId Subscription id.
+ * @param enabledMobileDataPolicies The enabled mobile data policies.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setEnabledMobileDataPolicies(int subId, @NonNull String enabledMobileDataPolicies) {
+ Objects.requireNonNull(enabledMobileDataPolicies);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ enabledMobileDataPolicies,
+ SubscriptionInfoInternal.Builder::setEnabledMobileDataPolicies);
+ }
+
+ /**
+ * Set the IMSI (International Mobile Subscriber Identity) of the subscription.
+ *
+ * @param subId Subscription id.
+ * @param imsi The IMSI.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setImsi(int subId, @NonNull String imsi) {
+ Objects.requireNonNull(imsi);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMSI, imsi,
+ SubscriptionInfoInternal.Builder::setImsi);
+ }
+
+ /**
+ * Set whether Uicc applications are configured to enable or not.
+ *
+ * @param subId Subscription id.
+ * @param areUiccApplicationsEnabled if Uicc applications are configured to enable.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setUiccApplicationsEnabled(int subId, boolean areUiccApplicationsEnabled) {
+ setUiccApplicationsEnabled(subId, areUiccApplicationsEnabled ? 1 : 0);
+ }
+
+ /**
+ * Set whether Uicc applications are configured to enable or not.
+ *
+ * @param subId Subscription id.
+ * @param areUiccApplicationsEnabled if Uicc applications are configured to enable.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setUiccApplicationsEnabled(int subId, int areUiccApplicationsEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+ areUiccApplicationsEnabled,
+ SubscriptionInfoInternal.Builder::setUiccApplicationsEnabled);
+ }
+
+ /**
+ * Set whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ *
+ * @param subId Subscription id.
+ * @param isRcsUceEnabled If the user enabled RCS UCE for this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setRcsUceEnabled(int subId, int isRcsUceEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, isRcsUceEnabled,
+ SubscriptionInfoInternal.Builder::setRcsUceEnabled);
+ }
+
+ /**
+ * Set whether the user has enabled cross SIM calling for this subscription.
+ *
+ * @param subId Subscription id.
+ * @param isCrossSimCallingEnabled If the user enabled cross SIM calling for this
+ * subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setCrossSimCallingEnabled(int subId, int isCrossSimCallingEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+ isCrossSimCallingEnabled,
+ SubscriptionInfoInternal.Builder::setCrossSimCallingEnabled);
+ }
+
+ /**
+ * Set the RCS config for this subscription.
+ *
+ * @param subId Subscription id.
+ * @param rcsConfig The RCS config for this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setRcsConfig(int subId, @NonNull byte[] rcsConfig) {
+ Objects.requireNonNull(rcsConfig);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_RCS_CONFIG, rcsConfig,
+ SubscriptionInfoInternal.Builder::setRcsConfig);
+ }
+
+ /**
+ * Set the allowed network types for reasons.
+ *
+ * @param subId Subscription id.
+ * @param allowedNetworkTypesForReasons The allowed network types for reasons in string
+ * format. The format is "[reason]=[network types bitmask], [reason]=[network types bitmask],
+ * ...". For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setAllowedNetworkTypesForReasons(int subId,
+ @NonNull String allowedNetworkTypesForReasons) {
+ Objects.requireNonNull(allowedNetworkTypesForReasons);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ allowedNetworkTypesForReasons,
+ SubscriptionInfoInternal.Builder::setAllowedNetworkTypesForReasons);
+ }
+
+ /**
+ * Set device to device sharing status.
+ *
+ * @param subId Subscription id.
+ * @param deviceToDeviceStatusSharingPreference Device to device sharing status.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setDeviceToDeviceStatusSharingPreference(int subId,
+ @DeviceToDeviceStatusSharingPreference int deviceToDeviceStatusSharingPreference) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING,
+ deviceToDeviceStatusSharingPreference,
+ SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingPreference);
+ }
+
+ /**
+ * Set whether the user has opted-in voice over IMS.
+ *
+ * @param subId Subscription id.
+ * @param isVoImsOptInEnabled Whether the user has opted-in voice over IMS.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setVoImsOptInEnabled(int subId, int isVoImsOptInEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, isVoImsOptInEnabled,
+ SubscriptionInfoInternal.Builder::setVoImsOptInEnabled);
+ }
+
+ /**
+ * Set contacts information that allow device to device sharing.
+ *
+ * @param subId Subscription id.
+ * @param deviceToDeviceStatusSharingContacts contacts information that allow device to
+ * device sharing.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setDeviceToDeviceStatusSharingContacts(int subId,
+ @NonNull String deviceToDeviceStatusSharingContacts) {
+ Objects.requireNonNull(deviceToDeviceStatusSharingContacts);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ deviceToDeviceStatusSharingContacts,
+ SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingContacts);
+ }
+
+ /**
+ * Set whether the user has enabled NR advanced calling.
+ *
+ * @param subId Subscription id.
+ * @param isNrAdvancedCallingEnabled Whether the user has enabled NR advanced calling.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setNrAdvancedCallingEnabled(int subId, int isNrAdvancedCallingEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ isNrAdvancedCallingEnabled,
+ SubscriptionInfoInternal.Builder::setNrAdvancedCallingEnabled);
+ }
+
+ /**
+ * Set the phone number retrieved from carrier.
+ *
+ * @param subId Subscription id.
+ * @param numberFromCarrier The phone number retrieved from carrier.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setNumberFromCarrier(int subId, @NonNull String numberFromCarrier) {
+ Objects.requireNonNull(numberFromCarrier);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+ numberFromCarrier, SubscriptionInfoInternal.Builder::setNumberFromCarrier);
+ }
+
+ /**
+ * Set the phone number retrieved from IMS.
+ *
+ * @param subId Subscription id.
+ * @param numberFromIms The phone number retrieved from IMS.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setNumberFromIms(int subId, @NonNull String numberFromIms) {
+ Objects.requireNonNull(numberFromIms);
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
+ numberFromIms, SubscriptionInfoInternal.Builder::setNumberFromIms);
+ }
+
+ /**
+ * Set the port index of the Uicc card.
+ *
+ * @param subId Subscription id.
+ * @param portIndex The port index of the Uicc card.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setPortIndex(int subId, int portIndex) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PORT_INDEX, portIndex,
+ SubscriptionInfoInternal.Builder::setPortIndex);
+ }
+
+ /**
+ * Set subscription's preferred usage setting.
+ *
+ * @param subId Subscription id.
+ * @param usageSetting Subscription's preferred usage setting.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setUsageSetting(int subId, @UsageSetting int usageSetting) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USAGE_SETTING, usageSetting,
+ SubscriptionInfoInternal.Builder::setUsageSetting);
+ }
+
+ /**
+ * Set last used TP message reference.
+ *
+ * @param subId Subscription id.
+ * @param lastUsedTPMessageReference Last used TP message reference.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setLastUsedTPMessageReference(int subId, int lastUsedTPMessageReference) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_TP_MESSAGE_REF,
+ lastUsedTPMessageReference,
+ SubscriptionInfoInternal.Builder::setLastUsedTPMessageReference);
+ }
+
+ /**
+ * Set the user id associated with this subscription.
+ *
+ * @param subId Subscription id.
+ * @param userId The user id associated with this subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setUserId(int subId, @UserIdInt int userId) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USER_HANDLE, userId,
+ SubscriptionInfoInternal.Builder::setUserId);
+ }
+
+ /**
+ * Set whether satellite is enabled or not.
+ *
+ * @param subId Subscription id.
+ * @param isSatelliteEnabled if satellite is enabled or not.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSatelliteEnabled(int subId, int isSatelliteEnabled) {
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SATELLITE_ENABLED,
+ isSatelliteEnabled,
+ SubscriptionInfoInternal.Builder::setSatelliteEnabled);
+ }
+
+ /**
+ * Set whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+ * we should disable this opportunistic subscription.
+ *
+ * @param subId Subscription id.
+ * @param isGroupDisabled if group of the subscription is disabled.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setGroupDisabled(int subId, boolean isGroupDisabled) {
+ // group disabled does not have a corresponding SimInfo column. So we only update the cache.
+
+ // Grab the write lock so no other threads can read or write the cache.
+ mReadWriteLock.writeLock().lock();
+ try {
+ SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
+ if (subInfoCache == null) {
+ throw new IllegalArgumentException("setGroupDisabled: Subscription doesn't exist. "
+ + "subId=" + subId);
+ }
+ mAllSubscriptionInfoInternalCache.put(subId,
+ new SubscriptionInfoInternal.Builder(subInfoCache)
+ .setGroupDisabled(isGroupDisabled).build());
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Reload the database from content provider to the cache.
+ */
+ public void reloadDatabase() {
+ if (mAsyncMode) {
+ post(this::loadDatabaseInternal);
+ } else {
+ loadDatabaseInternal();
+ }
+ }
+
+ /**
+ * Load the database from content provider to the cache.
+ */
+ private void loadDatabaseInternal() {
+ log("loadDatabaseInternal");
+ try (Cursor cursor = mContext.getContentResolver().query(
+ SimInfo.CONTENT_URI, null, null, null, null)) {
+ mReadWriteLock.writeLock().lock();
+ try {
+ Map<Integer, SubscriptionInfoInternal> newAllSubscriptionInfoInternalCache =
+ new HashMap<>();
+ boolean changed = false;
+ while (cursor != null && cursor.moveToNext()) {
+ SubscriptionInfoInternal subInfo = createSubscriptionInfoFromCursor(cursor);
+ newAllSubscriptionInfoInternalCache.put(subInfo.getSubscriptionId(), subInfo);
+ if (!Objects.equals(mAllSubscriptionInfoInternalCache
+ .get(subInfo.getSubscriptionId()), subInfo)) {
+ mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(
+ subInfo.getSubscriptionId()));
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ mAllSubscriptionInfoInternalCache.clear();
+ mAllSubscriptionInfoInternalCache.putAll(newAllSubscriptionInfoInternalCache);
+
+ logl("Loaded " + mAllSubscriptionInfoInternalCache.size()
+ + " records from the subscription database.");
+ mAllSubscriptionInfoInternalCache.forEach(
+ (subId, subInfo) -> log(" " + subInfo.toString()));
+ }
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+ }
+
+ /**
+ * Initialize the database cache. Load the entire database into the cache.
+ */
+ private void initializeDatabase() {
+ if (mAsyncMode) {
+ // Load the database asynchronously.
+ post(() -> {
+ synchronized (this) {
+ loadDatabaseInternal();
+ mDatabaseInitialized = true;
+ mCallback.invokeFromExecutor(mCallback::onInitialized);
+ }
+ });
+ } else {
+ // Load the database synchronously.
+ synchronized (this) {
+ loadDatabaseInternal();
+ mDatabaseInitialized = true;
+ mCallback.invokeFromExecutor(mCallback::onInitialized);
+ }
+ }
+ }
+
+ /**
+ * Build the {@link SubscriptionInfoInternal} from database.
+ *
+ * @param cursor The cursor of the database.
+ *
+ * @return The subscription info from a single database record.
+ */
+ @NonNull
+ private SubscriptionInfoInternal createSubscriptionInfoFromCursor(@NonNull Cursor cursor) {
+ SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder();
+ int id = cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+ builder.setId(id)
+ .setIccId(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ICC_ID))))
+ .setSimSlotIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SIM_SLOT_INDEX)))
+ .setDisplayName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_DISPLAY_NAME))))
+ .setCarrierName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CARRIER_NAME))))
+ .setDisplayNameSource(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_NAME_SOURCE)))
+ .setIconTint(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_COLOR)))
+ .setNumber(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_NUMBER))))
+ .setDataRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_DATA_ROAMING)))
+ .setMcc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_MCC_STRING))))
+ .setMnc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_MNC_STRING))))
+ .setEhplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_EHPLMNS))))
+ .setHplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_HPLMNS))))
+ .setEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_IS_EMBEDDED)));
+
+ String cardString = TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CARD_ID)));
+ builder.setCardString(cardString);
+ // publicCardId is the publicly exposed int card ID
+ int publicCardId = mUiccController.convertToPublicCardId(cardString);
+
+ byte[] rules = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_ACCESS_RULES));
+ if (rules != null) {
+ builder.setNativeAccessRules(rules);
+ }
+
+ rules = cursor.getBlob(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS));
+ if (rules != null) {
+ builder.setCarrierConfigAccessRules(rules);
+ }
+
+ byte[] config = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG));
+ if (config != null) {
+ builder.setRcsConfig(config);
+ }
+
+ builder.setCardId(publicCardId)
+ .setRemovableEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_IS_REMOVABLE)))
+ .setCellBroadcastExtremeThreatAlertEnabled(cursor.getInt(cursor
+ .getColumnIndexOrThrow(SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT)))
+ .setCellBroadcastSevereThreatAlertEnabled(cursor.getInt(cursor
+ .getColumnIndexOrThrow(SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT)))
+ .setCellBroadcastAmberAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_AMBER_ALERT)))
+ .setCellBroadcastEmergencyAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_EMERGENCY_ALERT)))
+ .setCellBroadcastAlertSoundDuration(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_ALERT_SOUND_DURATION)))
+ .setCellBroadcastAlertReminderInterval(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL)))
+ .setCellBroadcastAlertVibrationEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_ALERT_VIBRATE)))
+ .setCellBroadcastAlertSpeechEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_ALERT_SPEECH)))
+ .setCellBroadcastEtwsTestAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_ETWS_TEST_ALERT)))
+ .setCellBroadcastAreaInfoMessageEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_CHANNEL_50_ALERT)))
+ .setCellBroadcastTestAlertEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_CMAS_TEST_ALERT)))
+ .setCellBroadcastOptOutDialogEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CB_OPT_OUT_DIALOG)))
+ .setEnhanced4GModeEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED)))
+ .setVideoTelephonyEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_VT_IMS_ENABLED)))
+ .setWifiCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_WFC_IMS_ENABLED)))
+ .setWifiCallingMode(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_WFC_IMS_MODE)))
+ .setWifiCallingModeForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE)))
+ .setWifiCallingEnabledForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED)))
+ .setOpportunistic(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_IS_OPPORTUNISTIC)))
+ .setGroupUuid(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_GROUP_UUID))))
+ .setCountryIso(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ISO_COUNTRY_CODE))))
+ .setCarrierId(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CARRIER_ID)))
+ .setProfileClass(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_PROFILE_CLASS)))
+ .setType(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SUBSCRIPTION_TYPE)))
+ .setGroupOwner(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_GROUP_OWNER))))
+ .setEnabledMobileDataPolicies(TextUtils.emptyIfNull(
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES))))
+ .setImsi(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_IMSI))))
+ .setUiccApplicationsEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED)))
+ .setRcsUceEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)))
+ .setCrossSimCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)))
+ .setAllowedNetworkTypesForReasons(TextUtils.emptyIfNull(
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS))))
+ .setDeviceToDeviceStatusSharingPreference(cursor.getInt(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_D2D_STATUS_SHARING)))
+ .setVoImsOptInEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)))
+ .setDeviceToDeviceStatusSharingContacts(TextUtils.emptyIfNull(cursor.getString(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS))))
+ .setNrAdvancedCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED)))
+ .setNumberFromCarrier(TextUtils.emptyIfNull(cursor.getString(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER))))
+ .setNumberFromIms(TextUtils.emptyIfNull(cursor.getString(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS))))
+ .setPortIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_PORT_INDEX)))
+ .setUsageSetting(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_USAGE_SETTING)))
+ .setLastUsedTPMessageReference(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_TP_MESSAGE_REF)))
+ .setUserId(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_USER_HANDLE)))
+ .setSatelliteEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SATELLITE_ENABLED)));
+ return builder.build();
+ }
+
+ /**
+ * Sync the group sharing fields from reference subscription to the rest of the subscriptions in
+ * the same group. For example, if user enables wifi calling, the same setting should be applied
+ * to all subscriptions in the same group.
+ *
+ * @param subId The subscription id of reference subscription.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void syncToGroup(int subId) {
+ if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) {
+ throw new IllegalArgumentException("Invalid subId " + subId);
+ }
+
+ for (String column : GROUP_SHARING_COLUMNS) {
+ // Get the value from the reference subscription, and set to itself again.
+ // writeDatabaseAndCacheHelper() will automatically sync to the rest of the group.
+ setSubscriptionProperty(subId, column, getSubscriptionProperty(subId, column));
+ }
+ }
+
+ /**
+ * Get the subscription info by subscription id.
+ *
+ * @param subId The subscription id.
+ *
+ * @return The subscription info. {@code null} if not found.
+ */
+ @Nullable
+ public SubscriptionInfoInternal getSubscriptionInfoInternal(int subId) {
+ mReadWriteLock.readLock().lock();
+ try {
+ return mAllSubscriptionInfoInternalCache.get(subId);
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * @return All subscription infos in the database.
+ */
+ @NonNull
+ public List<SubscriptionInfoInternal> getAllSubscriptions() {
+ mReadWriteLock.readLock().lock();
+ try {
+ return new ArrayList<>(mAllSubscriptionInfoInternalCache.values());
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get subscription info by ICCID.
+ *
+ * @param iccId The ICCID of the SIM card.
+ * @return The subscription info if found. {@code null} if not found.
+ */
+ @Nullable
+ public SubscriptionInfoInternal getSubscriptionInfoInternalByIccId(@NonNull String iccId) {
+ mReadWriteLock.readLock().lock();
+ try {
+ return mAllSubscriptionInfoInternalCache.values().stream()
+ .filter(subInfo -> subInfo.getIccId().equals(iccId))
+ .findFirst()
+ .orElse(null);
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Log debug messages.
+ *
+ * @param s debug messages
+ */
+ private void log(@NonNull String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ /**
+ * Log error messages.
+ *
+ * @param s error messages
+ */
+ private void loge(@NonNull String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+ /**
+ * Log verbose messages.
+ *
+ * @param s debug messages.
+ */
+ private void logv(@NonNull String s) {
+ if (VDBG) Rlog.v(LOG_TAG, s);
+ }
+
+ /**
+ * Log error messages and also log into the local log.
+ *
+ * @param s debug messages
+ */
+ private void logel(@NonNull String s) {
+ loge(s);
+ mLocalLog.log(s);
+ }
+
+ /**
+ * 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 {@link SubscriptionDatabaseManager}.
+ *
+ * @param fd File descriptor
+ * @param printWriter Print writer
+ * @param args Arguments
+ */
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter printWriter,
+ @NonNull String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println(SubscriptionDatabaseManager.class.getSimpleName() + ":");
+ pw.increaseIndent();
+ pw.println("All subscriptions:");
+ pw.increaseIndent();
+ mReadWriteLock.readLock().lock();
+ try {
+ mAllSubscriptionInfoInternalCache.forEach((subId, subInfo) -> pw.println(subInfo));
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+ pw.decreaseIndent();
+ pw.println();
+ pw.println("mAsyncMode=" + mAsyncMode);
+ synchronized (this) {
+ pw.println("mDatabaseInitialized=" + mDatabaseInitialized);
+ }
+ pw.println("mReadWriteLock=" + mReadWriteLock);
+ pw.println();
+ pw.println("Local log:");
+ pw.increaseIndent();
+ mLocalLog.dump(fd, printWriter, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
new file mode 100644
index 0000000..b917698
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -0,0 +1,2689 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.provider.Telephony.SimInfo;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The class represents a single row of {@link SimInfo} table. All columns (excepts unused columns)
+ * in the database have a corresponding field in this class.
+ *
+ * The difference between {@link SubscriptionInfo} and this class is that {@link SubscriptionInfo}
+ * is a subset of this class. This is intended to solve the problem that some database fields
+ * required higher permission like
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} to access while
+ * {@link SubscriptionManager#getActiveSubscriptionIdList()} only requires
+ * {@link android.Manifest.permission#READ_PHONE_STATE} to access. Sometimes blanking out fields in
+ * {@link SubscriptionInfo} creates ambiguity for clients hard to distinguish between insufficient
+ * permission versus true failure.
+ *
+ * Also the fields in this class match the format used in database. For example, boolean values
+ * are stored as integer, or string arrays are stored as a single comma separated string.
+ */
+public class SubscriptionInfoInternal {
+ /**
+ * Subscription Identifier, this is a device unique number
+ * and not an index into an array
+ */
+ private final int mId;
+
+ /**
+ * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+ */
+ @NonNull
+ private final String mIccId;
+
+ /**
+ * The index of the SIM slot that currently contains the subscription and not necessarily unique
+ * and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the subscription
+ * is inactive.
+ */
+ private final int mSimSlotIndex;
+
+ /**
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ */
+ @NonNull
+ private final String mDisplayName;
+
+ /**
+ * The name displayed to the user that identifies subscription provider name. This name is the
+ * SPN displayed in status bar and many other places. Can't be renamed by the user.
+ */
+ @NonNull
+ private final String mCarrierName;
+
+ /**
+ * The source of the {@link #mDisplayName}.
+ */
+ @SimDisplayNameSource
+ private final int mDisplayNameSource;
+
+ /**
+ * The color to be used for tinting the icon when displaying to the user.
+ */
+ @ColorInt
+ private final int mIconTint;
+
+ /**
+ * The number presented to the user identify this subscription.
+ */
+ @NonNull
+ private final String mNumber;
+
+ /**
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ */
+ private final int mDataRoaming;
+
+ /**
+ * Mobile Country Code.
+ */
+ @NonNull
+ private final String mMcc;
+
+ /**
+ * Mobile Network Code.
+ */
+ @NonNull
+ private final String mMnc;
+
+ /**
+ * EHPLMNs associated with the subscription.
+ */
+ @NonNull
+ private final String mEhplmns;
+
+ /**
+ * HPLMNs associated with the subscription.
+ */
+ @NonNull
+ private final String mHplmns;
+
+ /**
+ * Whether the subscription is from eSIM. It is intended to use integer to fit the database
+ * format.
+ */
+ private final int mIsEmbedded;
+
+ /**
+ * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+ * EID for an eUICC card.
+ */
+ @NonNull
+ private final String mCardString;
+
+ /**
+ * The access rules for this subscription, if it is embedded and defines any. This does not
+ * include access rules for non-embedded subscriptions.
+ */
+ @NonNull
+ private final byte[] mNativeAccessRules;
+
+ /**
+ * The carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ */
+ @NonNull
+ private final byte[] mCarrierConfigAccessRules;
+
+ /**
+ * Whether an embedded subscription is on a removable card. Such subscriptions are marked
+ * inaccessible as soon as the current card is removed. Otherwise, they will remain accessible
+ * unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is {@code 1}. It
+ * is intended to use integer to fit the database format.
+ */
+ private final int mIsRemovableEmbedded;
+
+ /**
+ * Whether cell broadcast extreme threat alert is enabled by the user or not.
+ */
+ private int mIsExtremeThreatAlertEnabled;
+
+ /**
+ * Whether cell broadcast severe threat alert is enabled by the user or not.
+ */
+ private int mIsSevereThreatAlertEnabled;
+
+ /**
+ * Whether cell broadcast amber alert is enabled by the user or not.
+ */
+ private int mIsAmberAlertEnabled;
+
+ /**
+ * Whether cell broadcast emergency alert is enabled by the user or not.
+ */
+ private int mIsEmergencyAlertEnabled;
+
+ /**
+ * Cell broadcast alert sound duration in seconds.
+ */
+ private int mAlertSoundDuration;
+
+ /**
+ * Cell broadcast alert reminder interval in minutes.
+ */
+ private int mReminderInterval;
+
+ /**
+ * Whether cell broadcast alert vibration is enabled by the user or not.
+ */
+ private int mIsAlertVibrationEnabled;
+
+ /**
+ * Whether cell broadcast alert speech is enabled by the user or not.
+ */
+ private int mIsAlertSpeechEnabled;
+
+ /**
+ * Whether ETWS test alert is enabled by the user or not.
+ */
+ private int mIsEtwsTestAlertEnabled;
+
+ /**
+ * Whether area info message is enabled by the user or not.
+ */
+ private int mIsAreaInfoMessageEnabled;
+
+ /**
+ * Whether cell broadcast test alert is enabled by the user or not.
+ */
+ private int mIsTestAlertEnabled;
+
+ /**
+ * Whether cell broadcast opt-out dialog should be shown or not.
+ */
+ private int mIsOptOutDialogEnabled;
+
+ /**
+ * Whether enhanced 4G mode is enabled by the user or not. It is intended to use integer to fit
+ * the database format.
+ */
+ private final int mIsEnhanced4GModeEnabled;
+
+ /**
+ * Whether video telephony is enabled by the user or not. It is intended to use integer to fit
+ * the database format.
+ */
+ private final int mIsVideoTelephonyEnabled;
+
+ /**
+ * Whether Wi-Fi calling is enabled by the user or not when the device is not roaming. It is
+ * intended to use integer to fit the database format.
+ */
+ private final int mIsWifiCallingEnabled;
+
+ /**
+ * Wi-Fi calling mode when the device is not roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ private final int mWifiCallingMode;
+
+ /**
+ * Wi-Fi calling mode when the device is roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ private final int mWifiCallingModeForRoaming;
+
+ /**
+ * Whether Wi-Fi calling is enabled by the user or not when the device is roaming. It is
+ * intended to use integer to fit the database format.
+ */
+ private final int mIsWifiCallingEnabledForRoaming;
+
+ /**
+ * Whether the subscription is opportunistic. It is intended to use integer to fit the database
+ * format.
+ */
+ private final int mIsOpportunistic;
+
+ /**
+ * A UUID assigned to the subscription group in string format.
+ *
+ * @see SubscriptionManager#createSubscriptionGroup(List)
+ */
+ @NonNull
+ private final String mGroupUuid;
+
+ /**
+ * ISO Country code for the subscription's provider.
+ */
+ @NonNull
+ private final String mCountryIso;
+
+ /**
+ * The subscription carrier id.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ private final int mCarrierId;
+
+ /**
+ * The profile class populated from the profile metadata if present. Otherwise,
+ * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+ * profile metadata or the subscription is not on an eUICC ({@link #getEmbedded} returns
+ * {@code 0}).
+ */
+ @ProfileClass
+ private final int mProfileClass;
+
+ /**
+ * Type of the subscription.
+ */
+ @SubscriptionType
+ private final int mType;
+
+ /**
+ * A package name that specifies who created the group. Empty if not available.
+ */
+ @NonNull
+ private final String mGroupOwner;
+
+ /**
+ * The enabled mobile data policies in string format.
+ */
+ @NonNull
+ private final String mEnabledMobileDataPolicies;
+
+ /**
+ * The IMSI (International Mobile Subscriber Identity) of the subscription.
+ */
+ @NonNull
+ private final String mImsi;
+
+ /**
+ * Whether uicc applications are configured to enable or disable.
+ * By default it's true. It is intended to use integer to fit the database format.
+ */
+ private final int mAreUiccApplicationsEnabled;
+
+ /**
+ * Whether the user has enabled IMS RCS User Capability Exchange (UCE) for this subscription.
+ * It is intended to use integer to fit the database format.
+ */
+ private final int mIsRcsUceEnabled;
+
+ /**
+ * Whether the user has enabled cross SIM calling for this subscription. It is intended to
+ * use integer to fit the database format.
+ */
+ private final int mIsCrossSimCallingEnabled;
+
+ /**
+ * The RCS configuration.
+ */
+ @NonNull
+ private final byte[] mRcsConfig;
+
+ /**
+ * The allowed network types for reasons in string format. The format is
+ * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+ *
+ * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+ */
+ @NonNull
+ private final String mAllowedNetworkTypesForReasons;
+
+ /**
+ * Device to device sharing status.
+ */
+ @DeviceToDeviceStatusSharingPreference
+ private final int mDeviceToDeviceStatusSharingPreference;
+
+ /**
+ * Whether the user has opted-in voice over IMS. It is intended to use integer to fit the
+ * database format.
+ */
+ private final int mIsVoImsOptInEnabled;
+
+ /**
+ * Contacts information that allow device to device sharing.
+ */
+ @NonNull
+ private final String mDeviceToDeviceStatusSharingContacts;
+
+ /**
+ * Whether the user has enabled NR advanced calling. It is intended to use integer to fit the
+ * database format.
+ */
+ private final int mIsNrAdvancedCallingEnabled;
+
+ /**
+ * The phone number retrieved from carrier.
+ */
+ @NonNull
+ private final String mNumberFromCarrier;
+
+ /**
+ * The phone number retrieved from IMS.
+ */
+ @NonNull
+ private final String mNumberFromIms;
+
+ /**
+ * The port index of the Uicc card.
+ */
+ private final int mPortIndex;
+
+ /**
+ * Subscription's preferred usage setting.
+ */
+ @UsageSetting
+ private final int mUsageSetting;
+
+ /**
+ * Last used TP message reference.
+ */
+ private final int mLastUsedTPMessageReference;
+
+ /**
+ * The user id associated with this subscription.
+ */
+ private final int mUserId;
+
+ /**
+ * Whether satellite is enabled or disabled.
+ * By default, its disabled. It is intended to use integer to fit the database format.
+ */
+ private final int mIsSatelliteEnabled;
+
+ // Below are the fields that do not exist in the SimInfo table.
+ /**
+ * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
+ */
+ private final int mCardId;
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
+ * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
+ * this opportunistic subscription. It is intended to use integer to fit the database format.
+ */
+ private final boolean mIsGroupDisabled;
+
+ /**
+ * Constructor from builder.
+ *
+ * @param builder Builder of {@link SubscriptionInfoInternal}.
+ */
+ private SubscriptionInfoInternal(@NonNull Builder builder) {
+ this.mId = builder.mId;
+ this.mIccId = builder.mIccId;
+ this.mSimSlotIndex = builder.mSimSlotIndex;
+ this.mDisplayName = builder.mDisplayName;
+ this.mCarrierName = builder.mCarrierName;
+ this.mDisplayNameSource = builder.mDisplayNameSource;
+ this.mIconTint = builder.mIconTint;
+ this.mNumber = builder.mNumber;
+ this.mDataRoaming = builder.mDataRoaming;
+ this.mMcc = builder.mMcc;
+ this.mMnc = builder.mMnc;
+ this.mEhplmns = builder.mEhplmns;
+ this.mHplmns = builder.mHplmns;
+ this.mIsEmbedded = builder.mIsEmbedded;
+ this.mCardString = builder.mCardString;
+ this.mNativeAccessRules = builder.mNativeAccessRules;
+ this.mCarrierConfigAccessRules = builder.mCarrierConfigAccessRules;
+ this.mIsRemovableEmbedded = builder.mIsRemovableEmbedded;
+ this.mIsExtremeThreatAlertEnabled = builder.mIsExtremeThreatAlertEnabled;
+ this.mIsSevereThreatAlertEnabled = builder.mIsSevereThreatAlertEnabled;
+ this.mIsAmberAlertEnabled = builder.mIsAmberAlertEnabled;
+ this.mIsEmergencyAlertEnabled = builder.mIsEmergencyAlertEnabled;
+ this.mAlertSoundDuration = builder.mAlertSoundDuration;
+ this.mReminderInterval = builder.mReminderInterval;
+ this.mIsAlertVibrationEnabled = builder.mIsAlertVibrationEnabled;
+ this.mIsAlertSpeechEnabled = builder.mIsAlertSpeechEnabled;
+ this.mIsEtwsTestAlertEnabled = builder.mIsEtwsTestAlertEnabled;
+ this.mIsAreaInfoMessageEnabled = builder.mIsAreaInfoMessageEnabled;
+ this.mIsTestAlertEnabled = builder.mIsTestAlertEnabled;
+ this.mIsOptOutDialogEnabled = builder.mIsOptOutDialogEnabled;
+ this.mIsEnhanced4GModeEnabled = builder.mIsEnhanced4GModeEnabled;
+ this.mIsVideoTelephonyEnabled = builder.mIsVideoTelephonyEnabled;
+ this.mIsWifiCallingEnabled = builder.mIsWifiCallingEnabled;
+ this.mWifiCallingMode = builder.mWifiCallingMode;
+ this.mWifiCallingModeForRoaming = builder.mWifiCallingModeForRoaming;
+ this.mIsWifiCallingEnabledForRoaming = builder.mIsWifiCallingEnabledForRoaming;
+ this.mIsOpportunistic = builder.mIsOpportunistic;
+ this.mGroupUuid = builder.mGroupUuid;
+ this.mCountryIso = builder.mCountryIso;
+ this.mCarrierId = builder.mCarrierId;
+ this.mProfileClass = builder.mProfileClass;
+ this.mType = builder.mType;
+ this.mGroupOwner = builder.mGroupOwner;
+ this.mEnabledMobileDataPolicies = builder.mEnabledMobileDataPolicies;
+ this.mImsi = builder.mImsi;
+ this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
+ this.mIsRcsUceEnabled = builder.mIsRcsUceEnabled;
+ this.mIsCrossSimCallingEnabled = builder.mIsCrossSimCallingEnabled;
+ this.mRcsConfig = builder.mRcsConfig;
+ this.mAllowedNetworkTypesForReasons = builder.mAllowedNetworkTypesForReasons;
+ this.mDeviceToDeviceStatusSharingPreference =
+ builder.mDeviceToDeviceStatusSharingPreference;
+ this.mIsVoImsOptInEnabled = builder.mIsVoImsOptInEnabled;
+ this.mDeviceToDeviceStatusSharingContacts = builder.mDeviceToDeviceStatusSharingContacts;
+ this.mIsNrAdvancedCallingEnabled = builder.mIsNrAdvancedCallingEnabled;
+ this.mNumberFromCarrier = builder.mNumberFromCarrier;
+ this.mNumberFromIms = builder.mNumberFromIms;
+ this.mPortIndex = builder.mPortIndex;
+ this.mUsageSetting = builder.mUsageSetting;
+ this.mLastUsedTPMessageReference = builder.mLastUsedTPMessageReference;
+ this.mUserId = builder.mUserId;
+ this.mIsSatelliteEnabled = builder.mIsSatelliteEnabled;
+
+ // Below are the fields that do not exist in the SimInfo table.
+ this.mCardId = builder.mCardId;
+ this.mIsGroupDisabled = builder.mIsGroupDisabled;
+ }
+
+ /**
+ * @return The subscription ID.
+ */
+ public int getSubscriptionId() {
+ return mId;
+ }
+
+ /**
+ * Returns the ICC ID.
+ *
+ * @return the ICC ID, or an empty string if one of these requirements is not met
+ */
+ @NonNull
+ public String getIccId() {
+ return mIccId;
+ }
+
+ /**
+ * @return The index of the SIM slot that currently contains the subscription and not
+ * necessarily unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or
+ * the subscription is inactive.
+ */
+ public int getSimSlotIndex() {
+ return mSimSlotIndex;
+ }
+
+ /**
+ * @return The name displayed to the user that identifies this subscription. This name is
+ * used in Settings page and can be renamed by the user.
+ *
+ * @see #getCarrierName()
+ */
+ @NonNull
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
+ * @return The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @see #getDisplayName()
+ */
+ @NonNull
+ public String getCarrierName() {
+ return mCarrierName;
+ }
+
+ /**
+ * @return The source of the {@link #getDisplayName()}.
+ */
+ @SimDisplayNameSource
+ public int getDisplayNameSource() {
+ return mDisplayNameSource;
+ }
+
+ /**
+ * A highlight color to use in displaying information about this {@code PhoneAccount}.
+ *
+ * @return A hexadecimal color value.
+ */
+ @ColorInt
+ public int getIconTint() {
+ return mIconTint;
+ }
+
+ /**
+ * @return the number of this subscription.
+ */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ */
+ public int getDataRoaming() {
+ return mDataRoaming;
+ }
+
+ /**
+ * @return The mobile country code.
+ */
+ @NonNull
+ public String getMcc() {
+ return mMcc;
+ }
+
+ /**
+ * @return The mobile network code.
+ */
+ @NonNull
+ public String getMnc() {
+ return mMnc;
+ }
+
+ /**
+ * @return Extended home PLMNs associated with this subscription.
+ */
+ @NonNull
+ public String getEhplmns() {
+ return mEhplmns;
+ }
+
+ /**
+ * @return Home PLMNs associated with this subscription.
+ */
+ @NonNull
+ public String getHplmns() {
+ return mHplmns;
+ }
+
+ /**
+ * @return {@code true} if the subscription is from eSIM.
+ */
+ public boolean isEmbedded() {
+ return mIsEmbedded != 0;
+ }
+
+ /**
+ * @return {@code 1} if the subscription is from eSIM.
+ */
+ public int getEmbedded() {
+ return mIsEmbedded;
+ }
+
+ /**
+ * Returns the card string of the SIM card which contains the subscription.
+ *
+ * @return The card string of the SIM card which contains the subscription. The card string is
+ * the ICCID for UICCs or the EID for
+ * eUICCs.
+ */
+ @NonNull
+ public String getCardString() {
+ return mCardString;
+ }
+
+ /**
+ * @return The access rules for this subscription, if it is embedded and defines any. This
+ * does not include access rules for non-embedded subscriptions. This is the raw string
+ * stored in the database.
+ */
+ @NonNull
+ public byte[] getNativeAccessRules() {
+ return mNativeAccessRules;
+ }
+
+ /**
+ * @return The carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded. This
+ * is the raw string stored in the database.
+ */
+ public byte[] getCarrierConfigAccessRules() {
+ return mCarrierConfigAccessRules;
+ }
+
+ /**
+ * @return {@code true} if an embedded subscription is on a removable card. Such subscriptions
+ * are marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is 1.
+ */
+ public boolean isRemovableEmbedded() {
+ return mIsRemovableEmbedded != 0;
+ }
+
+ /**
+ * @return {@code 1} if an embedded subscription is on a removable card. Such subscriptions are
+ * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is 1.
+ */
+ public int getRemovableEmbedded() {
+ return mIsRemovableEmbedded;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast extreme threat alert is enabled by the user.
+ */
+ public int getCellBroadcastExtremeThreatAlertEnabled() {
+ return mIsExtremeThreatAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast amber alert is enabled by the user.
+ */
+ public int getCellBroadcastSevereThreatAlertEnabled() {
+ return mIsSevereThreatAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast emergency alert is enabled by the user.
+ */
+ public int getCellBroadcastAmberAlertEnabled() {
+ return mIsAmberAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast emergency alert is enabled by the user.
+ */
+ public int getCellBroadcastEmergencyAlertEnabled() {
+ return mIsEmergencyAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast alert sound duration in seconds.
+ */
+ public int getCellBroadcastAlertSoundDuration() {
+ return mAlertSoundDuration;
+ }
+
+ /**
+ * @return Cell broadcast alert reminder interval in minutes.
+ */
+ public int getCellBroadcastAlertReminderInterval() {
+ return mReminderInterval;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast alert vibration is enabled by the user.
+ */
+ public int getCellBroadcastAlertVibrationEnabled() {
+ return mIsAlertVibrationEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast alert speech is enabled by the user.
+ */
+ public int getCellBroadcastAlertSpeechEnabled() {
+ return mIsAlertSpeechEnabled;
+ }
+
+ /**
+ * @return {@code 1} if ETWS test alert is enabled by the user.
+ */
+ public int getCellBroadcastEtwsTestAlertEnabled() {
+ return mIsEtwsTestAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if area info message is enabled by the user.
+ */
+ public int getCellBroadcastAreaInfoMessageEnabled() {
+ return mIsAreaInfoMessageEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast test alert is enabled by the user.
+ */
+ public int getCellBroadcastTestAlertEnabled() {
+ return mIsTestAlertEnabled;
+ }
+
+ /**
+ * @return {@code 1} if cell broadcast opt-out dialog should be shown.
+ */
+ public int getCellBroadcastOptOutDialogEnabled() {
+ return mIsOptOutDialogEnabled;
+ }
+
+ /**
+ * @return {@code true} if enhanced 4G mode is enabled by the user or not.
+ */
+ public boolean isEnhanced4GModeEnabled() {
+ return mIsEnhanced4GModeEnabled == 1;
+ }
+
+ /**
+ * @return {@code 1} if enhanced 4G mode is enabled by the user or not. {@code 0} if disabled.
+ * {@code -1} if the user did not change any setting.
+ */
+ public int getEnhanced4GModeEnabled() {
+ return mIsEnhanced4GModeEnabled;
+ }
+
+ /**
+ * @return {@code true} if video telephony is enabled by the user or not.
+ */
+ public boolean isVideoTelephonyEnabled() {
+ return mIsVideoTelephonyEnabled != 0;
+ }
+
+ /**
+ * @return {@code 1} if video telephony is enabled by the user or not.
+ */
+ public int getVideoTelephonyEnabled() {
+ return mIsVideoTelephonyEnabled;
+ }
+
+ /**
+ * @return {@code true} if Wi-Fi calling is enabled by the user or not when the device is not
+ * roaming.
+ */
+ public boolean isWifiCallingEnabled() {
+ return mIsWifiCallingEnabled == 1;
+ }
+
+ /**
+ * @return {@code 1} if Wi-Fi calling is enabled by the user or not when the device is not
+ * roaming. {@code 0} if disabled. {@code -1} if the user did not change any setting.
+ */
+ public int getWifiCallingEnabled() {
+ return mIsWifiCallingEnabled;
+ }
+
+ /**
+ * @return Wi-Fi calling mode when the device is not roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ public int getWifiCallingMode() {
+ return mWifiCallingMode;
+ }
+
+ /**
+ * @return Wi-Fi calling mode when the device is roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ public int getWifiCallingModeForRoaming() {
+ return mWifiCallingModeForRoaming;
+ }
+
+ /**
+ * @return {@code true} if Wi-Fi calling is enabled by the user or not when the device is
+ * roaming. {@code 0} if disabled. {@code -1} if the user did not change any setting.
+ */
+ public boolean isWifiCallingEnabledForRoaming() {
+ return mIsWifiCallingEnabledForRoaming == 1;
+ }
+
+ /**
+ * @return {@code 1} if Wi-Fi calling is enabled by the user or not when the device is roaming.
+ */
+ public int getWifiCallingEnabledForRoaming() {
+ return mIsWifiCallingEnabledForRoaming;
+ }
+
+ /**
+ * An opportunistic subscription connects to a network that is
+ * limited in functionality and / or coverage.
+ *
+ * @return {@code true} if subscription is opportunistic.
+ */
+ public boolean isOpportunistic() {
+ return mIsOpportunistic != 0;
+ }
+
+ /**
+ * An opportunistic subscription connects to a network that is
+ * limited in functionality and / or coverage.
+ *
+ * @return {@code 1} if subscription is opportunistic.
+ */
+ public int getOpportunistic() {
+ return mIsOpportunistic;
+ }
+
+ /**
+ * Used in scenarios where different subscriptions are bundled as a group.
+ * It's typically a primary and an opportunistic subscription. (see {@link #getOpportunistic()})
+ * Such that those subscriptions will have some affiliated behaviors such as opportunistic
+ * subscription may be invisible to the user.
+ *
+ * @return Group UUID in string format.
+ */
+ @NonNull
+ public String getGroupUuid() {
+ return mGroupUuid;
+ }
+
+ /**
+ * @return The ISO country code. Empty if not available.
+ */
+ public String getCountryIso() {
+ return mCountryIso;
+ }
+
+ /**
+ * @return The carrier id of this subscription carrier.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ /**
+ * @return The profile class populated from the profile metadata if present. Otherwise,
+ * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+ * profile metadata or the subscription is not on an eUICC ({@link #getEmbedded} return
+ * {@code 0}).
+ */
+ @ProfileClass
+ public int getProfileClass() {
+ return mProfileClass;
+ }
+
+ /**
+ * This method returns the type of a subscription. It can be
+ * {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
+ * {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
+ *
+ * @return The type of the subscription.
+ */
+ @SubscriptionType
+ public int getSubscriptionType() {
+ return mType;
+ }
+
+ /**
+ * @return The owner package of group the subscription belongs to.
+ */
+ @NonNull
+ public String getGroupOwner() {
+ return mGroupOwner;
+ }
+
+ /**
+ * @return The enabled mobile data policies in string format.
+ *
+ * @see com.android.internal.telephony.data.DataSettingsManager#getMobileDataPolicyEnabled
+ */
+ @NonNull
+ public String getEnabledMobileDataPolicies() {
+ return mEnabledMobileDataPolicies;
+ }
+
+ /**
+ * @return The IMSI (International Mobile Subscriber Identity) of the subscription.
+ */
+ @NonNull
+ public String getImsi() {
+ return mImsi;
+ }
+
+ /**
+ * @return {@code true} if Uicc applications are set to be enabled or disabled.
+ */
+ public boolean areUiccApplicationsEnabled() {
+ return mAreUiccApplicationsEnabled != 0;
+ }
+
+ /**
+ * @return {@code 1} if Uicc applications are set to be enabled or disabled.
+ */
+ public int getUiccApplicationsEnabled() {
+ return mAreUiccApplicationsEnabled;
+ }
+
+ /**
+ * @return {@code true} if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ */
+ public boolean isRcsUceEnabled() {
+ return mIsRcsUceEnabled != 0;
+ }
+
+ /**
+ * @return {@code 1} if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ */
+ public int getRcsUceEnabled() {
+ return mIsRcsUceEnabled;
+ }
+
+ /**
+ * @return {@code true} if the user has enabled cross SIM calling for this subscription.
+ */
+ public boolean isCrossSimCallingEnabled() {
+ return mIsCrossSimCallingEnabled != 0;
+ }
+
+ /**
+ * @return {@code 1} if the user has enabled cross SIM calling for this subscription.
+ */
+ public int getCrossSimCallingEnabled() {
+ return mIsCrossSimCallingEnabled;
+ }
+
+ /**
+ * @return The RCS configuration.
+ */
+ @NonNull
+ public byte[] getRcsConfig() {
+ return mRcsConfig;
+ }
+
+ /**
+ * The allowed network types for reasons in string format. The format is
+ * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+ *
+ * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+ */
+ @NonNull
+ public String getAllowedNetworkTypesForReasons() {
+ return mAllowedNetworkTypesForReasons;
+ }
+
+ /**
+ * @return Device to device sharing status.
+ */
+ @DeviceToDeviceStatusSharingPreference
+ public int getDeviceToDeviceStatusSharingPreference() {
+ return mDeviceToDeviceStatusSharingPreference;
+ }
+
+ /**
+ * @return {@code true} if the user has opted-in voice over IMS.
+ */
+ public boolean isVoImsOptInEnabled() {
+ return mIsVoImsOptInEnabled != 0;
+ }
+
+ /**
+ * @return {@code 1} if the user has opted-in voice over IMS.
+ */
+ public int getVoImsOptInEnabled() {
+ return mIsVoImsOptInEnabled;
+ }
+
+ /**
+ * @return Contacts information that allow device to device sharing.
+ */
+ @NonNull
+ public String getDeviceToDeviceStatusSharingContacts() {
+ return mDeviceToDeviceStatusSharingContacts;
+ }
+
+ /**
+ * @return {@code true} if the user has enabled NR advanced calling.
+ */
+ public boolean isNrAdvancedCallingEnabled() {
+ return mIsNrAdvancedCallingEnabled == 1;
+ }
+
+ /**
+ * @return {@code 1} if the user has enabled NR advanced calling. {code 0} if disabled.
+ * {code -1} if the user did not change any setting.
+ */
+ public int getNrAdvancedCallingEnabled() {
+ return mIsNrAdvancedCallingEnabled;
+ }
+
+ /**
+ * @return Get the phone number retrieved from carrier.
+ */
+ @NonNull
+ public String getNumberFromCarrier() {
+ return mNumberFromCarrier;
+ }
+
+ /**
+ * @return Get the phone number retrieved from IMS.
+ */
+ @NonNull
+ public String getNumberFromIms() {
+ return mNumberFromIms;
+ }
+
+ /**
+ * @return The port index of the SIM card which contains the subscription.
+ */
+ public int getPortIndex() {
+ return mPortIndex;
+ }
+
+ /**
+ * Get the usage setting for this subscription.
+ *
+ * @return The usage setting used for this subscription.
+ */
+ @UsageSetting
+ public int getUsageSetting() {
+ return mUsageSetting;
+ }
+
+ /**
+ * @return Last used TP message reference.
+ */
+ public int getLastUsedTPMessageReference() {
+ return mLastUsedTPMessageReference;
+ }
+
+ /**
+ * @return The user id associated with this subscription.
+ */
+ @UserIdInt
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * @return {@code 1} if satellite is enabled.
+ */
+ public int getSatelliteEnabled() {
+ return mIsSatelliteEnabled;
+ }
+
+ // Below are the fields that do not exist in SimInfo table.
+ /**
+ * @return The card ID of the SIM card which contains the subscription.
+ *
+ * @see android.telephony.UiccCardInfo#getCardId().
+ */
+ public int getCardId() {
+ return mCardId;
+ }
+
+ /**
+ * @return {@code true} if the group of the subscription is disabled. This is only useful if
+ * it's a grouped opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we
+ * should disable this opportunistic subscription.
+ */
+ public boolean isGroupDisabled() {
+ return mIsGroupDisabled;
+ }
+
+ /**
+ * @return {@code true} if the subscription is from the actively used SIM.
+ */
+ public boolean isActive() {
+ return mSimSlotIndex >= 0 || mType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
+ }
+
+ /**
+ * @return {@code true} if the subscription is visible to the user.
+ */
+ public boolean isVisible() {
+ return !isOpportunistic() || TextUtils.isEmpty(mGroupUuid);
+ }
+
+ /** @return converted {@link SubscriptionInfo}. */
+ @NonNull
+ public SubscriptionInfo toSubscriptionInfo() {
+ return new SubscriptionInfo.Builder()
+ .setId(mId)
+ .setIccId(mIccId)
+ .setSimSlotIndex(mSimSlotIndex)
+ .setDisplayName(mDisplayName)
+ .setCarrierName(mCarrierName)
+ .setDisplayNameSource(mDisplayNameSource)
+ .setIconTint(mIconTint)
+ .setNumber(mNumber)
+ .setDataRoaming(mDataRoaming)
+ .setMcc(mMcc)
+ .setMnc(mMnc)
+ .setEhplmns(TextUtils.isEmpty(mEhplmns) ? null : mEhplmns.split(","))
+ .setHplmns(TextUtils.isEmpty(mHplmns) ? null : mHplmns.split(","))
+ .setCountryIso(mCountryIso)
+ .setEmbedded(mIsEmbedded != 0)
+ .setNativeAccessRules(mNativeAccessRules.length == 0
+ ? null : UiccAccessRule.decodeRules(mNativeAccessRules))
+ .setCardString(mCardString)
+ .setCardId(mCardId)
+ .setOpportunistic(mIsOpportunistic != 0)
+ .setGroupUuid(mGroupUuid)
+ .setGroupDisabled(mIsGroupDisabled)
+ .setCarrierId(mCarrierId)
+ .setProfileClass(mProfileClass)
+ .setType(mType)
+ .setGroupOwner(mGroupOwner)
+ .setCarrierConfigAccessRules(mCarrierConfigAccessRules.length == 0
+ ? null : UiccAccessRule.decodeRules(mCarrierConfigAccessRules))
+ .setUiccApplicationsEnabled(mAreUiccApplicationsEnabled != 0)
+ .setPortIndex(mPortIndex)
+ .setUsageSetting(mUsageSetting)
+ .build();
+ }
+
+ @Override
+ public String toString() {
+ return "[SubscriptionInfoInternal: id=" + mId
+ + " iccId=" + SubscriptionInfo.getPrintableId(mIccId)
+ + " simSlotIndex=" + mSimSlotIndex
+ + " portIndex=" + mPortIndex
+ + " isEmbedded=" + mIsEmbedded
+ + " isRemovableEmbedded=" + mIsRemovableEmbedded
+ + " carrierId=" + mCarrierId
+ + " displayName=" + mDisplayName
+ + " carrierName=" + mCarrierName
+ + " isOpportunistic=" + mIsOpportunistic
+ + " groupUuid=" + mGroupUuid
+ + " groupOwner=" + mGroupOwner
+ + " displayNameSource="
+ + SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
+ + " iconTint=" + mIconTint
+ + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+ + " dataRoaming=" + mDataRoaming
+ + " mcc=" + mMcc
+ + " mnc=" + mMnc
+ + " ehplmns=" + mEhplmns
+ + " hplmns=" + mHplmns
+ + " cardString=" + SubscriptionInfo.getPrintableId(mCardString)
+ + " cardId=" + mCardId
+ + " nativeAccessRules=" + IccUtils.bytesToHexString(mNativeAccessRules)
+ + " carrierConfigAccessRules=" + IccUtils.bytesToHexString(
+ mCarrierConfigAccessRules)
+ + " countryIso=" + mCountryIso
+ + " profileClass=" + mProfileClass
+ + " type=" + SubscriptionManager.subscriptionTypeToString(mType)
+ + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+ + " isEnhanced4GModeEnabled=" + mIsEnhanced4GModeEnabled
+ + " isVideoTelephonyEnabled=" + mIsVideoTelephonyEnabled
+ + " isWifiCallingEnabled=" + mIsWifiCallingEnabled
+ + " isWifiCallingEnabledForRoaming=" + mIsWifiCallingEnabledForRoaming
+ + " wifiCallingMode=" + ImsMmTelManager.wifiCallingModeToString(mWifiCallingMode)
+ + " wifiCallingModeForRoaming="
+ + ImsMmTelManager.wifiCallingModeToString(mWifiCallingModeForRoaming)
+ + " enabledMobileDataPolicies=" + mEnabledMobileDataPolicies
+ + " imsi=" + SubscriptionInfo.getPrintableId(mImsi)
+ + " rcsUceEnabled=" + mIsRcsUceEnabled
+ + " crossSimCallingEnabled=" + mIsCrossSimCallingEnabled
+ + " rcsConfig=" + IccUtils.bytesToHexString(mRcsConfig)
+ + " allowedNetworkTypesForReasons=" + mAllowedNetworkTypesForReasons
+ + " deviceToDeviceStatusSharingPreference=" + mDeviceToDeviceStatusSharingPreference
+ + " isVoImsOptInEnabled=" + mIsVoImsOptInEnabled
+ + " deviceToDeviceStatusSharingContacts=" + mDeviceToDeviceStatusSharingContacts
+ + " numberFromCarrier=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumberFromCarrier)
+ + " numberFromIms=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumberFromIms)
+ + " userId=" + mUserId
+ + " isSatelliteEnabled=" + mIsSatelliteEnabled
+ + " isGroupDisabled=" + mIsGroupDisabled
+ + "]";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+ return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
+ && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
+ && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
+ && mIsRemovableEmbedded == that.mIsRemovableEmbedded
+ && mIsExtremeThreatAlertEnabled == that.mIsExtremeThreatAlertEnabled
+ && mIsSevereThreatAlertEnabled == that.mIsSevereThreatAlertEnabled
+ && mIsAmberAlertEnabled == that.mIsAmberAlertEnabled
+ && mIsEmergencyAlertEnabled == that.mIsEmergencyAlertEnabled
+ && mAlertSoundDuration == that.mAlertSoundDuration
+ && mReminderInterval == that.mReminderInterval
+ && mIsAlertVibrationEnabled == that.mIsAlertVibrationEnabled
+ && mIsAlertSpeechEnabled == that.mIsAlertSpeechEnabled
+ && mIsEtwsTestAlertEnabled == that.mIsEtwsTestAlertEnabled
+ && mIsAreaInfoMessageEnabled == that.mIsAreaInfoMessageEnabled
+ && mIsEnhanced4GModeEnabled == that.mIsEnhanced4GModeEnabled
+ && mIsVideoTelephonyEnabled == that.mIsVideoTelephonyEnabled
+ && mIsWifiCallingEnabled == that.mIsWifiCallingEnabled
+ && mWifiCallingMode == that.mWifiCallingMode
+ && mWifiCallingModeForRoaming == that.mWifiCallingModeForRoaming
+ && mIsWifiCallingEnabledForRoaming == that.mIsWifiCallingEnabledForRoaming
+ && mIsOpportunistic == that.mIsOpportunistic && mCarrierId == that.mCarrierId
+ && mProfileClass == that.mProfileClass && mType == that.mType
+ && mAreUiccApplicationsEnabled == that.mAreUiccApplicationsEnabled
+ && mIsRcsUceEnabled == that.mIsRcsUceEnabled
+ && mIsCrossSimCallingEnabled == that.mIsCrossSimCallingEnabled
+ && mDeviceToDeviceStatusSharingPreference
+ == that.mDeviceToDeviceStatusSharingPreference
+ && mIsVoImsOptInEnabled == that.mIsVoImsOptInEnabled
+ && mIsNrAdvancedCallingEnabled == that.mIsNrAdvancedCallingEnabled
+ && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
+ && mLastUsedTPMessageReference == that.mLastUsedTPMessageReference
+ && mUserId == that.mUserId && mIsSatelliteEnabled == that.mIsSatelliteEnabled
+ && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
+ && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
+ && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
+ && mMcc.equals(that.mMcc) && mMnc.equals(that.mMnc) && mEhplmns.equals(
+ that.mEhplmns)
+ && mHplmns.equals(that.mHplmns) && mCardString.equals(that.mCardString)
+ && Arrays.equals(mNativeAccessRules, that.mNativeAccessRules)
+ && Arrays.equals(mCarrierConfigAccessRules, that.mCarrierConfigAccessRules)
+ && mGroupUuid.equals(that.mGroupUuid) && mCountryIso.equals(that.mCountryIso)
+ && mGroupOwner.equals(that.mGroupOwner) && mEnabledMobileDataPolicies.equals(
+ that.mEnabledMobileDataPolicies) && mImsi.equals(that.mImsi) && Arrays.equals(
+ mRcsConfig, that.mRcsConfig) && mAllowedNetworkTypesForReasons.equals(
+ that.mAllowedNetworkTypesForReasons) && mDeviceToDeviceStatusSharingContacts.equals(
+ that.mDeviceToDeviceStatusSharingContacts) && mNumberFromCarrier.equals(
+ that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
+ mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mEhplmns, mHplmns,
+ mIsEmbedded, mCardString, mIsRemovableEmbedded, mIsExtremeThreatAlertEnabled,
+ mIsSevereThreatAlertEnabled, mIsAmberAlertEnabled, mIsEmergencyAlertEnabled,
+ mAlertSoundDuration, mReminderInterval, mIsAlertVibrationEnabled,
+ mIsAlertSpeechEnabled,
+ mIsEtwsTestAlertEnabled, mIsAreaInfoMessageEnabled, mIsEnhanced4GModeEnabled,
+ mIsVideoTelephonyEnabled, mIsWifiCallingEnabled, mWifiCallingMode,
+ mWifiCallingModeForRoaming, mIsWifiCallingEnabledForRoaming, mIsOpportunistic,
+ mGroupUuid, mCountryIso, mCarrierId, mProfileClass, mType, mGroupOwner,
+ mEnabledMobileDataPolicies, mImsi, mAreUiccApplicationsEnabled, mIsRcsUceEnabled,
+ mIsCrossSimCallingEnabled, mAllowedNetworkTypesForReasons,
+ mDeviceToDeviceStatusSharingPreference, mIsVoImsOptInEnabled,
+ mDeviceToDeviceStatusSharingContacts, mIsNrAdvancedCallingEnabled,
+ mNumberFromCarrier,
+ mNumberFromIms, mPortIndex, mUsageSetting, mLastUsedTPMessageReference, mUserId,
+ mIsSatelliteEnabled, mCardId, mIsGroupDisabled);
+ result = 31 * result + Arrays.hashCode(mNativeAccessRules);
+ result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
+ result = 31 * result + Arrays.hashCode(mRcsConfig);
+ return result;
+ }
+
+ /**
+ * The builder class of {@link SubscriptionInfoInternal}.
+ */
+ public static class Builder {
+ /**
+ * The subscription id.
+ */
+ private int mId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ /**
+ * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+ */
+ @NonNull
+ private String mIccId = "";
+
+ /**
+ * The index of the SIM slot that currently contains the subscription and not necessarily
+ * unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the
+ * subscription is inactive.
+ */
+ private int mSimSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+
+ /**
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ */
+ @NonNull
+ private String mDisplayName = "";
+
+ /**
+ * The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ */
+ @NonNull
+ private String mCarrierName = "";
+
+ /**
+ * The source of the display name.
+ */
+ @SimDisplayNameSource
+ private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_UNKNOWN;
+
+ /**
+ * The color to be used for tinting the icon when displaying to the user.
+ */
+ private int mIconTint = 0;
+
+ /**
+ * The number presented to the user identify this subscription.
+ */
+ @NonNull
+ private String mNumber = "";
+
+ /**
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ */
+ private int mDataRoaming = SubscriptionManager.DATA_ROAMING_DISABLE;
+
+ /**
+ * The mobile country code.
+ */
+ @NonNull
+ private String mMcc = "";
+
+ /**
+ * The mobile network code.
+ */
+ @NonNull
+ private String mMnc = "";
+
+ /**
+ * EHPLMNs associated with the subscription.
+ */
+ @NonNull
+ private String mEhplmns = "";
+
+ /**
+ * HPLMNs associated with the subscription.
+ */
+ @NonNull
+ private String mHplmns = "";
+
+ /**
+ * Whether the subscription is from eSIM.
+ */
+ private int mIsEmbedded = 0;
+
+ /**
+ * The card string of the SIM card.
+ */
+ @NonNull
+ private String mCardString = "";
+
+ /**
+ * The native access rules for this subscription, if it is embedded and defines any. This
+ * does not include access rules for non-embedded subscriptions.
+ */
+ @NonNull
+ private byte[] mNativeAccessRules = new byte[0];
+
+ /**
+ * The carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ */
+ @NonNull
+ private byte[] mCarrierConfigAccessRules = new byte[0];
+
+ /**
+ * Whether an embedded subscription is on a removable card. Such subscriptions are marked
+ * inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is
+ * {@code 1}.
+ */
+ private int mIsRemovableEmbedded = 0;
+
+ /**
+ * Whether cell broadcast extreme threat alert is enabled by the user or not.
+ */
+ private int mIsExtremeThreatAlertEnabled = 1;
+
+ /**
+ * Whether cell broadcast severe threat alert is enabled by the user or not.
+ */
+ private int mIsSevereThreatAlertEnabled = 1;
+
+ /**
+ * Whether cell broadcast amber alert is enabled by the user or not.
+ */
+ private int mIsAmberAlertEnabled = 1;
+
+ /**
+ * Whether cell broadcast emergency alert is enabled by the user or not.
+ */
+ private int mIsEmergencyAlertEnabled = 1;
+
+ /**
+ * Cell broadcast alert sound duration in seconds.
+ */
+ private int mAlertSoundDuration = 4;
+
+ /**
+ * Cell broadcast alert reminder interval in minutes.
+ */
+ private int mReminderInterval = 0;
+
+ /**
+ * Whether cell broadcast alert vibration is enabled by the user or not.
+ */
+ private int mIsAlertVibrationEnabled = 1;
+
+ /**
+ * Whether cell broadcast alert speech is enabled by the user or not.
+ */
+ private int mIsAlertSpeechEnabled = 1;
+
+ /**
+ * Whether ETWS test alert is enabled by the user or not.
+ */
+ private int mIsEtwsTestAlertEnabled = 0;
+
+ /**
+ * Whether area info message is enabled by the user or not.
+ */
+ private int mIsAreaInfoMessageEnabled = 1;
+
+ /**
+ * Whether cell broadcast test alert is enabled by the user or not.
+ */
+ private int mIsTestAlertEnabled = 0;
+
+ /**
+ * Whether cell broadcast opt-out dialog should be shown or not.
+ */
+ private int mIsOptOutDialogEnabled = 1;
+
+ /**
+ * Whether enhanced 4G mode is enabled by the user or not.
+ */
+ private int mIsEnhanced4GModeEnabled = -1;
+
+ /**
+ * Whether video telephony is enabled by the user or not.
+ */
+ private int mIsVideoTelephonyEnabled = -1;
+
+ /**
+ * Whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+ */
+ private int mIsWifiCallingEnabled = -1;
+
+ /**
+ * Wi-Fi calling mode when the device is not roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ private int mWifiCallingMode = ImsMmTelManager.WIFI_MODE_UNKNOWN;
+
+ /**
+ * Wi-Fi calling mode when the device is roaming.
+ */
+ @ImsMmTelManager.WiFiCallingMode
+ private int mWifiCallingModeForRoaming = ImsMmTelManager.WIFI_MODE_UNKNOWN;
+
+ /**
+ * Whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+ */
+ private int mIsWifiCallingEnabledForRoaming = -1;
+
+ /**
+ * Whether the subscription is opportunistic or not.
+ */
+ private int mIsOpportunistic = 0;
+
+ /**
+ * The group UUID of the subscription group in string format.
+ */
+ @NonNull
+ private String mGroupUuid = "";
+
+ /**
+ * The ISO Country code for the subscription's provider.
+ */
+ @NonNull
+ private String mCountryIso = "";
+
+ /**
+ * The carrier id.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
+ * The profile class populated from the profile metadata if present. Otherwise, the profile
+ * class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no profile
+ * metadata or the subscription is not on an eUICC ({@link #getEmbedded} returns
+ * {@code 0}).
+ */
+ @ProfileClass
+ private int mProfileClass = SubscriptionManager.PROFILE_CLASS_UNSET;
+
+ /**
+ * The subscription type.
+ */
+ @SubscriptionType
+ private int mType = SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM;
+
+ /**
+ * The owner package of group the subscription belongs to.
+ */
+ @NonNull
+ private String mGroupOwner = "";
+
+ /**
+ * The enabled mobile data policies in string format.
+ */
+ @NonNull
+ private String mEnabledMobileDataPolicies = "";
+
+ /**
+ * The IMSI (International Mobile Subscriber Identity) of the subscription.
+ */
+ @NonNull
+ private String mImsi = "";
+
+ /**
+ * Whether Uicc applications are configured to enable or not.
+ */
+ private int mAreUiccApplicationsEnabled = 1;
+
+ /**
+ * Whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ */
+ private int mIsRcsUceEnabled = 0;
+
+ /**
+ * Whether the user has enabled cross SIM calling for this subscription.
+ */
+ private int mIsCrossSimCallingEnabled = 0;
+
+ /**
+ * The RCS configuration.
+ */
+ private byte[] mRcsConfig = new byte[0];
+
+ /**
+ * The allowed network types for reasons in string format. The format is
+ * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+ *
+ * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+ */
+ private String mAllowedNetworkTypesForReasons = "";
+
+ /**
+ * Device to device sharing status.
+ */
+ @DeviceToDeviceStatusSharingPreference
+ private int mDeviceToDeviceStatusSharingPreference =
+ SubscriptionManager.D2D_SHARING_DISABLED;
+
+ /**
+ * Whether the user has opted-in voice over IMS.
+ */
+ private int mIsVoImsOptInEnabled = 0;
+
+ /**
+ * Contacts information that allow device to device sharing.
+ */
+ @NonNull
+ private String mDeviceToDeviceStatusSharingContacts = "";
+
+ /**
+ * Whether the user has enabled NR advanced calling.
+ */
+ private int mIsNrAdvancedCallingEnabled = -1;
+
+ /**
+ * The phone number retrieved from carrier.
+ */
+ @NonNull
+ private String mNumberFromCarrier = "";
+
+ /**
+ * The phone number retrieved from IMS.
+ */
+ @NonNull
+ private String mNumberFromIms = "";
+
+ /**
+ * the port index of the Uicc card.
+ */
+ private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
+
+ /**
+ * Subscription's preferred usage setting.
+ */
+ @UsageSetting
+ private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+ /**
+ * Last used TP message reference.
+ */
+ private int mLastUsedTPMessageReference = -1;
+
+ /**
+ * The user id associated with this subscription.
+ */
+ private int mUserId = UserHandle.USER_NULL;
+
+ /**
+ * Whether satellite is enabled or not.
+ */
+ private int mIsSatelliteEnabled = -1;
+
+ // The following fields do not exist in the SimInfo table.
+ /**
+ * The card ID of the SIM card which contains the subscription.
+ */
+ private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+ * we should disable this opportunistic subscription.
+ */
+ private boolean mIsGroupDisabled;
+
+ /**
+ * Default constructor.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructor from {@link SubscriptionInfoInternal}.
+ *
+ * @param info The subscription info.
+ */
+ public Builder(@NonNull SubscriptionInfoInternal info) {
+ mId = info.mId;
+ mIccId = info.mIccId;
+ mSimSlotIndex = info.mSimSlotIndex;
+ mDisplayName = info.mDisplayName;
+ mCarrierName = info.mCarrierName;
+ mDisplayNameSource = info.mDisplayNameSource;
+ mIconTint = info.mIconTint;
+ mNumber = info.mNumber;
+ mDataRoaming = info.mDataRoaming;
+ mMcc = info.mMcc;
+ mMnc = info.mMnc;
+ mEhplmns = info.mEhplmns;
+ mHplmns = info.mHplmns;
+ mIsEmbedded = info.mIsEmbedded;
+ mCardString = info.mCardString;
+ mNativeAccessRules = info.mNativeAccessRules;
+ mCarrierConfigAccessRules = info.mCarrierConfigAccessRules;
+ mIsRemovableEmbedded = info.mIsRemovableEmbedded;
+ mIsExtremeThreatAlertEnabled = info.mIsExtremeThreatAlertEnabled;
+ mIsSevereThreatAlertEnabled = info.mIsSevereThreatAlertEnabled;
+ mIsAmberAlertEnabled = info.mIsAmberAlertEnabled;
+ mIsEmergencyAlertEnabled = info.mIsEmergencyAlertEnabled;
+ mAlertSoundDuration = info.mAlertSoundDuration;
+ mReminderInterval = info.mReminderInterval;
+ mIsAlertVibrationEnabled = info.mIsAlertVibrationEnabled;
+ mIsAlertSpeechEnabled = info.mIsAlertSpeechEnabled;
+ mIsEtwsTestAlertEnabled = info.mIsEtwsTestAlertEnabled;
+ mIsAreaInfoMessageEnabled = info.mIsAreaInfoMessageEnabled;
+ mIsTestAlertEnabled = info.mIsTestAlertEnabled;
+ mIsOptOutDialogEnabled = info.mIsOptOutDialogEnabled;
+ mIsEnhanced4GModeEnabled = info.mIsEnhanced4GModeEnabled;
+ mIsVideoTelephonyEnabled = info.mIsVideoTelephonyEnabled;
+ mIsWifiCallingEnabled = info.mIsWifiCallingEnabled;
+ mWifiCallingMode = info.mWifiCallingMode;
+ mWifiCallingModeForRoaming = info.mWifiCallingModeForRoaming;
+ mIsWifiCallingEnabledForRoaming = info.mIsWifiCallingEnabledForRoaming;
+ mIsOpportunistic = info.mIsOpportunistic;
+ mGroupUuid = info.mGroupUuid;
+ mCountryIso = info.mCountryIso;
+ mCarrierId = info.mCarrierId;
+ mProfileClass = info.mProfileClass;
+ mType = info.mType;
+ mGroupOwner = info.mGroupOwner;
+ mEnabledMobileDataPolicies = info.mEnabledMobileDataPolicies;
+ mImsi = info.mImsi;
+ mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
+ mIsRcsUceEnabled = info.mIsRcsUceEnabled;
+ mIsCrossSimCallingEnabled = info.mIsCrossSimCallingEnabled;
+ mRcsConfig = info.mRcsConfig;
+ mAllowedNetworkTypesForReasons = info.mAllowedNetworkTypesForReasons;
+ mDeviceToDeviceStatusSharingPreference = info.mDeviceToDeviceStatusSharingPreference;
+ mIsVoImsOptInEnabled = info.mIsVoImsOptInEnabled;
+ mDeviceToDeviceStatusSharingContacts = info.mDeviceToDeviceStatusSharingContacts;
+ mIsNrAdvancedCallingEnabled = info.mIsNrAdvancedCallingEnabled;
+ mNumberFromCarrier = info.mNumberFromCarrier;
+ mNumberFromIms = info.mNumberFromIms;
+ mPortIndex = info.mPortIndex;
+ mUsageSetting = info.mUsageSetting;
+ mLastUsedTPMessageReference = info.getLastUsedTPMessageReference();
+ mUserId = info.mUserId;
+ mIsSatelliteEnabled = info.mIsSatelliteEnabled;
+ // Below are the fields that do not exist in the SimInfo table.
+ mCardId = info.mCardId;
+ mIsGroupDisabled = info.mIsGroupDisabled;
+ }
+
+ /**
+ * Set the subscription id.
+ *
+ * @param id The subscription id.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * Set the ICCID of the SIM that is associated with this subscription.
+ *
+ * @param iccId The ICCID of the SIM that is associated with this subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setIccId(@NonNull String iccId) {
+ Objects.requireNonNull(iccId);
+ mIccId = iccId;
+ return this;
+ }
+
+ /**
+ * Set the SIM index of the slot that currently contains the subscription. Set to
+ * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+ *
+ * @param simSlotIndex The SIM slot index.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setSimSlotIndex(int simSlotIndex) {
+ mSimSlotIndex = simSlotIndex;
+ return this;
+ }
+
+ /**
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ *
+ * @param displayName The display name.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setDisplayName(@NonNull String displayName) {
+ Objects.requireNonNull(displayName);
+ mDisplayName = displayName;
+ return this;
+ }
+
+ /**
+ * The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @param carrierName The carrier name.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCarrierName(@NonNull String carrierName) {
+ Objects.requireNonNull(carrierName);
+ mCarrierName = carrierName;
+ return this;
+ }
+
+ /**
+ * Set the source of the display name.
+ *
+ * @param displayNameSource The source of the display name.
+ * @return The builder.
+ *
+ * @see SubscriptionInfoInternal#getDisplayName()
+ */
+ @NonNull
+ public Builder setDisplayNameSource(@SimDisplayNameSource int displayNameSource) {
+ mDisplayNameSource = displayNameSource;
+ return this;
+ }
+
+ /**
+ * Set the color to be used for tinting the icon when displaying to the user.
+ *
+ * @param iconTint The color to be used for tinting the icon when displaying to the user.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setIconTint(int iconTint) {
+ mIconTint = iconTint;
+ return this;
+ }
+
+ /**
+ * Set the number presented to the user identify this subscription.
+ *
+ * @param number the number presented to the user identify this subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNumber(@NonNull String number) {
+ Objects.requireNonNull(number);
+ mNumber = number;
+ return this;
+ }
+
+ /**
+ * Set whether user enables data roaming for this subscription or not.
+ *
+ * @param dataRoaming Data roaming mode. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setDataRoaming(int dataRoaming) {
+ mDataRoaming = dataRoaming;
+ return this;
+ }
+
+ /**
+ * Set the mobile country code.
+ *
+ * @param mcc The mobile country code.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setMcc(@NonNull String mcc) {
+ Objects.requireNonNull(mcc);
+ mMcc = mcc;
+ return this;
+ }
+
+ /**
+ * Set the mobile network code.
+ *
+ * @param mnc Mobile network code.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setMnc(@NonNull String mnc) {
+ Objects.requireNonNull(mnc);
+ mMnc = mnc;
+ return this;
+ }
+
+ /**
+ * Set EHPLMNs associated with the subscription.
+ *
+ * @param ehplmns EHPLMNs associated with the subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEhplmns(@NonNull String ehplmns) {
+ Objects.requireNonNull(ehplmns);
+ mEhplmns = ehplmns;
+ return this;
+ }
+
+ /**
+ * Set HPLMNs associated with the subscription.
+ *
+ * @param hplmns HPLMNs associated with the subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setHplmns(@NonNull String hplmns) {
+ Objects.requireNonNull(hplmns);
+ mHplmns = hplmns;
+ return this;
+ }
+
+ /**
+ * Set whether the subscription is from eSIM or not.
+ *
+ * @param isEmbedded {@code 1} if the subscription is from eSIM.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEmbedded(int isEmbedded) {
+ mIsEmbedded = isEmbedded;
+ return this;
+ }
+
+ /**
+ * Set the card string of the SIM card.
+ *
+ * @param cardString The card string of the SIM card.
+ *
+ * @return The builder.
+ *
+ * @see #getCardString()
+ */
+ @NonNull
+ public Builder setCardString(@NonNull String cardString) {
+ Objects.requireNonNull(cardString);
+ mCardString = cardString;
+ return this;
+ }
+
+ /**
+ * Set the native access rules for this subscription, if it is embedded and defines any.
+ * This does not include access rules for non-embedded subscriptions.
+ *
+ * @param nativeAccessRules The native access rules for this subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNativeAccessRules(@NonNull byte[] nativeAccessRules) {
+ Objects.requireNonNull(nativeAccessRules);
+ mNativeAccessRules = nativeAccessRules;
+ return this;
+ }
+
+ /**
+ * Set the native access rules for this subscription, if it is embedded and defines any.
+ * This does not include access rules for non-embedded subscriptions.
+ *
+ * @param nativeAccessRules The native access rules for this subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNativeAccessRules(@NonNull List<UiccAccessRule> nativeAccessRules) {
+ Objects.requireNonNull(nativeAccessRules);
+ if (!nativeAccessRules.isEmpty()) {
+ mNativeAccessRules = UiccAccessRule.encodeRules(
+ nativeAccessRules.toArray(new UiccAccessRule[0]));
+ }
+ return this;
+ }
+
+ /**
+ * Set the carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ *
+ * @param carrierConfigAccessRules The carrier certificates for this subscription.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCarrierConfigAccessRules(@NonNull byte[] carrierConfigAccessRules) {
+ Objects.requireNonNull(carrierConfigAccessRules);
+ mCarrierConfigAccessRules = carrierConfigAccessRules;
+ return this;
+ }
+
+ /**
+ * Set whether an embedded subscription is on a removable card. Such subscriptions are
+ * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is
+ * {@code 1}.
+ *
+ * @param isRemovableEmbedded {@code true} if the subscription is from the removable
+ * embedded SIM.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setRemovableEmbedded(boolean isRemovableEmbedded) {
+ mIsRemovableEmbedded = isRemovableEmbedded ? 1 : 0;
+ return this;
+ }
+
+ /**
+ * Set whether an embedded subscription is on a removable card. Such subscriptions are
+ * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+ * accessible unless explicitly deleted. Only meaningful when {@link #getEmbedded()} is
+ * {@code 1}.
+ *
+ * @param isRemovableEmbedded {@code 1} if the subscription is from the removable
+ * embedded SIM.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setRemovableEmbedded(int isRemovableEmbedded) {
+ mIsRemovableEmbedded = isRemovableEmbedded;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast extreme threat alert is enabled by the user or not.
+ *
+ * @param isExtremeThreatAlertEnabled whether cell broadcast extreme threat alert is enabled
+ * by the user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastExtremeThreatAlertEnabled(int isExtremeThreatAlertEnabled) {
+ mIsExtremeThreatAlertEnabled = isExtremeThreatAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast severe threat alert is enabled by the user or not.
+ *
+ * @param isSevereThreatAlertEnabled whether cell broadcast severe threat alert is enabled
+ * by the user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastSevereThreatAlertEnabled(int isSevereThreatAlertEnabled) {
+ mIsSevereThreatAlertEnabled = isSevereThreatAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast amber alert is enabled by the user or not.
+ *
+ * @param isAmberAlertEnabled whether cell broadcast amber alert is enabled by the user or
+ * not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastAmberAlertEnabled(int isAmberAlertEnabled) {
+ mIsAmberAlertEnabled = isAmberAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast emergency alert is enabled by the user or not.
+ *
+ * @param isEmergencyAlertEnabled whether cell broadcast emergency alert is enabled by the
+ * user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastEmergencyAlertEnabled(int isEmergencyAlertEnabled) {
+ mIsEmergencyAlertEnabled = isEmergencyAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set cell broadcast alert sound duration.
+ *
+ * @param alertSoundDuration Alert sound duration in seconds.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastAlertSoundDuration(int alertSoundDuration) {
+ mAlertSoundDuration = alertSoundDuration;
+ return this;
+ }
+
+ /**
+ * Set cell broadcast alert reminder interval in minutes.
+ *
+ * @param reminderInterval Alert reminder interval in minutes.
+ *
+ * @return The builder.
+ */
+ public Builder setCellBroadcastAlertReminderInterval(int reminderInterval) {
+ mReminderInterval = reminderInterval;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast alert vibration is enabled by the user or not.
+ *
+ * @param isAlertVibrationEnabled whether cell broadcast alert vibration is enabled by the
+ * user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastAlertVibrationEnabled(int isAlertVibrationEnabled) {
+ mIsAlertVibrationEnabled = isAlertVibrationEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast alert speech is enabled by the user or not.
+ *
+ * @param isAlertSpeechEnabled whether cell broadcast alert speech is enabled by the user or
+ * not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastAlertSpeechEnabled(int isAlertSpeechEnabled) {
+ mIsAlertSpeechEnabled = isAlertSpeechEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether ETWS test alert is enabled by the user or not.
+ *
+ * @param isEtwsTestAlertEnabled whether cell broadcast ETWS test alert is enabled by the
+ * user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastEtwsTestAlertEnabled(int isEtwsTestAlertEnabled) {
+ mIsEtwsTestAlertEnabled = isEtwsTestAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether area info message is enabled by the user or not.
+ *
+ * @param isAreaInfoMessageEnabled whether cell broadcast area info message is enabled by
+ * the user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastAreaInfoMessageEnabled(int isAreaInfoMessageEnabled) {
+ mIsAreaInfoMessageEnabled = isAreaInfoMessageEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast test alert is enabled by the user or not.
+ *
+ * @param isTestAlertEnabled whether cell broadcast test alert is enabled by the user or
+ * not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastTestAlertEnabled(int isTestAlertEnabled) {
+ mIsTestAlertEnabled = isTestAlertEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether cell broadcast opt-out dialog should be shown or not.
+ *
+ * @param isOptOutDialogEnabled whether cell broadcast opt-out dialog should be shown or
+ * not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCellBroadcastOptOutDialogEnabled(int isOptOutDialogEnabled) {
+ mIsOptOutDialogEnabled = isOptOutDialogEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether enhanced 4G mode is enabled by the user or not.
+ *
+ * @param isEnhanced4GModeEnabled whether enhanced 4G mode is enabled by the user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEnhanced4GModeEnabled(int isEnhanced4GModeEnabled) {
+ mIsEnhanced4GModeEnabled = isEnhanced4GModeEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether video telephony is enabled by the user or not.
+ *
+ * @param isVideoTelephonyEnabled whether video telephony is enabled by the user or not.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setVideoTelephonyEnabled(int isVideoTelephonyEnabled) {
+ mIsVideoTelephonyEnabled = isVideoTelephonyEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+ *
+ * @param isWifiCallingEnabled whether Wi-Fi calling is enabled by the user or not when
+ * the device is not roaming.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setWifiCallingEnabled(int isWifiCallingEnabled) {
+ mIsWifiCallingEnabled = isWifiCallingEnabled;
+ return this;
+ }
+
+ /**
+ * Set Wi-Fi calling mode when the device is not roaming.
+ *
+ * @param wifiCallingMode Wi-Fi calling mode when the device is not roaming.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setWifiCallingMode(@ImsMmTelManager.WiFiCallingMode int wifiCallingMode) {
+ mWifiCallingMode = wifiCallingMode;
+ return this;
+ }
+
+ /**
+ * Set Wi-Fi calling mode when the device is roaming.
+ *
+ * @param wifiCallingModeForRoaming Wi-Fi calling mode when the device is roaming.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setWifiCallingModeForRoaming(
+ @ImsMmTelManager.WiFiCallingMode int wifiCallingModeForRoaming) {
+ mWifiCallingModeForRoaming = wifiCallingModeForRoaming;
+ return this;
+ }
+
+ /**
+ * Set whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+ *
+ * @param wifiCallingEnabledForRoaming whether Wi-Fi calling is enabled by the user or not
+ * when the device is roaming.
+ *
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setWifiCallingEnabledForRoaming(int wifiCallingEnabledForRoaming) {
+ mIsWifiCallingEnabledForRoaming = wifiCallingEnabledForRoaming;
+ return this;
+ }
+
+ /**
+ * Set whether the subscription is opportunistic or not.
+ *
+ * @param isOpportunistic {@code 1} if the subscription is opportunistic.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setOpportunistic(int isOpportunistic) {
+ mIsOpportunistic = isOpportunistic;
+ return this;
+ }
+
+ /**
+ * Set the group UUID of the subscription group.
+ *
+ * @param groupUuid The group UUID.
+ * @return The builder.
+ *
+ */
+ @NonNull
+ public Builder setGroupUuid(@NonNull String groupUuid) {
+ Objects.requireNonNull(groupUuid);
+ mGroupUuid = groupUuid;
+ return this;
+ }
+
+ /**
+ * Set the ISO country code for the subscription's provider.
+ *
+ * @param countryIso The ISO country code for the subscription's provider.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCountryIso(@NonNull String countryIso) {
+ Objects.requireNonNull(countryIso);
+ mCountryIso = countryIso;
+ return this;
+ }
+
+ /**
+ * Set the subscription carrier id.
+ *
+ * @param carrierId The carrier id.
+ * @return The builder
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ @NonNull
+ public Builder setCarrierId(int carrierId) {
+ mCarrierId = carrierId;
+ return this;
+ }
+
+ /**
+ * Set the profile class populated from the profile metadata if present.
+ *
+ * @param profileClass the profile class populated from the profile metadata if present.
+ * @return The builder
+ *
+ * @see #getProfileClass()
+ */
+ @NonNull
+ public Builder setProfileClass(@ProfileClass int profileClass) {
+ mProfileClass = profileClass;
+ return this;
+ }
+
+ /**
+ * Set the subscription type.
+ *
+ * @param type Subscription type.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setType(@SubscriptionType int type) {
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Set the owner package of group the subscription belongs to.
+ *
+ * @param groupOwner Owner package of group the subscription belongs to.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setGroupOwner(@NonNull String groupOwner) {
+ Objects.requireNonNull(groupOwner);
+ mGroupOwner = groupOwner;
+ return this;
+ }
+
+ /**
+ * Set the enabled mobile data policies.
+ *
+ * @param enabledMobileDataPolicies The enabled mobile data policies.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEnabledMobileDataPolicies(@NonNull String enabledMobileDataPolicies) {
+ Objects.requireNonNull(enabledMobileDataPolicies);
+ mEnabledMobileDataPolicies = enabledMobileDataPolicies;
+ return this;
+ }
+
+ /**
+ * Set the IMSI (International Mobile Subscriber Identity) of the subscription.
+ *
+ * @param imsi The IMSI.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setImsi(@NonNull String imsi) {
+ Objects.requireNonNull(imsi);
+ mImsi = imsi;
+ return this;
+ }
+
+ /**
+ * Set whether Uicc applications are configured to enable or not.
+ *
+ * @param areUiccApplicationsEnabled {@code 1} if Uicc applications are configured to
+ * enable.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setUiccApplicationsEnabled(int areUiccApplicationsEnabled) {
+ mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ *
+ * @param isRcsUceEnabled If the user enabled RCS UCE for this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setRcsUceEnabled(int isRcsUceEnabled) {
+ mIsRcsUceEnabled = isRcsUceEnabled;
+ return this;
+ }
+
+ /**
+ * Set whether the user has enabled cross SIM calling for this subscription.
+ *
+ * @param isCrossSimCallingEnabled If the user enabled cross SIM calling for this
+ * subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCrossSimCallingEnabled(int isCrossSimCallingEnabled) {
+ mIsCrossSimCallingEnabled = isCrossSimCallingEnabled;
+ return this;
+ }
+
+ /**
+ * Set the RCS config for this subscription.
+ *
+ * @param rcsConfig The RCS config for this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setRcsConfig(byte[] rcsConfig) {
+ Objects.requireNonNull(rcsConfig);
+ mRcsConfig = rcsConfig;
+ return this;
+ }
+
+ /**
+ * Set the allowed network types for reasons.
+ *
+ * @param allowedNetworkTypesForReasons The allowed network types for reasons in string
+ * format. The format is
+ * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+ *
+ * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+ *
+ * @return The builder.
+ */
+ public Builder setAllowedNetworkTypesForReasons(
+ @NonNull String allowedNetworkTypesForReasons) {
+ Objects.requireNonNull(allowedNetworkTypesForReasons);
+ mAllowedNetworkTypesForReasons = allowedNetworkTypesForReasons;
+ return this;
+ }
+
+ /**
+ * Set device to device sharing status.
+ *
+ * @param deviceToDeviceStatusSharingPreference Device to device sharing status.
+ * @return The builder.
+ */
+ public Builder setDeviceToDeviceStatusSharingPreference(
+ @DeviceToDeviceStatusSharingPreference int deviceToDeviceStatusSharingPreference) {
+ mDeviceToDeviceStatusSharingPreference = deviceToDeviceStatusSharingPreference;
+ return this;
+ }
+
+ /**
+ * Set whether the user has opted-in voice over IMS.
+ *
+ * @param isVoImsOptInEnabled Whether the user has opted-in voice over IMS.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setVoImsOptInEnabled(int isVoImsOptInEnabled) {
+ mIsVoImsOptInEnabled = isVoImsOptInEnabled;
+ return this;
+ }
+
+ /**
+ * Set contacts information that allow device to device sharing.
+ *
+ * @param deviceToDeviceStatusSharingContacts contacts information that allow device to
+ * device sharing.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setDeviceToDeviceStatusSharingContacts(
+ @NonNull String deviceToDeviceStatusSharingContacts) {
+ Objects.requireNonNull(deviceToDeviceStatusSharingContacts);
+ mDeviceToDeviceStatusSharingContacts = deviceToDeviceStatusSharingContacts;
+ return this;
+ }
+
+ /**
+ * Set whether the user has enabled NR advanced calling.
+ *
+ * @param isNrAdvancedCallingEnabled Whether the user has enabled NR advanced calling.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNrAdvancedCallingEnabled(int isNrAdvancedCallingEnabled) {
+ mIsNrAdvancedCallingEnabled = isNrAdvancedCallingEnabled;
+ return this;
+ }
+
+ /**
+ * Set the phone number retrieved from carrier.
+ *
+ * @param numberFromCarrier The phone number retrieved from carrier.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNumberFromCarrier(@NonNull String numberFromCarrier) {
+ Objects.requireNonNull(numberFromCarrier);
+ mNumberFromCarrier = numberFromCarrier;
+ return this;
+ }
+
+ /**
+ * Set the phone number retrieved from IMS.
+ *
+ * @param numberFromIms The phone number retrieved from IMS.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNumberFromIms(@NonNull String numberFromIms) {
+ Objects.requireNonNull(numberFromIms);
+ mNumberFromIms = numberFromIms;
+ return this;
+ }
+
+ /**
+ * Set the port index of the Uicc card.
+ *
+ * @param portIndex The port index of the Uicc card.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setPortIndex(int portIndex) {
+ mPortIndex = portIndex;
+ return this;
+ }
+
+ /**
+ * Set subscription's preferred usage setting.
+ *
+ * @param usageSetting Subscription's preferred usage setting.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setUsageSetting(@UsageSetting int usageSetting) {
+ mUsageSetting = usageSetting;
+ return this;
+ }
+
+ /**
+ * Set last used TP message reference.
+ *
+ * @param lastUsedTPMessageReference Last used TP message reference.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setLastUsedTPMessageReference(
+ int lastUsedTPMessageReference) {
+ mLastUsedTPMessageReference = lastUsedTPMessageReference;
+ return this;
+ }
+
+ /**
+ * Set the user id associated with this subscription.
+ *
+ * @param userId The user id associated with this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setUserId(@UserIdInt int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ /**
+ * Set whether satellite is enabled or not.
+ * @param isSatelliteEnabled {@code 1} if satellite is enabled.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setSatelliteEnabled(int isSatelliteEnabled) {
+ mIsSatelliteEnabled = isSatelliteEnabled;
+ return this;
+ }
+
+ // Below are the fields that do not exist in the SimInfo table.
+ /**
+ * Set the card ID of the SIM card which contains the subscription.
+ *
+ * @param cardId The card ID of the SIM card which contains the subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCardId(int cardId) {
+ mCardId = cardId;
+ return this;
+ }
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+ * we should disable this opportunistic subscription.
+ *
+ * @param isGroupDisabled {@code 1} if group of the subscription is disabled.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setGroupDisabled(boolean isGroupDisabled) {
+ mIsGroupDisabled = isGroupDisabled;
+ return this;
+ }
+
+ /**
+ * Build the {@link SubscriptionInfoInternal}.
+ *
+ * @return The {@link SubscriptionInfoInternal} instance.
+ */
+ public SubscriptionInfoInternal build() {
+ return new SubscriptionInfoInternal(this);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
new file mode 100644
index 0000000..8e773c0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -0,0 +1,4046 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.TelephonyServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Telephony.SimInfo;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.service.euicc.EuiccService;
+import android.service.euicc.GetEuiccProfileInfoListResult;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
+import android.telephony.RadioAccessFamily;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.DataRoamingMode;
+import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
+import android.telephony.SubscriptionManager.PhoneNumberSource;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.SimState;
+import android.telephony.TelephonyRegistryManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Base64;
+import android.util.EventLog;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CarrierResolver;
+import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISub;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.MultiSimSettingController;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccSlot;
+import com.android.internal.telephony.util.ArrayUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * The subscription manager service is the backend service of {@link SubscriptionManager}.
+ * The service handles all SIM subscription related requests from clients.
+ */
+public class SubscriptionManagerService extends ISub.Stub {
+ private static final String LOG_TAG = "SMSVC";
+
+ /** Whether enabling verbose debugging message or not. */
+ private static final boolean VDBG = false;
+
+ /**
+ * The columns in {@link SimInfo} table that can be directly accessed through
+ * {@link #getSubscriptionProperty(int, String, String, String)} or
+ * {@link #setSubscriptionProperty(int, String, String)}. Usually those fields are not
+ * sensitive. Mostly they are related to user settings, for example, wifi calling
+ * user settings, cross sim calling user settings, etc...Those fields are protected with
+ * {@link Manifest.permission#READ_PHONE_STATE} permission only.
+ *
+ * For sensitive fields, they usually requires special methods to access. For example,
+ * {@link #getSubscriptionUserHandle(int)} or {@link #getPhoneNumber(int, int, String, String)}
+ * that requires higher permission to access.
+ */
+ private static final Set<String> DIRECT_ACCESS_SUBSCRIPTION_COLUMNS = Set.of(
+ SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT,
+ SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT,
+ SimInfo.COLUMN_CB_AMBER_ALERT,
+ SimInfo.COLUMN_CB_EMERGENCY_ALERT,
+ SimInfo.COLUMN_CB_ALERT_SOUND_DURATION,
+ SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL,
+ SimInfo.COLUMN_CB_ALERT_VIBRATE,
+ SimInfo.COLUMN_CB_ALERT_SPEECH,
+ SimInfo.COLUMN_CB_ETWS_TEST_ALERT,
+ SimInfo.COLUMN_CB_CHANNEL_50_ALERT,
+ SimInfo.COLUMN_CB_CMAS_TEST_ALERT,
+ SimInfo.COLUMN_CB_OPT_OUT_DIALOG,
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+ SimInfo.COLUMN_VT_IMS_ENABLED,
+ SimInfo.COLUMN_WFC_IMS_ENABLED,
+ SimInfo.COLUMN_WFC_IMS_MODE,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+ SimInfo.COLUMN_RCS_CONFIG,
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ SimInfo.COLUMN_D2D_STATUS_SHARING,
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+ SimInfo.COLUMN_SATELLITE_ENABLED
+ );
+
+ /**
+ * Apps targeting on Android T and beyond will get exception if there is no access to device
+ * identifiers nor has carrier privileges when calling
+ * {@link SubscriptionManager#getSubscriptionsInGroup}.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID = 213902861L;
+
+ /** Instance of subscription manager service. */
+ @NonNull
+ private static SubscriptionManagerService sInstance;
+
+ /** The context */
+ @NonNull
+ private final Context mContext;
+
+ /** App Ops manager instance. */
+ @NonNull
+ private final AppOpsManager mAppOpsManager;
+
+ /** Telephony manager instance. */
+ @NonNull
+ private final TelephonyManager mTelephonyManager;
+
+ /** Subscription manager instance. */
+ @NonNull
+ private final SubscriptionManager mSubscriptionManager;
+
+ /**
+ * Euicc manager instance. Will be null if the device does not support
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+ */
+ @Nullable
+ private final EuiccManager mEuiccManager;
+
+ /** Uicc controller instance. */
+ @NonNull
+ private final UiccController mUiccController;
+
+ /**
+ * Euicc controller instance. Will be null if the device does not support
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+ */
+ @Nullable
+ private EuiccController mEuiccController;
+
+ /**
+ * The main handler of subscription manager service. This is running on phone process's main
+ * thread.
+ */
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * The background handler. This is running on a separate thread.
+ */
+ @NonNull
+ private final Handler mBackgroundHandler;
+
+ /** Local log for most important debug messages. */
+ @NonNull
+ private final LocalLog mLocalLog = new LocalLog(256);
+
+ /** The subscription database manager. */
+ @NonNull
+ private final SubscriptionDatabaseManager mSubscriptionDatabaseManager;
+
+ /** The slot index subscription id map. Key is the slot index, and the value is sub id. */
+ @NonNull
+ private final SubscriptionMap<Integer, Integer> mSlotIndexToSubId = new SubscriptionMap<>();
+
+ /** Subscription manager service callbacks. */
+ @NonNull
+ private final Set<SubscriptionManagerServiceCallback> mSubscriptionManagerServiceCallbacks =
+ new ArraySet<>();
+
+ /**
+ * Default sub id. Derived from {@link #mDefaultVoiceSubId} and {@link #mDefaultDataSubId},
+ * depending on device capability.
+ */
+ @NonNull
+ private final WatchedInt mDefaultSubId;
+
+ /** Default voice subscription id. */
+ @NonNull
+ private final WatchedInt mDefaultVoiceSubId;
+
+ /** Default data subscription id. */
+ @NonNull
+ private final WatchedInt mDefaultDataSubId;
+
+ /** Default sms subscription id. */
+ @NonNull
+ private final WatchedInt mDefaultSmsSubId;
+
+ /** Sim state per logical SIM slot index. */
+ @NonNull
+ private final int[] mSimState;
+
+ /**
+ * Slot index/subscription map that automatically invalidate cache in
+ * {@link SubscriptionManager}.
+ *
+ * @param <K> The type of the key.
+ * @param <V> The type of the value.
+ */
+ @VisibleForTesting
+ public static class SubscriptionMap<K, V> extends ConcurrentHashMap<K, V> {
+ @Override
+ public void clear() {
+ super.clear();
+ SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
+ }
+
+ @Override
+ public V put(K key, V value) {
+ V oldValue = super.put(key, value);
+ if (!Objects.equals(oldValue, value)) {
+ SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
+ }
+ return oldValue;
+ }
+
+ @Override
+ public V remove(Object key) {
+ V oldValue = super.remove(key);
+ if (oldValue != null) {
+ SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
+ }
+ return oldValue;
+ }
+ }
+
+ /**
+ * Watched integer.
+ */
+ public static class WatchedInt {
+ protected int mValue;
+
+ /**
+ * Constructor.
+ *
+ * @param initialValue The initial value.
+ */
+ public WatchedInt(int initialValue) {
+ mValue = initialValue;
+ }
+
+ /**
+ * @return The value.
+ */
+ public int get() {
+ return mValue;
+ }
+
+ /**
+ * Set the value.
+ *
+ * @param newValue The new value.
+ *
+ * @return {@code true} if {@code newValue} is different from the existing value.
+ */
+ public boolean set(int newValue) {
+ if (mValue != newValue) {
+ mValue = newValue;
+ SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * This is the callback used for listening events from {@link SubscriptionManagerService}.
+ */
+ public static class SubscriptionManagerServiceCallback {
+ /** The executor of the callback. */
+ @NonNull
+ private final Executor mExecutor;
+
+ /**
+ * Constructor
+ *
+ * @param executor The executor of the callback.
+ */
+ public SubscriptionManagerServiceCallback(@NonNull @CallbackExecutor Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * @return The executor of the callback.
+ */
+ @NonNull
+ @VisibleForTesting
+ public Executor getExecutor() {
+ return mExecutor;
+ }
+
+ /**
+ * Invoke the callback from executor.
+ *
+ * @param runnable The callback method to invoke.
+ */
+ public void invokeFromExecutor(@NonNull Runnable runnable) {
+ mExecutor.execute(runnable);
+ }
+
+ /**
+ * Called when subscription changed.
+ *
+ * @param subId The subscription id.
+ */
+ public void onSubscriptionChanged(int subId) {}
+
+ /**
+ * Called when {@link SubscriptionInfoInternal#areUiccApplicationsEnabled()} changed.
+ *
+ * @param subId The subscription id.
+ */
+ public void onUiccApplicationsEnabledChanged(int subId) {}
+ }
+
+ /**
+ * The constructor
+ *
+ * @param context The context
+ * @param looper The looper for the handler.
+ */
+ public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper) {
+ logl("Created SubscriptionManagerService");
+ sInstance = this;
+ mContext = context;
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+ mEuiccManager = context.getSystemService(EuiccManager.class);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+
+ mUiccController = UiccController.getInstance();
+ mHandler = new Handler(looper);
+
+ HandlerThread backgroundThread = new HandlerThread(LOG_TAG);
+ backgroundThread.start();
+
+ mBackgroundHandler = new Handler(backgroundThread.getLooper());
+
+ mDefaultVoiceSubId = new WatchedInt(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID)) {
+ @Override
+ public boolean set(int newValue) {
+ int oldValue = mValue;
+ if (super.set(newValue)) {
+ logl("Default voice subId changed from " + oldValue + " to " + newValue);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, newValue);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ mDefaultDataSubId = new WatchedInt(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID)) {
+ @Override
+ public boolean set(int newValue) {
+ int oldValue = mValue;
+ if (super.set(newValue)) {
+ logl("Default data subId changed from " + oldValue + " to " + newValue);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, newValue);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ mDefaultSmsSubId = new WatchedInt(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID)) {
+ @Override
+ public boolean set(int newValue) {
+ int oldValue = mValue;
+ if (super.set(newValue)) {
+ logl("Default SMS subId changed from " + oldValue + " to " + newValue);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, newValue);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ mDefaultSubId = new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ mSimState = new int[mTelephonyManager.getSupportedModemCount()];
+ Arrays.fill(mSimState, TelephonyManager.SIM_STATE_UNKNOWN);
+
+ // Create a separate thread for subscription database manager. The database will be updated
+ // from a different thread.
+ HandlerThread handlerThread = new HandlerThread(LOG_TAG);
+ handlerThread.start();
+ mSubscriptionDatabaseManager = new SubscriptionDatabaseManager(context,
+ handlerThread.getLooper(), new SubscriptionDatabaseManagerCallback(mHandler::post) {
+ /**
+ * Called when database has been loaded into the cache.
+ */
+ @Override
+ public void onInitialized() {
+ log("Subscription database has been initialized.");
+ for (int phoneId = 0; phoneId < mTelephonyManager.getActiveModemCount()
+ ; phoneId++) {
+ markSubscriptionsInactive(phoneId);
+ }
+ }
+
+ /**
+ * Called when subscription changed.
+ *
+ * @param subId The subscription id.
+ */
+ @Override
+ public void onSubscriptionChanged(int subId) {
+ mSubscriptionManagerServiceCallbacks.forEach(
+ callback -> callback.invokeFromExecutor(
+ () -> callback.onSubscriptionChanged(subId)));
+
+ MultiSimSettingController.getInstance().notifySubscriptionInfoChanged();
+
+ TelephonyRegistryManager telephonyRegistryManager =
+ mContext.getSystemService(TelephonyRegistryManager.class);
+ if (telephonyRegistryManager != null) {
+ telephonyRegistryManager.notifySubscriptionInfoChanged();
+ }
+
+ SubscriptionInfoInternal subInfo =
+ mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId);
+ if (subInfo != null && subInfo.isOpportunistic()
+ && telephonyRegistryManager != null) {
+ telephonyRegistryManager.notifyOpportunisticSubscriptionInfoChanged();
+ }
+ }
+ });
+
+ // Broadcast sub Id on service initialized.
+ broadcastSubId(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,
+ getDefaultDataSubId());
+ broadcastSubId(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED,
+ getDefaultVoiceSubId());
+ broadcastSubId(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED,
+ getDefaultSmsSubId());
+ updateDefaultSubId();
+
+ TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer =
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer();
+ if (subscriptionServiceRegisterer.get() == null) {
+ subscriptionServiceRegisterer.register(this);
+ }
+
+ mHandler.post(() -> {
+ // EuiccController is created after SubscriptionManagerService. So we need to get
+ // the instance later in the handler.
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_EUICC)) {
+ mEuiccController = EuiccController.get();
+ }
+ });
+
+ SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
+
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateEmbeddedSubscriptions();
+ }
+ }, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+ logl("Registered iSub service");
+ }
+
+ /**
+ * @return The singleton instance of {@link SubscriptionManagerService}.
+ */
+ @NonNull
+ public static SubscriptionManagerService getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Check if the calling package can manage the subscription group.
+ *
+ * @param groupUuid a UUID assigned to the subscription group.
+ * @param callingPackage the package making the IPC.
+ *
+ * @return {@code true} if calling package is the owner of or has carrier privileges for all
+ * subscriptions in the group.
+ */
+ private boolean canPackageManageGroup(@NonNull ParcelUuid groupUuid,
+ @NonNull String callingPackage) {
+ if (groupUuid == null) {
+ throw new IllegalArgumentException("Invalid groupUuid");
+ }
+
+ if (TextUtils.isEmpty(callingPackage)) {
+ throw new IllegalArgumentException("Empty callingPackage");
+ }
+
+ List<SubscriptionInfo> infoList;
+
+ // Getting all subscriptions in the group.
+ infoList = mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(subInfo -> subInfo.getGroupUuid().equals(groupUuid.toString()))
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .collect(Collectors.toList());
+
+ // If the group does not exist, then by default the UUID is up for grabs so no need to
+ // restrict management of a group (that someone may be attempting to create).
+ if (ArrayUtils.isEmpty(infoList)) {
+ return true;
+ }
+
+ // If the calling package is the group owner, skip carrier permission check and return
+ // true as it was done before.
+ if (callingPackage.equals(infoList.get(0).getGroupOwner())) return true;
+
+ // Check carrier privilege for all subscriptions in the group.
+ return (checkCarrierPrivilegeOnSubList(infoList.stream()
+ .mapToInt(SubscriptionInfo::getSubscriptionId).toArray(), callingPackage));
+ }
+
+ /**
+ * Helper function to check if the caller has carrier privilege permissions on a list of subId.
+ * The check can either be processed against access rules on currently active SIM cards, or
+ * the access rules we keep in our database for currently inactive SIMs.
+ *
+ * @param subIdList List of subscription ids.
+ * @param callingPackage The package making the call.
+ *
+ * @throws IllegalArgumentException if the some subId is invalid or doesn't exist.
+ *
+ * @return {@code true} if checking passes on all subId, {@code false} otherwise.
+ */
+ private boolean checkCarrierPrivilegeOnSubList(@NonNull int[] subIdList,
+ @NonNull String callingPackage) {
+ for (int subId : subIdList) {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ loge("checkCarrierPrivilegeOnSubList: subId " + subId + " does not exist.");
+ return false;
+ }
+
+ if (subInfo.isActive()) {
+ if (!mTelephonyManager.hasCarrierPrivileges(subId)) {
+ loge("checkCarrierPrivilegeOnSubList: Does not have carrier privilege on sub "
+ + subId);
+ return false;
+ }
+ } else {
+ if (!mSubscriptionManager.canManageSubscription(subInfo.toSubscriptionInfo(),
+ callingPackage)) {
+ loge("checkCarrierPrivilegeOnSubList: cannot manage sub " + subId);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Sync the settings from specified subscription to all grouped subscriptions.
+ *
+ * @param subId The subscription id of the referenced subscription.
+ */
+ public void syncGroupedSetting(int subId) {
+ mHandler.post(() -> {
+ SubscriptionInfoInternal reference = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (reference == null) {
+ loge("syncSettings: Can't find subscription info for sub " + subId);
+ return;
+ }
+
+ mSubscriptionDatabaseManager.syncToGroup(subId);
+ });
+ }
+
+ /**
+ * Check whether the {@code callingPackage} has access to the phone number on the specified
+ * {@code subId} or not.
+ *
+ * @param subId The subscription id.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ * @param message Message to include in the exception or NoteOp.
+ *
+ * @return {@code true} if the caller has phone number access.
+ */
+ private boolean hasPhoneNumberAccess(int subId, @NonNull String callingPackage,
+ @Nullable String callingFeatureId, @Nullable String message) {
+ try {
+ return TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(mContext, subId,
+ callingPackage, callingFeatureId, message);
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether the {@code callingPackage} has access to subscriber identifiers on the
+ * specified {@code subId} or not.
+ *
+ * @param subId The subscription id.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ * @param message Message to include in the exception or NoteOp.
+ * @param reportFailure Indicates if failure should be reported.
+ *
+ * @return {@code true} if the caller has identifier access.
+ */
+ private boolean hasSubscriberIdentifierAccess(int subId, @NonNull String callingPackage,
+ @Nullable String callingFeatureId, @Nullable String message, boolean reportFailure) {
+ try {
+ return TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mContext, subId,
+ callingPackage, callingFeatureId, message, reportFailure);
+ } catch (SecurityException e) {
+ // A SecurityException indicates that the calling package is targeting at least the
+ // minimum level that enforces identifier access restrictions and the new access
+ // requirements are not met.
+ return false;
+ }
+ }
+
+ /**
+ * Conditionally removes identifiers from the provided {@link SubscriptionInfo} if the {@code
+ * callingPackage} does not meet the access requirements for identifiers and returns the
+ * potentially modified object.
+ *
+ * <p>
+ * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission,
+ * {@link SubscriptionInfo#getNumber()} will return empty string.
+ * If the caller does not have {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER},
+ * {@link SubscriptionInfo#getIccId()} and {@link SubscriptionInfo#getCardString()} will return
+ * empty string, and {@link SubscriptionInfo#getGroupUuid()} will return {@code null}.
+ *
+ * @param subInfo The subscription info.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ * @param message Message to include in the exception or NoteOp.
+ *
+ * @return The modified {@link SubscriptionInfo} depending on caller's permission.
+ */
+ @NonNull
+ private SubscriptionInfo conditionallyRemoveIdentifiers(@NonNull SubscriptionInfo subInfo,
+ @NonNull String callingPackage, @Nullable String callingFeatureId,
+ @Nullable String message) {
+ int subId = subInfo.getSubscriptionId();
+ boolean hasIdentifierAccess = hasSubscriberIdentifierAccess(subId, callingPackage,
+ callingFeatureId, message, true);
+ boolean hasPhoneNumberAccess = hasPhoneNumberAccess(subId, callingPackage,
+ callingFeatureId, message);
+
+ if (hasIdentifierAccess && hasPhoneNumberAccess) {
+ return subInfo;
+ }
+
+ SubscriptionInfo.Builder result = new SubscriptionInfo.Builder(subInfo);
+ if (!hasIdentifierAccess) {
+ result.setIccId(null);
+ result.setCardString(null);
+ result.setGroupUuid(null);
+ }
+
+ if (!hasPhoneNumberAccess) {
+ result.setNumber(null);
+ }
+ return result.build();
+ }
+
+ /**
+ * @return The list of ICCIDs from the inserted physical SIMs.
+ */
+ @NonNull
+ private List<String> getIccIdsOfInsertedPhysicalSims() {
+ List<String> iccidList = new ArrayList<>();
+ UiccSlot[] uiccSlots = mUiccController.getUiccSlots();
+ if (uiccSlots == null) return iccidList;
+
+ for (UiccSlot uiccSlot : uiccSlots) {
+ if (uiccSlot != null && uiccSlot.getCardState() != null
+ && uiccSlot.getCardState().isCardPresent() && !uiccSlot.isEuicc()) {
+ // Non euicc slots will have single port, so use default port index.
+ String iccId = uiccSlot.getIccId(TelephonyManager.DEFAULT_PORT_INDEX);
+ if (!TextUtils.isEmpty(iccId)) {
+ iccidList.add(IccUtils.stripTrailingFs(iccId));
+ }
+ }
+ }
+
+ return iccidList;
+ }
+
+ /**
+ * Set the subscription carrier id.
+ *
+ * @param subId Subscription id.
+ * @param carrierId The carrier id.
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid or the subscription does not
+ * exist.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ public void setCarrierId(int subId, int carrierId) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setCarrierId(subId, carrierId);
+ } catch (IllegalArgumentException e) {
+ loge("setCarrierId: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set MCC/MNC by subscription id.
+ *
+ * @param mccMnc MCC/MNC associated with the subscription.
+ * @param subId The subscription id.
+ */
+ public void setMccMnc(int subId, @NonNull String mccMnc) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setMcc(subId, mccMnc.substring(0, 3));
+ mSubscriptionDatabaseManager.setMnc(subId, mccMnc.substring(3));
+ } catch (IllegalArgumentException e) {
+ loge("setMccMnc: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set ISO country code by subscription id.
+ *
+ * @param iso ISO country code associated with the subscription.
+ * @param subId The subscription id.
+ */
+ public void setCountryIso(int subId, @NonNull String iso) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setCountryIso(subId, iso);
+ } catch (IllegalArgumentException e) {
+ loge("setCountryIso: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set the name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @param subId Subscription id.
+ * @param carrierName The carrier name.
+ */
+ public void setCarrierName(int subId, @NonNull String carrierName) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setCarrierName(subId, carrierName);
+ } catch (IllegalArgumentException e) {
+ loge("setCarrierName: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set last used TP message reference.
+ *
+ * @param subId Subscription id.
+ * @param lastUsedTPMessageReference Last used TP message reference.
+ */
+ public void setLastUsedTPMessageReference(int subId, int lastUsedTPMessageReference) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setLastUsedTPMessageReference(
+ subId, lastUsedTPMessageReference);
+ } catch (IllegalArgumentException e) {
+ loge("setLastUsedTPMessageReference: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set the enabled mobile data policies.
+ *
+ * @param subId Subscription id.
+ * @param enabledMobileDataPolicies The enabled mobile data policies.
+ */
+ public void setEnabledMobileDataPolicies(int subId, @NonNull String enabledMobileDataPolicies) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setEnabledMobileDataPolicies(
+ subId, enabledMobileDataPolicies);
+ } catch (IllegalArgumentException e) {
+ loge("setEnabledMobileDataPolicies: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Set the phone number retrieved from IMS.
+ *
+ * @param subId Subscription id.
+ * @param numberFromIms The phone number retrieved from IMS.
+ */
+ public void setNumberFromIms(int subId, @NonNull String numberFromIms) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setNumberFromIms(subId, numberFromIms);
+ } catch (IllegalArgumentException e) {
+ loge("setNumberFromIms: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Mark all subscriptions on this SIM slot index inactive.
+ *
+ * @param simSlotIndex The logical SIM slot index (i.e. phone id).
+ */
+ public void markSubscriptionsInactive(int simSlotIndex) {
+ logl("markSubscriptionsInactive: slot " + simSlotIndex);
+ mSlotIndexToSubId.remove(simSlotIndex);
+ mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(subInfo -> subInfo.getSimSlotIndex() == simSlotIndex)
+ .forEach(subInfo -> {
+ mSubscriptionDatabaseManager.setSimSlotIndex(subInfo.getSubscriptionId(),
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ // Sometime even though slot-port is inactive, proper iccid will be present,
+ // hence retry the port index from UiccSlot. (Pre-U behavior)
+ mSubscriptionDatabaseManager.setPortIndex(subInfo.getSubscriptionId(),
+ getPortIndex(subInfo.getIccId()));
+ });
+ updateGroupDisabled();
+ logl("markSubscriptionsInactive: current mapping " + slotMappingToString());
+ }
+
+ /**
+ * This is only for internal use and the returned priority is arbitrary. The idea is to give a
+ * higher value to name source that has higher priority to override other name sources.
+ *
+ * @param nameSource Source of display name.
+ *
+ * @return The priority. Higher value means higher priority.
+ */
+ private static int getNameSourcePriority(@SimDisplayNameSource int nameSource) {
+ int index = Arrays.asList(
+ SubscriptionManager.NAME_SOURCE_UNKNOWN,
+ SubscriptionManager.NAME_SOURCE_CARRIER_ID,
+ SubscriptionManager.NAME_SOURCE_SIM_PNN,
+ SubscriptionManager.NAME_SOURCE_SIM_SPN,
+ SubscriptionManager.NAME_SOURCE_CARRIER,
+ SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority.
+ ).indexOf(nameSource);
+ return Math.max(0, index);
+ }
+
+ /**
+ * Randomly pick a color from {@link R.array#sim_colors}.
+ *
+ * @return The selected color for the subscription.
+ */
+ private int getColor() {
+ int[] colors = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
+ if (colors.length == 0) return 0xFFFFFFFF; // white
+ Random rand = new Random();
+ return colors[rand.nextInt(colors.length)];
+ }
+
+ /**
+ * Get the port index by ICCID.
+ *
+ * @param iccId The ICCID.
+ * @return The port index.
+ */
+ private int getPortIndex(@NonNull String iccId) {
+ UiccSlot[] slots = mUiccController.getUiccSlots();
+ for (UiccSlot slot : slots) {
+ if (slot != null) {
+ int portIndex = slot.getPortIndexFromIccId(iccId);
+ if (portIndex != TelephonyManager.INVALID_PORT_INDEX) {
+ return portIndex;
+ }
+ }
+ }
+ return TelephonyManager.INVALID_PORT_INDEX;
+ }
+
+ /**
+ * Insert a new subscription into the database.
+ *
+ * @param iccId The ICCID.
+ * @param slotIndex The logical SIM slot index (i.e. phone id).
+ * @param displayName The display name.
+ * @param subscriptionType The subscription type.
+ *
+ * @return The subscription id.
+ */
+ private int insertSubscriptionInfo(@NonNull String iccId, int slotIndex,
+ @Nullable String displayName, @SubscriptionType int subscriptionType) {
+ String defaultAllowNetworkTypes = Phone.convertAllowedNetworkTypeMapIndexToDbName(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) + "="
+ + RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE);
+ SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder()
+ .setIccId(iccId)
+ .setCardString(iccId)
+ .setSimSlotIndex(slotIndex)
+ .setType(subscriptionType)
+ .setIconTint(getColor())
+ .setAllowedNetworkTypesForReasons(defaultAllowNetworkTypes);
+ if (displayName != null) {
+ builder.setDisplayName(displayName);
+ }
+
+ int subId = mSubscriptionDatabaseManager.insertSubscriptionInfo(builder.build());
+ logl("insertSubscriptionInfo: Inserted a new subscription. subId=" + subId
+ + ", slotIndex=" + slotIndex + ", iccId=" + SubscriptionInfo.getPrintableId(iccId)
+ + ", displayName=" + displayName + ", type="
+ + SubscriptionManager.subscriptionTypeToString(subscriptionType));
+ return subId;
+ }
+
+ /**
+ * Pull the embedded subscription from {@link EuiccController} for the eUICC with the given list
+ * of card IDs {@code cardIds}.
+ *
+ * @param cardIds The card ids of the embedded subscriptions.
+ * @param callback Callback to be called upon completion.
+ */
+ public void updateEmbeddedSubscriptions(@NonNull List<Integer> cardIds,
+ @Nullable Runnable callback) {
+ // Run this on a background thread.
+ mBackgroundHandler.post(() -> {
+ // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but
+ // they are filtered out of list calls as long as EuiccManager.isEnabled returns false).
+ if (mEuiccManager == null || !mEuiccManager.isEnabled() || mEuiccController == null) {
+ loge("updateEmbeddedSubscriptions: eUICC not enabled");
+ if (callback != null) {
+ callback.run();
+ }
+ return;
+ }
+
+ Set<Integer> embeddedSubs = new ArraySet<>();
+ log("updateEmbeddedSubscriptions: start to get euicc profiles.");
+
+ for (UiccSlot slot : mUiccController.getUiccSlots()) {
+ if (slot != null) {
+ log(" " + slot);
+ }
+ }
+
+ // The flag indicating getting successful result from EuiccController.
+ boolean isProfileUpdateSuccessful = false;
+
+ for (int cardId : cardIds) {
+ GetEuiccProfileInfoListResult result = mEuiccController
+ .blockingGetEuiccProfileInfoList(cardId);
+ logl("updateEmbeddedSubscriptions: cardId=" + cardId + ", result=" + result);
+ if (result == null) {
+ //TODO: Add back-off retry in the future if needed.
+ loge("Failed to get euicc profiles.");
+ continue;
+ }
+
+ if (result.getResult() != EuiccService.RESULT_OK) {
+ loge("Failed to get euicc profile info. result="
+ + EuiccService.resultToString(result.getResult()));
+ continue;
+ }
+
+ isProfileUpdateSuccessful = true;
+
+ if (result.getProfiles() == null || result.getProfiles().isEmpty()) {
+ loge("No profiles returned.");
+ continue;
+ }
+
+ final boolean isRemovable = result.getIsRemovable();
+
+ for (EuiccProfileInfo embeddedProfile : result.getProfiles()) {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(embeddedProfile.getIccid());
+
+ // The subscription does not exist in the database. Insert a new one here.
+ if (subInfo == null) {
+ int subId = insertSubscriptionInfo(embeddedProfile.getIccid(),
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX,
+ null, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId);
+ }
+
+ int nameSource = subInfo.getDisplayNameSource();
+ int carrierId = subInfo.getCarrierId();
+
+ SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal
+ .Builder(subInfo);
+
+ builder.setEmbedded(1);
+
+ List<UiccAccessRule> ruleList = embeddedProfile.getUiccAccessRules();
+ if (ruleList != null && !ruleList.isEmpty()) {
+ builder.setNativeAccessRules(embeddedProfile.getUiccAccessRules());
+ }
+ builder.setRemovableEmbedded(isRemovable);
+
+ // override DISPLAY_NAME if the priority of existing nameSource is <= carrier
+ String nickName = embeddedProfile.getNickname();
+ if (nickName != null
+ && getNameSourcePriority(nameSource) <= getNameSourcePriority(
+ SubscriptionManager.NAME_SOURCE_CARRIER)) {
+ builder.setDisplayName(nickName);
+ builder.setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER);
+ }
+ builder.setProfileClass(embeddedProfile.getProfileClass());
+ builder.setPortIndex(getPortIndex(embeddedProfile.getIccid()));
+
+ CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier();
+ if (cid != null) {
+ // Due to the limited subscription information, carrier id identified here
+ // might not be accurate compared with CarrierResolver. Only update carrier
+ // id if there is no valid carrier id present.
+ if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
+ builder.setCarrierId(CarrierResolver
+ .getCarrierIdFromIdentifier(mContext, cid));
+ }
+ String mcc = cid.getMcc();
+ String mnc = cid.getMnc();
+ builder.setMcc(mcc);
+ builder.setMnc(mnc);
+ }
+ // If cardId = unsupported or un-initialized, we have no reason to update DB.
+ // Additionally, if the device does not support cardId for default eUICC, the
+ // CARD_ID field should not contain the EID
+ if (cardId >= 0 && mUiccController.getCardIdForDefaultEuicc()
+ != TelephonyManager.UNSUPPORTED_CARD_ID) {
+ builder.setCardString(mUiccController.convertToCardString(cardId));
+ }
+
+ embeddedSubs.add(subInfo.getSubscriptionId());
+ subInfo = builder.build();
+ log("updateEmbeddedSubscriptions: update subscription " + subInfo);
+ mSubscriptionDatabaseManager.updateSubscription(subInfo);
+ }
+ }
+
+ // Marked the previous embedded subscriptions non-embedded if the latest profiles do
+ // not include them anymore.
+ if (isProfileUpdateSuccessful) {
+ // embeddedSubs contains all the existing embedded subs queried from EuiccManager,
+ // including active or inactive. If there are any embedded subscription in the
+ // database that is not in embeddedSubs, mark them as non-embedded. These were
+ // deleted embedded subscriptions, so we treated them as non-embedded (pre-U
+ // behavior) and they don't show up in Settings SIM page.
+ mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isEmbedded)
+ .filter(subInfo -> !embeddedSubs.contains(subInfo.getSubscriptionId()))
+ .forEach(subInfo -> {
+ logl("updateEmbeddedSubscriptions: Mark the deleted sub "
+ + subInfo.getSubscriptionId() + " as non-embedded.");
+ mSubscriptionDatabaseManager.setEmbedded(
+ subInfo.getSubscriptionId(), false);
+ });
+ if (mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .anyMatch(subInfo -> subInfo.isEmbedded()
+ && subInfo.isActive()
+ && subInfo.getPortIndex()
+ == TelephonyManager.INVALID_PORT_INDEX
+ && mSimState[subInfo.getSimSlotIndex()]
+ == TelephonyManager.SIM_STATE_LOADED)) {
+ //Report Anomaly if invalid portIndex is updated in Active subscriptions
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("38fdf63c-3bd9-4fc2-ad33-a20246a32fa7"),
+ "SubscriptionManagerService: Found Invalid portIndex"
+ + " in active subscriptions");
+ }
+ } else {
+ loge("The eSIM profiles update was not successful.");
+ }
+ log("updateEmbeddedSubscriptions: Finished embedded subscription update.");
+ // The runnable will be executed in the main thread. Pre Android-U behavior.
+ mHandler.post(() -> {
+ if (callback != null) {
+ callback.run();
+ }
+ });
+ });
+ }
+
+ /**
+ * Update embedded subscriptions from {@link EuiccController}.
+ */
+ private void updateEmbeddedSubscriptions() {
+ UiccSlot[] uiccSlots = mUiccController.getUiccSlots();
+ if (uiccSlots != null) {
+ List<Integer> cardIds = new ArrayList<>();
+ for (UiccSlot uiccSlot : uiccSlots) {
+ if (uiccSlot != null && uiccSlot.isEuicc() && uiccSlot.getUiccCard() != null) {
+ int cardId = mUiccController.convertToPublicCardId(
+ uiccSlot.getUiccCard().getCardId());
+ cardIds.add(cardId);
+ }
+ }
+ if (!cardIds.isEmpty()) {
+ updateEmbeddedSubscriptions(cardIds, null);
+ }
+ }
+ }
+
+ /**
+ * Check if the SIM application is enabled on the card or not.
+ *
+ * @param phoneId The phone id.
+ *
+ * @return {@code true} if the application is enabled.
+ */
+ public boolean areUiccAppsEnabledOnCard(int phoneId) {
+ // When uicc apps are disabled(supported in IRadio 1.5), we will still get IccId from
+ // cardStatus (since IRadio 1.2). And upon cardStatus change we'll receive another
+ // handleSimNotReady so this will be evaluated again.
+ UiccSlot slot = mUiccController.getUiccSlotForPhone(phoneId);
+ if (slot == null) return false;
+ UiccPort port = mUiccController.getUiccPort(phoneId);
+ String iccId = (port == null) ? null : port.getIccId();
+ if (iccId == null) {
+ return false;
+ }
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(IccUtils.stripTrailingFs(iccId));
+ return subInfo != null && subInfo.areUiccApplicationsEnabled();
+ }
+
+ /**
+ * Get ICCID by phone id.
+ *
+ * @param phoneId The phone id (i.e. Logical SIM slot index.)
+ *
+ * @return The ICCID. Empty string if not available.
+ */
+ @NonNull
+ private String getIccId(int phoneId) {
+ UiccPort port = mUiccController.getUiccPort(phoneId);
+ return (port == null) ? "" : TextUtils.emptyIfNull(
+ IccUtils.stripTrailingFs(port.getIccId()));
+ }
+
+ /**
+ * @return {@code true} if all the need-to-be-loaded subscriptions from SIM slots are already
+ * loaded. {@code false} if more than one are still being loaded.
+ */
+ private boolean areAllSubscriptionsLoaded() {
+ for (int phoneId = 0; phoneId < mTelephonyManager.getActiveModemCount(); phoneId++) {
+ UiccSlot slot = mUiccController.getUiccSlotForPhone(phoneId);
+ if (slot == null) {
+ log("areAllSubscriptionsLoaded: slot is null. phoneId=" + phoneId);
+ return false;
+ }
+ if (!slot.isActive()) {
+ log("areAllSubscriptionsLoaded: slot is inactive. phoneId=" + phoneId);
+ return false;
+ }
+ if (slot.isEuicc() && mUiccController.getUiccPort(phoneId) == null) {
+ log("Wait for port corresponding to phone " + phoneId + " to be active, portIndex "
+ + "is " + slot.getPortIndexFromPhoneId(phoneId));
+ return false;
+ }
+
+ if (mSimState[phoneId] == TelephonyManager.SIM_STATE_NOT_READY) {
+ // Check if this is the final state.
+ IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
+ if (!iccCard.isEmptyProfile() && areUiccAppsEnabledOnCard(phoneId)) {
+ log("areAllSubscriptionsLoaded: NOT_READY is not a final state.");
+ return false;
+ }
+ }
+
+ if (mSimState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN) {
+ log("areAllSubscriptionsLoaded: SIM " + phoneId + " state is still unknown.");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Update the subscription on the logical SIM slot index (i.e. phone id).
+ *
+ * @param phoneId The phone id (i.e. Logical SIM slot index)
+ */
+ private void updateSubscription(int phoneId) {
+ int simState = mSimState[phoneId];
+ log("updateSubscription: phoneId=" + phoneId + ", simState="
+ + TelephonyManager.simStateToString(simState));
+ for (UiccSlot slot : mUiccController.getUiccSlots()) {
+ if (slot != null) {
+ log(" " + slot);
+ }
+ }
+
+ if (simState == TelephonyManager.SIM_STATE_ABSENT) {
+ // Re-enable the pSIM when it's removed, so it will be in enabled state when it gets
+ // re-inserted again. (pre-U behavior)
+ List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
+ mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ // All the removed pSIMs (Note this could include some erased eSIM that has
+ // embedded bit removed).
+ .filter(subInfo -> !iccIds.contains(subInfo.getIccId())
+ && !subInfo.isEmbedded())
+ .forEach(subInfo -> {
+ int subId = subInfo.getSubscriptionId();
+ log("updateSubscription: Re-enable Uicc application on sub " + subId);
+ mSubscriptionDatabaseManager.setUiccApplicationsEnabled(subId, true);
+ // When sim is absent, set the port index to invalid port index.
+ // (pre-U behavior)
+ mSubscriptionDatabaseManager.setPortIndex(subId,
+ TelephonyManager.INVALID_PORT_INDEX);
+ });
+
+ if (mSlotIndexToSubId.containsKey(phoneId)) {
+ markSubscriptionsInactive(phoneId);
+ }
+ } 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.
+ IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
+ if (iccCard.isEmptyProfile()) log("updateSubscription: iccCard has empty profile.");
+ if (!iccCard.isEmptyProfile() && areUiccAppsEnabledOnCard(phoneId)) {
+ log("updateSubscription: SIM_STATE_NOT_READY is not a final state. Will update "
+ + "subscription later.");
+ return;
+ } else {
+ logl("updateSubscription: UICC app disabled on slot " + phoneId);
+ markSubscriptionsInactive(phoneId);
+ }
+ } else {
+ String iccId = getIccId(phoneId);
+ log("updateSubscription: Found iccId=" + SubscriptionInfo.getPrintableId(iccId)
+ + " on phone " + phoneId);
+
+ // For eSIM switching, SIM absent will not happen. Below is to exam if we find ICCID
+ // mismatch on the SIM slot. If that's the case, we need to mark all subscriptions on
+ // that logical slot invalid first. The correct subscription will be assigned the
+ // correct slot later.
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager.getAllSubscriptions()
+ .stream()
+ .filter(sub -> sub.getSimSlotIndex() == phoneId && !iccId.equals(
+ sub.getIccId()))
+ .findFirst()
+ .orElse(null);
+ if (subInfo != null) {
+ log("updateSubscription: Found previous active sub " + subInfo.getSubscriptionId()
+ + " that doesn't match current iccid on slot " + phoneId + ".");
+ markSubscriptionsInactive(phoneId);
+ }
+
+ if (!TextUtils.isEmpty(iccId)) {
+ // Check if the subscription already existed.
+ subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternalByIccId(iccId);
+ int subId;
+ if (subInfo == null) {
+ // This is a new SIM card. Insert a new record.
+ subId = insertSubscriptionInfo(iccId, phoneId, null,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ } else {
+ subId = subInfo.getSubscriptionId();
+ log("updateSubscription: Found existing subscription. subId= " + subId
+ + ", phoneId=" + phoneId);
+ }
+
+ subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId);
+ if (subInfo != null && subInfo.areUiccApplicationsEnabled()) {
+ mSlotIndexToSubId.put(phoneId, subId);
+ // Update the SIM slot index. This will make the subscription active.
+ mSubscriptionDatabaseManager.setSimSlotIndex(subId, phoneId);
+ logl("updateSubscription: current mapping " + slotMappingToString());
+ }
+
+ // Update the card id.
+ UiccCard card = mUiccController.getUiccCardForPhone(phoneId);
+ if (card != null) {
+ String cardId = card.getCardId();
+ if (cardId != null) {
+ mSubscriptionDatabaseManager.setCardString(subId, cardId);
+ }
+ }
+
+ // Update the port index.
+ mSubscriptionDatabaseManager.setPortIndex(subId, getPortIndex(iccId));
+
+ if (simState == TelephonyManager.SIM_STATE_LOADED) {
+ String mccMnc = mTelephonyManager.getSimOperatorNumeric(subId);
+ if (!TextUtils.isEmpty(mccMnc)) {
+ if (subId == getDefaultSubId()) {
+ MccTable.updateMccMncConfiguration(mContext, mccMnc);
+ }
+ setMccMnc(subId, mccMnc);
+ } else {
+ loge("updateSubscription: mcc/mnc is empty");
+ }
+
+ String iso = TelephonyManager.getSimCountryIsoForPhone(phoneId);
+
+ if (!TextUtils.isEmpty(iso)) {
+ setCountryIso(subId, iso);
+ } else {
+ loge("updateSubscription: sim country iso is null");
+ }
+
+ String msisdn = mTelephonyManager.getLine1Number(subId);
+ if (!TextUtils.isEmpty(msisdn)) {
+ setDisplayNumber(msisdn, subId);
+ }
+
+ String imsi = mTelephonyManager.createForSubscriptionId(
+ subId).getSubscriberId();
+ if (imsi != null) {
+ mSubscriptionDatabaseManager.setImsi(subId, imsi);
+ }
+
+ IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
+ if (iccCard != null) {
+ IccRecords records = iccCard.getIccRecords();
+ if (records != null) {
+ String[] ehplmns = records.getEhplmns();
+ if (ehplmns != null) {
+ mSubscriptionDatabaseManager.setEhplmns(subId, ehplmns);
+ }
+ String[] hplmns = records.getPlmnsFromHplmnActRecord();
+ if (hplmns != null) {
+ mSubscriptionDatabaseManager.setHplmns(subId, hplmns);
+ }
+ } else {
+ loge("updateSubscription: ICC records are not available.");
+ }
+ } else {
+ loge("updateSubscription: ICC card is not available.");
+ }
+
+ // Attempt to restore SIM specific settings when SIM is loaded.
+ mContext.getContentResolver().call(
+ SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ iccId, null);
+ log("Reload the database.");
+ mSubscriptionDatabaseManager.reloadDatabase();
+ }
+
+ log("updateSubscription: " + mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId));
+ } else {
+ log("updateSubscription: No ICCID available for phone " + phoneId);
+ mSlotIndexToSubId.remove(phoneId);
+ logl("updateSubscription: current mapping " + slotMappingToString());
+ }
+ }
+
+ if (areAllSubscriptionsLoaded()) {
+ log("Notify all subscriptions loaded.");
+ MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded();
+ }
+
+ updateGroupDisabled();
+ updateDefaultSubId();
+ }
+
+ /**
+ * Calculate the usage setting based on the carrier request.
+ *
+ * @param currentUsageSetting the current setting in the subscription DB.
+ * @param preferredUsageSetting provided by the carrier config.
+ *
+ * @return the calculated usage setting.
+ */
+ @VisibleForTesting
+ @UsageSetting public int calculateUsageSetting(@UsageSetting int currentUsageSetting,
+ @UsageSetting int preferredUsageSetting) {
+ int[] supportedUsageSettings;
+
+ // Load the resources to provide the device capability
+ try {
+ supportedUsageSettings = mContext.getResources().getIntArray(
+ com.android.internal.R.array.config_supported_cellular_usage_settings);
+ // If usage settings are not supported, return the default setting, which is UNKNOWN.
+ if (supportedUsageSettings == null
+ || supportedUsageSettings.length < 1) return currentUsageSetting;
+ } catch (Resources.NotFoundException nfe) {
+ loge("calculateUsageSetting: Failed to load usage setting resources!");
+ return currentUsageSetting;
+ }
+
+ // If the current setting is invalid, including the first time the value is set,
+ // update it to default (this will trigger a change in the DB).
+ if (currentUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
+ || currentUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
+ log("calculateUsageSetting: Updating usage setting for current subscription");
+ currentUsageSetting = SubscriptionManager.USAGE_SETTING_DEFAULT;
+ }
+
+ // Range check the inputs, and on failure, make no changes
+ if (preferredUsageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
+ || preferredUsageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
+ loge("calculateUsageSetting: Invalid usage setting!" + preferredUsageSetting);
+ return currentUsageSetting;
+ }
+
+ // Default is always allowed
+ if (preferredUsageSetting == SubscriptionManager.USAGE_SETTING_DEFAULT) {
+ return preferredUsageSetting;
+ }
+
+ // Forced setting must be explicitly supported
+ for (int supportedUsageSetting : supportedUsageSettings) {
+ if (preferredUsageSetting == supportedUsageSetting) return preferredUsageSetting;
+ }
+
+ // If the preferred setting is not possible, just keep the current setting.
+ return currentUsageSetting;
+ }
+
+ /**
+ * Called by CarrierConfigLoader to update the subscription before sending a broadcast.
+ */
+ public void updateSubscriptionByCarrierConfig(int phoneId, @NonNull String configPackageName,
+ @NonNull PersistableBundle config, @NonNull Runnable callback) {
+ mHandler.post(() -> {
+ updateSubscriptionByCarrierConfigInternal(phoneId, configPackageName, config);
+ callback.run();
+ });
+ }
+
+ private void updateSubscriptionByCarrierConfigInternal(int phoneId,
+ @NonNull String configPackageName, @NonNull PersistableBundle config) {
+ log("updateSubscriptionByCarrierConfig: phoneId=" + phoneId + ", configPackageName="
+ + configPackageName);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)
+ || TextUtils.isEmpty(configPackageName) || config == null) {
+ loge("updateSubscriptionByCarrierConfig: Failed to update the subscription. phoneId="
+ + phoneId + " configPackageName=" + configPackageName + " config="
+ + ((config == null) ? "null" : config.hashCode()));
+ return;
+ }
+
+ if (!mSlotIndexToSubId.containsKey(phoneId)) {
+ log("updateSubscriptionByCarrierConfig: No subscription is active for phone being "
+ + "updated.");
+ return;
+ }
+
+ int subId = mSlotIndexToSubId.get(phoneId);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ loge("updateSubscriptionByCarrierConfig: Couldn't retrieve subscription info for "
+ + "current subscription. subId=" + subId);
+ return;
+ }
+
+ ParcelUuid groupUuid;
+
+ // carrier certificates are not subscription-specific, so we want to load them even if
+ // this current package is not a CarrierServicePackage
+ String[] certs = config.getStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+ UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRulesFromCarrierConfig(
+ certs);
+ if (carrierConfigAccessRules != null) {
+ mSubscriptionDatabaseManager.setCarrierConfigAccessRules(
+ subId, carrierConfigAccessRules);
+ }
+
+ boolean isOpportunistic = config.getBoolean(
+ CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL,
+ subInfo.isOpportunistic());
+ mSubscriptionDatabaseManager.setOpportunistic(subId, isOpportunistic);
+
+ String groupUuidString = config.getString(
+ CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
+ String oldGroupUuidString = subInfo.getGroupUuid();
+ if (!TextUtils.isEmpty(groupUuidString)) {
+ try {
+ // Update via a UUID Structure to ensure consistent formatting
+ groupUuid = ParcelUuid.fromString(groupUuidString);
+ if (groupUuidString.equals(CarrierConfigManager.REMOVE_GROUP_UUID_STRING)) {
+ // Remove the group UUID.
+ mSubscriptionDatabaseManager.setGroupUuid(subId, "");
+ } else if (canPackageManageGroup(groupUuid, configPackageName)) {
+ mSubscriptionDatabaseManager.setGroupUuid(subId, groupUuidString);
+ mSubscriptionDatabaseManager.setGroupOwner(subId, configPackageName);
+ log("updateSubscriptionByCarrierConfig: Group added for sub " + subId);
+ } else {
+ loge("updateSubscriptionByCarrierConfig: configPackageName "
+ + configPackageName + " doesn't own groupUuid " + groupUuid);
+ }
+
+ if (!groupUuidString.equals(oldGroupUuidString)) {
+ MultiSimSettingController.getInstance()
+ .notifySubscriptionGroupChanged(groupUuid);
+ }
+ } catch (IllegalArgumentException e) {
+ loge("updateSubscriptionByCarrierConfig: Invalid Group UUID="
+ + groupUuidString);
+ }
+ }
+
+ updateGroupDisabled();
+
+ final int preferredUsageSetting = config.getInt(
+ CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT,
+ SubscriptionManager.USAGE_SETTING_UNKNOWN);
+
+ int newUsageSetting = calculateUsageSetting(
+ subInfo.getUsageSetting(), preferredUsageSetting);
+
+ if (newUsageSetting != subInfo.getUsageSetting()) {
+ mSubscriptionDatabaseManager.setUsageSetting(subId, newUsageSetting);
+ log("updateSubscriptionByCarrierConfig: UsageSetting changed,"
+ + " oldSetting=" + SubscriptionManager.usageSettingToString(
+ subInfo.getUsageSetting())
+ + " preferredSetting=" + SubscriptionManager.usageSettingToString(
+ preferredUsageSetting)
+ + " newSetting=" + SubscriptionManager.usageSettingToString(newUsageSetting));
+ }
+ }
+
+ /**
+ * Get all subscription info records from SIMs that are inserted now or previously inserted.
+ *
+ * <p>
+ * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission,
+ * {@link SubscriptionInfo#getNumber()} will return empty string.
+ * If the caller does not have {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER},
+ * {@link SubscriptionInfo#getIccId()} and {@link SubscriptionInfo#getCardString()} will return
+ * empty string, and {@link SubscriptionInfo#getGroupUuid()} will return {@code null}.
+ *
+ * <p>
+ * The carrier app will only get the list of subscriptions that it has carrier privilege on,
+ * but will have non-stripped {@link SubscriptionInfo} in the list.
+ *
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return List of all {@link SubscriptionInfo} records from SIMs that are inserted or
+ * previously inserted. Sorted by {@link SubscriptionInfo#getSimSlotIndex()}, then
+ * {@link SubscriptionInfo#getSubscriptionId()}.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public List<SubscriptionInfo> getAllSubInfoList(@NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ // Check if the caller has READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier
+ // privilege on any active subscription. The carrier app will get full subscription infos
+ // on the subs it has carrier privilege.
+ if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
+ "getAllSubInfoList")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+ }
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full
+ // list. Carrier apps can only get the subscriptions they have privileged.
+ .filter(subInfo -> TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
+ mContext, subInfo.getSubscriptionId(), callingPackage, callingFeatureId,
+ "getAllSubInfoList"))
+ // Remove the identifier if the caller does not have sufficient permission.
+ // carrier apps will get full subscription info on the subscriptions associated
+ // to them.
+ .map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(),
+ callingPackage, callingFeatureId, "getAllSubInfoList"))
+ .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
+ .thenComparing(SubscriptionInfo::getSubscriptionId))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get the active {@link SubscriptionInfo} with the subscription id key.
+ *
+ * @param subId The unique {@link SubscriptionInfo} key in database
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The subscription info.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @Nullable
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public SubscriptionInfo getActiveSubscriptionInfo(int subId, @NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
+ callingFeatureId, "getActiveSubscriptionInfo")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+ }
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null && subInfo.isActive()) {
+ return conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(), callingPackage,
+ callingFeatureId, "getActiveSubscriptionInfo");
+ }
+ return null;
+ }
+
+ /**
+ * Get the active {@link SubscriptionInfo} associated with the iccId.
+ *
+ * @param iccId the IccId of SIM card
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The subscription info.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @Nullable
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public SubscriptionInfo getActiveSubscriptionInfoForIccId(@NonNull String iccId,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ enforcePermissions("getActiveSubscriptionInfoForIccId",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ iccId = IccUtils.stripTrailingFs(iccId);
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(iccId);
+
+ return (subInfo != null && subInfo.isActive()) ? subInfo.toSubscriptionInfo() : null;
+
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get the active {@link SubscriptionInfo} associated with the logical SIM slot index.
+ *
+ * @param slotIndex the logical SIM slot index which the subscription is inserted.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return {@link SubscriptionInfo}, null for Remote-SIMs or non-active logical SIM slot index.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @Nullable
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ int subId = mSlotIndexToSubId.getOrDefault(slotIndex,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
+ callingPackage, callingFeatureId,
+ "getActiveSubscriptionInfoForSimSlotIndex")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+
+ }
+
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ throw new IllegalArgumentException("Invalid slot index " + slotIndex);
+ }
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo != null && subInfo.isActive()) {
+ return conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(), callingPackage,
+ callingFeatureId, "getActiveSubscriptionInfoForSimSlotIndex");
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted
+ * by {@link SubscriptionInfo#getSimSlotIndex} then by
+ * {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return Sorted list of the currently {@link SubscriptionInfo} records available on the
+ * device.
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public List<SubscriptionInfo> getActiveSubscriptionInfoList(@NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ // Check if the caller has READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier
+ // privilege on any active subscription. The carrier app will get full subscription infos
+ // on the subs it has carrier privilege.
+ if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
+ "getAllSubInfoList")) {
+ // Ideally we should avoid silent failure, but since this API has already been used by
+ // many apps and they do not expect the security exception, we return an empty list
+ // here so it's consistent with pre-U behavior.
+ loge("getActiveSubscriptionInfoList: " + callingPackage + " does not have enough "
+ + "permission. Returning empty list here.");
+ return Collections.emptyList();
+ }
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isActive)
+ // Remove the identifier if the caller does not have sufficient permission.
+ // carrier apps will get full subscription info on the subscriptions associated
+ // to them.
+ .map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(),
+ callingPackage, callingFeatureId, "getAllSubInfoList"))
+ .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
+ .thenComparing(SubscriptionInfo::getSubscriptionId))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get the number of active {@link SubscriptionInfo}.
+ *
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return the number of active subscriptions.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public int getActiveSubInfoCount(@NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
+ "getAllSubInfoList")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getActiveSubIdList(false).length;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * @return the maximum number of subscriptions this device will support at any one time.
+ */
+ @Override
+ public int getActiveSubInfoCountMax() {
+ return mTelephonyManager.getActiveModemCount();
+ }
+
+ /**
+ * Gets the SubscriptionInfo(s) of all available subscriptions, if any.
+ *
+ * Available subscriptions include active ones (those with a non-negative
+ * {@link SubscriptionInfo#getSimSlotIndex()}) as well as inactive but installed embedded
+ * subscriptions.
+ *
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The available subscription info.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @NonNull
+ public List<SubscriptionInfo> getAvailableSubscriptionInfoList(@NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ enforcePermissions("getAvailableSubscriptionInfoList",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ // Now that all security checks pass, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if
+ // they are in inactive slot or programmatically disabled, they are still considered
+ // available. In this case we get their iccid from slot info and include their
+ // subscriptionInfos.
+ List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId())
+ || (mEuiccManager != null && mEuiccManager.isEnabled()
+ && subInfo.isEmbedded()))
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
+ .thenComparing(SubscriptionInfo::getSubscriptionId))
+ .collect(Collectors.toList());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Gets the SubscriptionInfo(s) of all embedded subscriptions accessible to the calling app, if
+ * any.
+ *
+ * <p>Only those subscriptions for which the calling app has carrier privileges per the
+ * subscription metadata, if any, will be included in the returned list.
+ *
+ * <p>The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+ * {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * @return Sorted list of the current embedded {@link SubscriptionInfo} records available on the
+ * device which are accessible to the caller.
+ * <ul>
+ * <li>
+ *
+ * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+ * then by {@link SubscriptionInfo#getSubscriptionId}.
+ * </ul>
+ *
+ * @param callingPackage The package making the call.
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(
+ @NonNull String callingPackage) {
+ if (!mEuiccManager.isEnabled()) {
+ return null;
+ }
+
+ // Verify that the callingPackage belongs to the calling UID
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .filter(subInfo -> subInfo.isEmbedded()
+ && mSubscriptionManager.canManageSubscription(subInfo, callingPackage))
+ .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
+ .thenComparing(SubscriptionInfo::getSubscriptionId))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * @see SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
+ */
+ @Override
+ public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+ updateEmbeddedSubscriptions(List.of(cardId), null);
+ }
+
+ /**
+ * Add a new subscription info record, if needed. This should be only used for remote SIM.
+ *
+ * @param iccId ICCID of the SIM card.
+ * @param displayName human-readable name of the device the subscription corresponds to.
+ * @param slotIndex the logical SIM slot index assigned to this device.
+ * @param subscriptionType the type of subscription to be added
+ *
+ * @return 0 if success, < 0 on error
+ *
+ * @throws SecurityException if the caller does not have required permissions.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int addSubInfo(@NonNull String iccId, @NonNull String displayName, int slotIndex,
+ @SubscriptionType int subscriptionType) {
+ enforcePermissions("addSubInfo", Manifest.permission.MODIFY_PHONE_STATE);
+ logl("addSubInfo: iccId=" + SubscriptionInfo.getPrintableId(iccId) + ", slotIndex="
+ + slotIndex + ", displayName=" + displayName + ", type="
+ + SubscriptionManager.subscriptionTypeToString(subscriptionType) + ", "
+ + getCallingPackage());
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (TextUtils.isEmpty(iccId)) {
+ loge("addSubInfo: null or empty iccId");
+ return -1;
+ }
+
+ iccId = IccUtils.stripTrailingFs(iccId);
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(iccId);
+
+ // Check if the record exists or not.
+ if (subInfo == null) {
+ // Record does not exist.
+ if (mSlotIndexToSubId.containsKey(slotIndex)) {
+ loge("Already a subscription on slot " + slotIndex);
+ return -1;
+ }
+
+ int subId = insertSubscriptionInfo(iccId, slotIndex, displayName, subscriptionType);
+ updateGroupDisabled();
+ mSlotIndexToSubId.put(slotIndex, subId);
+ logl("addSubInfo: current mapping " + slotMappingToString());
+ } else {
+ // Record already exists.
+ loge("Subscription record already existed.");
+ return -1;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return 0;
+ }
+
+ /**
+ * Remove subscription info record from the subscription database.
+ *
+ * @param uniqueId This is the unique identifier for the subscription within the specific
+ * subscription type.
+ * @param subscriptionType the type of subscription to be removed.
+ *
+ * @return {@code true} if succeeded, otherwise {@code false}.
+ *
+ * @throws NullPointerException if {@code uniqueId} is {@code null}.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean removeSubInfo(@NonNull String uniqueId, int subscriptionType) {
+ enforcePermissions("removeSubInfo", Manifest.permission.MODIFY_PHONE_STATE);
+
+ logl("removeSubInfo: uniqueId=" + SubscriptionInfo.getPrintableId(uniqueId) + ", "
+ + SubscriptionManager.subscriptionTypeToString(subscriptionType) + ", "
+ + getCallingPackage());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(uniqueId);
+ if (subInfo == null) {
+ loge("Cannot find subscription with uniqueId " + uniqueId);
+ return false;
+ }
+ if (subInfo.getSubscriptionType() != subscriptionType) {
+ loge("The subscription type does not match.");
+ return false;
+ }
+ mSlotIndexToSubId.remove(subInfo.getSimSlotIndex());
+ mSubscriptionDatabaseManager.removeSubscriptionInfo(subInfo.getSubscriptionId());
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set SIM icon tint color by simInfo index.
+ *
+ * @param subId the unique subscription index in database
+ * @param tint the icon tint color of the SIM
+ *
+ * @return the number of records updated
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid or the subscription does not
+ * exist.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setIconTint(int subId, @ColorInt int tint) {
+ enforcePermissions("setIconTint", Manifest.permission.MODIFY_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid sub id passed as parameter");
+ }
+
+ mSubscriptionDatabaseManager.setIconTint(subId, tint);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set display name of a subscription.
+ *
+ * @param displayName The display name of SIM card.
+ * @param subId The subscription id.
+ * @param nameSource The display name source.
+ *
+ * @return the number of records updated
+ *
+ * @throws IllegalArgumentException if {@code nameSource} is invalid, or {@code subId} is
+ * invalid.
+ * @throws NullPointerException if {@code displayName} is {@code null}.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setDisplayNameUsingSrc(@NonNull String displayName, int subId,
+ @SimDisplayNameSource int nameSource) {
+ enforcePermissions("setDisplayNameUsingSrc", Manifest.permission.MODIFY_PHONE_STATE);
+
+ String callingPackage = getCallingPackage();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Objects.requireNonNull(displayName, "setDisplayNameUsingSrc");
+
+ if (nameSource < SubscriptionManager.NAME_SOURCE_CARRIER_ID
+ || nameSource > SubscriptionManager.NAME_SOURCE_SIM_PNN) {
+ throw new IllegalArgumentException("illegal name source " + nameSource);
+ }
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+
+ if (subInfo == null) {
+ throw new IllegalArgumentException("Cannot find subscription info with sub id "
+ + subId);
+ }
+
+ if (getNameSourcePriority(subInfo.getDisplayNameSource())
+ > getNameSourcePriority(nameSource)
+ || (getNameSourcePriority(subInfo.getDisplayNameSource())
+ == getNameSourcePriority(nameSource))
+ && (TextUtils.equals(displayName, subInfo.getDisplayName()))) {
+ log("No need to update the display name. nameSource="
+ + SubscriptionManager.displayNameSourceToString(nameSource)
+ + ", existing name=" + subInfo.getDisplayName() + ", source="
+ + SubscriptionManager.displayNameSourceToString(
+ subInfo.getDisplayNameSource()));
+ return 0;
+ }
+
+ String nameToSet;
+ if (TextUtils.isEmpty(displayName) || displayName.trim().length() == 0) {
+ nameToSet = mTelephonyManager.getSimOperatorName(subId);
+ if (TextUtils.isEmpty(nameToSet)) {
+ if (nameSource == SubscriptionManager.NAME_SOURCE_USER_INPUT
+ && SubscriptionManager.isValidSlotIndex(getSlotIndex(subId))) {
+ Resources r = Resources.getSystem();
+ nameToSet = r.getString(R.string.default_card_name,
+ (getSlotIndex(subId) + 1));
+ } else {
+ nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
+ }
+ }
+ } else {
+ nameToSet = displayName;
+ }
+
+ logl("setDisplayNameUsingSrc: subId=" + subId + ", name=" + nameToSet
+ + ", nameSource=" + SubscriptionManager.displayNameSourceToString(nameSource)
+ + ", calling package=" + callingPackage);
+ mSubscriptionDatabaseManager.setDisplayName(subId, nameToSet);
+ mSubscriptionDatabaseManager.setDisplayNameSource(subId, nameSource);
+
+ // Update the nickname on the eUICC chip if it's an embedded subscription.
+ SubscriptionInfo sub = getSubscriptionInfo(subId);
+ if (sub != null && sub.isEmbedded()) {
+ int cardId = sub.getCardId();
+ log("Updating embedded sub nickname on cardId: " + cardId);
+ mEuiccManager.updateSubscriptionNickname(subId, nameToSet,
+ // This PendingIntent simply fulfills the requirement to pass in a callback;
+ // we don't care about the result (hence 0 requestCode and no action
+ // specified on the intent).
+ PendingIntent.getService(mContext, 0 /* requestCode */, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE /* flags */));
+ }
+
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set phone number by subscription id.
+ *
+ * @param number the phone number of the SIM.
+ * @param subId the unique SubscriptionInfo index in database.
+ *
+ * @return the number of records updated.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ * @throws NullPointerException if {@code number} is {@code null}.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setDisplayNumber(@NonNull String number, int subId) {
+ enforcePermissions("setDisplayNumber", Manifest.permission.MODIFY_PHONE_STATE);
+ logl("setDisplayNumber: subId=" + subId + ", number="
+ + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, number)
+ + ", calling package=" + getCallingPackage());
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mSubscriptionDatabaseManager.setNumber(subId, number);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set data roaming by simInfo index
+ *
+ * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
+ * @param subId the unique SubscriptionInfo index in database
+ *
+ * @return the number of records updated
+ *
+ * @throws IllegalArgumentException if {@code subId} or {@code roaming} is not valid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setDataRoaming(@DataRoamingMode int roaming, int subId) {
+ enforcePermissions("setDataRoaming", Manifest.permission.MODIFY_PHONE_STATE);
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (roaming < 0) {
+ throw new IllegalArgumentException("Invalid roaming value " + roaming);
+ }
+
+ mSubscriptionDatabaseManager.setDataRoaming(subId, roaming);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Switch to a certain subscription.
+ *
+ * @param opportunistic whether it’s opportunistic subscription
+ * @param subId the unique SubscriptionInfo index in database
+ * @param callingPackage The package making the call
+ *
+ * @return the number of records updated
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_PHONE_STATE,
+ "carrier privileges",
+ })
+ public int setOpportunistic(boolean opportunistic, int subId, @NonNull String callingPackage) {
+ TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
+ mContext, Binder.getCallingUid(), subId, true, "setOpportunistic",
+ Manifest.permission.MODIFY_PHONE_STATE);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ mSubscriptionDatabaseManager.setOpportunistic(subId, opportunistic);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Inform SubscriptionManager that subscriptions in the list are bundled as a group. Typically
+ * it's a primary subscription and an opportunistic subscription. It should only affect
+ * multi-SIM scenarios where primary and opportunistic subscriptions can be activated together.
+ *
+ * Being in the same group means they might be activated or deactivated together, some of them
+ * may be invisible to the users, etc.
+ *
+ * Caller will either have {@link Manifest.permission#MODIFY_PHONE_STATE} permission or
+ * can manage all subscriptions in the list, according to their access rules.
+ *
+ * @param subIdList list of subId that will be in the same group.
+ * @param callingPackage The package making the call.
+ *
+ * @return groupUUID a UUID assigned to the subscription group. It returns null if fails.
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_PHONE_STATE,
+ "carrier privileges",
+ })
+ public ParcelUuid createSubscriptionGroup(@NonNull int[] subIdList,
+ @NonNull String callingPackage) {
+ // Verify that the callingPackage belongs to the calling UID
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+
+ Objects.requireNonNull(subIdList, "createSubscriptionGroup");
+ if (subIdList.length == 0) {
+ throw new IllegalArgumentException("Invalid subIdList " + Arrays.toString(subIdList));
+ }
+
+ // If it doesn't have modify phone state permission, or carrier privilege permission,
+ // a SecurityException will be thrown.
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED && !checkCarrierPrivilegeOnSubList(
+ subIdList, callingPackage)) {
+ throw new SecurityException("CreateSubscriptionGroup needs MODIFY_PHONE_STATE or"
+ + " carrier privilege permission on all specified subscriptions");
+ }
+
+ long identity = Binder.clearCallingIdentity();
+
+ try {
+ // Generate a UUID.
+ ParcelUuid groupUUID = new ParcelUuid(UUID.randomUUID());
+ String uuidString = groupUUID.toString();
+
+ for (int subId : subIdList) {
+ mSubscriptionDatabaseManager.setGroupUuid(subId, uuidString);
+ mSubscriptionDatabaseManager.setGroupOwner(subId, callingPackage);
+ }
+ updateGroupDisabled();
+
+ MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUUID);
+ return groupUUID;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set which subscription is preferred for cellular data. It's designed to overwrite default
+ * data subscription temporarily.
+ *
+ * @param subId which subscription is preferred to for cellular data
+ * @param needValidation whether validation is needed before switching
+ * @param callback callback upon request completion
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
+ @Nullable ISetOpportunisticDataCallback callback) {
+ enforcePermissions("setPreferredDataSubscriptionId",
+ Manifest.permission.MODIFY_PHONE_STATE);
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
+ if (phoneSwitcher == null) {
+ loge("Set preferred data sub: phoneSwitcher is null.");
+ if (callback != null) {
+ try {
+ callback.onComplete(
+ TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+ } catch (RemoteException exception) {
+ loge("RemoteException " + exception);
+ }
+ }
+ return;
+ }
+
+ phoneSwitcher.trySetOpportunisticDataSubscription(subId, needValidation, callback);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * @return The subscription id of preferred subscription for cellular data. This reflects
+ * the active modem which can serve large amount of cellular data.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getPreferredDataSubscriptionId() {
+ enforcePermissions("getPreferredDataSubscriptionId",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
+ if (phoneSwitcher == null) {
+ loge("getPreferredDataSubscriptionId: PhoneSwitcher not available. Return the "
+ + "default data sub " + getDefaultDataSubId());
+ return getDefaultDataSubId();
+ }
+
+ return phoneSwitcher.getAutoSelectedDataSubId();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get the opportunistic subscriptions.
+ *
+ * Callers with {@link Manifest.permission#READ_PHONE_STATE} or
+ * {@link Manifest.permission#READ_PRIVILEGED_PHONE_STATE} will have a full list of
+ * opportunistic subscriptions. Subscriptions that the carrier app has no privilege will be
+ * excluded from the list.
+ *
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The list of opportunistic subscription info that can be accessed by the callers.
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public List<SubscriptionInfo> getOpportunisticSubscriptions(@NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ // Check if the caller has READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier
+ // privilege on any active subscription. The carrier app will get full subscription infos
+ // on the subs it has carrier privilege.
+ if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
+ "getOpportunisticSubscriptions")) {
+ // Ideally we should avoid silent failure, but since this API has already been used by
+ // many apps and they do not expect the security exception, we return an empty list
+ // here so it's consistent with pre-U behavior.
+ loge("getOpportunisticSubscriptions: " + callingPackage + " does not have enough "
+ + "permission. Returning empty list here.");
+ return Collections.emptyList();
+ }
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full
+ // list. Carrier apps can only get the subscriptions they have privileged.
+ .filter(subInfo -> subInfo.isOpportunistic()
+ && TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
+ mContext, subInfo.getSubscriptionId(), callingPackage,
+ callingFeatureId, "getOpportunisticSubscriptions"))
+ // Remove the identifier if the caller does not have sufficient permission.
+ // carrier apps will get full subscription info on the subscriptions associated
+ // to them.
+ .map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(),
+ callingPackage, callingFeatureId, "getOpportunisticSubscriptions"))
+ .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
+ .thenComparing(SubscriptionInfo::getSubscriptionId))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Remove a list of subscriptions from their subscription group.
+ *
+ * @param subIdList list of subId that need removing from their groups.
+ * @param groupUuid The UUID of the subscription group.
+ * @param callingPackage The package making the call.
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+ * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the
+ * specified group.
+ *
+ * @see SubscriptionManager#createSubscriptionGroup(List)
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void removeSubscriptionsFromGroup(@NonNull int[] subIdList,
+ @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) {
+ // If it doesn't have modify phone state permission, or carrier privilege permission,
+ // a SecurityException will be thrown. If it's due to invalid parameter or internal state,
+ // it will return null.
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED
+ && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
+ && canPackageManageGroup(groupUuid, callingPackage))) {
+ throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or"
+ + " carrier privilege permission on all specified subscriptions.");
+ }
+
+ Objects.requireNonNull(subIdList);
+ Objects.requireNonNull(groupUuid);
+
+ if (subIdList.length == 0) {
+ throw new IllegalArgumentException("subIdList is empty.");
+ }
+
+ long identity = Binder.clearCallingIdentity();
+
+ try {
+ for (int subId : subIdList) {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ throw new IllegalArgumentException("The provided sub id " + subId
+ + " is not valid.");
+ }
+ if (!groupUuid.toString().equals(subInfo.getGroupUuid())) {
+ throw new IllegalArgumentException("Subscription " + subInfo.getSubscriptionId()
+ + " doesn't belong to group " + groupUuid);
+ }
+ }
+
+ for (SubscriptionInfoInternal subInfo :
+ mSubscriptionDatabaseManager.getAllSubscriptions()) {
+ if (IntStream.of(subIdList).anyMatch(
+ subId -> subId == subInfo.getSubscriptionId())) {
+ mSubscriptionDatabaseManager.setGroupUuid(subInfo.getSubscriptionId(), "");
+ mSubscriptionDatabaseManager.setGroupOwner(subInfo.getSubscriptionId(), "");
+ } else if (subInfo.getGroupUuid().equals(groupUuid.toString())) {
+ // Pre-T behavior. If there are still subscriptions having the same UUID, update
+ // to the new owner.
+ mSubscriptionDatabaseManager.setGroupOwner(
+ subInfo.getSubscriptionId(), callingPackage);
+ }
+ }
+
+ updateGroupDisabled();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Add a list of subscriptions into a group.
+ *
+ * Caller should either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscriptions.
+ *
+ * @param subIdList list of subId that need adding into the group
+ * @param groupUuid the groupUuid the subscriptions are being added to.
+ * @param callingPackage The package making the call.
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+ * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
+ *
+ * @see SubscriptionManager#createSubscriptionGroup(List)
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_PHONE_STATE,
+ "carrier privileges",
+ })
+ public void addSubscriptionsIntoGroup(@NonNull int[] subIdList, @NonNull ParcelUuid groupUuid,
+ @NonNull String callingPackage) {
+ Objects.requireNonNull(subIdList, "subIdList");
+ if (subIdList.length == 0) {
+ throw new IllegalArgumentException("Invalid subId list");
+ }
+
+ Objects.requireNonNull(groupUuid, "groupUuid");
+ String groupUuidString = groupUuid.toString();
+ if (groupUuidString.equals(CarrierConfigManager.REMOVE_GROUP_UUID_STRING)) {
+ throw new IllegalArgumentException("Invalid groupUuid");
+ }
+
+ // Verify that the callingPackage belongs to the calling UID
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+
+ // If it doesn't have modify phone state permission, or carrier privilege permission,
+ // a SecurityException will be thrown.
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED
+ && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
+ && canPackageManageGroup(groupUuid, callingPackage))) {
+ throw new SecurityException("Requires MODIFY_PHONE_STATE or carrier privilege"
+ + " permissions on subscriptions and the group.");
+ }
+
+ long identity = Binder.clearCallingIdentity();
+
+ try {
+ for (int subId : subIdList) {
+ mSubscriptionDatabaseManager.setGroupUuid(subId, groupUuidString);
+ mSubscriptionDatabaseManager.setGroupOwner(subId, callingPackage);
+ }
+
+ updateGroupDisabled();
+ MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
+ logl("addSubscriptionsIntoGroup: add subs " + Arrays.toString(subIdList)
+ + " to the group.");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
+ * See {@link #createSubscriptionGroup(int[], String)} for more details.
+ *
+ * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * or carrier privilege permission on the subscription.
+ *
+ * <p>Starting with API level 33, the caller also needs permission to access device identifiers
+ * to get the list of subscriptions associated with a group UUID.
+ * This method can be invoked if one of the following requirements is met:
+ * <ul>
+ * <li>If the app has carrier privilege permission.
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ * <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and
+ * access to device identifiers.
+ * </ul>
+ *
+ * @param groupUuid of which list of subInfo will be returned.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return List of {@link SubscriptionInfo} that belong to the same group, including the given
+ * subscription itself. It will return an empty list if no subscription belongs to the group.
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ // If the calling app neither has carrier privileges nor READ_PHONE_STATE and access to
+ // device identifiers, it will throw a SecurityException.
+ if (CompatChanges.isChangeEnabled(REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID,
+ Binder.getCallingUid())) {
+ try {
+ if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
+ callingPackage, callingFeatureId, "getSubscriptionsInGroup")) {
+ EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
+ throw new SecurityException("Need to have carrier privileges or access to "
+ + "device identifiers to call getSubscriptionsInGroup");
+ }
+ } catch (SecurityException e) {
+ EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
+ throw e;
+ }
+ }
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .filter(info -> groupUuid.equals(info.getGroupUuid())
+ && (mSubscriptionManager.canManageSubscription(info, callingPackage)
+ || TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
+ mContext, info.getSubscriptionId(), callingPackage,
+ callingFeatureId, "getSubscriptionsInGroup")))
+ .map(subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
+ callingPackage, callingFeatureId, "getSubscriptionsInGroup"))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get slot index associated with the subscription.
+ *
+ * @param subId The subscription id.
+ *
+ * @return Logical slot index (i.e. phone id) as a positive integer or
+ * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the supplied {@code subId} doesn't have
+ * an associated slot index.
+ */
+ @Override
+ public int getSlotIndex(int subId) {
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ for (Map.Entry<Integer, Integer> entry : mSlotIndexToSubId.entrySet()) {
+ if (entry.getValue() == subId) return entry.getKey();
+ }
+
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ }
+
+ /**
+ * Get the subscription id for specified slot index.
+ *
+ * @param slotIndex Logical SIM slot index.
+ * @return The subscription id. {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if SIM is
+ * absent.
+ */
+ @Override
+ public int getSubId(int slotIndex) {
+ if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+ slotIndex = getSlotIndex(getDefaultSubId());
+ }
+
+ // Check that we have a valid slotIndex or the slotIndex is for a remote SIM (remote SIM
+ // uses special slot index that may be invalid otherwise)
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)
+ && slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ return mSlotIndexToSubId.getOrDefault(slotIndex,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ /**
+ * Update default sub id.
+ */
+ private void updateDefaultSubId() {
+ int subId;
+ boolean isVoiceCapable = mTelephonyManager.isVoiceCapable();
+
+ if (isVoiceCapable) {
+ subId = getDefaultVoiceSubId();
+ } else {
+ subId = getDefaultDataSubId();
+ }
+
+ // If the subId is not active, use the fist active subscription's subId.
+ if (!mSlotIndexToSubId.containsValue(subId)) {
+ int[] activeSubIds = getActiveSubIdList(true);
+ if (activeSubIds.length > 0) {
+ subId = activeSubIds[0];
+ log("updateDefaultSubId: First available active sub = " + subId);
+ } else {
+ subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ }
+
+ if (mDefaultSubId.get() != subId) {
+ int phoneId = getPhoneId(subId);
+ logl("updateDefaultSubId: Default sub id updated from " + mDefaultSubId.get() + " to "
+ + subId + ", phoneId=" + phoneId);
+ mDefaultSubId.set(subId);
+
+ Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ /**
+ * @return The default subscription id.
+ */
+ @Override
+ public int getDefaultSubId() {
+ return mDefaultSubId.get();
+ }
+
+ /**
+ * Get phone id from the subscription id. In the implementation, the logical SIM slot index
+ * is equivalent to phone id. So this method is same as {@link #getSlotIndex(int)}.
+ *
+ * @param subId The subscription id.
+ *
+ * @return The phone id.
+ */
+ @Override
+ public int getPhoneId(int subId) {
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return SubscriptionManager.INVALID_PHONE_INDEX;
+ }
+
+ // slot index and phone id are equivalent in the current implementation.
+ int slotIndex = getSlotIndex(subId);
+ if (SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ return slotIndex;
+ }
+
+ return SubscriptionManager.DEFAULT_PHONE_INDEX;
+ }
+
+ /**
+ * @return Subscription id of the default cellular data. This reflects the user's default data
+ * choice, which might be a little bit different than the active one returned by
+ * {@link #getPreferredDataSubscriptionId()}.
+ */
+ @Override
+ public int getDefaultDataSubId() {
+ return mDefaultDataSubId.get();
+ }
+
+ /**
+ * Set the default data subscription id.
+ *
+ * @param subId The default data subscription id.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultDataSubId(int subId) {
+ enforcePermissions("setDefaultDataSubId", Manifest.permission.MODIFY_PHONE_STATE);
+
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUBSCRIPTION_ID");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mDefaultDataSubId.set(subId)) {
+ MultiSimSettingController.getInstance().notifyDefaultDataSubChanged();
+
+ broadcastSubId(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,
+ subId);
+
+ updateDefaultSubId();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * @return The default subscription id for voice.
+ */
+ @Override
+ public int getDefaultVoiceSubId() {
+ return mDefaultVoiceSubId.get();
+ }
+
+ /**
+ * Set the default voice subscription id.
+ *
+ * @param subId The default SMS subscription id.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultVoiceSubId(int subId) {
+ enforcePermissions("setDefaultVoiceSubId", Manifest.permission.MODIFY_PHONE_STATE);
+
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mDefaultVoiceSubId.set(subId)) {
+ broadcastSubId(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED,
+ subId);
+
+ PhoneAccountHandle newHandle = subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ ? null : mTelephonyManager.getPhoneAccountHandleForSubscriptionId(subId);
+
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ if (telecomManager != null) {
+ telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle);
+ }
+
+ updateDefaultSubId();
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * @return The default subscription id for SMS.
+ */
+ @Override
+ public int getDefaultSmsSubId() {
+ return mDefaultSmsSubId.get();
+ }
+
+ /**
+ * Set the default SMS subscription id.
+ *
+ * @param subId The default SMS subscription id.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDefaultSmsSubId(int subId) {
+ enforcePermissions("setDefaultSmsSubId", Manifest.permission.MODIFY_PHONE_STATE);
+
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mDefaultSmsSubId.set(subId)) {
+ broadcastSubId(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED,
+ subId);
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Broadcast a sub Id with the given action.
+ * @param action The intent action.
+ * @param newSubId The sub Id to broadcast.
+ */
+ private void broadcastSubId(@NonNull String action, int newSubId) {
+ Intent intent = new Intent(action);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ SubscriptionManager.putSubscriptionIdExtra(intent, newSubId);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ log("broadcastSubId action: " + action + " subId= " + newSubId);
+ }
+
+ /**
+ * Get the active subscription id list.
+ *
+ * @param visibleOnly {@code true} if only includes user visible subscription's sub id.
+ *
+ * @return List of the active subscription id.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int[] getActiveSubIdList(boolean visibleOnly) {
+ enforcePermissions("getActiveSubIdList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mSlotIndexToSubId.values().stream()
+ .filter(subId -> {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && (!visibleOnly || subInfo.isVisible()); })
+ .mapToInt(x -> x)
+ .toArray();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Set a field in the subscription database. Note not all fields are supported.
+ *
+ * @param subId Subscription Id of Subscription.
+ * @param columnName Column name in the database. Note not all fields are supported.
+ * @param value Value to store in the database.
+ *
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ * @throws SecurityException if callers do not hold the required permission.
+ *
+ * @see #getSubscriptionProperty(int, String, String, String)
+ * @see SimInfo for all the columns.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setSubscriptionProperty(int subId, @NonNull String columnName,
+ @NonNull String value) {
+ enforcePermissions("setSubscriptionProperty", Manifest.permission.MODIFY_PHONE_STATE);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ logl("setSubscriptionProperty: subId=" + subId + ", columnName=" + columnName
+ + ", value=" + value + ", calling package=" + getCallingPackage());
+
+ if (!SimInfo.getAllColumns().contains(columnName)) {
+ throw new IllegalArgumentException("Invalid column name " + columnName);
+ }
+
+ // Check if the columns are allowed to be accessed through the generic
+ // getSubscriptionProperty method.
+ if (!DIRECT_ACCESS_SUBSCRIPTION_COLUMNS.contains(columnName)) {
+ throw new SecurityException("Column " + columnName + " is not allowed be directly "
+ + "accessed through setSubscriptionProperty.");
+ }
+
+ mSubscriptionDatabaseManager.setSubscriptionProperty(subId, columnName, value);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get specific field in string format from the subscription info database.
+ *
+ * @param subId Subscription id of the subscription.
+ * @param columnName Column name in subscription database.
+ *
+ * @return Value in string format associated with {@code subscriptionId} and {@code columnName}
+ * from the database. {@code null} if the {@code subscriptionId} is invalid (for backward
+ * compatible).
+ *
+ * @throws IllegalArgumentException if the field is not exposed.
+ * @throws SecurityException if callers do not hold the required permission.
+ *
+ * @see SimInfo for all the columns.
+ */
+ @Override
+ @Nullable
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public String getSubscriptionProperty(int subId, @NonNull String columnName,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ Objects.requireNonNull(columnName);
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
+ callingPackage, callingFeatureId,
+ "getSubscriptionProperty")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+ }
+
+ if (!SimInfo.getAllColumns().contains(columnName)) {
+ throw new IllegalArgumentException("Invalid column name " + columnName);
+ }
+
+ // Check if the columns are allowed to be accessed through the generic
+ // getSubscriptionProperty method.
+ if (!DIRECT_ACCESS_SUBSCRIPTION_COLUMNS.contains(columnName)) {
+ throw new SecurityException("Column " + columnName + " is not allowed be directly "
+ + "accessed through getSubscriptionProperty.");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Object value = mSubscriptionDatabaseManager.getSubscriptionProperty(subId, columnName);
+ // The raw types of subscription database should only have 3 different types.
+ if (value instanceof Integer) {
+ return String.valueOf(value);
+ } else if (value instanceof String) {
+ return (String) value;
+ } else if (value instanceof byte[]) {
+ return Base64.encodeToString((byte[]) value, Base64.DEFAULT);
+ } else {
+ // This should not happen unless SubscriptionDatabaseManager.getSubscriptionProperty
+ // did not implement correctly.
+ throw new RuntimeException("Unexpected type " + value.getClass().getTypeName()
+ + " was returned from SubscriptionDatabaseManager for column "
+ + columnName);
+ }
+ } catch (IllegalArgumentException e) {
+ loge("getSubscriptionProperty: Invalid subId " + subId + ", columnName=" + columnName);
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Check if a subscription is active.
+ *
+ * @param subId The subscription id to check.
+ *
+ * @return {@code true} if the subscription is active.
+ *
+ * @throws IllegalArgumentException if the provided slot index is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isSubscriptionEnabled(int subId) {
+ enforcePermissions("isSubscriptionEnabled",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription id " + subId);
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && subInfo.isActive();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get the active subscription id by logical SIM slot index.
+ *
+ * @param slotIndex The logical SIM slot index.
+ * @return The active subscription id.
+ *
+ * @throws IllegalArgumentException if the provided slot index is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getEnabledSubscriptionId(int slotIndex) {
+ enforcePermissions("getEnabledSubscriptionId",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ throw new IllegalArgumentException("Invalid slot index " + slotIndex);
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(subInfo -> subInfo.isActive() && subInfo.getSimSlotIndex() == slotIndex)
+ .mapToInt(SubscriptionInfoInternal::getSubscriptionId)
+ .findFirst()
+ .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Check if a subscription is active.
+ *
+ * @param subId The subscription id.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return {@code true} if the subscription is active.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public boolean isActiveSubId(int subId, @NonNull String callingPackage,
+ @Nullable String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
+ callingFeatureId, "isActiveSubId")) {
+ throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ + "carrier privilege");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && subInfo.isActive();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get active data subscription id. Active data subscription refers to the subscription
+ * currently chosen to provide cellular internet connection to the user. This may be
+ * different from getDefaultDataSubscriptionId().
+ *
+ * @return Active data subscription id if any is chosen, or
+ * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not.
+ *
+ * @see android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
+ */
+ @Override
+ public int getActiveDataSubscriptionId() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
+ if (phoneSwitcher != null) {
+ int activeDataSubId = phoneSwitcher.getActiveDataSubId();
+ if (SubscriptionManager.isUsableSubscriptionId(activeDataSubId)) {
+ return activeDataSubId;
+ }
+ }
+ // If phone switcher isn't ready, or active data sub id is not available, use default
+ // sub id from settings.
+ return getDefaultDataSubId();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM.
+ *
+ * Physical SIM refers non-euicc, or aka non-programmable SIM.
+ *
+ * It provides whether a physical SIM card can be disabled without taking it out, which is done
+ * via {@link SubscriptionManager#setSubscriptionEnabled(int, boolean)} API.
+ *
+ * @return whether can disable subscriptions on physical SIMs.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean canDisablePhysicalSubscription() {
+ enforcePermissions("canDisablePhysicalSubscription",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Phone phone = PhoneFactory.getDefaultPhone();
+ return phone != null && phone.canDisablePhysicalSubscription();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set uicc applications being enabled or disabled.
+ *
+ * The value will be remembered on the subscription and will be applied whenever it's present.
+ * If the subscription in currently present, it will also apply the setting to modem
+ * immediately (the setting in the modem will not change until the modem receives and responds
+ * to the request, but typically this should only take a few seconds. The user visible setting
+ * available from {@link SubscriptionInfo#areUiccApplicationsEnabled()} will be updated
+ * immediately.)
+ *
+ * @param enabled whether uicc applications are enabled or disabled.
+ * @param subId which subscription to operate on.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUiccApplicationsEnabled(boolean enabled, int subId) {
+ enforcePermissions("setUiccApplicationsEnabled",
+ Manifest.permission.MODIFY_PHONE_STATE);
+ logl("setUiccApplicationsEnabled: subId=" + subId + ", enabled=" + enabled
+ + ", calling package=" + getCallingPackage());
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ throw new IllegalArgumentException("setUiccApplicationsEnabled: Subscription "
+ + "doesn't exist. subId=" + subId);
+ }
+
+ if (subInfo.areUiccApplicationsEnabled() != enabled) {
+ mSubscriptionDatabaseManager.setUiccApplicationsEnabled(subId, enabled);
+ mSubscriptionManagerServiceCallbacks.forEach(
+ callback -> callback.invokeFromExecutor(
+ () -> callback.onUiccApplicationsEnabledChanged(subId)));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set the device to device status sharing user preference for a subscription ID. The setting
+ * app uses this method to indicate with whom they wish to share device to device status
+ * information.
+ *
+ * @param sharing the status sharing preference.
+ * @param subId the unique Subscription ID in database.
+ *
+ * @return the number of records updated.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist, or the sharing
+ * preference is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharingPreference int sharing,
+ int subId) {
+ enforcePermissions("setDeviceToDeviceStatusSharing",
+ Manifest.permission.MODIFY_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (sharing < SubscriptionManager.D2D_SHARING_DISABLED
+ || sharing > SubscriptionManager.D2D_SHARING_ALL) {
+ throw new IllegalArgumentException("invalid sharing " + sharing);
+ }
+
+ mSubscriptionDatabaseManager.setDeviceToDeviceStatusSharingPreference(subId, sharing);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set the list of contacts that allow device to device status sharing for a subscription ID.
+ * The setting app uses this method to indicate with whom they wish to share device to device
+ * status information.
+ *
+ * @param contacts The list of contacts that allow device to device status sharing
+ * @param subId The unique Subscription ID in database.
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ * @throws NullPointerException if {@code contacts} is {@code null}.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public int setDeviceToDeviceStatusSharingContacts(@NonNull String contacts, int subId) {
+ enforcePermissions("setDeviceToDeviceStatusSharingContacts",
+ Manifest.permission.MODIFY_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Objects.requireNonNull(contacts, "contacts");
+ mSubscriptionDatabaseManager.setDeviceToDeviceStatusSharingContacts(subId, contacts);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Returns the phone number for the given {@code subscriptionId} and {@code source},
+ * or an empty string if not available.
+ *
+ * <p>General apps that need to know the phone number should use
+ * {@link SubscriptionManager#getPhoneNumber(int)} instead. This API may be suitable specific
+ * apps that needs to know the phone number from a specific source. For example, a carrier app
+ * needs to know exactly what's on {@link SubscriptionManager#PHONE_NUMBER_SOURCE_UICC UICC} and
+ * decide if the previously set phone number of source
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated.
+ *
+ * <p>The API provides no guarantees of what format the number is in: the format can vary
+ * depending on the {@code source} and the network etc. Programmatic parsing should be done
+ * cautiously, for example, after formatting the number to a consistent format with
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+ *
+ * <p>Note the assumption is that one subscription (which usually means one SIM) has
+ * only one phone number. The multiple sources backup each other so hopefully at least one
+ * is available. For example, for a carrier that doesn't typically set phone numbers
+ * on {@link SubscriptionManager#PHONE_NUMBER_SOURCE_UICC UICC}, the source
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS IMS} may provide one. Or, a carrier may
+ * decide to provide the phone number via source
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER carrier} if neither source UICC nor
+ * IMS is available.
+ *
+ * <p>The availability and correctness of the phone number depends on the underlying source
+ * and the network etc. Additional verification is needed to use this number for
+ * security-related or other sensitive scenarios.
+ *
+ * @param subId The subscription ID.
+ * @param source The source of the phone number.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The phone number, or an empty string if not available.
+ *
+ * @throws IllegalArgumentException if {@code source} is invalid.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ *
+ * @see SubscriptionManager#PHONE_NUMBER_SOURCE_UICC
+ * @see SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER
+ * @see SubscriptionManager#PHONE_NUMBER_SOURCE_IMS
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public String getPhoneNumber(int subId, @PhoneNumberSource int source,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
+ mContext, subId, Binder.getCallingUid(), "getPhoneNumber",
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ final long identity = Binder.clearCallingIdentity();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+
+ if (subInfo == null) {
+ loge("Invalid sub id " + subId + ", callingPackage=" + callingPackage);
+ return "";
+ }
+
+ try {
+ switch(source) {
+ case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC:
+ Phone phone = PhoneFactory.getPhone(getPhoneId(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 phone number from first available source. The order would be
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER},
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_UICC}, then
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS}.
+ *
+ * @param subId The subscription ID.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @return The phone number from the first available source.
+ *
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ * @throws SecurityException if callers do not hold the required permission.
+ */
+ @Override
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public String getPhoneNumberFromFirstAvailableSource(int subId,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
+ mContext, subId, Binder.getCallingUid(), "getPhoneNumberFromFirstAvailableSource",
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ 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 "";
+ }
+
+ /**
+ * Set the phone number of the subscription.
+ *
+ * @param subId The subscription id.
+ * @param source The phone number source.
+ * @param number The phone number.
+ * @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
+ *
+ * @throws IllegalArgumentException {@code subId} is invalid, or {@code source} is not
+ * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER}.
+ * @throws NullPointerException if {@code number} is {@code null}.
+ */
+ @Override
+ @RequiresPermission("carrier privileges")
+ public void setPhoneNumber(int subId, @PhoneNumberSource int source, @NonNull String number,
+ @NonNull String callingPackage, @Nullable String callingFeatureId) {
+ if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
+ throw new SecurityException("setPhoneNumber for CARRIER needs carrier privilege.");
+ }
+
+ if (source != SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER) {
+ throw new IllegalArgumentException("setPhoneNumber doesn't accept source "
+ + SubscriptionManager.phoneNumberSourceToString(source));
+ }
+
+ Objects.requireNonNull(number, "number");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mSubscriptionDatabaseManager.setNumberFromCarrier(subId, number);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set the Usage Setting for this subscription.
+ *
+ * @param usageSetting the usage setting for this subscription
+ * @param subId the unique SubscriptionInfo index in database
+ * @param callingPackage The package making the IPC.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist, or {@code usageSetting}
+ * is invalid.
+ * @throws SecurityException if doesn't have MODIFY_PHONE_STATE or Carrier Privileges
+ */
+ @Override
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_PHONE_STATE,
+ "carrier privileges",
+ })
+ public int setUsageSetting(@UsageSetting int usageSetting, int subId,
+ @NonNull String callingPackage) {
+ TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(
+ mContext, Binder.getCallingUid(), subId, true, "setUsageSetting",
+ Manifest.permission.MODIFY_PHONE_STATE);
+
+ if (usageSetting < SubscriptionManager.USAGE_SETTING_DEFAULT
+ || usageSetting > SubscriptionManager.USAGE_SETTING_DATA_CENTRIC) {
+ throw new IllegalArgumentException("setUsageSetting: Invalid usage setting: "
+ + usageSetting);
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSubscriptionDatabaseManager.setUsageSetting(subId, usageSetting);
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Set UserHandle for this subscription.
+ *
+ * @param userHandle the userHandle associated with the subscription
+ * Pass {@code null} user handle to clear the association
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated.
+ *
+ * @throws SecurityException if callers do not hold the required permission.
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+ public int setSubscriptionUserHandle(@Nullable UserHandle userHandle, int subId) {
+ enforcePermissions("setSubscriptionUserHandle",
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ if (userHandle == null) {
+ userHandle = UserHandle.of(UserHandle.USER_NULL);
+ }
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ mSubscriptionDatabaseManager.setUserId(subId, userHandle.getIdentifier());
+ return 1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get UserHandle of this subscription.
+ *
+ * @param subId the unique SubscriptionInfo index in database
+ * @return userHandle associated with this subscription
+ * or {@code null} if subscription is not associated with any user.
+ *
+ * @throws SecurityException if doesn't have required permission.
+ * @throws IllegalArgumentException if {@code subId} is invalid.
+ */
+ @Override
+ @Nullable
+ @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+ public UserHandle getSubscriptionUserHandle(int subId) {
+ enforcePermissions("getSubscriptionUserHandle",
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ long token = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ throw new IllegalArgumentException("getSubscriptionUserHandle: Invalid subId: "
+ + subId);
+ }
+
+ UserHandle userHandle = UserHandle.of(subInfo.getUserId());
+ logv("getSubscriptionUserHandle subId = " + subId + " userHandle = " + userHandle);
+ if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
+ return null;
+ }
+ return userHandle;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Check if subscription and user are associated with each other.
+ *
+ * @param subscriptionId the subId of the subscription
+ * @param userHandle user handle of the user
+ * @return {@code true} if subscription is associated with user
+ * {@code true} if there are no subscriptions on device
+ * else {@code false} if subscription is not associated with user.
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
+ *
+ */
+ @Override
+ public boolean isSubscriptionAssociatedWithUser(int subscriptionId,
+ @NonNull UserHandle userHandle) {
+ enforcePermissions("isSubscriptionAssociatedWithUser",
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ // Return true if there are no subscriptions on the device.
+ List<SubscriptionInfo> subInfoList = getAllSubInfoList(
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ if (subInfoList == null || subInfoList.isEmpty()) {
+ return true;
+ }
+
+ List<Integer> subIdList = subInfoList.stream().map(SubscriptionInfo::getSubscriptionId)
+ .collect(Collectors.toList());
+ if (!subIdList.contains(subscriptionId)) {
+ // Return true as this subscription is not available on the device.
+ return true;
+ }
+
+ // Get list of subscriptions associated with this user.
+ List<SubscriptionInfo> associatedSubscriptionsList =
+ getSubscriptionInfoListAssociatedWithUser(userHandle);
+ if (associatedSubscriptionsList.isEmpty()) {
+ return false;
+ }
+
+ // Return true if required subscription is present in associated subscriptions list.
+ for (SubscriptionInfo subInfo: associatedSubscriptionsList) {
+ if (subInfo.getSubscriptionId() == subscriptionId){
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get list of subscriptions associated with user.
+ *
+ * If user handle is associated with some subscriptions, return subscriptionsAssociatedWithUser
+ * else return all the subscriptions which are not associated with any user.
+ *
+ * @param userHandle user handle of the user
+ * @return list of subscriptionInfo associated with the user.
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
+ *
+ */
+ @Override
+ public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(
+ @NonNull UserHandle userHandle) {
+ enforcePermissions("getSubscriptionInfoListAssociatedWithUser",
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> subInfoList = getAllSubInfoList(
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ if (subInfoList == null || subInfoList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ List<SubscriptionInfo> subscriptionsAssociatedWithUser = new ArrayList<>();
+ List<SubscriptionInfo> subscriptionsWithNoAssociation = new ArrayList<>();
+ for (SubscriptionInfo subInfo : subInfoList) {
+ int subId = subInfo.getSubscriptionId();
+ UserHandle subIdUserHandle = getSubscriptionUserHandle(subId);
+ if (userHandle.equals(subIdUserHandle)) {
+ // Store subscriptions whose user handle matches with required user handle.
+ subscriptionsAssociatedWithUser.add(subInfo);
+ } else if (subIdUserHandle == null) {
+ // Store subscriptions whose user handle is set to null.
+ subscriptionsWithNoAssociation.add(subInfo);
+ }
+ }
+
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if ((userManager != null)
+ && (userManager.isManagedProfile(userHandle.getIdentifier()))) {
+ // For work profile, return subscriptions associated only with work profile
+ return subscriptionsAssociatedWithUser;
+ }
+
+ // For all other profiles, if subscriptionsAssociatedWithUser is empty return all the
+ // subscriptionsWithNoAssociation.
+ return subscriptionsAssociatedWithUser.isEmpty() ?
+ subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in the subscription database {@link SimInfo}.
+ * Internally, it will store the backup data in an internal file. This file will persist on
+ * device for device's lifetime and will be used later on when a SIM is inserted to restore that
+ * specific SIM's settings. End result is subscription database is modified to match any backed
+ * up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link SubscriptionManager#SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is
+ * notified if any {@link SimInfo} entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @Override
+ public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+ enforcePermissions("restoreAllSimSpecificSettingsFromBackup",
+ Manifest.permission.MODIFY_PHONE_STATE);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+ logl("restoreAllSimSpecificSettingsFromBackup");
+ mContext.getContentResolver().call(
+ SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ null, bundle);
+ // After restoring, we need to reload the content provider into the cache.
+ mSubscriptionDatabaseManager.reloadDatabase();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Register the callback for receiving information from {@link SubscriptionManagerService}.
+ *
+ * @param callback The callback.
+ */
+ public void registerCallback(@NonNull SubscriptionManagerServiceCallback callback) {
+ mSubscriptionManagerServiceCallbacks.add(callback);
+ }
+
+ /**
+ * Unregister the previously registered {@link SubscriptionManagerServiceCallback}.
+ *
+ * @param callback The callback to unregister.
+ */
+ public void unregisterCallback(@NonNull SubscriptionManagerServiceCallback callback) {
+ mSubscriptionManagerServiceCallbacks.remove(callback);
+ }
+
+ /**
+ * Enforce callers have any of the provided permissions.
+ *
+ * @param message Message to include in the exception.
+ * @param permissions The permissions to enforce.
+ *
+ * @throws SecurityException if the caller does not have any permissions.
+ */
+ private void enforcePermissions(@Nullable String message, @NonNull String ...permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ throw new SecurityException(message + ". Does not have any of the following permissions. "
+ + Arrays.toString(permissions));
+ }
+
+ /**
+ * Get the {@link SubscriptionInfoInternal} by subscription id.
+ *
+ * @param subId The subscription id.
+ *
+ * @return The subscription info. {@code null} if not found.
+ */
+ @Nullable
+ public SubscriptionInfoInternal getSubscriptionInfoInternal(int subId) {
+ return mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId);
+ }
+
+ /**
+ * Get the {@link SubscriptionInfo} by subscription id.
+ *
+ * @param subId The subscription id.
+ *
+ * @return The subscription info. {@code null} if not found.
+ */
+ @Nullable
+ public SubscriptionInfo getSubscriptionInfo(int subId) {
+ SubscriptionInfoInternal subscriptionInfoInternal = getSubscriptionInfoInternal(subId);
+ return subscriptionInfoInternal != null
+ ? subscriptionInfoInternal.toSubscriptionInfo() : null;
+ }
+
+ /**
+ * Called when SIM becomes inactive.
+ *
+ * @param slotIndex The logical SIM slot index.
+ * @param iccId iccId of the SIM in inactivate slot.
+ */
+ public void updateSimStateForInactivePort(int slotIndex, @NonNull String iccId) {
+ mHandler.post(() -> {
+ logl("updateSimStateForInactivePort: slotIndex=" + slotIndex + ", iccId="
+ + SubscriptionInfo.getPrintableId(iccId));
+ if (mSlotIndexToSubId.containsKey(slotIndex)) {
+ // Re-enable the UICC application , so it will be in enabled state when it becomes
+ // active again. (Pre-U behavior)
+ mSubscriptionDatabaseManager.setUiccApplicationsEnabled(
+ mSlotIndexToSubId.get(slotIndex), true);
+ updateSubscription(slotIndex);
+ }
+ if (!TextUtils.isEmpty(iccId)) {
+ // When port is inactive, sometimes valid iccid is present in the slot status,
+ // hence update the portIndex. (Pre-U behavior)
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternalByIccId(IccUtils.stripTrailingFs(iccId));
+ int subId;
+ if (subInfo != null) {
+ subId = subInfo.getSubscriptionId();
+ log("updateSimStateForInactivePort: Found existing subscription. subId="
+ + subId);
+ } else {
+ // If iccId is new, add a subscription record in the database so it can be
+ // activated later. (Pre-U behavior)
+ subId = insertSubscriptionInfo(IccUtils.stripTrailingFs(iccId),
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX, "",
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ mSubscriptionDatabaseManager.setDisplayName(subId,
+ mContext.getResources().getString(R.string.default_card_name, subId));
+ log("updateSimStateForInactivePort: Insert a new subscription for inactive SIM."
+ + " subId=" + subId);
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubscriptionDatabaseManager.setPortIndex(subId, getPortIndex(iccId));
+ }
+ }
+ });
+ }
+
+ /**
+ * Update SIM state. This method is supposed to be called by {@link UiccController} only.
+ *
+ * @param slotIndex The logical SIM slot index.
+ * @param simState SIM state.
+ * @param executor The executor to execute the callback.
+ * @param updateCompleteCallback The callback to call when subscription manager service
+ * completes subscription update. SIM state changed event will be broadcasted by
+ * {@link UiccController} upon receiving callback.
+ */
+ public void updateSimState(int slotIndex, @SimState int simState,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable Runnable updateCompleteCallback) {
+ mHandler.post(() -> {
+ mSimState[slotIndex] = simState;
+ logl("updateSimState: slot " + slotIndex + " "
+ + TelephonyManager.simStateToString(simState));
+ switch (simState) {
+ case TelephonyManager.SIM_STATE_ABSENT:
+ case TelephonyManager.SIM_STATE_PIN_REQUIRED:
+ case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+ case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+ case TelephonyManager.SIM_STATE_PERM_DISABLED:
+ case TelephonyManager.SIM_STATE_READY:
+ case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
+ case TelephonyManager.SIM_STATE_LOADED:
+ case TelephonyManager.SIM_STATE_NOT_READY:
+ updateSubscription(slotIndex);
+ break;
+ case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
+ default:
+ // No specific things needed to be done. Just return and broadcast the SIM
+ // states.
+ break;
+ }
+ if (executor != null && updateCompleteCallback != null) {
+ executor.execute(updateCompleteCallback);
+ }
+ });
+ }
+
+ /**
+ * Get the calling package(s).
+ *
+ * @return The calling package(s).
+ */
+ @NonNull
+ private String getCallingPackage() {
+ if (Binder.getCallingUid() == Process.PHONE_UID) {
+ // Too many packages running with phone uid. Just return one here.
+ return "com.android.phone";
+ }
+ return Arrays.toString(mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid()));
+ }
+
+ /**
+ * Update the {@link SubscriptionInfo#isGroupDisabled()} bit for the opportunistic
+ * subscriptions.
+ *
+ * If all primary (non-opportunistic) subscriptions in the group are deactivated
+ * (unplugged pSIM or deactivated eSIM profile), we should disable this opportunistic
+ * subscriptions.
+ */
+ @VisibleForTesting
+ public void updateGroupDisabled() {
+ List<SubscriptionInfo> activeSubscriptions = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName(), mContext.getFeatureId());
+ for (SubscriptionInfo oppSubInfo : getOpportunisticSubscriptions(
+ mContext.getOpPackageName(), mContext.getFeatureId())) {
+ boolean groupDisabled = activeSubscriptions.stream()
+ .noneMatch(subInfo -> !subInfo.isOpportunistic()
+ && Objects.equals(oppSubInfo.getGroupUuid(), subInfo.getGroupUuid()));
+ mSubscriptionDatabaseManager.setGroupDisabled(
+ oppSubInfo.getSubscriptionId(), groupDisabled);
+ }
+ }
+
+ /**
+ * @return The logical SIM slot/sub mapping to string.
+ */
+ @NonNull
+ private String slotMappingToString() {
+ return "[" + mSlotIndexToSubId.entrySet().stream()
+ .map(e -> "slot " + e.getKey() + ": subId=" + e.getValue())
+ .collect(Collectors.joining(", ")) + "]";
+ }
+
+ /**
+ * Log debug messages.
+ *
+ * @param s debug messages
+ */
+ private void log(@NonNull String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ /**
+ * Log error messages.
+ *
+ * @param s error messages
+ */
+ private void loge(@NonNull String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Log verbose messages.
+ *
+ * @param s verbose messages
+ */
+ private void logv(@NonNull String s) {
+ Rlog.v(LOG_TAG, s);
+ }
+
+ /**
+ * Dump the state of {@link SubscriptionManagerService}.
+ *
+ * @param fd File descriptor
+ * @param printWriter Print writer
+ * @param args Arguments
+ */
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter printWriter,
+ @NonNull String[] args) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
+ "Requires android.Manifest.permission.DUMP");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println(SubscriptionManagerService.class.getSimpleName() + ":");
+ pw.println("Active modem count=" + mTelephonyManager.getActiveModemCount());
+ pw.println("Logical SIM slot sub id mapping:");
+ pw.increaseIndent();
+ mSlotIndexToSubId.forEach((slotIndex, subId)
+ -> pw.println("Logical SIM slot " + slotIndex + ": subId=" + subId));
+ pw.decreaseIndent();
+ pw.println("ICCID:");
+ pw.increaseIndent();
+ for (int i = 0; i < mTelephonyManager.getActiveModemCount(); i++) {
+ pw.println("slot " + i + ": " + SubscriptionInfo.getPrintableId(getIccId(i)));
+ }
+ pw.decreaseIndent();
+ pw.println();
+ pw.println("defaultSubId=" + getDefaultSubId());
+ pw.println("defaultVoiceSubId=" + getDefaultVoiceSubId());
+ pw.println("defaultDataSubId=" + getDefaultDataSubId());
+ pw.println("activeDataSubId=" + getActiveDataSubscriptionId());
+ pw.println("defaultSmsSubId=" + getDefaultSmsSubId());
+ pw.println("areAllSubscriptionsLoaded=" + areAllSubscriptionsLoaded());
+ pw.println();
+ for (int i = 0; i < mSimState.length; i++) {
+ pw.println("mSimState[" + i + "]="
+ + TelephonyManager.simStateToString(mSimState[i]));
+ }
+
+ pw.println();
+ pw.println("Active subscriptions:");
+ pw.increaseIndent();
+ mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isActive).forEach(pw::println);
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("All subscriptions:");
+ pw.increaseIndent();
+ mSubscriptionDatabaseManager.getAllSubscriptions().forEach(pw::println);
+ pw.decreaseIndent();
+ pw.println();
+
+ pw.print("Embedded subscriptions: [");
+ pw.println(mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isEmbedded)
+ .map(subInfo -> String.valueOf(subInfo.getSubscriptionId()))
+ .collect(Collectors.joining(", ")) + "]");
+
+ pw.print("Opportunistic subscriptions: [");
+ pw.println(mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isOpportunistic)
+ .map(subInfo -> String.valueOf(subInfo.getSubscriptionId()))
+ .collect(Collectors.joining(", ")) + "]");
+
+ pw.print("getAvailableSubscriptionInfoList: [");
+ pw.println(getAvailableSubscriptionInfoList(
+ mContext.getOpPackageName(), mContext.getFeatureId()).stream()
+ .map(subInfo -> String.valueOf(subInfo.getSubscriptionId()))
+ .collect(Collectors.joining(", ")) + "]");
+
+ pw.print("getSelectableSubscriptionInfoList: [");
+ pw.println(mSubscriptionManager.getSelectableSubscriptionInfoList().stream()
+ .map(subInfo -> String.valueOf(subInfo.getSubscriptionId()))
+ .collect(Collectors.joining(", ")) + "]");
+
+ if (mEuiccManager != null) {
+ pw.println("Euicc enabled=" + mEuiccManager.isEnabled());
+ }
+ pw.println();
+ pw.println("Local log:");
+ pw.increaseIndent();
+ mLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println();
+ mSubscriptionDatabaseManager.dump(fd, pw, args);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnCapacity.java b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
index 479786c..f2cab4f 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
@@ -110,6 +110,21 @@
return mMaxAdnCount > 0;
}
+ @Override
+ public String toString() {
+ String capacity = "getAdnRecordsCapacity : max adn=" + mMaxAdnCount
+ + ", used adn=" + mUsedAdnCount
+ + ", max email=" + mMaxEmailCount
+ + ", used email=" + mUsedEmailCount
+ + ", max anr=" + mMaxAnrCount
+ + ", used anr=" + mUsedAnrCount
+ + ", max name length=" + mMaxNameLength
+ + ", max number length =" + mMaxNumberLength
+ + ", max email length =" + mMaxEmailLength
+ + ", max anr length =" + mMaxAnrLength;
+ return capacity;
+ }
+
public static final Parcelable.Creator<AdnCapacity> CREATOR
= new Parcelable.Creator<AdnCapacity>() {
@Override
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
index 21a6e37..90c9491 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
@@ -23,10 +23,12 @@
import android.os.Message;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.gsm.UsimPhoneBookManager;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.Locale;
/**
* {@hide}
@@ -58,14 +60,16 @@
static final int EVENT_UPDATE_ADN_DONE = 2;
//***** Constructor
-
-
-
AdnRecordCache(IccFileHandler fh) {
mFh = fh;
mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, this);
}
+ public AdnRecordCache(IccFileHandler fh, UsimPhoneBookManager usimPhoneBookManager) {
+ mFh = fh;
+ mUsimPhoneBookManager = usimPhoneBookManager;
+ }
+
//***** Called from SIMRecords
/**
@@ -156,14 +160,14 @@
int extensionEF = extensionEfForEf(efid);
if (extensionEF < 0) {
sendErrorResponse(response, "EF is not known ADN-like EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
return;
}
Message pendingResponse = mUserWriteResponse.get(efid);
if (pendingResponse != null) {
sendErrorResponse(response, "Have pending update for EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
return;
}
@@ -190,16 +194,14 @@
*/
public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
String pin2, Message response) {
-
int extensionEF;
extensionEF = extensionEfForEf(efid);
if (extensionEF < 0) {
sendErrorResponse(response, "EF is not known ADN-like EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
return;
}
-
ArrayList<AdnRecord> oldAdnList;
if (efid == EF_PBR) {
@@ -207,13 +209,11 @@
} else {
oldAdnList = getRecordsIfLoaded(efid);
}
-
if (oldAdnList == null) {
sendErrorResponse(response, "Adn list not exist for EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
return;
}
-
int index = -1;
int count = 1;
for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
@@ -223,7 +223,6 @@
}
count++;
}
-
if (index == -1) {
sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
return;
@@ -244,7 +243,7 @@
if (pendingResponse != null) {
sendErrorResponse(response, "Have pending update for EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
return;
}
@@ -307,7 +306,7 @@
if (response != null) {
AsyncResult.forMessage(response).exception
= new RuntimeException("EF is not known ADN-like EF:0x" +
- Integer.toHexString(efid).toUpperCase());
+ Integer.toHexString(efid).toUpperCase(Locale.ROOT));
response.sendToTarget();
}
@@ -342,7 +341,6 @@
handleMessage(Message msg) {
AsyncResult ar;
int efid;
-
switch(msg.what) {
case EVENT_LOAD_ALL_ADN_LIKE_DONE:
/* arg1 is efid, obj.result is ArrayList<AdnRecord>*/
@@ -381,4 +379,24 @@
break;
}
}
+
+ @VisibleForTesting
+ protected void setAdnLikeWriters(int key, ArrayList<Message> waiters) {
+ mAdnLikeWaiters.put(EF_MBDN, waiters);
+ }
+
+ @VisibleForTesting
+ protected void setAdnLikeFiles(int key, ArrayList<AdnRecord> adnRecordList) {
+ mAdnLikeFiles.put(EF_MBDN, adnRecordList);
+ }
+
+ @VisibleForTesting
+ protected void setUserWriteResponse(int key, Message message) {
+ mUserWriteResponse.put(EF_MBDN, message);
+ }
+
+ @VisibleForTesting
+ protected UsimPhoneBookManager getUsimPhoneBookManager() {
+ return mUsimPhoneBookManager;
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
index a688c6e..5b0a6a3 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
@@ -59,6 +59,7 @@
static final int EVENT_EF_LINEAR_RECORD_SIZE_DONE = 4;
static final int EVENT_UPDATE_RECORD_DONE = 5;
+ static final int VOICEMAIL_ALPHATAG_ARG = 1;
//***** Constructor
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -177,14 +178,34 @@
data = adn.buildAdnString(recordSize[0]);
if(data == null) {
- throw new RuntimeException("wrong ADN format",
- ar.exception);
+ /**
+ * The voicemail number saving to the SIM is in name(alphaTag) and number
+ * format. {@link recordSize[0]} indicates the SIM EF memory size that the
+ * sim can have to save both voicemail name and number. 14 byte of memory
+ * is reserved to save the voicemail number and remaining memory is reserved
+ * for the alphaTag. In case if we receive the alphaTag which is more than
+ * the reserved memory size then SIM will throw the exception and it don't
+ * save both the voicemail number and alphaTag. To avoid this problem, in
+ * case alphaTag length is more we nullify the alphaTag and save the same.
+ */
+ if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+ adn.mAlphaTag = null;
+ data = adn.buildAdnString(recordSize[0]);
+ }
+ if (data == null) {
+ throw new RuntimeException("wrong ADN format",
+ ar.exception);
+ }
}
-
- mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
- data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
-
+ // Send adn record to caller to track the changes made to alphaTag
+ if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+ mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
+ data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE, adn));
+ } else {
+ mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
+ data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
+ }
mPendingExtLoads = 1;
break;
@@ -195,7 +216,12 @@
ar.exception);
}
mPendingExtLoads = 0;
- mResult = null;
+ // send the adn record back to caller through the result of AsyncResult
+ if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+ mResult = ar.userObj;
+ } else {
+ mResult = null;
+ }
break;
case EVENT_ADN_LOAD_DONE:
ar = (AsyncResult)(msg.obj);
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index ec07780..f0d949d 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -20,6 +20,7 @@
import android.os.Build;
import android.telephony.SubscriptionInfo;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -90,6 +91,30 @@
public IccSlotPortMapping mSlotPortMapping;
+ public MultipleEnabledProfilesMode mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
+
+ /**
+ * Set the MultipleEnabledProfilesMode according to the input mode.
+ */
+ public void setMultipleEnabledProfilesMode(int mode) {
+ switch(mode) {
+ case 0:
+ mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
+ break;
+ case 1:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_A1;
+ break;
+ case 2:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_A2;
+ break;
+ case 3:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_MultipleEnabledProfilesMode: " + mode);
+ }
+ }
+
public void setCardState(int state) {
switch(state) {
case 0:
@@ -172,8 +197,9 @@
}
sb.append(",atr=").append(atr);
- sb.append(",iccid=").append(SubscriptionInfo.givePrintableIccid(iccid));
+ sb.append(",iccid=").append(SubscriptionInfo.getPrintableId(iccid));
sb.append(",eid=").append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, eid));
+ sb.append(",SupportedMepMode=").append(mSupportedMepMode);
sb.append(",SlotPortMapping=").append(mSlotPortMapping);
sb.append("}");
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 5eae070..a9275be 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -45,6 +45,8 @@
static final int EF_SST = 0x6F38;
static final int EF_CFIS = 0x6FCB;
static final int EF_IMG = 0x4F20;
+ static final int EF_PSISMSC = 0x6FE5;
+ static final int EF_SMSS = 0x6F43;
// USIM SIM file ids from TS 131.102
public static final int EF_PBR = 0x4F30;
@@ -84,7 +86,6 @@
static final int EF_DOMAIN = 0x6F03;
static final int EF_IST = 0x6F07;
static final int EF_PCSCF = 0x6F09;
- static final int EF_PSI = 0x6FE5;
//PLMN Selection Information w/ Access Technology TS 131.102
static final int EF_PLMN_W_ACT = 0x6F60;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index f3b1d0b..5f8ccb4 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.Message;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import java.util.ArrayList;
@@ -108,7 +109,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final String mAid;
- static class LoadLinearFixedContext {
+ public static class LoadLinearFixedContext {
int mEfid;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -145,14 +146,11 @@
mOnLoaded = onLoaded;
mPath = path;
}
+ }
- LoadLinearFixedContext(int efid, Message onLoaded) {
- mEfid = efid;
- mRecordNum = 1;
- mLoadAll = true;
- mOnLoaded = onLoaded;
- mPath = null;
- }
+ @VisibleForTesting
+ public int getEfid(LoadLinearFixedContext lc) {
+ return lc.mEfid;
}
/**
@@ -164,6 +162,13 @@
mCi = ci;
}
+ @VisibleForTesting
+ public IccFileHandler(CommandsInterface ci) {
+ mParentApp = null;
+ mAid = null;
+ mCi = ci;
+ }
+
public void dispose() {
}
@@ -267,8 +272,7 @@
* @param path Path of the EF on the card
* @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the size of data int
*/
- public void getEFTransparentRecordSize(int fileid, String path, Message onLoaded) {
- String efPath = (path == null) ? getEFPath(fileid) : path;
+ public void getEFTransparentRecordSize(int fileid, Message onLoaded) {
Message response = obtainMessage(EVENT_GET_EF_TRANSPARENT_SIZE_DONE, fileid, 0, onLoaded);
mCi.iccIOForApp(
COMMAND_GET_RESPONSE,
@@ -284,16 +288,6 @@
}
/**
- * Get record size for a transparent EF
- *
- * @param fileid EF id
- * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the size of the data int
- */
- public void getEFTransparentRecordSize(int fileid, Message onLoaded) {
- getEFTransparentRecordSize(fileid, getEFPath(fileid), onLoaded);
- }
-
- /**
* Load all records from a SIM Linear Fixed EF
*
* @param fileid EF id
@@ -693,7 +687,7 @@
case EF_EXT1:
case EF_EXT2:
case EF_EXT3:
- case EF_PSI:
+ case EF_PSISMSC:
return MF_SIM + DF_TELECOM;
case EF_ICCID:
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 3a7db7f..f803696 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -30,19 +30,23 @@
import android.telephony.SubscriptionInfo;
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.CommandsInterface;
import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.gsm.SimTlv;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
@@ -53,8 +57,11 @@
* {@hide}
*/
public abstract class IccRecords extends Handler implements IccConstants {
+ private static final String LOG_TAG = "IccRecords";
protected static final boolean DBG = true;
- protected static final boolean VDBG = false; // STOPSHIP if true
+ private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+ protected static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+ Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
public static final int PLMN_MIN_LENGTH = CellIdentity.MCC_LENGTH
+ CellIdentity.MNC_MIN_LENGTH;
@@ -119,6 +126,10 @@
protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
protected int mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
+ // EF_SMSS fields tpmr invalid, min and max declarations
+ protected static final int SMSS_INVALID_TPMR = -1;
+ private static final int TPMR_MIN = 0x00;
+ private static final int TPMR_MAX = 0xFF;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public String mIccId; // Includes only decimals (no hex)
@@ -173,6 +184,14 @@
protected String[] mEhplmns;
protected String[] mFplmns;
+ // SIP or TEL URI [ Public Service Identity of the SM-SC]
+ // Reference: TS 31.102 section 4.5.9
+ protected String mPsiSmsc;
+
+ // EF_SMSS value which is combination of TPMR and Memory exceed flag
+ // Reference: TS 31.102 section 4.2.9
+ protected byte[] mSmssValues;
+
CarrierTestOverride mCarrierTestOverride;
//Arbitrary offset for the Handler
@@ -232,6 +251,14 @@
// arrive and returning null to the callers.
private static final long ICC_SIM_CHALLENGE_TIMEOUT_MILLIS = 2500;
+ // TAG value to retrieve EF_PSISMSC from parsed SimTlv object
+ private static final int TAG_TLV_USIM_VALUE_80 = 0x80;
+
+ // call back received on this upon EF_SMSS record update.
+ public static final int EVENT_SET_SMSS_RECORD_DONE = 201;
+
+ private static final int EVENT_GET_FDN_DONE = 202;
+
/**
* There are two purposes for this class. First, each instance of AuthAsyncResponse acts as a
* lock to for calling thead to wait in getIccSimChallengeResponse(). Second, pass the IMS
@@ -244,7 +271,7 @@
@Override
public String toString() {
- String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
+ String iccIdToPrint = SubscriptionInfo.getPrintableId(mFullIccId);
return "mDestroyed=" + mDestroyed
+ " mContext=" + mContext
+ " mCi=" + mCi
@@ -954,6 +981,35 @@
}
break;
+ case EVENT_SET_SMSS_RECORD_DONE:
+ ar = (AsyncResult) msg.obj;
+ SmssRecord smssRecord = null;
+ if (ar.userObj != null) {
+ smssRecord = (SmssRecord) ar.userObj;
+ }
+ if (ar.exception == null && smssRecord.getSmssValue() != null) {
+ mSmssValues = smssRecord.getSmssValue().clone();
+ } else {
+ loge("SIM EF_SMSS field updating error=" + ar.exception);
+ }
+ if (smssRecord != null && smssRecord.getMessage() != null) {
+ Message message = smssRecord.getMessage();
+ AsyncResult.forMessage(message, ar.result, ar.exception);
+ message.sendToTarget();
+ } else {
+ loge("smssRecord or smssRecord.getMessage() object is null");
+ }
+ break;
+
+ case EVENT_GET_FDN_DONE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loge("Failed to read USIM EF_FDN field error=" + ar.exception);
+ } else {
+ log("EF_FDN read successfully");
+ }
+ break;
+
default:
super.handleMessage(msg);
}
@@ -1306,6 +1362,34 @@
return mSmsCountOnIcc;
}
+ /**
+ * parse EF PSISMSC value [3GPP TS 31.102 Section 4.5.9]
+ *
+ * @param data read from EF PSISMSC field of type byte[]
+ * @return SIP URI or tel URI of type string
+ */
+ protected String parseEfPsiSmsc(byte[] data) {
+ SimTlv tlv = new SimTlv(data, 0, data.length);
+ if (tlv.isValidObject() && tlv.getData() != null) {
+ if (tlv.getTag() == TAG_TLV_USIM_VALUE_80) {
+ return new String(tlv.getData(), Charset.forName("UTF-8"));
+ }
+ }
+ if (VDBG) {
+ log("Can't find EF PSISMSC field in SIM = " + IccUtils.bytesToHexString(data));
+ }
+ return null;
+ }
+
+ /**
+ * SMSC address read from the elementary file EF_PSISMSC
+ *
+ * @return SIP URI or tel URI of type string
+ */
+ public String getSmscIdentity() {
+ return mPsiSmsc;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("IccRecords: " + this);
pw.println(" mDestroyed=" + mDestroyed);
@@ -1355,7 +1439,7 @@
pw.println(" mRecordsToLoad=" + mRecordsToLoad);
pw.println(" mRdnCache=" + mAdnCache);
- String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
+ String iccIdToPrint = SubscriptionInfo.getPrintableId(mFullIccId);
pw.println(" iccid=" + iccIdToPrint);
pw.println(" mMsisdn=" + Rlog.pii(VDBG, mMsisdn));
pw.println(" mMsisdnTag=" + mMsisdnTag);
@@ -1532,4 +1616,81 @@
return "{fullName = " + fullName + ", shortName = " + shortName + "}";
}
}
+
+ /**
+ * Sets the elementary (EF_SMSS) field with latest last used TP-Message reference value.
+ * First byte of EF_SMSS represents the TPMR value as per the spec
+ * (Section 4.2.9 of 3GPP TS 31.102)
+ *
+ * @param tpmr: Last used TP-Message reference parameter of type int
+ * @param onComplete: android.os.Message to be notified upon completion
+ */
+ public void setSmssTpmrValue(int tpmr, Message onComplete) {
+ if(VDBG) log("setSmssTpmrValue()");
+ if (mSmssValues != null && mSmssValues.length > 0 && tpmr >= TPMR_MIN && tpmr <= TPMR_MAX) {
+ byte[] tempSmss = mSmssValues.clone();
+ tempSmss[0] = (byte) (tpmr & 0xFF);
+ SmssRecord smssRecord = createSmssRecord(onComplete, tempSmss);
+ mFh.updateEFTransparent(IccConstants.EF_SMSS, tempSmss,
+ obtainMessage(EVENT_SET_SMSS_RECORD_DONE, smssRecord));
+ } else if (onComplete != null) {
+ loge("Failed to set EF_SMSS [TPMR] field to SIM");
+ if (mSmssValues == null || mSmssValues.length <= 0) {
+ AsyncResult.forMessage((onComplete)).exception =
+ new FileNotFoundException("EF_SMSS file not found");
+ } else if (tpmr < TPMR_MIN || tpmr > TPMR_MAX) {
+ AsyncResult.forMessage((onComplete)).exception =
+ new IllegalArgumentException("TPMR value is not in allowed range");
+ }
+ onComplete.sendToTarget();
+ }
+ }
+
+ /**
+ * Fetches the last used TPMR value from elementary (EF_SMSS) field. First byte of EF_SMSS
+ * represents the TPMR value as per the spec (Section 4.2.9 of 3GPP TS 31.102)
+ *
+ * @return TP-Message reference parameter of type int, -1 in case if it fails to read the
+ * EF_SMSS field from the sim.
+ */
+ public int getSmssTpmrValue() {
+ if (mSmssValues != null && mSmssValues.length > 0) {
+ return (mSmssValues[0] & 0xFF);
+ }
+ loge("IccRecords - EF_SMSS is null");
+ return SMSS_INVALID_TPMR;
+ }
+
+ @VisibleForTesting
+ public SmssRecord createSmssRecord(Message msg, byte[] smss) {
+ return new SmssRecord(msg, smss);
+ }
+
+
+ static class SmssRecord {
+
+ private Message mMsg;
+ private byte[] mSmss;
+
+ SmssRecord (Message msg, byte[] smss) {
+ mMsg = msg;
+ mSmss = smss;
+ }
+
+ private byte[] getSmssValue() {
+ return mSmss;
+ }
+
+ private Message getMessage() {
+ return mMsg;
+ }
+ }
+
+ public void loadFdnRecords() {
+ if (mParentApp != null) {
+ log("Loading FdnRecords");
+ mAdnCache.requestLoadAllAdnLike(IccConstants.EF_FDN, EF_EXT2,
+ obtainMessage(EVENT_GET_FDN_DONE));
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java b/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
index 9a5e10d..4164a1e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
+++ b/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
@@ -51,7 +51,7 @@
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{").append("iccid=")
- .append(SubscriptionInfo.givePrintableIccid(mIccId)).append(",")
+ .append(SubscriptionInfo.getPrintableId(mIccId)).append(",")
.append("logicalSlotIndex=").append(mLogicalSlotIndex).append(",")
.append("portActive=").append(mPortActive)
.append("}");
diff --git a/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
index 96a3a33..8e55f7c 100644
--- a/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
@@ -30,11 +30,46 @@
/* Added state active to check slotState in old HAL case.*/
public static final int STATE_ACTIVE = 1;
+ public enum MultipleEnabledProfilesMode {
+ /**
+ * If there is no jointly supported MEP mode, set supported MEP mode to NONE.
+ */
+ NONE,
+ /**
+ * In case of MEP-A1, the ISD-R is selected on eSIM port 0 only and profiles are selected
+ * on eSIM ports 1 and higher, with the eSIM port being assigned by the LPA or platform.
+ */
+ MEP_A1,
+ /**
+ * In case of MEP-A2, the ISD-R is selected on eSIM port 0 only and profiles are selected
+ * on eSIM ports 1 and higher, with the eSIM port being assigned by the eUICC.
+ */
+ MEP_A2,
+ /**
+ * In case of MEP-B, profiles are selected on eSIM ports 0 and higher, with the ISD-R being
+ * selectable on any of these eSIM ports.
+ */
+ MEP_B;
+
+ public boolean isMepAMode() {
+ return (this == MEP_A1 || this == MEP_A2);
+ }
+
+ public boolean isMepA1Mode() {
+ return this == MEP_A1;
+ }
+
+ public boolean isMepMode() {
+ return this != NONE;
+ }
+ }
+
public IccCardStatus.CardState cardState;
public String atr;
public String eid;
public IccSimPortInfo[] mSimPortInfos;
+ public MultipleEnabledProfilesMode mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
/**
* Set the cardState according to the input state.
@@ -58,6 +93,28 @@
}
}
+ /**
+ * Set the MultipleEnabledProfilesMode according to the input mode.
+ */
+ public void setMultipleEnabledProfilesMode(int mode) {
+ switch(mode) {
+ case 0:
+ mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
+ break;
+ case 1:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_A1;
+ break;
+ case 2:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_A2;
+ break;
+ case 3:
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_MultipleEnabledProfilesMode: " + mode);
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -72,6 +129,7 @@
} else {
sb.append("num_ports=null");
}
+ sb.append(", SupportedMepMode=" + mSupportedMepMode);
sb.append("}");
return sb.toString();
}
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
index 412295d..7666f4c 100644
--- a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
@@ -38,6 +38,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* Utility methods for installing the carrier app when a SIM is insterted without the carrier app
@@ -178,7 +179,7 @@
*/
@VisibleForTesting
public static String getAppNameFromPackageName(String packageName, String mapString) {
- packageName = packageName.toLowerCase();
+ packageName = packageName.toLowerCase(Locale.ROOT);
final String pairDelim = "\\s*;\\s*";
final String keyValueDelim = "\\s*:\\s*";
diff --git a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
index fe900cb..4d5f8d1 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
@@ -38,6 +38,7 @@
case EF_DOMAIN:
case EF_IST:
case EF_PCSCF:
+ case EF_SMSS:
return MF_SIM + DF_ADF;
}
String path = getCommonIccEFPath(efid);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java b/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java
new file mode 100644
index 0000000..289229c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+public final class IsimServiceTable extends IccServiceTable {
+ private static final String TAG = "IsimServiceTable";
+ public enum IsimService {
+ PCSCF_ADDRESS,
+ GBA, // Generic Bootstrapping Architecture (GBA)
+ HTTP_DIGEST,
+ GBA_LOCALKEY_ESTABLISHMENT, // GBA-based Local Key Establishment Mechanism
+ PCSCF_DISCOVERY_FOR_IMS, // Support of P-CSCF discovery for IMS Local Break Out
+ SMS,
+ SMSR, // Short Message Status Reports
+ SM_OVERIP_AND_DATA_DL_VIA_SMS_PP, // Support for SM-over-IP including data download via
+ // SMS-PP
+ COMMUNICATION_CONTROL_FOR_IMS_BY_ISIM,
+ UICC_ACCESS_TO_IMS
+ }
+
+ public IsimServiceTable(byte[] table) {
+ super(table);
+ }
+
+ public boolean isAvailable(IsimService service) {
+ return super.isAvailable(service.ordinal());
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected Object[] getValues() {
+ return IsimService.values();
+ }
+
+ public byte[] getISIMServiceTable() {
+ return mServiceTable;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 1fe6223..9591a498 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -23,7 +23,9 @@
import android.os.Build;
import android.os.Message;
import android.telephony.SubscriptionManager;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.gsm.SimTlv;
import com.android.telephony.Rlog;
@@ -41,7 +43,9 @@
protected static final String LOG_TAG = "IsimUiccRecords";
private static final boolean DBG = true;
- private static final boolean VDBG = false; // STOPSHIP if true
+ private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+ private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+ Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
private static final boolean DUMP_RECORDS = false; // Note: PII is logged when this is true
// STOPSHIP if true
public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
@@ -67,9 +71,11 @@
return "IsimUiccRecords: " + super.toString()
+ (DUMP_RECORDS ? (" mIsimImpi=" + mIsimImpi
+ " mIsimDomain=" + mIsimDomain
- + " mIsimImpu=" + mIsimImpu
+ + " mIsimImpu=" + Arrays.toString(mIsimImpu)
+ " mIsimIst=" + mIsimIst
- + " mIsimPcscf=" + mIsimPcscf) : "");
+ + " mIsimPcscf=" + Arrays.toString(mIsimPcscf)
+ + " mPsiSmsc=" + mPsiSmsc
+ + " mSmss TPMR=" + getSmssTpmrValue()) : "");
}
public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -111,7 +117,6 @@
broadcastRefresh();
super.handleMessage(msg);
break;
-
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
@@ -143,6 +148,13 @@
mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
mRecordsToLoad++;
+ mFh.loadEFTransparent(EF_SMSS, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimSmssLoaded()));
+ mRecordsToLoad++;
+
+ mFh.loadEFLinearFixed(EF_PSISMSC, 1, obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPsiSmscLoaded()));
+ mRecordsToLoad++;
if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
}
@@ -212,6 +224,28 @@
if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
}
}
+
+ @VisibleForTesting
+ public EfIsimIstLoaded getIsimIstObject() {
+ return new EfIsimIstLoaded();
+ }
+
+ private class EfIsimSmssLoaded implements IccRecords.IccRecordLoaded {
+
+ @Override
+ public String getEfName() {
+ return "EF_ISIM_SMSS";
+ }
+
+ @Override
+ public void onRecordLoaded(AsyncResult ar) {
+ mSmssValues = (byte[]) ar.result;
+ if (VDBG) {
+ log("IsimUiccRecords - EF_SMSS TPMR value = " + getSmssTpmrValue());
+ }
+ }
+ }
+
private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
public String getEfName() {
return "EF_ISIM_PCSCF";
@@ -229,6 +263,29 @@
}
}
+ private class EfIsimPsiSmscLoaded implements IccRecords.IccRecordLoaded {
+
+ @Override
+ public String getEfName() {
+ return "EF_ISIM_PSISMSC";
+ }
+
+ @Override
+ public void onRecordLoaded(AsyncResult ar) {
+ byte[] data = (byte[]) ar.result;
+ if (data != null && data.length > 0) {
+ mPsiSmsc = parseEfPsiSmsc(data);
+ if (VDBG) {
+ log("IsimUiccRecords - EF_PSISMSC value = " + mPsiSmsc);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public EfIsimPsiSmscLoaded getPsiSmscObject() {
+ return new EfIsimPsiSmscLoaded();
+ }
/**
* ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
* with tag value 0x80.
@@ -432,19 +489,28 @@
pw.println("IsimRecords: " + this);
pw.println(" extends:");
super.dump(fd, pw, args);
+ pw.println(" mIsimServiceTable=" + getIsimServiceTable());
if (DUMP_RECORDS) {
pw.println(" mIsimImpi=" + mIsimImpi);
pw.println(" mIsimDomain=" + mIsimDomain);
pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
- pw.println(" mIsimIst" + mIsimIst);
- pw.println(" mIsimPcscf" + mIsimPcscf);
+ pw.println(" mIsimPcscf" + Arrays.toString(mIsimPcscf));
+ pw.println(" mPsismsc=" + mPsiSmsc);
+ pw.println(" mSmss TPMR=" + getSmssTpmrValue());
}
pw.flush();
}
+ // Just to return the Enums of service table to print in DUMP
+ private IsimServiceTable getIsimServiceTable() {
+ if (mIsimIst != null) {
+ byte[] istTable = IccUtils.hexStringToBytes(mIsimIst);
+ return new IsimServiceTable(istTable);
+ }
+ return null;
+ }
@Override
public int getVoiceMessageCount() {
return 0; // Not applicable to Isim
}
-
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
index 1154e0f..23769ad 100644
--- a/src/java/com/android/internal/telephony/uicc/PinStorage.java
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -28,6 +28,7 @@
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_COUNT_NOT_MATCHING_AFTER_REBOOT;
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_DECRYPTION_ERROR;
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_KEY_MISSING;
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_REQUIRED_AFTER_REBOOT;
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_STORED_FOR_VERIFICATION;
import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
@@ -54,6 +55,7 @@
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.SimState;
import android.util.Base64;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import com.android.internal.R;
@@ -61,7 +63,6 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.nano.StoredPinProto.EncryptedPin;
import com.android.internal.telephony.nano.StoredPinProto.StoredPin;
@@ -133,7 +134,6 @@
// Events
private static final int ICC_CHANGED_EVENT = 1;
- private static final int CARRIER_CONFIG_CHANGED_EVENT = 2;
private static final int TIMER_EXPIRATION_EVENT = 3;
private static final int USER_UNLOCKED_EVENT = 4;
private static final int SUPPLY_PIN_COMPLETE = 5;
@@ -157,14 +157,11 @@
private final SparseArray<byte[]> mRamStorage;
/** Receiver for the required intents. */
- private final BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
- int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, -1);
- sendMessage(obtainMessage(CARRIER_CONFIG_CHANGED_EVENT, slotId, 0));
- } else if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(action)
+ if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(action)
|| TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) {
int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, -1);
int state = intent.getIntExtra(
@@ -189,11 +186,16 @@
// Register for necessary intents.
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intentFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
intentFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
- mContext.registerReceiver(mCarrierConfigChangedReceiver, intentFilter);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+ // Callback directly handle config change and should be executed in handler thread
+ ccm.registerCarrierConfigChangeListener(this::post,
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ onCarrierConfigurationChanged(slotIndex));
// Initialize the long term secret key. This needs to be present in all cases:
// - if the device is not secure or is locked: key does not require user authentication
@@ -519,8 +521,8 @@
/** Handle the update of the {@code state} of the SIM card in {@code slotId}. */
private synchronized void onSimStatusChange(int slotId, @SimState int state) {
- logd("SIM card/application changed[%d]: %s",
- slotId, SubscriptionInfoUpdater.simStateString(state));
+ logd("SIM card/application changed[%d]: %s", slotId,
+ TelephonyManager.simStateToString(state));
switch (state) {
case TelephonyManager.SIM_STATE_ABSENT:
case TelephonyManager.SIM_STATE_PIN_REQUIRED: {
@@ -561,7 +563,7 @@
}
}
- private void onCarrierConfigChanged(int slotId) {
+ private void onCarrierConfigurationChanged(int slotId) {
logv("onCarrierConfigChanged[%d]", slotId);
if (!isCacheAllowed(slotId)) {
logd("onCarrierConfigChanged[%d] - PIN caching not allowed", slotId);
@@ -591,9 +593,6 @@
case ICC_CHANGED_EVENT:
onSimStatusChange(/* slotId= */ msg.arg1, /* state= */ msg.arg2);
break;
- case CARRIER_CONFIG_CHANGED_EVENT:
- onCarrierConfigChanged(/* slotId= */ msg.arg1);
- break;
case TIMER_EXPIRATION_EVENT:
onTimerExpiration();
break;
@@ -724,7 +723,11 @@
*/
@Nullable
private StoredPin decryptStoredPin(byte[] blob, @Nullable SecretKey secretKey) {
- if (secretKey != null) {
+ if (secretKey == null) {
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_KEY_MISSING,
+ /* number_of_pins= */ 1, /* package_name= */ "");
+ } else {
try {
byte[] decryptedPin = decrypt(secretKey, blob);
if (decryptedPin.length > 0) {
@@ -994,14 +997,15 @@
PersistableBundle config = null;
CarrierConfigManager configManager =
mContext.getSystemService(CarrierConfigManager.class);
- if (configManager != null) {
- Phone phone = PhoneFactory.getPhone(slotId);
- if (phone != null) {
- // If an invalid subId is used, this bundle will contain default values.
- config = configManager.getConfigForSubId(phone.getSubId());
- }
+ Phone phone = PhoneFactory.getPhone(slotId);
+ if (configManager != null && phone != null) {
+ config =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mContext,
+ phone.getSubId(),
+ CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL);
}
- if (config == null) {
+ if (config == null || config.isEmpty()) {
config = CarrierConfigManager.getDefaultConfig();
}
@@ -1206,16 +1210,18 @@
Rlog.e(TAG, msg, tr);
}
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("PinStorage:");
- pw.println(" mIsDeviceSecure=" + mIsDeviceSecure);
- pw.println(" mIsDeviceLocked=" + mIsDeviceLocked);
- pw.println(" isLongTermSecretKey=" + (boolean) (mLongTermSecretKey != null));
- pw.println(" isShortTermSecretKey=" + (boolean) (mShortTermSecretKey != null));
- pw.println(" isCacheAllowedByDevice=" + isCacheAllowedByDevice());
+ pw.increaseIndent();
+ pw.println("mIsDeviceSecure=" + mIsDeviceSecure);
+ pw.println("mIsDeviceLocked=" + mIsDeviceLocked);
+ pw.println("isLongTermSecretKey=" + (boolean) (mLongTermSecretKey != null));
+ pw.println("isShortTermSecretKey=" + (boolean) (mShortTermSecretKey != null));
+ pw.println("isCacheAllowedByDevice=" + isCacheAllowedByDevice());
int slotCount = getSlotCount();
for (int i = 0; i < slotCount; i++) {
- pw.println(" isCacheAllowedByCarrier[" + i + "]=" + isCacheAllowedByCarrier(i));
+ pw.println("isCacheAllowedByCarrier[" + i + "]=" + isCacheAllowedByCarrier(i));
}
if (VDBG) {
SparseArray<StoredPin> storedPins = loadPinInformation();
@@ -1223,5 +1229,6 @@
pw.println(" pin=" + storedPins.valueAt(i).toString());
}
}
+ pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java b/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
old mode 100755
new mode 100644
diff --git a/src/java/com/android/internal/telephony/uicc/PortUtils.java b/src/java/com/android/internal/telephony/uicc/PortUtils.java
new file mode 100644
index 0000000..4a18b56
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/PortUtils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.annotation.NonNull;
+
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
+
+/**
+ * Various methods, useful for dealing with port.
+ */
+public class PortUtils {
+
+ /**
+ * Converts the port index to compatible with the HAL.
+ *
+ * @param mepMode supported MultipleEnabledProfilesMode
+ * @param portIndex port index
+ * @return target index according to the MultipleEnabledProfilesMode
+ */
+ public static int convertToHalPortIndex(@NonNull MultipleEnabledProfilesMode mepMode,
+ int portIndex) {
+ // In case of MEP-A1 and MEP-A2, profiles are selected on eSIM Ports 1 and higher, hence
+ // HAL expects the ports are indexed with 1, 2... etc.
+ // So inorder to compatible with HAL, shift the port index.
+ return mepMode.isMepAMode() ? ++portIndex : portIndex;
+ }
+
+ /**
+ * Converts the port index to compatible with the HAL.
+ *
+ * @param slotIndex physical slot index corresponding to the portIndex
+ * @param portIndex port index
+ * @return target port index according to the MultipleEnabledProfilesMode
+ */
+ public static int convertToHalPortIndex(int slotIndex, int portIndex) {
+ return convertToHalPortIndex(UiccController.getInstance().getSupportedMepMode(slotIndex),
+ portIndex);
+ }
+
+ /**
+ * Converts the port index to compatible with the platform.
+ *
+ * @param slotIndex physical slot index corresponding to the portIndex
+ * @param portIndex target port index
+ * @param cardState cardState
+ * @param supportedMepMode supported MEP mode
+ * @return shifted port index according to the MultipleEnabledProfilesMode
+ */
+ public static int convertFromHalPortIndex(int slotIndex, int portIndex,
+ IccCardStatus.CardState cardState, MultipleEnabledProfilesMode supportedMepMode) {
+ // In case of MEP-A1 and MEP-A2, profiles are selected on eSIM Ports 1 and higher.
+ // But inorder to platform code MEP mode agnostic, platform always expects the ports
+ // are indexed with 0, 1... etc. Hence shift the target port index to be compatible
+ // with platform.
+
+ // When the SIM_STATUS is related to CARDSTATE_ABSENT, CardStatus will not contain proper
+ // MEP mode info, fallback onto to the supportedMepMode data available in UiccSlot.
+ MultipleEnabledProfilesMode mepMode = cardState.isCardPresent() ? supportedMepMode
+ : UiccController.getInstance().getSupportedMepMode(slotIndex);
+ return mepMode.isMepAMode() ? --portIndex : portIndex;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
old mode 100755
new mode 100644
index f905bdc..2e490e3
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -33,7 +33,6 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.util.BitwiseInputStream;
@@ -81,8 +80,8 @@
+ " mMyMobileNumber=" + "xxxx"
+ " mMin2Min1=" + mMin2Min1
+ " mPrlVersion=" + mPrlVersion
- + " mEFpl=" + mEFpl
- + " mEFli=" + mEFli
+ + " mEFpl=" + IccUtils.bytesToHexString(mEFpl)
+ + " mEFli=" + IccUtils.bytesToHexString(mEFli)
+ " mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition
+ " mMdn=" + mMdn
+ " mMin=" + mMin
@@ -691,7 +690,7 @@
mIccId = IccUtils.bcdToString(data, 0, data.length);
mFullIccId = IccUtils.bchToString(data, 0, data.length);
- log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
+ log("iccid: " + SubscriptionInfo.getPrintableId(mFullIccId));
break;
@@ -818,10 +817,9 @@
mLoaded.set(true);
mRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
- // TODO: The below is hacky since the SubscriptionController may not be ready at this time.
if (!TextUtils.isEmpty(mMdn)) {
int phoneId = mParentApp.getUiccProfile().getPhoneId();
- int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(phoneId);
+ int subId = SubscriptionManager.getSubscriptionId(phoneId);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subId);
} else {
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 9958bfb..a97b00b 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -30,13 +30,15 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Pair;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.gsm.SimTlv;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.telephony.Rlog;
@@ -54,8 +56,9 @@
protected static final String LOG_TAG = "SIMRecords";
private static final boolean CRASH_RIL = false;
-
- private static final boolean VDBG = false;
+ private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+ private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+ Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
// ***** Instance Variables
@@ -102,13 +105,15 @@
+ " mVmConfig" + mVmConfig
+ " callForwardingEnabled=" + mCallForwardingStatus
+ " spnState=" + mSpnState
- + " mCphsInfo=" + mCphsInfo
+ + " mCphsInfo=" + IccUtils.bytesToHexString(mCphsInfo)
+ " mCspPlmnEnabled=" + mCspPlmnEnabled
- + " efMWIS=" + mEfMWIS
- + " efCPHS_MWI=" + mEfCPHS_MWI
- + " mEfCff=" + mEfCff
- + " mEfCfis=" + mEfCfis
- + " getOperatorNumeric=" + getOperatorNumeric();
+ + " efMWIS=" + IccUtils.bytesToHexString(mEfMWIS)
+ + " efCPHS_MWI=" + IccUtils.bytesToHexString(mEfCPHS_MWI)
+ + " mEfCff=" + IccUtils.bytesToHexString(mEfCff)
+ + " mEfCfis=" + IccUtils.bytesToHexString(mEfCfis)
+ + " getOperatorNumeric=" + getOperatorNumeric()
+ + " mPsiSmsc=" + mPsiSmsc
+ + " TPMR=" + getSmssTpmrValue();
}
// ***** Constants
@@ -163,7 +168,7 @@
private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE;
protected static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE;
protected static final int EVENT_GET_OPL_DONE = 16 + SIM_RECORD_EVENT_BASE;
- private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
+ protected static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_SET_MBDN_DONE = 20 + SIM_RECORD_EVENT_BASE;
@@ -184,6 +189,9 @@
private static final int EVENT_GET_FPLMN_DONE = 41 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_GET_FPLMN_SIZE_DONE = 42 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_SET_FPLMN_DONE = 43 + SIM_RECORD_EVENT_BASE;
+ protected static final int EVENT_GET_SMSS_RECORD_DONE = 46 + SIM_RECORD_EVENT_BASE;
+ protected static final int EVENT_GET_PSISMSC_DONE = 47 + SIM_RECORD_EVENT_BASE;
+
// ***** Constructor
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -272,10 +280,23 @@
return mUsimServiceTable;
}
+ /**
+ * Fetches the USIM service table from UsimServiceTable
+ *
+ * @return HexString representation of USIM service table
+ */
+ public String getSimServiceTable() {
+ if (mUsimServiceTable != null) {
+ return IccUtils.bytesToHexString(mUsimServiceTable.getUSIMServiceTable());
+ }
+ return null;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getExtFromEf(int ef) {
int ext;
switch (ef) {
+ case EF_FDN: return EF_EXT2;
case EF_MSISDN:
/* For USIM apps use EXT5. (TS 31.102 Section 4.2.37) */
if (mParentApp.getType() == AppType.APPTYPE_USIM) {
@@ -378,18 +399,20 @@
mNewVoiceMailTag = alphaTag;
AdnRecord adn = new AdnRecord(mNewVoiceMailTag, mNewVoiceMailNum);
-
if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
new AdnRecordLoader(mFh).updateEF(adn, EF_MBDN, EF_EXT6,
mMailboxIndex, null,
- obtainMessage(EVENT_SET_MBDN_DONE, onComplete));
+ obtainMessage(EVENT_SET_MBDN_DONE, AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+ 0 /* ignored arg2 */, onComplete));
} else if (isCphsMailboxEnabled()) {
new AdnRecordLoader(mFh).updateEF(adn, EF_MAILBOX_CPHS,
EF_EXT1, 1, null,
- obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));
+ obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
+ AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+ 0 /* ignored arg2 */, onComplete));
} else {
AsyncResult.forMessage((onComplete)).exception =
@@ -639,7 +662,6 @@
" while being destroyed. Ignoring.");
return;
}
-
try {
switch (msg.what) {
/* IO events */
@@ -836,7 +858,7 @@
mIccId = IccUtils.bcdToString(data, 0, data.length);
mFullIccId = IccUtils.bchToString(data, 0, data.length);
- log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
+ log("iccid: " + SubscriptionInfo.getPrintableId(mFullIccId));
break;
case EVENT_GET_AD_DONE:
@@ -1013,10 +1035,20 @@
if (DBG) log("EVENT_SET_MBDN_DONE ex:" + ar.exception);
if (ar.exception == null) {
+ /**
+ * Check for any changes made to voicemail alphaTag while saving to SIM.
+ * if voicemail alphaTag length is more than allowed limit of SIM EF then
+ * null alphaTag will be saved to SIM {@code AdnRecordLoader}.
+ */
+ if (ar.result != null) {
+ AdnRecord adnRecord = (AdnRecord) (ar.result);
+ if (adnRecord != null) {
+ mNewVoiceMailTag = adnRecord.mAlphaTag;
+ }
+ }
mVoiceMailNum = mNewVoiceMailNum;
mVoiceMailTag = mNewVoiceMailTag;
}
-
if (isCphsMailboxEnabled()) {
adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
Message onCphsCompleted = (Message) ar.userObj;
@@ -1040,14 +1072,15 @@
new AdnRecordLoader(mFh)
.updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
- onCphsCompleted));
+ AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+ 0 /* ignored arg2 */, onCphsCompleted));
} else {
if (ar.userObj != null) {
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (ar.exception != null && configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(
- SubscriptionController.getInstance().getSubIdUsingPhoneId(
+ SubscriptionManager.getSubscriptionId(
mParentApp.getPhoneId()));
if (b != null && b.getBoolean(
CarrierConfigManager.KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL)) {
@@ -1072,6 +1105,12 @@
isRecordLoadResponse = false;
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
+ if (ar.result != null) {
+ AdnRecord adnRecord = (AdnRecord) (ar.result);
+ if (adnRecord != null) {
+ mNewVoiceMailTag = adnRecord.mAlphaTag;
+ }
+ }
mVoiceMailNum = mNewVoiceMailNum;
mVoiceMailTag = mNewVoiceMailTag;
} else {
@@ -1291,6 +1330,35 @@
}
break;
+ case EVENT_GET_PSISMSC_DONE:
+ isRecordLoadResponse = true;
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loge("Failed to read USIM EF_PSISMSC field error=" + ar.exception);
+ } else {
+ data = (byte[]) ar.result;
+ if (data != null && data.length > 0) {
+ mPsiSmsc = parseEfPsiSmsc(data);
+ if (VDBG) {
+ log("SIMRecords - EF_PSISMSC value = " + mPsiSmsc);
+ }
+ }
+ }
+ break;
+
+ case EVENT_GET_SMSS_RECORD_DONE:
+ isRecordLoadResponse = true;
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loge("Failed to read USIM EF_SMSS field error=" + ar.exception);
+ } else {
+ mSmssValues = (byte[]) ar.result;
+ if (VDBG) {
+ log("SIMRecords - EF_SMSS TPMR value = " + getSmssTpmrValue());
+ }
+ }
+ break;
+
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
}
@@ -1680,12 +1748,18 @@
mFh.getEFLinearRecordSize(EF_SMS, obtainMessage(EVENT_GET_SMS_RECORD_SIZE_DONE));
mRecordsToLoad++;
+ mFh.loadEFLinearFixed(EF_PSISMSC, 1, obtainMessage(EVENT_GET_PSISMSC_DONE));
+ mRecordsToLoad++;
+
// XXX should seek instead of examining them all
if (false) { // XXX
mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
mRecordsToLoad++;
}
+ mFh.loadEFTransparent(EF_SMSS, obtainMessage(EVENT_GET_SMSS_RECORD_DONE));
+ mRecordsToLoad++;
+
if (CRASH_RIL) {
String sms = "0107912160130310f20404d0110041007030208054832b0120"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
@@ -1970,7 +2044,7 @@
// Byte 5 and 6 are for lacTacEnd.
// Byte 7 is for PNN Record Identifier.
if (data.length != 8) {
- loge("Invalid length for OPL record " + data);
+ loge("Invalid length for OPL record " + IccUtils.bytesToHexString(data));
continue;
}
@@ -2110,6 +2184,11 @@
log("[CSP] Value Added Service Group (0xC0), not found!");
}
+ @VisibleForTesting
+ public void setMailboxIndex(int mailboxIndex) {
+ mMailboxIndex = mailboxIndex;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("SIMRecords: " + this);
@@ -2118,14 +2197,14 @@
pw.println(" mVmConfig=" + mVmConfig);
pw.println(" mCallForwardingStatus=" + mCallForwardingStatus);
pw.println(" mSpnState=" + mSpnState);
- pw.println(" mCphsInfo=" + mCphsInfo);
+ pw.println(" mCphsInfo=" + IccUtils.bytesToHexString(mCphsInfo));
pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
pw.println(" mCarrierNameDisplayCondition=" + mCarrierNameDisplayCondition);
- pw.println(" mSpdi[]=" + mSpdi);
+ pw.println(" mSpdi[]=" + Arrays.toString(mSpdi));
pw.println(" mUsimServiceTable=" + mUsimServiceTable);
pw.println(" mGid1=" + mGid1);
if (mCarrierTestOverride.isInTestMode()) {
@@ -2144,6 +2223,8 @@
pw.println(" mHplmnActRecords[]=" + Arrays.toString(mHplmnActRecords));
pw.println(" mFplmns[]=" + Arrays.toString(mFplmns));
pw.println(" mEhplmns[]=" + Arrays.toString(mEhplmns));
+ pw.println(" mPsismsc=" + mPsiSmsc);
+ pw.println(" TPMR=" + getSmssTpmrValue());
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
index e06e008..149e605 100644
--- a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
+++ b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
@@ -442,17 +442,20 @@
if (!newCapacity.isSimEmpty()){
mIsCacheInvalidated.set(true);
fillCacheWithoutWaiting();
- } else {
+ } else if (newCapacity.isSimValid()) {
notifyAdnLoadingWaiters();
+ tryFireUpdatePendingList();
+ } else {
+ logd("ADN capacity is invalid");
}
mIsInitialized.set(true); // Let's say the whole process is ready
} else {
// There is nothing from PB, so notify waiters directly if any
- if (newCapacity.isSimEmpty()
- || !newCapacity.isSimValid()) {
+ if (newCapacity.isSimValid() && newCapacity.isSimEmpty()) {
mIsCacheInvalidated.set(false);
notifyAdnLoadingWaiters();
- } else if (!mIsUpdateDone) {
+ tryFireUpdatePendingList();
+ } else if (!mIsUpdateDone && !newCapacity.isSimEmpty()) {
invalidateSimPbCache();
fillCacheWithoutWaiting();
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 689e4b7..960a166 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -21,16 +21,20 @@
import android.os.Build;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.uicc.euicc.EuiccCard;
import com.android.internal.telephony.uicc.euicc.EuiccPort;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.LinkedHashMap;
/**
* {@hide}
@@ -49,17 +53,17 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CardState mCardState;
protected String mCardId;
- protected boolean mIsSupportsMultipleEnabledProfiles;
+ protected MultipleEnabledProfilesMode mSupportedMepMode;
- protected HashMap<Integer, UiccPort> mUiccPorts = new HashMap<>();
+ protected LinkedHashMap<Integer, UiccPort> mUiccPorts = new LinkedHashMap<>();
private HashMap<Integer, Integer> mPhoneIdToPortIdx = new HashMap<>();
public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock,
- boolean isSupportsMultipleEnabledProfiles) {
+ MultipleEnabledProfilesMode supportedMepMode) {
if (DBG) log("Creating");
mCardState = ics.mCardState;
mLock = lock;
- mIsSupportsMultipleEnabledProfiles = isSupportsMultipleEnabledProfiles;
+ mSupportedMepMode = supportedMepMode;
update(c, ci, ics, phoneId);
}
@@ -109,7 +113,7 @@
if (port == null) {
if (this instanceof EuiccCard) {
port = new EuiccPort(c, ci, ics, phoneId, mLock, this,
- mIsSupportsMultipleEnabledProfiles); // eSim
+ mSupportedMepMode); // eSim
} else {
port = new UiccPort(c, ci, ics, phoneId, mLock, this); // pSim
}
@@ -143,13 +147,13 @@
/**
- * Updates MEP(Multiple Enabled Profile) support flag.
+ * Updates MEP(Multiple Enabled Profile) supported mode flag.
*
* <p>If IccSlotStatus comes later, the number of ports reported is only known after the
* UiccCard creation which will impact UICC MEP capability.
*/
- public void updateSupportMultipleEnabledProfile(boolean supported) {
- mIsSupportsMultipleEnabledProfiles = supported;
+ public void updateSupportedMepMode(MultipleEnabledProfilesMode supportedMepMode) {
+ mSupportedMepMode = supportedMepMode;
}
@UnsupportedAppUsage
@@ -167,8 +171,11 @@
if (!TextUtils.isEmpty(mCardId)) {
return mCardId;
} else {
- UiccProfile uiccProfile = mUiccPorts.get(TelephonyManager.DEFAULT_PORT_INDEX)
- .getUiccProfile();
+ UiccPort uiccPort = mUiccPorts.get(TelephonyManager.DEFAULT_PORT_INDEX);
+ if (uiccPort == null) {
+ return null;
+ }
+ UiccProfile uiccProfile = uiccPort.getUiccProfile();
return uiccProfile == null ? null : uiccProfile.getIccId();
}
}
@@ -210,15 +217,20 @@
Rlog.e(LOG_TAG, msg);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("UiccCard:");
- pw.println(" mCardState=" + mCardState);
- pw.println(" mCardId=" + mCardId);
- pw.println(" mNumberOfPorts=" + mUiccPorts.size());
- pw.println( "mIsSupportsMultipleEnabledProfiles=" + mIsSupportsMultipleEnabledProfiles);
- pw.println();
+ pw.increaseIndent();
+ pw.println("mCardState=" + mCardState);
+ pw.println("mCardId=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mCardId));
+ pw.println("mNumberOfPorts=" + mUiccPorts.size());
+ pw.println("mSupportedMepMode=" + mSupportedMepMode);
+ pw.println("mUiccPorts= size=" + mUiccPorts.size());
+ pw.increaseIndent();
for (UiccPort uiccPort : mUiccPorts.values()) {
uiccPort.dump(fd, pw, args);
}
+ pw.decreaseIndent();
+ pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 3839610..fe19e99 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -24,6 +24,7 @@
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.util.IndentingPrintWriter;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
@@ -59,6 +60,9 @@
*/
public static final int AUTH_CONTEXT_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
public static final int AUTH_CONTEXT_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
+ public static final int AUTH_CONTEXT_GBA_BOOTSTRAP = PhoneConstants.AUTH_CONTEXT_GBA_BOOTSTRAP;
+ public static final int AUTHTYPE_GBA_NAF_KEY_EXTERNAL =
+ PhoneConstants.AUTHTYPE_GBA_NAF_KEY_EXTERNAL;
public static final int AUTH_CONTEXT_UNDEFINED = PhoneConstants.AUTH_CONTEXT_UNDEFINED;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -266,6 +270,9 @@
} else {
loge("Bogus facility lock response");
}
+ if (mIccFdnEnabled && mIccFdnAvailable) {
+ mIccRecords.loadFdnRecords();
+ }
}
}
@@ -981,40 +988,27 @@
Rlog.e(LOG_TAG, msg);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("UiccCardApplication: " + this);
- pw.println(" mUiccProfile=" + mUiccProfile);
- pw.println(" mAppState=" + mAppState);
- pw.println(" mAppType=" + mAppType);
- pw.println(" mPersoSubState=" + mPersoSubState);
- pw.println(" mAid=" + mAid);
- pw.println(" mAppLabel=" + mAppLabel);
- pw.println(" mPin1Replaced=" + mPin1Replaced);
- pw.println(" mPin1State=" + mPin1State);
- pw.println(" mPin2State=" + mPin2State);
- pw.println(" mIccFdnEnabled=" + mIccFdnEnabled);
- pw.println(" mDesiredFdnEnabled=" + mDesiredFdnEnabled);
- pw.println(" mIccLockEnabled=" + mIccLockEnabled);
- pw.println(" mDesiredPinLocked=" + mDesiredPinLocked);
- pw.println(" mCi=" + mCi);
- pw.println(" mIccRecords=" + mIccRecords);
- pw.println(" mIccFh=" + mIccFh);
- pw.println(" mDestroyed=" + mDestroyed);
- pw.println(" mReadyRegistrants: size=" + mReadyRegistrants.size());
- for (int i = 0; i < mReadyRegistrants.size(); i++) {
- pw.println(" mReadyRegistrants[" + i + "]="
- + ((Registrant)mReadyRegistrants.get(i)).getHandler());
- }
- pw.println(" mPinLockedRegistrants: size=" + mPinLockedRegistrants.size());
- for (int i = 0; i < mPinLockedRegistrants.size(); i++) {
- pw.println(" mPinLockedRegistrants[" + i + "]="
- + ((Registrant)mPinLockedRegistrants.get(i)).getHandler());
- }
- pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
- for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
- pw.println(" mNetworkLockedRegistrants[" + i + "]="
- + ((Registrant)mNetworkLockedRegistrants.get(i)).getHandler());
- }
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("UiccCardApplication: ");
+ pw.increaseIndent();
+ pw.println("mUiccProfile=" + mUiccProfile);
+ pw.println("mAppState=" + mAppState);
+ pw.println("mAppType=" + mAppType);
+ pw.println("mPersoSubState=" + mPersoSubState);
+ pw.println("mAid=" + mAid);
+ pw.println("mAppLabel=" + mAppLabel);
+ pw.println("mPin1Replaced=" + mPin1Replaced);
+ pw.println("mPin1State=" + mPin1State);
+ pw.println("mPin2State=" + mPin2State);
+ pw.println("mIccFdnEnabled=" + mIccFdnEnabled);
+ pw.println("mDesiredFdnEnabled=" + mDesiredFdnEnabled);
+ pw.println("mIccLockEnabled=" + mIccLockEnabled);
+ pw.println("mDesiredPinLocked=" + mDesiredPinLocked);
+ pw.println("mIccRecords=" + mIccRecords);
+ pw.println("mIccFh=" + mIccFh);
+ pw.println("mDestroyed=" + mDestroyed);
+ pw.decreaseIndent();
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 08521e6..596ccf6 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -28,6 +28,7 @@
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
@@ -426,8 +427,8 @@
if (ar.exception == null && ar.result != null) {
mChannelId = ((int[]) ar.result)[0];
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3,
- DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId,
- mAIDInUse));
+ DATA, false /*isEs10Command*/, obtainMessage(
+ EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId, mAIDInUse));
} else {
if (shouldRetry(ar, mRetryCount)) {
log("should retry");
@@ -483,7 +484,7 @@
}
} else {
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
- P1, P2_EXTENDED_DATA, P3, DATA,
+ P1, P2_EXTENDED_DATA, P3, DATA, false /*isEs10Command*/,
obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
mChannelId, mAIDInUse));
break;
@@ -495,7 +496,8 @@
}
} else {
if (mAIDInUse == ARAM) {
- String errorMsg = "Invalid response: payload=" + response.payload
+ String errorMsg = "Invalid response: payload="
+ + IccUtils.bytesToHexString(response.payload)
+ " sw1=" + response.sw1 + " sw2=" + response.sw2;
updateState(STATE_ERROR, errorMsg);
}
@@ -517,7 +519,7 @@
}
}
- mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage(
+ mUiccProfile.iccCloseLogicalChannel(mChannelId, false /*isEs10*/, obtainMessage(
EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse));
mChannelId = -1;
break;
@@ -692,16 +694,20 @@
/**
* Dumps info to Dumpsys - useful for debugging.
*/
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("UiccCarrierPrivilegeRules: " + this);
- pw.println(" mState=" + getStateString(mState.get()));
- pw.println(" mStatusMessage=");
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("UiccCarrierPrivilegeRules:");
+ pw.increaseIndent();
+ pw.println("mState=" + getStateString(mState.get()));
+ pw.println("mStatusMessage=");
mStatusMessage.dump(fd, pw, args);
if (mAccessRules != null) {
- pw.println(" mAccessRules: ");
+ pw.println("mAccessRules: ");
+ pw.increaseIndent();
for (UiccAccessRule ar : mAccessRules) {
pw.println(" rule='" + ar + "'");
}
+ pw.decreaseIndent();
} else {
pw.println(" mAccessRules: null");
}
@@ -711,6 +717,7 @@
} else {
pw.println(" mUiccPkcs15: null");
}
+ pw.decreaseIndent();
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 70cfce0..566bec2 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -21,6 +21,8 @@
import static java.util.Arrays.copyOf;
+import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions;
import android.compat.annotation.UnsupportedAppUsage;
@@ -32,32 +34,40 @@
import android.os.Build;
import android.os.Handler;
import android.os.Message;
-import android.os.Registrant;
import android.os.RegistrantList;
import android.preference.PreferenceManager;
import android.sysprop.TelephonyProperties;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.SimState;
import android.telephony.UiccCardInfo;
import android.telephony.UiccPortInfo;
import android.telephony.UiccSlotMapping;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CarrierServiceBindHelper;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IntentBroadcaster;
import com.android.internal.telephony.PhoneConfigurationManager;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RadioConfig;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.euicc.EuiccCard;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -67,6 +77,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
@@ -140,6 +151,9 @@
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 10;
// NOTE: any new EVENT_* values must be added to eventToString.
+ @NonNull
+ private final TelephonyManager mTelephonyManager;
+
// this needs to be here, because on bootup we dont know which index maps to which UiccSlot
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CommandsInterface[] mCis;
@@ -151,11 +165,25 @@
// This maps the externally exposed card ID (int) to the internal card ID string (ICCID/EID).
// The array index is the card ID (int).
// This mapping exists to expose card-based functionality without exposing the EID, which is
- // considered sensetive information.
+ // considered sensitive information.
// mCardStrings is populated using values from the IccSlotStatus and IccCardStatus. For
// HAL < 1.2, these do not contain the EID or the ICCID, so mCardStrings will be empty
private ArrayList<String> mCardStrings;
+ /**
+ * SIM card state.
+ */
+ @NonNull
+ @SimState
+ private final int[] mSimCardState;
+
+ /**
+ * SIM application state.
+ */
+ @NonNull
+ @SimState
+ private final int[] mSimApplicationState;
+
// This is the card ID of the default eUICC. It starts as UNINITIALIZED_CARD_ID.
// When we load the EID (either with slot status or from the EuiccCard), we set it to the eUICC
// with the lowest slot index.
@@ -205,6 +233,9 @@
protected RegistrantList mIccChangedRegistrants = new RegistrantList();
+ @NonNull
+ private final CarrierServiceBindHelper mCarrierServiceBindHelper;
+
private UiccStateChangedLauncher mLauncher;
private RadioConfig mRadioConfig;
@@ -243,8 +274,13 @@
numPhysicalSlots = mCis.length;
}
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+
mUiccSlots = new UiccSlot[numPhysicalSlots];
mPhoneIdToSlotId = new int[mCis.length];
+ int supportedModemCount = mTelephonyManager.getSupportedModemCount();
+ mSimCardState = new int[supportedModemCount];
+ mSimApplicationState = new int[supportedModemCount];
Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
if (VDBG) logPhoneIdToSlotIdMapping();
mRadioConfig = RadioConfig.getInstance();
@@ -260,6 +296,8 @@
mCardStrings = loadCardStrings();
mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;
+ mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
+
mEuiccSlots = mContext.getResources()
.getIntArray(com.android.internal.R.array.non_removable_euicc_slots);
mHasBuiltInEuicc = hasBuiltInEuicc();
@@ -300,13 +338,11 @@
@UnsupportedAppUsage
public static UiccController getInstance() {
- synchronized (mLock) {
- if (mInstance == null) {
- throw new RuntimeException(
- "UiccController.getInstance can't be called before make()");
- }
- return mInstance;
+ if (mInstance == null) {
+ throw new RuntimeException(
+ "UiccController.getInstance can't be called before make()");
}
+ return mInstance;
}
@UnsupportedAppUsage
@@ -707,38 +743,271 @@
}
}
- static void updateInternalIccStateForInactivePort(
- Context context, int prevActivePhoneId, String iccId) {
- if (SubscriptionManager.isValidPhoneId(prevActivePhoneId)) {
- // Mark SIM state as ABSENT on previously phoneId.
- TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- telephonyManager.setSimStateForPhone(prevActivePhoneId,
- IccCardConstants.State.ABSENT.toString());
- }
+ /**
+ * Update SIM state for the inactive eSIM port.
+ *
+ * @param phoneId Previously active phone id.
+ * @param iccId ICCID of the SIM.
+ */
+ public void updateSimStateForInactivePort(int phoneId, String iccId) {
+ post(() -> {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ // Mark SIM state as ABSENT on previously phoneId.
+ mTelephonyManager.setSimStateForPhone(phoneId,
+ IccCardConstants.State.ABSENT.toString());
+ }
- SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
- if (subInfoUpdator != null) {
- subInfoUpdator.updateInternalIccStateForInactivePort(prevActivePhoneId, iccId);
- } else {
- Rlog.e(LOG_TAG, "subInfoUpdate is null.");
+ SubscriptionManagerService.getInstance().updateSimStateForInactivePort(phoneId,
+ TextUtils.emptyIfNull(iccId));
+ });
+ }
+
+ /**
+ * Broadcast the legacy SIM state changed event.
+ *
+ * @param phoneId The phone id.
+ * @param state The legacy SIM state.
+ * @param reason The reason of SIM state change.
+ */
+ private void broadcastSimStateChanged(int phoneId, @NonNull String state,
+ @Nullable String reason) {
+ // Note: This intent is way deprecated and is only being kept around because there's no
+ // graceful way to deprecate a sticky broadcast that has a lot of listeners.
+ // DO NOT add any new extras to this broadcast -- it is not protected by any permissions.
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
+ intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
+ intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+ Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason "
+ + reason + " for phone: " + phoneId);
+ IntentBroadcaster.getInstance().broadcastStickyIntent(mContext, intent, phoneId);
+ }
+
+ /**
+ * Broadcast SIM card state changed event.
+ *
+ * @param phoneId The phone id.
+ * @param state The SIM card state.
+ */
+ private void broadcastSimCardStateChanged(int phoneId, @SimState int state) {
+ if (state != mSimCardState[phoneId]) {
+ mSimCardState[phoneId] = state;
+ Intent intent = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+ // TODO(b/130664115) we manually populate this intent with the slotId. In the future we
+ // should do a review of whether to make this public
+ UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
+ int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, slotId);
+ if (slot != null) {
+ intent.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId));
+ }
+ Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED "
+ + TelephonyManager.simStateToString(state) + " for phone: " + phoneId
+ + " slot: " + slotId + " port: " + slot.getPortIndexFromPhoneId(phoneId));
+ mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ TelephonyMetrics.getInstance().updateSimState(phoneId, state);
}
}
- static void updateInternalIccState(Context context, IccCardConstants.State state, String reason,
- int phoneId) {
- TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- telephonyManager.setSimStateForPhone(phoneId, state.toString());
-
- SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
- if (subInfoUpdator != null) {
- subInfoUpdator.updateInternalIccState(getIccStateIntentString(state), reason, phoneId);
- } else {
- Rlog.e(LOG_TAG, "subInfoUpdate is null.");
+ /**
+ * Broadcast SIM application state changed event.
+ *
+ * @param phoneId The phone id.
+ * @param state The SIM application state.
+ */
+ private void broadcastSimApplicationStateChanged(int phoneId, @SimState int state) {
+ // Broadcast if the state has changed, except if old state was UNKNOWN and new is NOT_READY,
+ // because that's the initial state and a broadcast should be sent only on a transition
+ // after SIM is PRESENT. The only exception is eSIM boot profile, where NOT_READY is the
+ // terminal state.
+ boolean isUnknownToNotReady =
+ (mSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN
+ && state == TelephonyManager.SIM_STATE_NOT_READY);
+ IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
+ boolean emptyProfile = iccCard != null && iccCard.isEmptyProfile();
+ if (state != mSimApplicationState[phoneId] && (!isUnknownToNotReady || emptyProfile)) {
+ mSimApplicationState[phoneId] = state;
+ Intent intent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+ // TODO(b/130664115) we populate this intent with the actual slotId. In the future we
+ // should do a review of whether to make this public
+ UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
+ int slotId = UiccController.getInstance().getSlotIdFromPhoneId(phoneId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, slotId);
+ if (slot != null) {
+ intent.putExtra(PhoneConstants.PORT_KEY, slot.getPortIndexFromPhoneId(phoneId));
+ }
+ Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED "
+ + TelephonyManager.simStateToString(state)
+ + " for phone: " + phoneId + " slot: " + slotId + "port: "
+ + slot.getPortIndexFromPhoneId(phoneId));
+ mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ TelephonyMetrics.getInstance().updateSimState(phoneId, state);
}
}
+ /**
+ * Get SIM state from SIM lock reason.
+ *
+ * @param lockedReason The SIM lock reason.
+ *
+ * @return The SIM state.
+ */
+ @SimState
+ private static int getSimStateFromLockedReason(String lockedReason) {
+ switch (lockedReason) {
+ case IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN:
+ return TelephonyManager.SIM_STATE_PIN_REQUIRED;
+ case IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK:
+ return TelephonyManager.SIM_STATE_PUK_REQUIRED;
+ case IccCardConstants.INTENT_VALUE_LOCKED_NETWORK:
+ return TelephonyManager.SIM_STATE_NETWORK_LOCKED;
+ case IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED:
+ return TelephonyManager.SIM_STATE_PERM_DISABLED;
+ default:
+ Rlog.e(LOG_TAG, "Unexpected SIM locked reason " + lockedReason);
+ return TelephonyManager.SIM_STATE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Broadcast SIM state events.
+ *
+ * @param phoneId The phone id.
+ * @param simState The SIM state.
+ * @param reason SIM state changed reason.
+ */
+ private void broadcastSimStateEvents(int phoneId, IccCardConstants.State simState,
+ @Nullable String reason) {
+ String legacyStringSimState = getIccStateIntentString(simState);
+ int cardState = TelephonyManager.SIM_STATE_UNKNOWN;
+ int applicationState = TelephonyManager.SIM_STATE_UNKNOWN;
+
+ switch (simState) {
+ case ABSENT:
+ cardState = TelephonyManager.SIM_STATE_ABSENT;
+ break;
+ case PIN_REQUIRED:
+ case PUK_REQUIRED:
+ case NETWORK_LOCKED:
+ case PERM_DISABLED:
+ cardState = TelephonyManager.SIM_STATE_PRESENT;
+ applicationState = getSimStateFromLockedReason(reason);
+ break;
+ case READY:
+ case NOT_READY:
+ // Both READY and NOT_READY have the same card state and application state.
+ cardState = TelephonyManager.SIM_STATE_PRESENT;
+ applicationState = TelephonyManager.SIM_STATE_NOT_READY;
+ break;
+ case CARD_IO_ERROR:
+ cardState = TelephonyManager.SIM_STATE_CARD_IO_ERROR;
+ applicationState = TelephonyManager.SIM_STATE_NOT_READY;
+ break;
+ case CARD_RESTRICTED:
+ cardState = TelephonyManager.SIM_STATE_CARD_RESTRICTED;
+ applicationState = TelephonyManager.SIM_STATE_NOT_READY;
+ break;
+ case LOADED:
+ cardState = TelephonyManager.SIM_STATE_PRESENT;
+ applicationState = TelephonyManager.SIM_STATE_LOADED;
+ break;
+ case UNKNOWN:
+ default:
+ break;
+ }
+
+ broadcastSimStateChanged(phoneId, legacyStringSimState, reason);
+ broadcastSimCardStateChanged(phoneId, cardState);
+ broadcastSimApplicationStateChanged(phoneId, applicationState);
+ }
+
+ /**
+ * Update carrier service.
+ *
+ * @param phoneId The phone id.
+ * @param simState The SIM state.
+ */
+ private void updateCarrierServices(int phoneId, @NonNull String simState) {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+ if (configManager != null) {
+ configManager.updateConfigForPhoneId(phoneId, simState);
+ }
+ mCarrierServiceBindHelper.updateForPhoneId(phoneId, simState);
+ }
+
+ /**
+ * Update the SIM state.
+ *
+ * @param phoneId Phone id.
+ * @param state SIM state (legacy).
+ * @param reason The reason for SIM state update.
+ */
+ public void updateSimState(int phoneId, @NonNull IccCardConstants.State state,
+ @Nullable String reason) {
+ post(() -> {
+ log("updateSimState: phoneId=" + phoneId + ", state=" + state + ", reason="
+ + reason);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ Rlog.e(LOG_TAG, "updateSimState: Invalid phone id " + phoneId);
+ return;
+ }
+
+ mTelephonyManager.setSimStateForPhone(phoneId, state.toString());
+
+ String legacySimState = getIccStateIntentString(state);
+ int simState = state.ordinal();
+ SubscriptionManagerService.getInstance().updateSimState(phoneId, simState,
+ this::post,
+ () -> {
+ // The following are executed after subscription update completed in
+ // subscription manager service.
+
+ broadcastSimStateEvents(phoneId, state, reason);
+
+ UiccProfile uiccProfile = getUiccProfileForPhone(phoneId);
+
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ // SIM_STATE_READY is not a final state.
+ return;
+ }
+
+ if (simState == TelephonyManager.SIM_STATE_NOT_READY
+ && (uiccProfile != null && !uiccProfile.isEmptyProfile())
+ && SubscriptionManagerService.getInstance()
+ .areUiccAppsEnabledOnCard(phoneId)) {
+ // STATE_NOT_READY is not a final state for when both
+ // 1) It's not an empty profile, and
+ // 2) Its uicc applications are set to enabled.
+ //
+ // At this phase, we consider STATE_NOT_READY not a final state, so
+ // return for now.
+ log("updateSimState: SIM_STATE_NOT_READY is not a final "
+ + "state.");
+ return;
+ }
+
+ // At this point, the SIM state must be a final state (meaning we won't
+ // get more SIM state updates). So resolve the carrier id and update the
+ // carrier services.
+ log("updateSimState: resolve carrier id and update carrier "
+ + "services.");
+ PhoneFactory.getPhone(phoneId).resolveSubscriptionCarrierId(
+ legacySimState);
+ updateCarrierServices(phoneId, legacySimState);
+ }
+ );
+ });
+ }
+
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
if (ar.exception != null) {
Rlog.e(LOG_TAG,"Error getting ICC status. "
@@ -760,10 +1029,10 @@
IccCardStatus status = (IccCardStatus)ar.result;
- logWithLocalLog("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
+ logWithLocalLog("onGetIccCardStatusDone: phoneId-" + index + " IccCardStatus: " + status);
int slotId = status.mSlotPortMapping.mPhysicalSlotIndex;
- if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
+ if (VDBG) log("onGetIccCardStatusDone: phoneId-" + index + " physicalSlotIndex " + slotId);
if (slotId == INVALID_SLOT_ID) {
slotId = index;
}
@@ -794,6 +1063,14 @@
return;
}
+ UiccPort port = card.getUiccPort(status.mSlotPortMapping.mPortIndex);
+ if (port == null) {
+ if (DBG) log("mUiccSlots[" + slotId + "] has no UiccPort with index["
+ + status.mSlotPortMapping.mPortIndex + "]. Notifying IccChangedRegistrants");
+ mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
+ return;
+ }
+
String cardString = null;
boolean isEuicc = mUiccSlots[slotId].isEuicc();
if (isEuicc) {
@@ -894,55 +1171,57 @@
* Returns the UiccCardInfo of all currently inserted UICCs and embedded eUICCs.
*/
public ArrayList<UiccCardInfo> getAllUiccCardInfos() {
- ArrayList<UiccCardInfo> infos = new ArrayList<>();
- for (int slotIndex = 0; slotIndex < mUiccSlots.length; slotIndex++) {
- final UiccSlot slot = mUiccSlots[slotIndex];
- if (slot == null) continue;
- boolean isEuicc = slot.isEuicc();
- String eid = null;
- UiccCard card = slot.getUiccCard();
- int cardId = UNINITIALIZED_CARD_ID;
- boolean isRemovable = slot.isRemovable();
+ synchronized (mLock) {
+ ArrayList<UiccCardInfo> infos = new ArrayList<>();
+ for (int slotIndex = 0; slotIndex < mUiccSlots.length; slotIndex++) {
+ final UiccSlot slot = mUiccSlots[slotIndex];
+ if (slot == null) continue;
+ boolean isEuicc = slot.isEuicc();
+ String eid = null;
+ UiccCard card = slot.getUiccCard();
+ int cardId = UNINITIALIZED_CARD_ID;
+ boolean isRemovable = slot.isRemovable();
- // first we try to populate UiccCardInfo using the UiccCard, but if it doesn't exist
- // (e.g. the slot is for an inactive eUICC) then we try using the UiccSlot.
- if (card != null) {
- if (isEuicc) {
- eid = ((EuiccCard) card).getEid();
- cardId = convertToPublicCardId(eid);
- } else {
- // In case of non Euicc, use default port index to get the IccId.
- UiccPort port = card.getUiccPort(TelephonyManager.DEFAULT_PORT_INDEX);
- if (port == null) {
- AnomalyReporter.reportAnomaly(
- UUID.fromString("92885ba7-98bb-490a-ba19-987b1c8b2055"),
- "UiccController: Found UiccPort Null object.");
+ // first we try to populate UiccCardInfo using the UiccCard, but if it doesn't exist
+ // (e.g. the slot is for an inactive eUICC) then we try using the UiccSlot.
+ if (card != null) {
+ if (isEuicc) {
+ eid = ((EuiccCard) card).getEid();
+ cardId = convertToPublicCardId(eid);
+ } else {
+ // In case of non Euicc, use default port index to get the IccId.
+ UiccPort port = card.getUiccPort(TelephonyManager.DEFAULT_PORT_INDEX);
+ if (port == null) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("92885ba7-98bb-490a-ba19-987b1c8b2055"),
+ "UiccController: Found UiccPort Null object.");
+ }
+ String iccId = (port != null) ? port.getIccId() : null;
+ cardId = convertToPublicCardId(iccId);
}
- String iccId = (port != null) ? port.getIccId() : null;
- cardId = convertToPublicCardId(iccId);
+ } else {
+ // This iccid is used for non Euicc only, so use default port index
+ String iccId = slot.getIccId(TelephonyManager.DEFAULT_PORT_INDEX);
+ // Fill in the fields we can
+ if (!isEuicc && !TextUtils.isEmpty(iccId)) {
+ cardId = convertToPublicCardId(iccId);
+ }
}
- } else {
- // This iccid is used for non Euicc only, so use default port index
- String iccId = slot.getIccId(TelephonyManager.DEFAULT_PORT_INDEX);
- // Fill in the fields we can
- if (!isEuicc && !TextUtils.isEmpty(iccId)) {
- cardId = convertToPublicCardId(iccId);
- }
- }
- List<UiccPortInfo> portInfos = new ArrayList<>();
- int[] portIndexes = slot.getPortList();
- for (int portIdx : portIndexes) {
- String iccId = IccUtils.stripTrailingFs(slot.getIccId(portIdx));
- portInfos.add(new UiccPortInfo(iccId, portIdx,
- slot.getPhoneIdFromPortIndex(portIdx), slot.isPortActive(portIdx)));
+ List<UiccPortInfo> portInfos = new ArrayList<>();
+ int[] portIndexes = slot.getPortList();
+ for (int portIdx : portIndexes) {
+ String iccId = IccUtils.stripTrailingFs(slot.getIccId(portIdx));
+ portInfos.add(new UiccPortInfo(iccId, portIdx,
+ slot.getPhoneIdFromPortIndex(portIdx), slot.isPortActive(portIdx)));
+ }
+ UiccCardInfo info = new UiccCardInfo(
+ isEuicc, cardId, eid, slotIndex, isRemovable,
+ slot.isMultipleEnabledProfileSupported(), portInfos);
+ infos.add(info);
}
- UiccCardInfo info = new UiccCardInfo(
- isEuicc, cardId, eid, slotIndex, isRemovable,
- slot.isMultipleEnabledProfileSupported(), portInfos);
- infos.add(info);
+ return infos;
}
- return infos;
}
/**
@@ -1453,6 +1732,18 @@
return mUseRemovableEsimAsDefault;
}
+ /**
+ * Returns the MEP mode supported by the UiccSlot associated with slotIndex.
+ * @param slotIndex physical slot index
+ * @return MultipleEnabledProfilesMode supported by the slot
+ */
+ public IccSlotStatus.MultipleEnabledProfilesMode getSupportedMepMode(int slotIndex) {
+ synchronized (mLock) {
+ UiccSlot slot = getUiccSlot(slotIndex);
+ return slot != null ? slot.getSupportedMepMode()
+ : IccSlotStatus.MultipleEnabledProfilesMode.NONE;
+ }
+ }
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String string) {
@@ -1474,35 +1765,40 @@
sLocalLog.log(data);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("UiccController: " + this);
- pw.println(" mContext=" + mContext);
- pw.println(" mInstance=" + mInstance);
- pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
- for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
- pw.println(" mIccChangedRegistrants[" + i + "]="
- + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
+ private List<String> getPrintableCardStrings() {
+ if (!ArrayUtils.isEmpty(mCardStrings)) {
+ return mCardStrings.stream().map(SubscriptionInfo::getPrintableId).collect(
+ Collectors.toList());
}
- pw.println();
- pw.flush();
- pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
- pw.println(" mHasBuiltInEuicc=" + mHasBuiltInEuicc);
- pw.println(" mHasActiveBuiltInEuicc=" + mHasActiveBuiltInEuicc);
- pw.println(" mCardStrings=" + mCardStrings);
- pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
- pw.println(" mPhoneIdToSlotId=" + Arrays.toString(mPhoneIdToSlotId));
- pw.println(" mUseRemovableEsimAsDefault=" + mUseRemovableEsimAsDefault);
- pw.println(" mUiccSlots: size=" + mUiccSlots.length);
+ return mCardStrings;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("mIsCdmaSupported=" + isCdmaSupported(mContext));
+ pw.println("mHasBuiltInEuicc=" + mHasBuiltInEuicc);
+ pw.println("mHasActiveBuiltInEuicc=" + mHasActiveBuiltInEuicc);
+ pw.println("mCardStrings=" + getPrintableCardStrings());
+ pw.println("mDefaultEuiccCardId=" + mDefaultEuiccCardId);
+ pw.println("mPhoneIdToSlotId=" + Arrays.toString(mPhoneIdToSlotId));
+ pw.println("mUseRemovableEsimAsDefault=" + mUseRemovableEsimAsDefault);
+ pw.println("mUiccSlots: size=" + mUiccSlots.length);
+ pw.increaseIndent();
for (int i = 0; i < mUiccSlots.length; i++) {
if (mUiccSlots[i] == null) {
- pw.println(" mUiccSlots[" + i + "]=null");
+ pw.println("mUiccSlots[" + i + "]=null");
} else {
- pw.println(" mUiccSlots[" + i + "]=" + mUiccSlots[i]);
+ pw.println("mUiccSlots[" + i + "]:");
+ pw.increaseIndent();
mUiccSlots[i].dump(fd, pw, args);
+ pw.decreaseIndent();
}
}
- pw.println(" sLocalLog= ");
- sLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.println();
+ pw.println("sLocalLog= ");
+ pw.increaseIndent();
mPinStorage.dump(fd, pw, args);
+ sLocalLog.dump(fd, pw, args);
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
index 9543908..be045c4 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -81,7 +81,7 @@
private void selectFile() {
if (mChannelId >= 0) {
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02,
- mFileId, obtainMessage(EVENT_SELECT_FILE_DONE));
+ mFileId, false /*isEs10Command*/, obtainMessage(EVENT_SELECT_FILE_DONE));
} else {
log("EF based");
}
@@ -90,7 +90,7 @@
private void readBinary() {
if (mChannelId >=0 ) {
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
- "", obtainMessage(EVENT_READ_BINARY_DONE));
+ "", false /*isEs10Command*/, obtainMessage(EVENT_READ_BINARY_DONE));
} else {
log("EF based");
}
@@ -281,8 +281,8 @@
private void cleanUp() {
log("cleanUp");
if (mChannelId >= 0) {
- mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage(
- EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
+ mUiccProfile.iccCloseLogicalChannel(mChannelId, false /*isEs10*/,
+ obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
mChannelId = -1;
}
mLoadedCallback.sendToTarget();
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index 0152dda..d89eab1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -22,6 +22,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.telephony.SubscriptionInfo;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -216,12 +217,12 @@
/**
* Exposes {@link CommandsInterface#iccCloseLogicalChannel}
* @deprecated Please use
- * {@link UiccProfile#iccCloseLogicalChannel(int, Message)} instead.
+ * {@link UiccProfile#iccCloseLogicalChannel(int, boolean, Message)} instead.
*/
@Deprecated
public void iccCloseLogicalChannel(int channel, Message response) {
if (mUiccProfile != null) {
- mUiccProfile.iccCloseLogicalChannel(channel, response);
+ mUiccProfile.iccCloseLogicalChannel(channel, false /*isEs10*/, response);
} else {
loge("iccCloseLogicalChannel Failed!");
}
@@ -230,15 +231,15 @@
/**
* Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel}
* @deprecated Please use {@link
- * UiccProfile#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String, Message)}
- * instead.
+ * UiccProfile#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String,
+ * boolean, Message)} instead.
*/
@Deprecated
public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
int p1, int p2, int p3, String data, Message response) {
if (mUiccProfile != null) {
mUiccProfile.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3,
- data, response);
+ data, false /*isEs10Command*/, response);
} else {
loge("iccTransmitApduLogicalChannel Failed!");
}
@@ -358,18 +359,19 @@
Rlog.e(LOG_TAG, msg);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("UiccPort:");
- pw.println(" this=" + this);
- pw.println(" mPortIdx=" + mPortIdx);
- pw.println(" mCi=" + mCi);
- pw.println(" mIccid=" + SubscriptionInfo.givePrintableIccid(mIccid));
- pw.println(" mPhoneId=" + mPhoneId);
- pw.println(" mPhysicalSlotIndex=" + mPhysicalSlotIndex);
+ pw.increaseIndent();
+ pw.println("mPortIdx=" + mPortIdx);
+ pw.println("mCi=" + mCi);
+ pw.println("mIccid=" + SubscriptionInfo.getPrintableId(mIccid));
+ pw.println("mPhoneId=" + mPhoneId);
+ pw.println("mPhysicalSlotIndex=" + mPhysicalSlotIndex);
synchronized (mOpenChannelRecords) {
- pw.println(" mOpenChannelRecords=" + mOpenChannelRecords);
+ pw.println("mOpenChannelRecords=" + mOpenChannelRecords);
}
- pw.println();
+ pw.println("mUiccProfile");
if (mUiccProfile != null) {
mUiccProfile.dump(fd, pw, args);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 2809895..83db022 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -35,6 +35,7 @@
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
+import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
@@ -50,6 +51,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CarrierAppUtils;
@@ -61,9 +63,10 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -77,6 +80,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -121,6 +125,8 @@
private final int mPhoneId;
private final PinStorage mPinStorage;
+ private final CarrierConfigManager mCarrierConfigManager;
+
private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
private static final int EVENT_ICC_LOCKED = 2;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -181,14 +187,19 @@
};
private boolean mUserUnlockReceiverRegistered;
- private final BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARRIER_CONFIG_CHANGED));
- }
- }
- };
+ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId,
+ int carrierId, int specificCarrierId) {
+ if (logicalSlotIndex == mPhoneId) {
+ log("onCarrierConfigChanged: slotIndex=" + logicalSlotIndex
+ + ", subId=" + subscriptionId + ", carrierId=" + carrierId);
+ handleCarrierNameOverride();
+ handleSimCountryIsoOverride();
+ }
+ }
+ };
@VisibleForTesting
public final Handler mHandler = new Handler() {
@@ -337,9 +348,10 @@
ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
resetProperties();
- IntentFilter intentfilter = new IntentFilter();
- intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- c.registerReceiver(mCarrierConfigChangedReceiver, intentfilter);
+ mCarrierConfigManager = c.getSystemService(CarrierConfigManager.class);
+ // Listener callback directly handles config change and thus runs on handler thread
+ mCarrierConfigManager.registerCarrierConfigChangeListener(mHandler::post,
+ mCarrierConfigChangeListener);
}
/**
@@ -372,7 +384,11 @@
InstallCarrierAppUtils.unregisterPackageInstallReceiver(mContext);
mCi.unregisterForOffOrNotAvailable(mHandler);
- mContext.unregisterReceiver(mCarrierConfigChangedReceiver);
+
+ if (mCarrierConfigManager != null && mCarrierConfigChangeListener != null) {
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
+ }
if (mCatService != null) mCatService.dispose();
for (UiccCardApplication app : mUiccApplications) {
@@ -431,8 +447,7 @@
* if an override is provided.
*/
private void handleCarrierNameOverride() {
- SubscriptionController subCon = SubscriptionController.getInstance();
- final int subId = subCon.getSubIdUsingPhoneId(mPhoneId);
+ final int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
loge("subId not valid for Phone " + mPhoneId);
return;
@@ -445,7 +460,16 @@
return;
}
- PersistableBundle config = configLoader.getConfigForSubId(subId);
+ PersistableBundle config =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mContext,
+ subId,
+ CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL,
+ CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ if (config.isEmpty()) {
+ loge("handleCarrierNameOverride: fail to get carrier configs.");
+ return;
+ }
boolean preferCcName = config.getBoolean(
CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
String ccName = config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
@@ -481,7 +505,7 @@
mOperatorBrandOverrideRegistrants.notifyRegistrants();
}
- updateCarrierNameForSubscription(subCon, subId, nameSource);
+ updateCarrierNameForSubscription(subId, nameSource);
}
/**
@@ -496,8 +520,7 @@
* MCC table
*/
private void handleSimCountryIsoOverride() {
- SubscriptionController subCon = SubscriptionController.getInstance();
- final int subId = subCon.getSubIdUsingPhoneId(mPhoneId);
+ final int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
loge("subId not valid for Phone " + mPhoneId);
return;
@@ -510,20 +533,26 @@
return;
}
- PersistableBundle config = configLoader.getConfigForSubId(subId);
+ PersistableBundle config =
+ CarrierConfigManager.getCarrierConfigSubset(
+ mContext, subId, CarrierConfigManager.KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING);
+ if (config.isEmpty()) {
+ loge("handleSimCountryIsoOverride: fail to get carrier configs.");
+ return;
+ }
String iso = config.getString(CarrierConfigManager.KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING);
- if (!TextUtils.isEmpty(iso) &&
- !iso.equals(mTelephonyManager.getSimCountryIsoForPhone(mPhoneId))) {
+ if (!TextUtils.isEmpty(iso)
+ && !iso.equals(TelephonyManager.getSimCountryIsoForPhone(mPhoneId))) {
mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, iso);
- subCon.setCountryIso(iso, subId);
+ SubscriptionManagerService.getInstance().setCountryIso(subId, iso);
}
}
- private void updateCarrierNameForSubscription(SubscriptionController subCon, int subId,
- int nameSource) {
+ private void updateCarrierNameForSubscription(int subId, int nameSource) {
/* update display name with carrier override */
- SubscriptionInfo subInfo = subCon.getActiveSubscriptionInfo(
- subId, mContext.getOpPackageName(), mContext.getAttributionTag());
+ SubscriptionInfo subInfo = SubscriptionManagerService.getInstance()
+ .getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
if (subInfo == null) {
return;
@@ -534,7 +563,8 @@
if (!TextUtils.isEmpty(newCarrierName) && !newCarrierName.equals(oldSubName)) {
log("sim name[" + mPhoneId + "] = " + newCarrierName);
- subCon.setDisplayNameUsingSrc(newCarrierName, subId, nameSource);
+ SubscriptionManagerService.getInstance().setDisplayNameUsingSrc(
+ newCarrierName, subId, nameSource);
}
}
@@ -791,8 +821,8 @@
}
log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
- UiccController.updateInternalIccState(mContext, mExternalState,
- getIccStateReason(mExternalState), mPhoneId);
+ UiccController.getInstance().updateSimState(mPhoneId, mExternalState,
+ getIccStateReason(mExternalState));
}
}
@@ -1393,7 +1423,7 @@
Set<String> uninstalledCarrierPackages = new ArraySet<>();
List<UiccAccessRule> accessRules = rules.getAccessRules();
for (UiccAccessRule accessRule : accessRules) {
- String certHexString = accessRule.getCertificateHexString().toUpperCase();
+ String certHexString = accessRule.getCertificateHexString().toUpperCase(Locale.ROOT);
String pkgName = certPackageMap.get(certHexString);
if (!TextUtils.isEmpty(pkgName) && !isPackageBundled(mContext, pkgName)) {
uninstalledCarrierPackages.add(pkgName);
@@ -1423,7 +1453,7 @@
String[] keyValue = keyValueString.split(keyValueDelim);
if (keyValue.length == 2) {
- map.put(keyValue[0].toUpperCase(), keyValue[1]);
+ map.put(keyValue[0].toUpperCase(Locale.ROOT), keyValue[1]);
} else {
loge("Incorrect length of key-value pair in carrier app allow list map. "
+ "Length should be exactly 2");
@@ -1583,9 +1613,9 @@
/**
* Exposes {@link CommandsInterface#iccCloseLogicalChannel}
*/
- public void iccCloseLogicalChannel(int channel, Message response) {
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response) {
logWithLocalLog("iccCloseLogicalChannel: " + channel);
- mCi.iccCloseLogicalChannel(channel,
+ mCi.iccCloseLogicalChannel(channel, isEs10,
mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
}
@@ -1593,9 +1623,9 @@
* Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel}
*/
public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
- int p1, int p2, int p3, String data, Message response) {
- mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3,
- data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, response));
+ int p1, int p2, int p3, String data, boolean isEs10Command, Message response) {
+ mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3, data, isEs10Command,
+ mHandler.obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, response));
}
/**
@@ -1689,13 +1719,32 @@
*/
public boolean setOperatorBrandOverride(String brand) {
log("setOperatorBrandOverride: " + brand);
- log("current iccId: " + SubscriptionInfo.givePrintableIccid(getIccId()));
+ log("current iccId: " + SubscriptionInfo.getPrintableId(getIccId()));
String iccId = getIccId();
if (TextUtils.isEmpty(iccId)) {
return false;
}
- if (!SubscriptionController.getInstance().checkPhoneIdAndIccIdMatch(getPhoneId(), iccId)) {
+
+ int subId = SubscriptionManager.getSubscriptionId(getPhoneId());
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(subId);
+ if (subInfo == null) {
+ loge("setOperatorBrandOverride: Cannot find subscription info for sub " + subId);
+ return false;
+ }
+
+ List<SubscriptionInfo> subInfos = new ArrayList<>();
+ subInfos.add(subInfo.toSubscriptionInfo());
+ String groupUuid = subInfo.getGroupUuid();
+ if (!TextUtils.isEmpty(groupUuid)) {
+ subInfos.addAll(SubscriptionManagerService.getInstance()
+ .getSubscriptionsInGroup(ParcelUuid.fromString(groupUuid),
+ mContext.getOpPackageName(), mContext.getFeatureId()));
+ }
+
+ if (subInfos.stream().noneMatch(info -> TextUtils.equals(IccUtils.stripTrailingFs(
+ info.getIccId()), IccUtils.stripTrailingFs(iccId)))) {
loge("iccId doesn't match current active subId.");
return false;
}
@@ -1802,27 +1851,29 @@
/**
* Dump
*/
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("UiccProfile:");
- pw.println(" mCi=" + mCi);
- pw.println(" mCatService=" + mCatService);
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.increaseIndent();
+ pw.println("mCatService=" + mCatService);
for (int i = 0; i < mOperatorBrandOverrideRegistrants.size(); i++) {
- pw.println(" mOperatorBrandOverrideRegistrants[" + i + "]="
+ pw.println("mOperatorBrandOverrideRegistrants[" + i + "]="
+ ((Registrant) mOperatorBrandOverrideRegistrants.get(i)).getHandler());
}
- pw.println(" mUniversalPinState=" + mUniversalPinState);
- pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
- pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
- pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
- pw.println(" mUiccApplications: length=" + mUiccApplications.length);
+ pw.println("mUniversalPinState=" + mUniversalPinState);
+ pw.println("mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
+ pw.println("mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
+ pw.println("mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
+ pw.println("mUiccApplications: length=" + mUiccApplications.length);
+ pw.increaseIndent();
for (int i = 0; i < mUiccApplications.length; i++) {
if (mUiccApplications[i] == null) {
- pw.println(" mUiccApplications[" + i + "]=" + null);
+ pw.println("mUiccApplications[" + i + "]=" + null);
} else {
- pw.println(" mUiccApplications[" + i + "]="
+ pw.println("mUiccApplications[" + i + "]="
+ mUiccApplications[i].getType() + " " + mUiccApplications[i]);
}
}
+ pw.decreaseIndent();
pw.println();
// Print details of all applications
for (UiccCardApplication app : mUiccApplications) {
@@ -1843,28 +1894,31 @@
}
// Print UiccCarrierPrivilegeRules and registrants.
if (mCarrierPrivilegeRules == null) {
- pw.println(" mCarrierPrivilegeRules: null");
+ pw.println("mCarrierPrivilegeRules: null");
} else {
- pw.println(" mCarrierPrivilegeRules: " + mCarrierPrivilegeRules);
+ pw.println("mCarrierPrivilegeRules: ");
+ pw.increaseIndent();
mCarrierPrivilegeRules.dump(fd, pw, args);
+ pw.decreaseIndent();
}
if (mTestOverrideCarrierPrivilegeRules != null) {
- pw.println(" mTestOverrideCarrierPrivilegeRules: "
+ pw.println("mTestOverrideCarrierPrivilegeRules: "
+ mTestOverrideCarrierPrivilegeRules);
mTestOverrideCarrierPrivilegeRules.dump(fd, pw, args);
}
pw.flush();
- pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
+ pw.println("mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
pw.println(" mNetworkLockedRegistrants[" + i + "]="
+ ((Registrant) mNetworkLockedRegistrants.get(i)).getHandler());
}
- pw.println(" mCurrentAppType=" + mCurrentAppType);
- pw.println(" mUiccCard=" + mUiccCard);
- pw.println(" mUiccApplication=" + mUiccApplication);
- pw.println(" mIccRecords=" + mIccRecords);
- pw.println(" mExternalState=" + mExternalState);
+ pw.println("mCurrentAppType=" + mCurrentAppType);
+ pw.println("mUiccCard=" + mUiccCard);
+ pw.println("mUiccApplication=" + mUiccApplication);
+ pw.println("mIccRecords=" + mIccRecords);
+ pw.println("mExternalState=" + mExternalState);
+ pw.decreaseIndent();
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index 9b5b315..db10271 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -33,6 +33,8 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.view.WindowManager;
import com.android.internal.R;
@@ -41,7 +43,9 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.uicc.euicc.EuiccCard;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -79,7 +83,6 @@
private final Object mLock = new Object();
private boolean mActive;
private boolean mStateIsUnknown = true;
- private CardState mCardState;
private Context mContext;
private UiccCard mUiccCard;
private boolean mIsEuicc;
@@ -87,12 +90,17 @@
private String mEid;
private AnswerToReset mAtr;
private boolean mIsRemovable;
+ private MultipleEnabledProfilesMode mSupportedMepMode;
+
// Map each available portIdx to phoneId
private HashMap<Integer, Integer> mPortIdxToPhoneId = new HashMap<>();
//Map each available portIdx with old radio state for state checking
private HashMap<Integer, Integer> mLastRadioState = new HashMap<>();
// Store iccId of each port.
private HashMap<Integer, String> mIccIds = new HashMap<>();
+ // IccCardStatus and IccSlotStatus events order is not guaranteed. Inorder to handle MEP mode,
+ // map each available portIdx with CardState for card state checking
+ private HashMap<Integer, CardState> mCardState = new HashMap<>();
private static final int EVENT_CARD_REMOVED = 13;
private static final int EVENT_CARD_ADDED = 14;
@@ -101,28 +109,31 @@
if (DBG) log("Creating");
mContext = c;
mActive = isActive;
- mCardState = null;
+ mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
}
/**
* Update slot. The main trigger for this is a change in the ICC Card status.
*/
public void update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex) {
- if (DBG) log("cardStatus update: " + ics.toString());
synchronized (mLock) {
mPortIdxToPhoneId.put(ics.mSlotPortMapping.mPortIndex, phoneId);
- CardState oldState = mCardState;
- mCardState = ics.mCardState;
+ CardState oldState = mCardState.get(ics.mSlotPortMapping.mPortIndex);
+ mCardState.put(ics.mSlotPortMapping.mPortIndex, ics.mCardState);
mIccIds.put(ics.mSlotPortMapping.mPortIndex, ics.iccid);
parseAtr(ics.atr);
mIsRemovable = isSlotRemovable(slotIndex);
+ // Update supported MEP mode in IccCardStatus if the CardState is present.
+ if (ics.mCardState.isCardPresent()) {
+ updateSupportedMepMode(ics.mSupportedMepMode);
+ }
int radioState = ci.getRadioState();
if (DBG) {
log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
}
- if (absentStateUpdateNeeded(oldState)) {
+ if (absentStateUpdateNeeded(oldState, ics.mSlotPortMapping.mPortIndex)) {
updateCardStateAbsent(ci.getRadioState(), phoneId,
ics.mSlotPortMapping.mPortIndex);
// Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
@@ -130,7 +141,8 @@
// 1. mCardState is changing from ABSENT to non ABSENT.
// 2. The latest mCardState is not ABSENT, but there is no UiccCard instance.
} else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT
- || mUiccCard == null) && mCardState != CardState.CARDSTATE_ABSENT) {
+ || mUiccCard == null) && mCardState.get(ics.mSlotPortMapping.mPortIndex)
+ != CardState.CARDSTATE_ABSENT) {
// No notification while we are just powering up
if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE
&& mLastRadioState.getOrDefault(ics.mSlotPortMapping.mPortIndex,
@@ -141,14 +153,17 @@
}
// card is present in the slot now; create new mUiccCard
- if (mUiccCard != null) {
+ if (mUiccCard != null && (!mIsEuicc
+ || ArrayUtils.isEmpty(mUiccCard.getUiccPortList()))) {
loge("update: mUiccCard != null when card was present; disposing it now");
mUiccCard.dispose();
+ mUiccCard = null;
}
if (!mIsEuicc) {
// Uicc does not support MEP, passing false by default.
- mUiccCard = new UiccCard(mContext, ci, ics, phoneId, mLock, false);
+ mUiccCard = new UiccCard(mContext, ci, ics, phoneId, mLock,
+ MultipleEnabledProfilesMode.NONE);
} else {
// The EID should be reported with the card status, but in case it's not we want
// to catch that here
@@ -156,8 +171,14 @@
loge("update: eid is missing. ics.eid="
+ Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, ics.eid));
}
- mUiccCard = new EuiccCard(mContext, ci, ics, phoneId, mLock,
- isMultipleEnabledProfileSupported());
+ if (mUiccCard == null) {
+ mUiccCard = new EuiccCard(mContext, ci, ics, phoneId, mLock,
+ getSupportedMepMode());
+ } else {
+ // In MEP case, UiccCard instance is already created, just call update API.
+ // UiccPort initialization is handled inside UiccCard.
+ mUiccCard.update(mContext, ci, ics, phoneId);
+ }
}
} else {
if (mUiccCard != null) {
@@ -172,22 +193,21 @@
* Update slot based on IccSlotStatus.
*/
public void update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex) {
- if (DBG) log("slotStatus update: " + iss.toString());
synchronized (mLock) {
IccSimPortInfo[] simPortInfos = iss.mSimPortInfos;
- CardState oldState = mCardState;
parseAtr(iss.atr);
- mCardState = iss.cardState;
mEid = iss.eid;
mIsRemovable = isSlotRemovable(slotIndex);
for (int i = 0; i < simPortInfos.length; i++) {
int phoneId = iss.mSimPortInfos[i].mLogicalSlotIndex;
+ CardState oldState = mCardState.get(i);
+ mCardState.put(i, iss.cardState);
mIccIds.put(i, simPortInfos[i].mIccId);
if (!iss.mSimPortInfos[i].mPortActive) {
// TODO: (b/79432584) evaluate whether should broadcast card state change
// even if it's inactive.
- UiccController.updateInternalIccStateForInactivePort(mContext,
+ UiccController.getInstance().updateSimStateForInactivePort(
mPortIdxToPhoneId.getOrDefault(i, INVALID_PHONE_ID),
iss.mSimPortInfos[i].mIccId);
mLastRadioState.put(i, TelephonyManager.RADIO_POWER_UNAVAILABLE);
@@ -196,7 +216,7 @@
mUiccCard.disposePort(i);
}
} else {
- if (absentStateUpdateNeeded(oldState)) {
+ if (absentStateUpdateNeeded(oldState, i)) {
int radioState = SubscriptionManager.isValidPhoneId(phoneId) ?
ci[phoneId].getRadioState() :
TelephonyManager.RADIO_POWER_UNAVAILABLE;
@@ -223,10 +243,28 @@
mPortIdxToPhoneId.put(i, simPortInfos[i].mPortActive ?
simPortInfos[i].mLogicalSlotIndex : INVALID_PHONE_ID);
}
- // Since the MEP capability is related with number ports reported, thus need to
+ updateSupportedMepMode(iss.mSupportedMepMode);
+ // Since the MEP capability is related to supported MEP mode, thus need to
// update the flag after UiccCard creation.
if (mUiccCard != null) {
- mUiccCard.updateSupportMultipleEnabledProfile(isMultipleEnabledProfileSupported());
+ mUiccCard.updateSupportedMepMode(getSupportedMepMode());
+ }
+ }
+ }
+
+ private void updateSupportedMepMode(MultipleEnabledProfilesMode mode) {
+ mSupportedMepMode = mode;
+ // If SupportedMepMode is MultipleEnabledProfilesMode.NONE, validate ATR and
+ // num of ports to handle backward compatibility for < RADIO_HAL_VERSION_2_1.
+ if (mode == MultipleEnabledProfilesMode.NONE) {
+ // Even ATR suggest UICC supports multiple enabled profiles, MEP can be disabled per
+ // carrier restrictions, so checking the real number of ports reported from modem is
+ // necessary.
+ if (mPortIdxToPhoneId.size() > 1
+ && mAtr != null && mAtr.isMultipleEnabledProfilesSupported()) {
+ // Set MEP-B mode in case if modem sends wrong mode even though supports MEP.
+ Log.i(TAG, "Modem does not send proper supported MEP mode or older HAL version");
+ mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B;
}
}
}
@@ -300,16 +338,14 @@
/* Returns true if multiple enabled profiles are supported */
public boolean isMultipleEnabledProfileSupported() {
- // even ATR suggest UICC supports multiple enabled profiles, MEP can be disabled per
- // carrier restrictions, so checking the real number of ports reported from modem is
- // necessary.
- return mPortIdxToPhoneId.size() > 1 && mAtr != null &&
- mAtr.isMultipleEnabledProfilesSupported();
+ synchronized (mLock) {
+ return mSupportedMepMode.isMepMode();
+ }
}
- private boolean absentStateUpdateNeeded(CardState oldState) {
+ private boolean absentStateUpdateNeeded(CardState oldState, int portIndex) {
return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null)
- && mCardState == CardState.CARDSTATE_ABSENT;
+ && mCardState.get(portIndex) == CardState.CARDSTATE_ABSENT;
}
private void updateCardStateAbsent(int radioState, int phoneId, int portIndex) {
@@ -321,10 +357,13 @@
if (DBG) log("update: notify card removed");
sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
}
- UiccController.updateInternalIccState(mContext, IccCardConstants.State.ABSENT,
- null, phoneId);
- // no card present in the slot now; dispose card and make mUiccCard null
- nullifyUiccCard(false /* sim state is not unknown */);
+
+ UiccController.getInstance().updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
+ // no card present in the slot now; dispose port and then card if needed.
+ disposeUiccCardIfNeeded(false /* sim state is not unknown */, portIndex);
+ // If SLOT_STATUS is the last event, wrong subscription is getting invalidate during
+ // slot switch event. To avoid it, reset the phoneId corresponding to the portIndex.
+ mPortIdxToPhoneId.put(portIndex, INVALID_PHONE_ID);
mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE);
}
@@ -339,8 +378,23 @@
mUiccCard = null;
}
+ private void disposeUiccCardIfNeeded(boolean isStateUnknown, int portIndex) {
+ if (mUiccCard != null) {
+ // First dispose UiccPort corresponding to the portIndex
+ mUiccCard.disposePort(portIndex);
+ if (ArrayUtils.isEmpty(mUiccCard.getUiccPortList())) {
+ // No UiccPort objects are found, safe to dispose the card
+ nullifyUiccCard(isStateUnknown);
+ }
+ } else {
+ mStateIsUnknown = isStateUnknown;
+ }
+ }
+
public boolean isStateUnknown() {
- if (mCardState == null || mCardState == CardState.CARDSTATE_ABSENT) {
+ // CardState is not specific to any port index, use default port.
+ CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
+ if (cardState == null || cardState == CardState.CARDSTATE_ABSENT) {
// mStateIsUnknown is valid only in this scenario.
return mStateIsUnknown;
}
@@ -551,11 +605,9 @@
*/
public CardState getCardState() {
synchronized (mLock) {
- if (mCardState == null) {
- return CardState.CARDSTATE_ABSENT;
- } else {
- return mCardState;
- }
+ // CardState is not specific to any port index, use default port.
+ CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
+ return cardState == null ? CardState.CARDSTATE_ABSENT : cardState;
}
}
@@ -569,19 +621,27 @@
}
/**
+ * Returns the supported MEP mode.
+ */
+ public MultipleEnabledProfilesMode getSupportedMepMode() {
+ synchronized (mLock) {
+ return mSupportedMepMode;
+ }
+ }
+ /**
* Processes radio state unavailable event
*/
public void onRadioStateUnavailable(int phoneId) {
- nullifyUiccCard(true /* sim state is unknown */);
+ int portIndex = getPortIndexFromPhoneId(phoneId);
+ disposeUiccCardIfNeeded(true /* sim state is unknown */, portIndex);
if (phoneId != INVALID_PHONE_ID) {
- UiccController.updateInternalIccState(
- mContext, IccCardConstants.State.UNKNOWN, null, phoneId);
- mLastRadioState.put(getPortIndexFromPhoneId(phoneId),
- TelephonyManager.RADIO_POWER_UNAVAILABLE);
+ UiccController.getInstance().updateSimState(phoneId,
+ IccCardConstants.State.UNKNOWN, null);
}
-
- mCardState = null;
+ mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE);
+ // Reset CardState
+ mCardState.put(portIndex, null);
}
private void log(String msg) {
@@ -595,32 +655,41 @@
private Map<Integer, String> getPrintableIccIds() {
Map<Integer, String> printableIccIds = mIccIds.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
- e -> SubscriptionInfo.givePrintableIccid(e.getValue())));
+ e -> SubscriptionInfo.getPrintableId(e.getValue())));
return printableIccIds;
}
/**
* Dump
*/
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("UiccSlot:");
- pw.println(" mActive=" + mActive);
- pw.println(" mIsEuicc=" + mIsEuicc);
- pw.println(" isEuiccSupportsMultipleEnabledProfiles="
- + isMultipleEnabledProfileSupported());
- pw.println(" mIsRemovable=" + mIsRemovable);
- pw.println(" mLastRadioState=" + mLastRadioState);
- pw.println(" mIccIds=" + getPrintableIccIds());
- pw.println(" mPortIdxToPhoneId=" + mPortIdxToPhoneId);
- pw.println(" mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid));
- pw.println(" mCardState=" + mCardState);
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("mActive=" + mActive);
+ pw.println("mIsEuicc=" + mIsEuicc);
+ pw.println("isEuiccSupportsMultipleEnabledProfiles=" + isMultipleEnabledProfileSupported());
+ pw.println("mIsRemovable=" + mIsRemovable);
+ pw.println("mLastRadioState=" + mLastRadioState);
+ pw.println("mIccIds=" + getPrintableIccIds());
+ pw.println("mPortIdxToPhoneId=" + mPortIdxToPhoneId);
+ pw.println("mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid));
+ pw.println("mCardState=" + mCardState);
+ pw.println("mSupportedMepMode=" + mSupportedMepMode);
if (mUiccCard != null) {
- pw.println(" mUiccCard=" + mUiccCard);
+ pw.println("mUiccCard=");
mUiccCard.dump(fd, pw, args);
} else {
- pw.println(" mUiccCard=null");
+ pw.println("mUiccCard=null");
}
pw.println();
pw.flush();
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "[UiccSlot: mActive=" + mActive + ", mIccId=" + getPrintableIccIds() + ", mIsEuicc="
+ + mIsEuicc + ", MEP=" + isMultipleEnabledProfileSupported() + ", mPortIdxToPhoneId="
+ + mPortIdxToPhoneId + ", mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
+ + ", mCardState=" + mCardState + " mSupportedMepMode=" + mSupportedMepMode + "]";
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
old mode 100755
new mode 100644
index ff8d63d..bc46f80
--- a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
@@ -68,6 +68,7 @@
case EF_FPLMN:
case EF_LRPLMNSI:
case EF_HPPLMN:
+ case EF_SMSS:
return MF_SIM + DF_ADF;
case EF_PBR:
diff --git a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
index fc58d3c..ea2bf42 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
@@ -157,4 +157,8 @@
protected Object[] getValues() {
return UsimService.values();
}
+
+ public byte[] getUSIMServiceTable() {
+ return mServiceTable;
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index 75bc3ba..698fbc8 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc.euicc;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.AsyncResult;
import android.os.Handler;
@@ -23,10 +24,12 @@
import android.os.RegistrantList;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
@@ -43,8 +46,8 @@
private RegistrantList mEidReadyRegistrants;
public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock,
- boolean isSupportsMultipleEnabledProfiles) {
- super(c, ci, ics, phoneId, lock, isSupportsMultipleEnabledProfiles);
+ MultipleEnabledProfilesMode supportedMepMode) {
+ super(c, ci, ics, phoneId, lock, supportedMepMode);
if (TextUtils.isEmpty(ics.eid)) {
loge("no eid given in constructor for phone " + phoneId);
loadEidAndNotifyRegistrants();
@@ -55,17 +58,17 @@
}
/**
- * Updates MEP(Multiple Enabled Profile) support flag.
+ * Updates MEP(Multiple Enabled Profile) supported mode flag.
*
* <p>If IccSlotStatus comes later, the number of ports reported is only known after the
- * UiccCard creation which will impact UICC MEP capability.
+ * UiccCard creation which will impact UICC MEP capability in case of old HAL version.
*/
@Override
- public void updateSupportMultipleEnabledProfile(boolean supported) {
- mIsSupportsMultipleEnabledProfiles = supported;
+ public void updateSupportedMepMode(MultipleEnabledProfilesMode supportedMepMode) {
+ mSupportedMepMode = supportedMepMode;
for (UiccPort port : mUiccPorts.values()) {
if (port instanceof EuiccPort) {
- ((EuiccPort) port).updateSupportMultipleEnabledProfile(supported);
+ ((EuiccPort) port).updateSupportedMepMode(supportedMepMode);
} else {
loge("eUICC card has non-euicc port object:" + port.toString());
}
@@ -175,10 +178,14 @@
}
}
+ @NonNull
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ super.dump(fd, printWriter, args);
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("EuiccCard:");
- pw.println(" mEid=" + mEid);
+ pw.increaseIndent();
+ pw.println("mEid=" + mEid);
+ pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
index e797107..3bd66f8 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
@@ -28,6 +28,7 @@
import android.telephony.euicc.EuiccNotification;
import android.telephony.euicc.EuiccRulesAuthTable;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
@@ -35,7 +36,9 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.PortUtils;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
@@ -124,10 +127,10 @@
private EuiccSpecVersion mSpecVersion;
private volatile String mEid;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public boolean mIsSupportsMultipleEnabledProfiles;
+ public MultipleEnabledProfilesMode mSupportedMepMode;
public EuiccPort(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock,
- UiccCard card, boolean isSupportsMultipleEnabledProfiles) {
+ UiccCard card, MultipleEnabledProfilesMode supportedMepMode) {
super(c, ci, ics, phoneId, lock, card);
// TODO: Set supportExtendedApdu based on ATR.
mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
@@ -137,7 +140,7 @@
mEid = ics.eid;
mCardId = ics.eid;
}
- mIsSupportsMultipleEnabledProfiles = isSupportsMultipleEnabledProfiles;
+ mSupportedMepMode = supportedMepMode;
}
/**
@@ -165,12 +168,12 @@
}
/**
- * Updates MEP(Multiple Enabled Profile) support flag.
+ * Updates MEP(Multiple Enabled Profile) supported mode flag.
* The flag can be updated after the port creation.
*/
- public void updateSupportMultipleEnabledProfile(boolean supported) {
- logd("updateSupportMultipleEnabledProfile");
- mIsSupportsMultipleEnabledProfiles = supported;
+ public void updateSupportedMepMode(MultipleEnabledProfilesMode supportedMepMode) {
+ logd("updateSupportedMepMode");
+ mSupportedMepMode = supportedMepMode;
}
/**
@@ -181,7 +184,7 @@
* @since 1.1.0 [GSMA SGP.22]
*/
public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) {
- byte[] profileTags = mIsSupportsMultipleEnabledProfiles ? Tags.EUICC_PROFILE_MEP_TAGS
+ byte[] profileTags = mSupportedMepMode.isMepMode() ? Tags.EUICC_PROFILE_MEP_TAGS
: Tags.EUICC_PROFILE_TAGS;
sendApdu(
newRequestProvider((RequestBuilder requestBuilder) ->
@@ -223,7 +226,7 @@
*/
public final void getProfile(String iccid, AsyncResultCallback<EuiccProfileInfo> callback,
Handler handler) {
- byte[] profileTags = mIsSupportsMultipleEnabledProfiles ? Tags.EUICC_PROFILE_MEP_TAGS
+ byte[] profileTags = mSupportedMepMode.isMepMode() ? Tags.EUICC_PROFILE_MEP_TAGS
: Tags.EUICC_PROFILE_TAGS;
sendApdu(
newRequestProvider((RequestBuilder requestBuilder) ->
@@ -279,7 +282,7 @@
return null;
case CODE_PROFILE_NOT_IN_EXPECTED_STATE:
logd("Profile is already disabled, iccid: "
- + SubscriptionInfo.givePrintableIccid(iccid));
+ + SubscriptionInfo.getPrintableId(iccid));
return null;
default:
throw new EuiccCardErrorException(
@@ -303,11 +306,20 @@
sendApduWithSimResetErrorWorkaround(
newRequestProvider((RequestBuilder requestBuilder) -> {
byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid));
- requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_ENABLE_PROFILE)
+ Asn1Node.Builder builder = Asn1Node.newBuilder(Tags.TAG_ENABLE_PROFILE)
.addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
.addChildAsBytes(Tags.TAG_ICCID, iccidBytes))
- .addChildAsBoolean(Tags.TAG_CTX_1, refresh)
- .build().toHex());
+ .addChildAsBoolean(Tags.TAG_CTX_1, refresh);
+ // Port index should be added only in case of MEP-A1 mode.
+ if (mSupportedMepMode.isMepA1Mode()) {
+ // In case of MEP-A1 and MEP-A2, profiles are selected on eSIM Ports 1 and
+ // higher (refer as target port). Hence, convert the portIndex to
+ // target port index before adding.
+ builder.addChildAsInteger(Tags.TAG_CTX_2,
+ PortUtils.convertToHalPortIndex(mSupportedMepMode,
+ super.getPortIdx()));
+ }
+ requestBuilder.addStoreData(builder.build().toHex());
}),
response -> {
int result;
@@ -318,7 +330,7 @@
return null;
case CODE_PROFILE_NOT_IN_EXPECTED_STATE:
logd("Profile is already enabled, iccid: "
- + SubscriptionInfo.givePrintableIccid(iccid));
+ + SubscriptionInfo.getPrintableId(iccid));
return null;
default:
throw new EuiccCardErrorException(
@@ -979,15 +991,17 @@
}
String devCap = split[0].trim();
- Integer version;
+ String[] fullVer = (split[1].trim()).split("\\.");
+ Integer version, subVersion = 0;
try {
- version = Integer.parseInt(split[1].trim());
+ version = Integer.parseInt(fullVer[0]);
+ if (fullVer.length > 1) subVersion = Integer.parseInt(fullVer[1]);
} catch (NumberFormatException e) {
loge("Invalid device capability version number.", e);
return;
}
- byte[] versionBytes = new byte[] { version.byteValue(), 0, 0 };
+ byte[] versionBytes = new byte[] { version.byteValue(), subVersion.byteValue(), 0 };
switch (devCap) {
case DEV_CAP_GSM:
devCapBuilder.addChildAsBytes(Tags.TAG_CTX_0, versionBytes);
@@ -1410,10 +1424,13 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ super.dump(fd, printWriter, args);
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("EuiccPort:");
- pw.println(" mEid=" + mEid);
- pw.println(" mIsSupportsMultipleEnabledProfiles=" + mIsSupportsMultipleEnabledProfiles);
+ pw.increaseIndent();
+ pw.println("mEid=" + mEid);
+ pw.println("mSupportedMepMode=" + mSupportedMepMode);
+ pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduCommand.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduCommand.java
index 7a8978f..8fbeb44 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduCommand.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduCommand.java
@@ -43,6 +43,12 @@
/** Command data of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
public final String cmdHex;
+ /**
+ * isEs10 indicates that the current streaming APDU contains an ES10 command or it is a regular
+ * APDU. (As per spec SGP.22 V3.0, ES10 commands needs to be sent over command port of MEP-A1)
+ */
+ public final boolean isEs10;
+
/** The parameters are defined as in GlobalPlatform Card Specification v.2.3. */
ApduCommand(int channel, int cla, int ins, int p1, int p2, int p3, String cmdHex) {
this.channel = channel;
@@ -52,11 +58,14 @@
this.p2 = p2;
this.p3 = p3;
this.cmdHex = cmdHex;
+ // TODO: Currently ApduCommand is used for ES10 commands, so updating to true by default.
+ // Modify it in case used for non ES10 commands in future.
+ this.isEs10 = true;
}
@Override
public String toString() {
return "ApduCommand(channel=" + channel + ", cla=" + cla + ", ins=" + ins + ", p1=" + p1
- + ", p2=" + p2 + ", p3=" + p3 + ", cmd=" + cmdHex + ")";
+ + ", p2=" + p2 + ", p3=" + p3 + ", cmd=" + cmdHex + ", isEs10=" + isEs10 + ")";
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/CloseLogicalChannelInvocation.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/CloseLogicalChannelInvocation.java
index 82ddb80..a69977b 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/CloseLogicalChannelInvocation.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/CloseLogicalChannelInvocation.java
@@ -43,7 +43,10 @@
@Override
protected void sendRequestMessage(Integer channel, Message msg) {
Rlog.v(LOG_TAG, "Channel: " + channel);
- mCi.iccCloseLogicalChannel(channel, msg);
+ // TODO: Currently CloseLogicalChannelInvocation is used from ApduSender for closing the
+ // channel opened on ISD-R. Hence passing isEs10 as true by default.
+ // Should modify the logic in future if the ApduSender is used with non ISD-R AID.
+ mCi.iccCloseLogicalChannel(channel, true /*isEs10*/, msg);
}
@Override
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/TransmitApduLogicalChannelInvocation.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/TransmitApduLogicalChannelInvocation.java
index 09de54a..ca75beb 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/TransmitApduLogicalChannelInvocation.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/TransmitApduLogicalChannelInvocation.java
@@ -48,7 +48,8 @@
protected void sendRequestMessage(ApduCommand command, Message msg) {
Rlog.v(LOG_TAG, "Send: " + command);
mCi.iccTransmitApduLogicalChannel(command.channel, command.cla | command.channel,
- command.ins, command.p1, command.p2, command.p3, command.cmdHex, msg);
+ command.ins, command.p1, command.p2, command.p3, command.cmdHex, command.isEs10,
+ msg);
}
@Override
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
index 17f2611..da429b7 100644
--- a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -16,12 +16,11 @@
package com.android.internal.telephony.util;
-import com.android.internal.telephony.ImsSmsDispatcher;
-import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.ImsSmsDispatcher;
import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
import com.android.internal.telephony.gsm.GsmSMSDispatcher;
/**
@@ -95,6 +94,46 @@
}
/**
+ * Trigger the proper implementation for getting submit pdu for text sms based on format.
+ *
+ * @param isCdma true if cdma format should be used.
+ * @param scAddr is the service center address or null to use the current default SMSC
+ * @param destAddr the address to send the message to
+ * @param message the body of the message.
+ * @param statusReportRequested whether or not a status report is requested.
+ * @param smsHeader message header.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ * @param messageRef TP Message Reference number
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+ String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader,
+ int priority, int validityPeriod, int messageRef) {
+ if (isCdma) {
+ return getSubmitPduCdma(scAddr, destAddr, message, statusReportRequested, smsHeader,
+ priority);
+ } else {
+ return getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+ validityPeriod, messageRef);
+ }
+ }
+
+ /**
* Gsm implementation for
* {@link #getSubmitPdu(boolean, String, String, String, boolean)}
*
@@ -132,6 +171,28 @@
}
/**
+ * Gsm implementation for
+ * {@link #getSubmitPdu(boolean, String, String, String, boolean, int)}
+ *
+ * @param scAddr is the service center address or null to use the current default SMSC
+ * @param destAddr the address to send the message to
+ * @param message the body of the message.
+ * @param statusReportRequested whether or not a status report is requested.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ * @param messageRef TP Message Reference number
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, int validityPeriod, int messageRef) {
+ return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr, message,
+ statusReportRequested, null, 0, 0, 0, validityPeriod, messageRef);
+ }
+
+ /**
* Cdma implementation for
* {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
*
@@ -197,6 +258,29 @@
}
/**
+ * Trigger the proper implementation for getting submit pdu for data sms based on format.
+ *
+ * @param isCdma true if cdma format should be used.
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param message the body of the message to send
+ * @param statusReportRequested whether or not a status report is requested.
+ * @param messageRef TP Message Reference number
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+ String destAddr, int destPort, byte[] message, boolean statusReportRequested,
+ int messageRef) {
+ if (isCdma) {
+ return getSubmitPduCdma(scAddr, destAddr, destPort, message, statusReportRequested);
+ } else {
+ return getSubmitPduGsm(scAddr, destAddr, destPort, message, statusReportRequested,
+ messageRef);
+ }
+ }
+
+ /**
* Cdma implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
* @param destAddr the address to send the message to
@@ -230,6 +314,22 @@
}
/**
+ * Gsm implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param message the body of the message to send
+ * @param statusReportRequested whether or not a status report is requested.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+ return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr,
+ destPort, message, statusReportRequested, messageRef);
+ }
+
+ /**
* Calculate the number of septets needed to encode the message. This function should only be
* called for individual segments of multipart message.
*
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index f197101..2aa446d 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -48,6 +48,7 @@
test_suites: [
"device-tests",
],
+ min_sdk_version: "29",
}
genrule {
diff --git a/tests/telephonytests/assets/eccdata b/tests/telephonytests/assets/eccdata
index 4c5959b..bd9ff47 100644
--- a/tests/telephonytests/assets/eccdata
+++ b/tests/telephonytests/assets/eccdata
Binary files differ
diff --git a/tests/telephonytests/assets/eccdata_input.txt b/tests/telephonytests/assets/eccdata_input.txt
index 8dcfa75..6f8632c 100644
--- a/tests/telephonytests/assets/eccdata_input.txt
+++ b/tests/telephonytests/assets/eccdata_input.txt
@@ -10,6 +10,57 @@
types: MOUNTAIN_RESCUE
types: MIEC
types: AIEC
+ routing: NORMAL
+ normal_routing_mncs: "05"
+ }
+ eccs {
+ phone_number: "888"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ types: MARINE_GUARD
+ types: MOUNTAIN_RESCUE
+ types: MIEC
+ types: AIEC
+ routing: NORMAL
+ normal_routing_mncs: "45"
+ normal_routing_mncs: "47"
+ normal_routing_mncs: "05"
+ }
+ eccs {
+ phone_number: "654321"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ types: MARINE_GUARD
+ types: MOUNTAIN_RESCUE
+ types: MIEC
+ types: AIEC
+ routing: EMERGENCY
+ normal_routing_mncs: ""
+ }
+ eccs {
+ phone_number: "7654321"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ types: MARINE_GUARD
+ types: MOUNTAIN_RESCUE
+ types: MIEC
+ types: AIEC
+ routing: NORMAL
+ normal_routing_mncs: ""
+ }
+ eccs {
+ phone_number: "4321"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ types: MARINE_GUARD
+ types: MOUNTAIN_RESCUE
+ types: MIEC
+ types: AIEC
+ routing: NORMAL
}
ecc_fallback: "911"
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
index af80bd7..56ce6bc 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
@@ -16,9 +16,13 @@
package android.telephony.ims;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -147,7 +151,33 @@
ImsReasonInfo info = new ImsReasonInfo();
mRegistration.onDeregistered(info);
- verify(mCallback).onDeregistered(eq(info));
+ verify(mCallback).onDeregistered(eq(info), anyInt(), anyInt());
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnDeregisteredWithSuggestedAction() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo();
+ mRegistration.onDeregistered(info,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_LTE);
+
+ verify(mCallback).onDeregistered(eq(info),
+ eq(SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK), eq(REGISTRATION_TECH_LTE));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnDeregisteredWithRegistrationAttributes()
+ throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo();
+ SipDetails details = new SipDetails.Builder(SipDetails.METHOD_REGISTER)
+ .setCSeq(1).setSipResponseCode(200, "OK")
+ .setSipResponseReasonHeader(10, "reasonText")
+ .setCallId("testCallId").build();
+
+ mRegistration.onDeregistered(info, details);
+
+ verify(mCallback).onDeregisteredWithDetails(eq(info), anyInt(), anyInt(), eq(details));
}
@SmallTest
@@ -218,10 +248,10 @@
// The original callback that has been registered should get LTE tech in disconnected
// message
- verify(mCallback).onDeregistered(eq(info));
+ verify(mCallback).onDeregistered(eq(info), anyInt(), anyInt());
// A callback that has just been registered should get NONE for tech in disconnected
// message
- verify(mCallback2).onDeregistered(eq(info));
+ verify(mCallback2).onDeregistered(eq(info), anyInt(), anyInt());
}
@SmallTest
@@ -231,7 +261,7 @@
mRegistration.onDeregistered(info);
- verify(mCallback).onDeregistered(eq(info));
+ verify(mCallback).onDeregistered(eq(info), anyInt(), anyInt());
assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
mRegBinder.getRegistrationTechnology());
}
@@ -242,7 +272,7 @@
mRegBinder.addRegistrationCallback(mCallback2);
// Verify that if we have never set the registration state, we do not callback immediately
// with onUnregistered.
- verify(mCallback2, never()).onDeregistered(any(ImsReasonInfo.class));
+ verify(mCallback2, never()).onDeregistered(any(ImsReasonInfo.class), anyInt(), anyInt());
}
@SmallTest
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index 5af871c..b002b35 100644
--- a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -151,13 +152,27 @@
mFeature.incomingCall(session);
ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
- verify(mListener).onIncomingCall(captor.capture(), any());
+ verify(mListener).onIncomingCall(captor.capture(), eq(null), any());
assertEquals(sessionBinder, captor.getValue());
}
@SmallTest
@Test
+ public void testNewIncomingCallReturnListener() throws Exception {
+ IImsCallSession sessionBinder = Mockito.mock(IImsCallSession.class);
+ ImsCallSessionImplBase session = new ImsCallSessionImplBase();
+ session.setServiceImpl(sessionBinder);
+ String callId = "callID";
+ Bundle extra = new Bundle();
+ mFeature.incomingCall(session, callId, extra);
+ ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
+ verify(mListener).onIncomingCall(captor.capture(), eq(callId), eq(extra));
+ assertEquals(sessionBinder, captor.getValue());
+ }
+
+ @SmallTest
+ @Test
public void testSetTtyMessageMessenger() throws Exception {
Message resultMessage = Message.obtain(mHandler, TEST_TTY_RESULT);
resultMessage.replyTo = mHandlerMessenger;
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index 9ee1e30..1269cc8 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -64,6 +64,11 @@
notifyIncomingCall(c, new Bundle());
}
+ public ImsCallSessionListener incomingCall(
+ ImsCallSessionImplBase c, String callId, Bundle extra) {
+ return notifyIncomingCall(c, callId, extra);
+ }
+
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType) {
return super.createCallProfile(callSessionType, callType);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallStateTest.java
new file mode 100644
index 0000000..4e319a1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallStateTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telephony.CallQuality;
+import android.telephony.CallState;
+import android.telephony.PreciseCallState;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Simple GTS test verifying the parceling and unparceling of CallAttributes.
+ */
+public class CallStateTest extends AndroidTestCase {
+
+
+ @SmallTest
+ public void testParcelUnparcelPreciseCallState() {
+ CallState data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_INCOMING)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(null)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_RINGING)
+ .setImsCallSessionId("1")
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NONE)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_NONE).build();
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CallState unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertEquals("PreciseCallState is not equal after parceled/unparceled",
+ data.getCallState(),
+ unparceledData.getCallState());
+ }
+
+ @SmallTest
+ public void testParcelUnparcelCallQuality() {
+ CallQuality quality = new CallQuality();
+ CallState data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_IDLE)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(null)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_FOREGROUND)
+ .setImsCallSessionId(null)
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NONE)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_NONE).build();
+
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CallState unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertNull(unparceledData.getCallQuality());
+
+ data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_IDLE)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(quality)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_FOREGROUND)
+ .setImsCallSessionId(null)
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NONE)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_NONE).build();
+
+
+ parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertEquals("CallQuality is not equal after parceled/unparceled",
+ data.getCallQuality(),
+ unparceledData.getCallQuality());
+ }
+
+ @SmallTest
+ public void testParcelUnparcelNetworkTypeAndClassification() {
+ CallQuality quality = new CallQuality();
+ CallState data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_DIALING)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(null)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_FOREGROUND)
+ .setImsCallSessionId("3")
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NONE)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_NONE).build();
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CallState unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertEquals("NetworkType is not equal after parceled/unparceled",
+ data.getNetworkType(),
+ unparceledData.getNetworkType());
+ assertEquals("Call classification is not equal after parceled/unparceled",
+ data.getCallClassification(),
+ unparceledData.getCallClassification());
+ }
+
+ @Test
+ public void testParcelUnparcelImsCallInfo() {
+ CallQuality quality = new CallQuality();
+ CallState data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_DIALING)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(null)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_FOREGROUND)
+ .setImsCallSessionId(null)
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE).build();
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CallState unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertNull(unparceledData.getImsCallSessionId());
+
+ assertEquals("Ims call service type is not equal after parceled/unparceled",
+ data.getImsCallServiceType(),
+ unparceledData.getImsCallServiceType());
+
+ assertEquals("Ims call type is not equal after parceled/unparceled",
+ data.getImsCallType(),
+ unparceledData.getImsCallType());
+
+ data = new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCallQuality(quality)
+ .setCallClassification(CallState.CALL_CLASSIFICATION_FOREGROUND)
+ .setImsCallSessionId("2")
+ .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL)
+ .setImsCallType(ImsCallProfile.CALL_TYPE_VT).build();
+
+ parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ unparceledData = CallState.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertEquals("Ims call session ID is not equal after parceled/unparceled",
+ data.getImsCallSessionId(),
+ unparceledData.getImsCallSessionId());
+
+ assertEquals("Ims call service type is not equal after parceled/unparceled",
+ data.getImsCallServiceType(),
+ unparceledData.getImsCallServiceType());
+
+ assertEquals("Ims call type is not equal after parceled/unparceled",
+ data.getImsCallType(),
+ unparceledData.getImsCallType());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java
new file mode 100644
index 0000000..eb18adb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+
+import static com.android.internal.telephony.CallWaitingController.KEY_CS_SYNC;
+import static com.android.internal.telephony.CallWaitingController.KEY_STATE;
+import static com.android.internal.telephony.CallWaitingController.KEY_SUB_ID;
+import static com.android.internal.telephony.CallWaitingController.PREFERENCE_TBCW;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CallWaitingControllerTest extends TelephonyTest {
+ private static final int FAKE_SUB_ID = 1;
+
+ private static final int GET_DONE = 1;
+
+ private CallWaitingController mCWC;
+ private GetTestHandler mHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ mSimulatedCommands.setRadioPower(true, null);
+ mPhone.mCi = this.mSimulatedCommands;
+ doReturn(FAKE_SUB_ID).when(mPhone).getSubId();
+
+ mCWC = new CallWaitingController(mPhone);
+ logd("CallWaitingController initiated, waiting for Power on");
+ /* Make sure radio state is power on before dial.
+ * When radio state changed from off to on, CallTracker
+ * will poll result from RIL. Avoid dialing triggered at the same*/
+ processAllMessages();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCWC = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testSetTerminalBasedCallWaitingSupported() {
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+ }
+
+ @Test
+ @SmallTest
+ public void testInitialize() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_NONE);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), any());
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_NONE);
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ bundle = getConfigBundle(false, CALL_WAITING_SYNC_NONE, false);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), any());
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierConfigChanged() {
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+ bundle = getConfigBundle(false, CALL_WAITING_SYNC_NONE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, false);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+ }
+
+
+ private static class GetTestHandler extends Handler {
+ public int[] resp;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case GET_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ resp = (int[]) ar.result;
+ break;
+ default:
+ }
+ }
+
+ public void reset() {
+ resp = null;
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testGetCallWaitingSyncNone() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ assertFalse(mCWC.getCallWaiting(null));
+
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ mHandler = new GetTestHandler();
+
+ assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+ mHandler.reset();
+
+ assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCallWaitingSyncNone() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE | SERVICE_CLASS_DATA, null));
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+ assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_DATA, null));
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+ assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_NONE, null));
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+ }
+
+ @Test
+ @SmallTest
+ public void testSyncUserChange() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_USER_CHANGE);
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_USER_CHANGE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ mHandler = new GetTestHandler();
+
+ mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+ mHandler.reset();
+
+ mSimulatedCommands.setCallWaiting(true, SERVICE_CLASS_VOICE, null);
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ mHandler.reset();
+
+ assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+ }
+
+ @Test
+ @SmallTest
+ public void testSyncFirstPowerUp() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_FIRST_POWER_UP);
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_FIRST_POWER_UP, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+ assertFalse(mCWC.getSyncState());
+
+ mCWC.notifyRegisteredToNetwork();
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getSyncState());
+ }
+
+ @Test
+ @SmallTest
+ public void testSyncFirstChange() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_FIRST_CHANGE);
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_FIRST_CHANGE, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+ mCWC.setImsRegistrationState(false);
+
+ assertFalse(mCWC.getSyncState());
+
+ mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+ mCWC.getCallWaiting(null);
+ mTestableLooper.processAllMessages();
+
+ assertFalse(mCWC.getSyncState());
+
+ mSimulatedCommands.setCallWaiting(true, SERVICE_CLASS_VOICE, null);
+ mCWC.getCallWaiting(null);
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getSyncState());
+
+ assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mSimulatedCommands.mCallWaitActivated);
+
+ assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ // Local setting changed, but no change in CS network.
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mSimulatedCommands.mCallWaitActivated);
+ }
+
+ @Test
+ @SmallTest
+ public void testSyncImsOnly() {
+ mCWC.setTerminalBasedCallWaitingSupported(false);
+ setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+ TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_IMS_ONLY);
+ mCWC.setTerminalBasedCallWaitingSupported(true);
+ PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_IMS_ONLY, true);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(bundle);
+ mCWC.updateCarrierConfig(FAKE_SUB_ID, true);
+
+ mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+
+ // IMS is registered
+ mCWC.setImsRegistrationState(true);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ mHandler = new GetTestHandler();
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ // result carries the service state from IMS service
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ mHandler.reset();
+
+ // IMS is not registered
+ mCWC.setImsRegistrationState(false);
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ // result carries the service state from CS
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+
+ // service state not synchronized between CS and IMS
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+ mHandler.reset();
+
+ // IMS is registered
+ mCWC.setImsRegistrationState(true);
+
+ assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+ // IMS is not registered
+ mCWC.setImsRegistrationState(false);
+
+ assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+ mTestableLooper.processAllMessages();
+
+ // result carries the service state from CS
+ assertNotNull(mHandler.resp);
+ assertEquals(2, mHandler.resp.length);
+ assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+ assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+ // service state not synchronized between CS and IMS
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+ assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+ assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+ }
+
+ private PersistableBundle getConfigBundle(boolean provisioned,
+ int preference, boolean defaultState) {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
+ provisioned ? new int[] { SUPPLEMENTARY_SERVICE_CW } : new int[] { });
+ bundle.putInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, preference);
+ bundle.putBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL, defaultState);
+ return bundle;
+ }
+
+ private int retrieveStatePreference(int subId) {
+ SharedPreferences sp =
+ mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+ return sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+ }
+
+ private void setPreference(int phoneId, int subId, int state, int syncPreference) {
+ SharedPreferences sp =
+ mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(KEY_SUB_ID + phoneId, subId);
+ editor.putInt(KEY_STATE + subId, state);
+ editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
+ editor.apply();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
index 7727e79..4f91994 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
@@ -92,6 +92,7 @@
final Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
IccCardConstants.INTENT_VALUE_ICC_LOADED);
+ intent.putExtra(PhoneConstants.PHONE_KEY, mPhone.getPhoneId());
mContext.sendBroadcast(intent);
processAllMessages();
@@ -134,6 +135,7 @@
final Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
IccCardConstants.INTENT_VALUE_ICC_LOADED);
+ intent.putExtra(PhoneConstants.PHONE_KEY, mPhone.getPhoneId());
mContext.sendBroadcast(intent);
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index 0b7da6f..40e1821 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -45,6 +45,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import java.security.PublicKey;
@@ -61,6 +62,8 @@
private CarrierKeyDownloadManager mCarrierKeyDM;
+ private PersistableBundle mBundle;
+
private final String mURL = "http://www.google.com";
private static final String CERT = "-----BEGIN CERTIFICATE-----\r\nMIIFjzCCBHegAwIBAgIUPxj3SLif82Ky1RlUy8p2EWJCh8MwDQYJKoZIhvcNAQELBQAwgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJkYW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNVBAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gUHVibGljIFN1cmVTZXJ2ZXIgQ0EgRzE0LVNIQTIwHhcNMTcwODE0MTc0MzM4WhcNMTkwODE0MTc0MzM4WjCBmTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFjAUBgNVBAcTDUJhc2tpbmcgUmlkZ2UxIjAgBgNVBAoTGVZlcml6b24gRGF0YSBTZXJ2aWNlcyBMTEMxHzAdBgNVBAsTFk5ldHdvcmsgU3lzdGVtIFN1cHBvcnQxGDAWBgNVBAMTD3ZpMWx2Lmltc3ZtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUQKWTHi4Hjpd1LQwJ87RXa0Rs3rVonvVevliqdUH5BikjhAzvIqwPSXeRQqkaRTFIyp0NKcNqGdjAaHRo43gdHeWSH331sS6CMZDg988gZznskzCqJJo6ii5FuLC8qe2YDsHxT+CefXev2rn6Bj1ei2X74uZsy5KlkBRZfFHtPdK6/EK5TpzrvcXfDyOK1rn8FTno1bQOTAhL39GPcLhdrXV7AN+lu+EBpdCqlTdcoDxsqavi/91MwUIVEzxJmycKloT6OWfU44r7+L5SYYgc88NTaGL/BvCFwHRIa1ZgYSGeAPes45792MGG7tfr/ttAGp9UEwTv2zWTxzWnRP/UCAwEAAaOCAdcwggHTMAwGA1UdEwEB/wQCMAAwTAYDVR0gBEUwQzBBBgkrBgEEAbE+ATIwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly9zZWN1cmUub21uaXJvb3QuY29tL3JlcG9zaXRvcnkwgakGCCsGAQUFBwEBBIGcMIGZMC0GCCsGAQUFBzABhiFodHRwOi8vdnBzc2cxNDIub2NzcC5vbW5pcm9vdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jYWNlcnQub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNydDAzBggrBgEFBQcwAoYnaHR0cDovL2NhY2VydC5vbW5pcm9vdC5jb20vdnBzc2cxNDIuZGVyMBoGA1UdEQQTMBGCD3ZpMWx2Lmltc3ZtLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFOQtu5EBZSYftHo/oxUlpM6MRDM7MD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly92cHNzZzE0Mi5jcmwub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNybDAdBgNVHQ4EFgQUv5SaSyNM/yXw1v0N9TNpjsFCaPcwDQYJKoZIhvcNAQELBQADggEBACNJusTULj1KyV4RwiskKfp4wI9Hsz3ESbZS/ijF9D57BQ0UwkELU9r6rEAhsYLUvMq4sDhDbYIdupgP4MBzFnjkKult7VQm5W3nCcuHgXYFAJ9Y1a4OZAo/4hrHj70W9TsQ1ioSMjUT4F8bDUYZI0kcyH8e/+2DaTsLUpHw3L+Keu8PsJVBLnvcKJjWrZD/Bgd6JuaTX2G84i0rY0GJuO9CxLNJa6n61Mz5cqLYIuwKgiVgTA2n71YITyFICOFPFX1vSx35AWvD6aVYblxtC8mpCdF2h4s1iyrpXeji2GCJLwsNVtTtNQ4zWX3Gnq683wzkYZeyOHUyftIgAQZ+HsY=\r\n-----END CERTIFICATE-----";
@@ -83,11 +86,23 @@
"{ \"carrier-keys\": [ { \"key-identifier\": \"key1=value\", "
+ "\"public-key\": \"" + CERT + "\"}]}";
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
@Before
public void setUp() throws Exception {
logd("CarrierActionAgentTest +Setup!");
super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
+
processAllMessages();
logd("CarrierActionAgentTest -Setup!");
}
@@ -322,7 +337,7 @@
}
/**
- * Test sending the ACTION_CARRIER_CONFIG_CHANGED intent.
+ * Test notifying the carrier config change from listener.
* Verify that the right mnc/mcc gets stored in the preferences.
**/
@Test
@@ -337,16 +352,16 @@
when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
- Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
- mContext.sendBroadcast(mIntent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
assertEquals(1, mCarrierKeyDM.mCarrierId);
}
/**
- * Tests sending the ACTION_CARRIER_CONFIG_CHANGED intent with an empty key.
+ * Tests notifying carrier config change from listener with an empty key.
* Verify that the carrier keys are removed if IMSI_KEY_DOWNLOAD_URL_STRING is null.
*/
@Test
@@ -359,9 +374,9 @@
bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, null);
- Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
- mContext.sendBroadcast(mIntent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
assertNull(mCarrierKeyDM.mMccMncForDownload);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
index 07011a3..06b63a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
@@ -17,8 +17,6 @@
package com.android.internal.telephony;
import static android.os.UserHandle.SYSTEM;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -30,10 +28,8 @@
import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -44,7 +40,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
@@ -55,9 +50,6 @@
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
import android.service.carrier.CarrierService;
@@ -76,8 +68,8 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
-import org.mockito.Mock;
import java.security.MessageDigest;
import java.util.ArrayList;
@@ -91,7 +83,6 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CarrierPrivilegesTrackerTest extends TelephonyTest {
- private static final int REGISTRANT_WHAT = 1;
private static final int PHONE_ID = 2;
private static final int PHONE_ID_INCORRECT = 3;
private static final int SUB_ID = 4;
@@ -129,11 +120,9 @@
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
- @Mock private Signature mSignature;
-
private PersistableBundle mCarrierConfigs;
- private CarrierPrivilegesTrackerTestHandler mHandler;
private CarrierPrivilegesTracker mCarrierPrivilegesTracker;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
@Before
public void setUp() throws Exception {
@@ -144,7 +133,6 @@
when(mPhone.getSubId()).thenReturn(SUB_ID);
mCarrierConfigs = new PersistableBundle();
- mHandler = new CarrierPrivilegesTrackerTestHandler();
// set mock behavior so CarrierPrivilegeTracker initializes with no privileged UIDs
setupCarrierConfigRules();
@@ -154,7 +142,6 @@
@After
public void tearDown() throws Exception {
- mHandler = null;
mCarrierPrivilegesTracker = null;
mCarrierConfigs = null;
super.tearDown();
@@ -164,7 +151,8 @@
private void setupCarrierConfigRules(String... rules) {
mCarrierConfigs.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, rules);
mCarrierConfigs.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
- when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfigs);
+ when(mCarrierConfigManager.getConfigForSubId(eq(SUB_ID), any()))
+ .thenReturn(mCarrierConfigs);
}
private static String carrierConfigRuleString(String certificateHash, String... packageNames) {
@@ -222,13 +210,17 @@
* #setupInstalledPackages}.
*/
private CarrierPrivilegesTracker createCarrierPrivilegesTracker() throws Exception {
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
CarrierPrivilegesTracker cpt =
new CarrierPrivilegesTracker(mTestableLooper.getLooper(), mPhone, mContext);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
mTestableLooper.processAllMessages();
- cpt.registerCarrierPrivilegesListener(mHandler, REGISTRANT_WHAT, null);
mTestableLooper.processAllMessages();
- mHandler.reset();
return cpt;
}
@@ -252,29 +244,6 @@
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
}
- private class CarrierPrivilegesTrackerTestHandler extends Handler {
- public int[] privilegedUids;
- public int numUidUpdates;
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case REGISTRANT_WHAT:
- AsyncResult asyncResult = (AsyncResult) msg.obj;
- privilegedUids = (int[]) asyncResult.result;
- numUidUpdates++;
- break;
- default:
- fail("Unexpected msg received. what=" + msg.what);
- }
- }
-
- void reset() {
- privilegedUids = null;
- numUidUpdates = 0;
- }
- }
-
private void verifyCurrentState(Set<String> expectedPackageNames, int[] expectedUids) {
assertEquals(
expectedPackageNames, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges());
@@ -290,11 +259,6 @@
}
}
- private void verifyRegistrantUpdates(@Nullable int[] expectedUids, int expectedUidUpdates) {
- assertArrayEquals(expectedUids, mHandler.privilegedUids);
- assertEquals(expectedUidUpdates, mHandler.numUidUpdates);
- }
-
private void verifyCarrierPrivilegesChangedUpdates(
List<Pair<Set<String>, Set<Integer>>> expectedUpdates) {
if (expectedUpdates.isEmpty()) {
@@ -330,56 +294,6 @@
}
@Test
- public void testRegisterListener() throws Exception {
- mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
- // mHandler registered in createCarrierPrivilegesTracker(), so reset it
- mHandler = new CarrierPrivilegesTrackerTestHandler();
-
- mCarrierPrivilegesTracker.registerCarrierPrivilegesListener(
- mHandler, REGISTRANT_WHAT, null);
- mTestableLooper.processAllMessages();
-
- // No updates triggered, but the registrant gets an empty update.
- verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0] /* expectedUids */, 1 /* expectedUidUpdates */);
- verifyCarrierPrivilegesChangedUpdates(List.of());
- }
-
- @Test
- public void testUnregisterListener() throws Exception {
- // Start with privileges. Verify no updates received after clearing UIDs.
- setupCarrierPrivilegesTrackerWithCarrierConfigUids();
- // mHandler registered in createCarrierPrivilegesTracker(), so reset it
- mHandler = new CarrierPrivilegesTrackerTestHandler();
-
- mCarrierPrivilegesTracker.registerCarrierPrivilegesListener(
- mHandler, REGISTRANT_WHAT, null);
- mTestableLooper.processAllMessages();
-
- verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS /* expectedUids */, 1 /* expectedUidUpdates */);
- verifyCarrierPrivilegesChangedUpdates(
- List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
- mHandler.reset();
- reset(mTelephonyRegistryManager);
-
- mCarrierPrivilegesTracker.unregisterCarrierPrivilegesListener(mHandler);
- mTestableLooper.processAllMessages();
-
- verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(null /* expectedUids */, 0 /* expectedUidUpdates */);
- verifyCarrierPrivilegesChangedUpdates(List.of());
-
- // Clear UIDs
- sendCarrierConfigChangedIntent(INVALID_SUBSCRIPTION_ID, PHONE_ID);
- mTestableLooper.processAllMessages();
-
- verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(null /* expectedUids */, 0 /* expectedUidUpdates */);
- verifyCarrierPrivilegesChangedUpdates(List.of(new Pair<>(Set.of(), Set.of())));
- }
-
- @Test
public void testCarrierConfigUpdated() throws Exception {
// Start with packages installed and no certs
setupInstalledPackages(
@@ -389,11 +303,10 @@
setupCarrierConfigRules(
carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
- sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ sendCarrierConfigChanged(SUB_ID, PHONE_ID);
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -403,11 +316,10 @@
// Start with privileges. Incorrect phoneId shouldn't affect certs
setupCarrierPrivilegesTrackerWithCarrierConfigUids();
- sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID_INCORRECT);
+ sendCarrierConfigChanged(SUB_ID, PHONE_ID_INCORRECT);
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(null /* expectedUids */, 0 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -417,11 +329,10 @@
// Start with privileges, verify clearing certs clears UIDs
setupCarrierPrivilegesTrackerWithCarrierConfigUids();
- sendCarrierConfigChangedIntent(INVALID_SUBSCRIPTION_ID, PHONE_ID);
+ sendCarrierConfigChanged(INVALID_SUBSCRIPTION_ID, PHONE_ID);
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0] /* expectedUids */, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(
new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET),
@@ -434,13 +345,13 @@
setupCarrierPrivilegesTrackerWithCarrierConfigUids();
mCarrierConfigs.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
- when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfigs);
+ when(mCarrierConfigManager.getConfigForSubId(eq(SUB_ID), any()))
+ .thenReturn(mCarrierConfigs);
- sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ sendCarrierConfigChanged(SUB_ID, PHONE_ID);
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0] /* expectedUids */, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(List.of(new Pair<>(Set.of(), Set.of())));
}
@@ -456,11 +367,10 @@
carrierConfigRuleString(getHash(CERT_1), PACKAGE_1),
carrierConfigRuleString(getHash(CERT_2), PACKAGE_1));
- sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ sendCarrierConfigChanged(SUB_ID, PHONE_ID);
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1});
- verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), Set.of(UID_1))));
@@ -469,11 +379,10 @@
carrierConfigRuleString(getHash(CERT_1), PACKAGE_1),
carrierConfigRuleString(getHash(CERT_2), PACKAGE_1, PACKAGE_2));
- sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ sendCarrierConfigChanged(SUB_ID, PHONE_ID);
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 2 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -492,7 +401,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -511,7 +419,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -527,7 +434,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(
new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET),
@@ -545,7 +451,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(null /* expectedUids */, 0 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -568,7 +473,6 @@
// Check again, the carrier privileges should be emptied
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(
new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET),
@@ -597,7 +501,6 @@
// verify all carrier privileges should remain, no CP change notified
verifyCurrentState(Set.of(PACKAGE_1, PACKAGE_2), new int[]{UID_1, UID_2});
- verifyRegistrantUpdates(null /* expectedUidUpdates */, 0 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(List.of());
}
@@ -618,7 +521,6 @@
// verify the carrier privileges should be emptied
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(), Set.of())));
}
@@ -640,7 +542,6 @@
// verify the carrier privileges should be emptied
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(), Set.of())));
}
@@ -667,7 +568,6 @@
// Carrier privileges should be updated and CP change should be notified
verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1});
- verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), Set.of(UID_1))));
}
@@ -727,7 +627,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1});
- verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), Set.of(UID_1))));
@@ -741,7 +640,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 2 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -758,7 +656,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1});
- verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), Set.of(UID_1))));
}
@@ -778,7 +675,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_1), PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), PRIVILEGED_UIDS_SET)));
}
@@ -799,7 +695,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_1), PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_1), PRIVILEGED_UIDS_SET)));
}
@@ -824,7 +719,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_2), new int[] {UID_2});
- verifyRegistrantUpdates(new int[] {UID_2}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_2), Set.of(UID_2))));
}
@@ -848,7 +742,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_2), new int[] {UID_2});
- verifyRegistrantUpdates(new int[] {UID_2}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_2), Set.of(UID_2))));
}
@@ -867,7 +760,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(PACKAGE_2), new int[] {UID_2});
- verifyRegistrantUpdates(new int[] {UID_2}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(Set.of(PACKAGE_2), Set.of(UID_2))));
}
@@ -882,7 +774,6 @@
mTestableLooper.processAllMessages();
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(null /* expectedUidUpdates */, 0 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(List.of());
}
@@ -938,7 +829,6 @@
// Expect no package will have privilege at last
verifyCurrentState(Set.of(), new int[0]);
- verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(
new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET),
@@ -950,7 +840,6 @@
// Expect all privileges from Carrier Config come back
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 2 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -966,7 +855,6 @@
// Expect only PACKAGE_3 will have privilege at last
verifyCurrentState(Set.of(PACKAGE_3), new int[]{UID_3});
- verifyRegistrantUpdates(new int[]{UID_3}, 1 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(
new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET),
@@ -979,7 +867,6 @@
// Expect all privileges from UICC come back
verifyCurrentState(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS);
- verifyRegistrantUpdates(PRIVILEGED_UIDS, 2 /* expectedUidUpdates */);
verifyCarrierPrivilegesChangedUpdates(
List.of(new Pair<>(PRIVILEGED_PACKAGES, PRIVILEGED_UIDS_SET)));
}
@@ -1137,11 +1024,93 @@
verify(mPackageManager).queryIntentServices(any(), anyInt());
}
- private void sendCarrierConfigChangedIntent(int subId, int phoneId) {
- mContext.sendBroadcast(
- new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
- .putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
- .putExtra(EXTRA_SLOT_INDEX, phoneId));
+ @Test
+ public void testSetCarrierServiceOverride_noCarrierPrivileges() throws Exception {
+ // Setup one package, without a matching cert to grant carrier privileges
+ setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
+ ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
+
+ doReturn(List.of(carrierService))
+ .when(mPackageManager)
+ .queryIntentServices(any(), anyInt());
+ when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+
+ // Set override, and verify the carrier service package was not set due to a lack of a
+ // matching cert.
+ mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
+ mCarrierPrivilegesTracker.setTestOverrideCarrierServicePackage(PACKAGE_1);
+ mTestableLooper.processAllMessages();
+
+ assertNull(mCarrierPrivilegesTracker.getCarrierServicePackageName());
+ assertEquals(Process.INVALID_UID, mCarrierPrivilegesTracker.getCarrierServicePackageUid());
+ }
+
+ @Test
+ public void testSetCarrierServiceOverride_withCarrierConfigCarrierPrivileges()
+ throws Exception {
+ // Setup two packages with matching carrier config certificates
+ setupCarrierConfigRules(carrierConfigRuleString(getHash(CERT_1)));
+ setupInstalledPackages(
+ new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
+ new PackageCertInfo(PACKAGE_2, CERT_1, USER_1, UID_2));
+ ResolveInfo service1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
+ ResolveInfo service2 = new ResolveInfoBuilder().setService(PACKAGE_2).build();
+
+ doReturn(List.of(service1, service2))
+ .when(mPackageManager)
+ .queryIntentServices(any(), anyInt());
+ when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+ when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+
+ mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
+
+ // Set override, and verify the carrier service package was truly set.
+ mCarrierPrivilegesTracker.setTestOverrideCarrierServicePackage(PACKAGE_1);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(PACKAGE_1, mCarrierPrivilegesTracker.getCarrierServicePackageName());
+ assertEquals(UID_1, mCarrierPrivilegesTracker.getCarrierServicePackageUid());
+
+ // Set other package as override, and verify the carrier service package was truly set.
+ mCarrierPrivilegesTracker.setTestOverrideCarrierServicePackage(PACKAGE_2);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(PACKAGE_2, mCarrierPrivilegesTracker.getCarrierServicePackageName());
+ assertEquals(UID_2, mCarrierPrivilegesTracker.getCarrierServicePackageUid());
+
+ // Clear override, and verify that everything went back to null.
+ mCarrierPrivilegesTracker.setTestOverrideCarrierServicePackage(null);
+ mTestableLooper.processAllMessages();
+
+ assertNull(mCarrierPrivilegesTracker.getCarrierServicePackageName());
+ assertEquals(Process.INVALID_UID, mCarrierPrivilegesTracker.getCarrierServicePackageUid());
+ }
+
+ @Test
+ public void testSetCarrierServiceOverride_invalidPackage() throws Exception {
+ // Setup one package, without a matching cert to grant carrier privileges
+ setupCarrierConfigRules(carrierConfigRuleString(getHash(CERT_1)));
+ setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
+ ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
+
+ doReturn(List.of(carrierService))
+ .when(mPackageManager)
+ .queryIntentServices(any(), anyInt());
+ when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+
+ // Set override, and expect that an invalid package name would not be selected as the
+ // carrier config service.
+ mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
+ mCarrierPrivilegesTracker.setTestOverrideCarrierServicePackage("invalid.package");
+ mTestableLooper.processAllMessages();
+
+ assertNull(mCarrierPrivilegesTracker.getCarrierServicePackageName());
+ assertEquals(Process.INVALID_UID, mCarrierPrivilegesTracker.getCarrierServicePackageUid());
+ }
+
+ private void sendCarrierConfigChanged(int subId, int phoneId) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
}
private void sendSimCardStateChangedIntent(int phoneId, int simState) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 07cf0b9..fadbff1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -28,7 +29,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
-import android.content.Intent;
import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -43,9 +43,11 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.MockitoAnnotations;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
@@ -57,6 +59,7 @@
private CarrierServiceStateTracker mSpyCarrierSST;
private CarrierServiceStateTracker mCarrierSST;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private static final int SUB_ID = 1;
@@ -68,10 +71,18 @@
MockitoAnnotations.initMocks(this);
logd(LOG_TAG + "Setup!");
super.setUp(getClass().getSimpleName());
+ doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
mBundle = mContextFixture.getCarrierConfigBundle();
when(mPhone.getSubId()).thenReturn(SUB_ID);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
mSpyCarrierSST = spy(mCarrierSST);
mNotificationManager = (NotificationManager) mContext.getSystemService(
@@ -140,8 +151,8 @@
@SmallTest
public void testSendPrefNetworkNotification() {
logd(LOG_TAG + ":testSendPrefNetworkNotification()");
- Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mContext.sendBroadcast(intent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */, SUB_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
@@ -190,8 +201,8 @@
@SmallTest
public void testSendEmergencyNetworkNotification() {
logd(LOG_TAG + ":testSendEmergencyNetworkNotification()");
- Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mContext.sendBroadcast(intent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */, SUB_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
index e070b06..4dfadd2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
@@ -61,6 +61,7 @@
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -70,6 +71,8 @@
private CarrierSignalAgent mCarrierSignalAgentUT;
private PersistableBundle mBundle;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
private static final String PCO_RECEIVER = "pak/PCO_RECEIVER";
private static final String DC_ERROR_RECEIVER = "pak/DC_ERROR_RECEIVER";
private static final String LEGACY_RECEIVER = "old.pkg/LEGACY_RECEIVER";
@@ -103,6 +106,7 @@
FAKE_DEFAULT_NETWORK_INTENT.putExtra(
TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, true);
}
+ private static final int PHONE_ID = 0;
// Mocked classes
ResolveInfo mResolveInfo;
@@ -112,8 +116,17 @@
logd("CarrierSignalAgentTest +Setup!");
super.setUp(getClass().getSimpleName());
mResolveInfo = mock(ResolveInfo.class);
+ doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mCarrierSignalAgentUT = new CarrierSignalAgent(mPhone);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
ComponentName legacyReceiverComponent = ComponentName.unflattenFromString(LEGACY_RECEIVER);
ApplicationInfo fakeLegacyApplicationInfo = new ApplicationInfo();
@@ -159,9 +172,10 @@
verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
// Trigger carrier config reloading
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
- count++;
// Verify no broadcast has been sent due to no manifest receivers
mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
@@ -208,14 +222,16 @@
});
// Trigger carrier config reloading
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
// Verify broadcast has been sent to two different registered manifest receivers
doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
.when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
- int broadcastCount = 1;
+ int broadcastCount = 0;
{
mCarrierSignalAgentUT.notifyCarrierSignalReceivers(new Intent(FAKE_PCO_INTENT));
broadcastCount++;
@@ -325,9 +341,10 @@
verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
// Trigger carrier config reloading
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
- count++;
// Verify broadcast has been sent to registered components
mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
@@ -362,9 +379,10 @@
argThat(o -> Objects.equals(o.getAction(), ACTION_CARRIER_SIGNAL_PCO_VALUE)),
anyInt());
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
- count++;
// Wake signal for PAK_PCO_RECEIVER
mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
@@ -417,7 +435,9 @@
CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE + ","
+ ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED });
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
// verify no reset action on initial config load
verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
@@ -427,7 +447,9 @@
CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
// verify no reset action for the same config (different order)
verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
@@ -437,7 +459,9 @@
CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{ DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
// verify there is no reset action
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
new file mode 100644
index 0000000..40be490
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.CellBroadcastConfigTracker.mergeRangesAsNeeded;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.CellBroadcastIdRange;
+import android.telephony.SmsCbMessage;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public final class CellBroadcastConfigTrackerTest extends TelephonyTest {
+
+ private CommandsInterface mSpyCi;
+ private CellBroadcastConfigTracker mTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mSpyCi = spy(mSimulatedCommands);
+ mPhone = new GsmCdmaPhone(mContext, mSpyCi, mNotifier, true, 0,
+ PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+ mTracker = CellBroadcastConfigTracker.make(mPhone, mPhone, true);
+ mPhone.mCellBroadcastConfigTracker = mTracker;
+ processAllMessages();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mPhone.removeCallbacksAndMessages(null);
+ mPhone = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testSetCellBroadcastIdRangesSuccess() throws Exception {
+ final int[][] channelValues = {
+ {0, 999}, {1000, 1003}, {1004, 0x0FFF}, {0x1000, 0x10FF}, {0x1100, 0x112F},
+ {0x1130, 0x1900}, {0x1901, 0x9FFF}, {0xA000, 0xFFFE}, {0xFFFF, 0xFFFF}};
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+ for (int i = 0; i < channelValues.length; i++) {
+ ranges.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ SmsCbMessage.MESSAGE_FORMAT_3GPP, i > 0 ? true : false));
+ }
+
+ List<SmsBroadcastConfigInfo> gsmConfigs = new ArrayList<>();
+ gsmConfigs.add(new SmsBroadcastConfigInfo(0, 999, 0, 255, false));
+ gsmConfigs.add(new SmsBroadcastConfigInfo(1000, 0xFFFF, 0, 255, true));
+
+ ArgumentCaptor<SmsBroadcastConfigInfo[]> gsmCaptor = ArgumentCaptor.forClass(
+ SmsBroadcastConfigInfo[].class);
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+ mockCommandInterface();
+
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastConfig(gsmCaptor.capture(), msgCaptor.capture());
+ List<SmsBroadcastConfigInfo> gsmArgs = Arrays.asList(
+ (SmsBroadcastConfigInfo[]) gsmCaptor.getValue());
+ assertEquals(gsmConfigs, gsmArgs);
+
+ Message msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastActivation(eq(true), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, never()).setCdmaBroadcastConfig(any(), any());
+ verify(mSpyCi, never()).setCdmaBroadcastActivation(anyBoolean(), any());
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), mergeRangesAsNeeded(ranges));
+
+ //Verify to set cdma config and activate, but no more for gsm as no change
+ for (int i = 0; i < channelValues.length; i++) {
+ ranges.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ SmsCbMessage.MESSAGE_FORMAT_3GPP2, i > 0 ? true : false));
+ }
+ List<CdmaSmsBroadcastConfigInfo> cdmaConfigs = new ArrayList<>();
+ cdmaConfigs.add(new CdmaSmsBroadcastConfigInfo(0, 999, 1, false));
+ cdmaConfigs.add(new CdmaSmsBroadcastConfigInfo(1000, 0xFFFF, 1, true));
+ ArgumentCaptor<CdmaSmsBroadcastConfigInfo[]> cdmaCaptor = ArgumentCaptor.forClass(
+ CdmaSmsBroadcastConfigInfo[].class);
+
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastConfig(any(), any());
+ verify(mSpyCi, times(1)).setCdmaBroadcastConfig(cdmaCaptor.capture(), msgCaptor.capture());
+ List<CdmaSmsBroadcastConfigInfo> cdmaArgs = Arrays.asList(
+ (CdmaSmsBroadcastConfigInfo[]) cdmaCaptor.getValue());
+ assertEquals(cdmaConfigs, cdmaArgs);
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastActivation(anyBoolean(), any());
+ verify(mSpyCi, times(1)).setCdmaBroadcastActivation(eq(true), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), mergeRangesAsNeeded(ranges));
+
+ // Verify not to set cdma or gsm config as the config is not changed
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setCdmaBroadcastConfig(any(), any());
+ verify(mSpyCi, times(1)).setCdmaBroadcastActivation(anyBoolean(), any());
+ verify(mSpyCi, times(1)).setGsmBroadcastConfig(any(), any());
+ verify(mSpyCi, times(1)).setGsmBroadcastActivation(anyBoolean(), any());
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), mergeRangesAsNeeded(ranges));
+
+ // Verify to reset ranges with empty ranges list
+ mPhone.setCellBroadcastIdRanges(new ArrayList<>(), r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setGsmBroadcastConfig(gsmCaptor.capture(), msgCaptor.capture());
+ assertEquals(0, ((SmsBroadcastConfigInfo[]) gsmCaptor.getValue()).length);
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Verify to deavtivate gsm broadcast on empty ranges
+ verify(mSpyCi, times(1)).setGsmBroadcastActivation(eq(false), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setCdmaBroadcastConfig(cdmaCaptor.capture(), msgCaptor.capture());
+ assertEquals(0, ((CdmaSmsBroadcastConfigInfo[]) cdmaCaptor.getValue()).length);
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Verify to deavtivate cdma broadcast on empty ranges
+ verify(mSpyCi, times(1)).setCdmaBroadcastActivation(eq(false), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertTrue(mPhone.getCellBroadcastIdRanges().isEmpty());
+
+ //Verify to set gsm and cdma config then activate again
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+
+ processAllMessages();
+
+ verify(mSpyCi, times(3)).setGsmBroadcastConfig(gsmCaptor.capture(), msgCaptor.capture());
+ gsmArgs = Arrays.asList((SmsBroadcastConfigInfo[]) gsmCaptor.getValue());
+ assertEquals(gsmConfigs, gsmArgs);
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setGsmBroadcastActivation(eq(true), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(3)).setCdmaBroadcastConfig(cdmaCaptor.capture(), msgCaptor.capture());
+ cdmaArgs = Arrays.asList((CdmaSmsBroadcastConfigInfo[]) cdmaCaptor.getValue());
+ assertEquals(cdmaConfigs, cdmaArgs);
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setCdmaBroadcastActivation(eq(true), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), mergeRangesAsNeeded(ranges));
+ }
+
+ @Test
+ public void testSetCellBroadcastIdRangesFailure() throws Exception {
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+
+ // Verify to throw exception for invalid ranges
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true));
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, false));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mPhone.setCellBroadcastIdRanges(ranges, r -> {}));
+
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+ ranges.clear();
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true));
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP2, true));
+
+ mockCommandInterface();
+
+ // Verify the result on setGsmBroadcastConfig failure
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastConfig(any(), msgCaptor.capture());
+
+ Message msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg).exception = new RuntimeException();
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(0)).setGsmBroadcastActivation(anyBoolean(), any());
+ verify(mSpyCi, times(0)).setCdmaBroadcastConfig(any(), any());
+ verify(mSpyCi, times(0)).setCdmaBroadcastActivation(anyBoolean(), any());
+ assertTrue(mPhone.getCellBroadcastIdRanges().isEmpty());
+
+ // Verify the result on setGsmBroadcastActivation failure
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setGsmBroadcastConfig(any(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setGsmBroadcastActivation(anyBoolean(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg).exception = new RuntimeException();
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(0)).setCdmaBroadcastConfig(any(), any());
+ verify(mSpyCi, times(0)).setCdmaBroadcastActivation(anyBoolean(), any());
+ assertTrue(mPhone.getCellBroadcastIdRanges().isEmpty());
+
+ // Verify the result on setCdmaBroadcastConfig failure
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG == r));
+ processAllMessages();
+
+ verify(mSpyCi, times(3)).setGsmBroadcastConfig(any(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(2)).setGsmBroadcastActivation(anyBoolean(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setCdmaBroadcastConfig(any(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg).exception = new RuntimeException();
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(0)).setCdmaBroadcastActivation(anyBoolean(), any());
+
+ List<CellBroadcastIdRange> ranges3gpp = new ArrayList<>();
+ ranges3gpp.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true));
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), ranges3gpp);
+
+ // Verify the result on setCdmaBroadcastActivation failure
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION == r));
+ processAllMessages();
+
+ // Verify no more calls as there is no change of ranges for 3gpp
+ verify(mSpyCi, times(3)).setGsmBroadcastConfig(any(), any());
+ verify(mSpyCi, times(2)).setGsmBroadcastActivation(anyBoolean(), any());
+ verify(mSpyCi, times(2)).setCdmaBroadcastConfig(any(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ verify(mSpyCi, times(1)).setCdmaBroadcastActivation(anyBoolean(), msgCaptor.capture());
+
+ msg = msgCaptor.getValue();
+ assertNotNull(msg);
+ AsyncResult.forMessage(msg).exception = new RuntimeException();
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), ranges3gpp);
+ }
+
+ @Test
+ public void testClearCellBroadcastConfigOnRadioOff() {
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true));
+
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+ processAllMessages();
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), ranges);
+
+ mPhone.sendEmptyMessage(Phone.EVENT_RADIO_OFF_OR_NOT_AVAILABLE);
+ processAllMessages();
+
+ // Verify the config is reset
+ assertTrue(mPhone.getCellBroadcastIdRanges().isEmpty());
+ }
+
+ @Test
+ public void testClearCellBroadcastConfigOnSubscriptionChanged() {
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+ ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true));
+
+ mPhone.setCellBroadcastIdRanges(ranges, r -> assertTrue(
+ TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS == r));
+ processAllMessages();
+
+ assertEquals(mPhone.getCellBroadcastIdRanges(), ranges);
+
+ mTracker.mSubChangedListener.onSubscriptionsChanged();
+ processAllMessages();
+
+ // Verify the config is not reset when the sub id is not changed
+ assertEquals(mPhone.getCellBroadcastIdRanges(), ranges);
+
+ mTracker.mSubId = mTracker.mSubId % SubscriptionManager.DEFAULT_SUBSCRIPTION_ID + 1;
+
+ mTracker.mSubChangedListener.onSubscriptionsChanged();
+ processAllMessages();
+
+ // Verify the config is reset when the sub id is changed
+ assertTrue(mPhone.getCellBroadcastIdRanges().isEmpty());
+ }
+
+ @Test
+ public void testMergeCellBroadcastIdRangesAsNeeded() {
+ final int[][] channelValues = {
+ {0, 999}, {1000, 1003}, {1004, 0x0FFF}, {0x1000, 0x10FF}, {0x1100, 0x112F},
+ {0x1130, 0x1900}, {0x1901, 0x9FFF}, {0xA000, 0xFFFE}, {0xFFFF, 0xFFFF}};
+ final int[] typeValues = {
+ SmsCbMessage.MESSAGE_FORMAT_3GPP, SmsCbMessage.MESSAGE_FORMAT_3GPP2};
+ final boolean[] enabledValues = {true, false};
+
+ List<CellBroadcastIdRange> ranges = new ArrayList<>();
+ for (int i = 0; i < channelValues.length; i++) {
+ ranges.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ typeValues[0], enabledValues[0]));
+ }
+
+ ranges = mergeRangesAsNeeded(ranges);
+
+ assertEquals(1, ranges.size());
+ assertEquals(ranges.get(0).getStartId(), channelValues[0][0]);
+ assertEquals(ranges.get(0).getEndId(), channelValues[channelValues.length - 1][0]);
+
+ // Verify not to merge the ranges with different types.
+ ranges.clear();
+ for (int i = 0; i < channelValues.length; i++) {
+ ranges.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ typeValues[0], enabledValues[0]));
+ ranges.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ typeValues[1], enabledValues[0]));
+ }
+
+ ranges = mergeRangesAsNeeded(ranges);
+
+ assertEquals(2, ranges.size());
+ assertEquals(ranges.get(0).getStartId(), channelValues[0][0]);
+ assertEquals(ranges.get(0).getEndId(), channelValues[channelValues.length - 1][0]);
+ assertEquals(ranges.get(1).getStartId(), channelValues[0][0]);
+ assertEquals(ranges.get(1).getEndId(), channelValues[channelValues.length - 1][0]);
+ assertTrue(ranges.get(0).getType() != ranges.get(1).getType());
+
+ // Verify to throw IllegalArgumentException if the same range is enabled and disabled
+ // in the range list.
+ final List<CellBroadcastIdRange> ranges2 = new ArrayList<>();
+ for (int i = 0; i < channelValues.length; i++) {
+ ranges2.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ typeValues[0], enabledValues[0]));
+ ranges2.add(new CellBroadcastIdRange(channelValues[i][0], channelValues[i][1],
+ typeValues[0], enabledValues[1]));
+ }
+
+ assertThrows(IllegalArgumentException.class, () ->
+ mergeRangesAsNeeded(ranges2));
+ }
+
+ @Test
+ public void testMakeCellBroadcastConfigTracker() {
+ Phone phone = spy(mPhone);
+ CellBroadcastConfigTracker tracker = CellBroadcastConfigTracker.make(phone, phone, false);
+ processAllMessages();
+
+ verify(phone, never()).registerForRadioOffOrNotAvailable(any(), anyInt(), any());
+ verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(
+ any(), eq(tracker.mSubChangedListener));
+
+ tracker.start();
+ processAllMessages();
+
+ verify(phone, times(1)).registerForRadioOffOrNotAvailable(any(), anyInt(), any());
+ verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+ any(), eq(tracker.mSubChangedListener));
+
+ tracker = CellBroadcastConfigTracker.make(phone, phone, true);
+ processAllMessages();
+
+ verify(phone, times(2)).registerForRadioOffOrNotAvailable(any(), anyInt(), any());
+ verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+ any(), eq(tracker.mSubChangedListener));
+ }
+
+ private void mockCommandInterface() {
+ doNothing().when(mSpyCi).setGsmBroadcastConfig(any(), any());
+ doNothing().when(mSpyCi).setGsmBroadcastActivation(anyBoolean(), any());
+ doNothing().when(mSpyCi).setCdmaBroadcastConfig(any(), any());
+ doNothing().when(mSpyCi).setCdmaBroadcastActivation(anyBoolean(), any());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
index 8dc028e..11d57bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
@@ -22,7 +22,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import android.hardware.radio.V1_6.NrSignalStrength;
+import android.hardware.radio.network.NrSignalStrength;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -55,9 +55,11 @@
private static final int CSICQI_TABLE_INDEX = 1;
private static final ArrayList<Byte> CSICQI_REPORT =
new ArrayList<>(Arrays.asList((byte) 3, (byte) 2, (byte) 1));
+ private static final byte[] CSICQI_REPORT_PRIMITIVE = new byte[] {(byte) 3, (byte) 2, (byte) 1};
private static final int SSRSRP = -112;
private static final int SSRSRQ = -13;
private static final int SSSINR = 32;
+ private static final int TIMING_ADVANCE = 10;
// Mocked classes
ServiceState mSS;
@@ -75,7 +77,7 @@
private List<Integer> getCsiCqiList() {
return CSICQI_REPORT.stream()
- .map(cqi -> new Integer(Byte.toUnsignedInt(cqi)))
+ .map(Byte::toUnsignedInt)
.collect(Collectors.toList());
}
@@ -83,7 +85,7 @@
public void testGetMethod() {
// GIVEN an instance of CellSignalStrengthNr
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN the get method should return correct value
assertThat(css.getCsiRsrp()).isEqualTo(CSIRSRP);
@@ -95,20 +97,22 @@
assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(css.getSsSinr()).isEqualTo(SSSINR);
assertThat(css.getDbm()).isEqualTo(SSRSRP);
+ assertThat(css.getTimingAdvanceMicros()).isEqualTo(TIMING_ADVANCE);
}
@Test
public void testGetMethodWithHal() {
// GIVEN an instance of NrSignalStrength with some positive values
NrSignalStrength nrSignalStrength = new NrSignalStrength();
- nrSignalStrength.base.csiRsrp = -CSIRSRP;
- nrSignalStrength.base.csiRsrq = -CSIRSRQ;
- nrSignalStrength.base.csiSinr = CSISINR;
+ nrSignalStrength.csiRsrp = -CSIRSRP;
+ nrSignalStrength.csiRsrq = -CSIRSRQ;
+ nrSignalStrength.csiSinr = CSISINR;
nrSignalStrength.csiCqiTableIndex = CSICQI_TABLE_INDEX;
- nrSignalStrength.csiCqiReport = CSICQI_REPORT;
- nrSignalStrength.base.ssRsrp = -SSRSRP;
- nrSignalStrength.base.ssRsrq = -SSRSRQ;
- nrSignalStrength.base.ssSinr = SSSINR;
+ nrSignalStrength.csiCqiReport = CSICQI_REPORT_PRIMITIVE;
+ nrSignalStrength.ssRsrp = -SSRSRP;
+ nrSignalStrength.ssRsrq = -SSRSRQ;
+ nrSignalStrength.ssSinr = SSSINR;
+ nrSignalStrength.timingAdvance = TIMING_ADVANCE;
// THEN the get method should return the correct value
CellSignalStrengthNr css = RILUtils.convertHalNrSignalStrength(nrSignalStrength);
@@ -121,20 +125,22 @@
assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(css.getSsSinr()).isEqualTo(SSSINR);
assertThat(css.getDbm()).isEqualTo(SSRSRP);
+ assertThat(css.getTimingAdvanceMicros()).isEqualTo(TIMING_ADVANCE);
}
@Test
public void testUnavailableValueWithHal() {
// GIVEN an instance of NrSignalStrength
NrSignalStrength nrSignalStrength = new NrSignalStrength();
- nrSignalStrength.base.csiRsrp = CellInfo.UNAVAILABLE;
- nrSignalStrength.base.csiRsrq = CellInfo.UNAVAILABLE;
- nrSignalStrength.base.csiSinr = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiRsrp = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiRsrq = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiSinr = CellInfo.UNAVAILABLE;
nrSignalStrength.csiCqiTableIndex = CellInfo.UNAVAILABLE;
- nrSignalStrength.csiCqiReport = new ArrayList<Byte>();
- nrSignalStrength.base.ssRsrp = CellInfo.UNAVAILABLE;
- nrSignalStrength.base.ssRsrq = CellInfo.UNAVAILABLE;
- nrSignalStrength.base.ssSinr = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiCqiReport = new byte[]{};
+ nrSignalStrength.ssRsrp = CellInfo.UNAVAILABLE;
+ nrSignalStrength.ssRsrq = CellInfo.UNAVAILABLE;
+ nrSignalStrength.ssSinr = CellInfo.UNAVAILABLE;
+ nrSignalStrength.timingAdvance = CellInfo.UNAVAILABLE;
// THEN the get method should return unavailable value
CellSignalStrengthNr css = RILUtils.convertHalNrSignalStrength(nrSignalStrength);
@@ -147,15 +153,16 @@
assertThat(css.getSsRsrq()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getSsSinr()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getDbm()).isEqualTo(CellInfo.UNAVAILABLE);
+ assertThat(css.getTimingAdvanceMicros()).isEqualTo(CellInfo.UNAVAILABLE);
}
@Test
public void testEquals_sameParameters() {
// GIVEN an instance of CellSignalStrengthNr and another object with the same parameters
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN this two objects are equivalent
assertThat(css).isEqualTo(anotherCss);
@@ -166,10 +173,10 @@
// GIVEN an instance of CellSignalStrengthNr and another object with some different
// parameters
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(ANOTHER_CSIRSRP,
ANOTHER_CSIRSRQ, CSISINR, CSICQI_TABLE_INDEX, CSICQI_REPORT,
- SSRSRP, SSRSRQ, SSSINR);
+ SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN this two objects are different
assertThat(css).isNotEqualTo(anotherCss);
@@ -179,7 +186,7 @@
public void testAusLevel_validValue() {
// GIVEN an instance of CellSignalStrengthNr with valid csirsrp
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN the asu level is in range [0, 97]
assertThat(css.getAsuLevel()).isIn(Range.range(0, BoundType.CLOSED, 97, BoundType.CLOSED));
@@ -188,8 +195,8 @@
@Test
public void testAsuLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, INVALID_SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(INVALID_CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, INVALID_SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN the asu level is unknown
assertThat(css.getAsuLevel()).isEqualTo(CellSignalStrengthNr.UNKNOWN_ASU_LEVEL);
@@ -197,10 +204,10 @@
@Test
public void testSignalLevel_validValue() {
- for (int ssRsrp = -140; ssRsrp <= -44; ssRsrp++) {
+ for (int ssRsrp = -156; ssRsrp <= -31; ssRsrp++) {
// GIVEN an instance of CellSignalStrengthNr with valid csirsrp
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, INVALID_SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, ssRsrp, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN the signal level is valid
assertThat(css.getLevel()).isIn(Range.range(
@@ -212,8 +219,8 @@
@Test
public void testSignalLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(INVALID_CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// THEN the signal level is unknown
assertThat(css.getLevel()).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
@@ -223,7 +230,7 @@
public void testParcel() {
// GIVEN an instance of CellSignalStrengthNr
CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
- CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR, TIMING_ADVANCE);
// WHEN write the object to parcel and create another object with that parcel
Parcel parcel = Parcel.obtain();
@@ -241,6 +248,7 @@
assertThat(anotherCss.getSsRsrp()).isEqualTo(SSRSRP);
assertThat(anotherCss.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(anotherCss.getSsSinr()).isEqualTo(SSSINR);
+ assertThat(anotherCss.getTimingAdvanceMicros()).isEqualTo(TIMING_ADVANCE);
}
@Test
@@ -259,7 +267,7 @@
}
@Test
- public void testSignalLevel_ThresholdBoundaries() {
+ public void testSignalLevel_thresholdBoundaries() {
int[] ssRsrpThresholds = {
-110, /* SIGNAL_STRENGTH_POOR */
-90, /* SIGNAL_STRENGTH_MODERATE */
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
old mode 100755
new mode 100644
index d1e643e..0bce5cb
--- a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.os.Handler;
import android.os.Looper;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +43,9 @@
// Mocked classes
protected Call mCall;
+ protected EmergencyNumberTracker mEmergencyNumberTracker2;
+ protected Connection.PhoneFactoryProxy mPhoneFactoryProxy;
+ protected Connection mTestConnection;
private class TestConnection extends Connection {
@@ -117,7 +124,15 @@
super.setUp(getClass().getSimpleName());
mCall = mock(Call.class);
doReturn(mPhone).when(mCall).getPhone();
+ doReturn(mPhone).when(mCT).getPhone();
replaceInstance(Handler.class, "mLooper", mCT, Looper.getMainLooper());
+
+ mEmergencyNumberTracker2 = mock(EmergencyNumberTracker.class);
+ doReturn(mEmergencyNumberTracker2).when(mPhone2).getEmergencyNumberTracker();
+
+ mTestConnection = new TestConnection(TEST_PHONE_TYPE);
+ mPhoneFactoryProxy = mock(Connection.PhoneFactoryProxy.class);
+ mTestConnection.setPhoneFactoryProxy(mPhoneFactoryProxy);
}
@After
@@ -156,5 +171,24 @@
assertTrue(connection.hasKnownUserIntentEmergency());
}
+ @Test
+ public void testSetEmergencyCallInfo() {
+ //Replicate Dual-SIM:
+ Phone [] phones = {mPhone, mPhone2};
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(phones);
+ doReturn(1).when(mPhone).getPhoneId();
+ doReturn(2).when(mPhone2).getPhoneId();
+
+ //Replicate behavior when a number is an emergency number
+ // on the secondary SIM but not on the default SIM:
+ when(mPhone.getEmergencyNumberTracker().getEmergencyNumber(any())).thenReturn(null);
+ when(mEmergencyNumberTracker2.getEmergencyNumber(any()))
+ .thenReturn(getTestEmergencyNumber());
+
+ //Ensure the connection is considered as an emergency call:
+ mTestConnection.setEmergencyCallInfo(mCT);
+ assertTrue(mTestConnection.isEmergencyCall());
+ }
+
// TODO Verify more methods in Connection
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index b8c10a5..ea19b62 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -176,6 +176,11 @@
mKeyValuePairs.put(request, (String)args.get("value"));
mNumKeyValuePairs++;
break;
+ case Settings.CALL_METHOD_PUT_CONFIG:
+ logd("PUT_config called");
+ logd("adding config flag: " + request + "-" + args.getString("value"));
+ mFlags.put(request, args.getString("value"));
+ break;
case Settings.CALL_METHOD_LIST_CONFIG:
logd("LIST_config: " + mFlags);
Bundle result = new Bundle();
@@ -349,6 +354,10 @@
return Context.NETWORK_POLICY_SERVICE;
} else if (serviceClass == PowerManager.class) {
return Context.POWER_SERVICE;
+ } else if (serviceClass == EuiccManager.class) {
+ return Context.EUICC_SERVICE;
+ } else if (serviceClass == AlarmManager.class) {
+ return Context.ALARM_SERVICE;
}
return super.getSystemServiceName(serviceClass);
}
@@ -771,6 +780,7 @@
doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
doReturn(mBundle).when(mCarrierConfigManager).getConfig();
+ doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
doAnswer(invocation -> mNetworkId++).when(mNetwork).getNetId();
doReturn(mNetwork).when(mConnectivityManager).registerNetworkAgent(
any(), any(), any(), any(), any(), any(), anyInt());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DataSpecificRegistrationInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/DataSpecificRegistrationInfoTest.java
new file mode 100644
index 0000000..50390cc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/DataSpecificRegistrationInfoTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.os.Parcel;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.LteVopsSupportInfo;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/** Unit tests for {@link DataSpecificRegistrationInfo}. */
+public class DataSpecificRegistrationInfoTest {
+ @Test
+ @SmallTest
+ public void testBuilder() {
+ DataSpecificRegistrationInfo dsri =
+ new DataSpecificRegistrationInfo.Builder(3)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .build();
+
+ assertEquals(3, dsri.maxDataCalls);
+ assertEquals(false, dsri.isDcNrRestricted);
+ assertEquals(true, dsri.isNrAvailable);
+ assertEquals(true, dsri.isEnDcAvailable);
+ assertNull(dsri.getVopsSupportInfo());
+
+ LteVopsSupportInfo vopsInfo = new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED, LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+ dsri = new DataSpecificRegistrationInfo.Builder(5)
+ .setDcNrRestricted(true)
+ .setVopsSupportInfo(vopsInfo)
+ .build();
+
+ assertEquals(5, dsri.maxDataCalls);
+ assertEquals(true, dsri.isDcNrRestricted);
+ assertEquals(false, dsri.isNrAvailable);
+ assertEquals(false, dsri.isEnDcAvailable);
+ assertEquals(vopsInfo, dsri.getVopsSupportInfo());
+ }
+
+ @Test
+ @SmallTest
+ public void testParcel() {
+ DataSpecificRegistrationInfo dsri =
+ new DataSpecificRegistrationInfo.Builder(3)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setLteAttachResultType(DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED)
+ .setLteAttachExtraInfo(DataSpecificRegistrationInfo.LTE_ATTACH_EXTRA_INFO_SMS_ONLY)
+ .build();
+
+ Parcel p = Parcel.obtain();
+ dsri.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ DataSpecificRegistrationInfo newDsri =
+ DataSpecificRegistrationInfo.CREATOR.createFromParcel(p);
+ assertEquals(dsri, newDsri);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 6a05d4f..2f4182a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -30,8 +31,11 @@
import android.telephony.PreciseDisconnectCause;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telephony.imsphone.ImsPhoneCall;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -53,6 +57,9 @@
GsmCdmaCall mForeGroundCall;
GsmCdmaCall mBackGroundCall;
GsmCdmaCall mRingingCall;
+ ImsPhoneCall mImsForeGroundCall;
+ ImsPhoneCall mImsBackGroundCall;
+ ImsPhoneCall mImsRingingCall;
@Before
public void setUp() throws Exception {
@@ -62,6 +69,9 @@
mForeGroundCall = mock(GsmCdmaCall.class);
mBackGroundCall = mock(GsmCdmaCall.class);
mRingingCall = mock(GsmCdmaCall.class);
+ mImsForeGroundCall = mock(ImsPhoneCall.class);
+ mImsBackGroundCall = mock(ImsPhoneCall.class);
+ mImsRingingCall = mock(ImsPhoneCall.class);
mDefaultPhoneNotifierUT = new DefaultPhoneNotifier(mContext);
}
@@ -168,61 +178,144 @@
@Test @SmallTest
public void testNotifyPreciseCallState() throws Exception {
-
//mock forground/background/ringing call and call state
doReturn(Call.State.IDLE).when(mForeGroundCall).getState();
doReturn(Call.State.IDLE).when(mBackGroundCall).getState();
doReturn(Call.State.IDLE).when(mRingingCall).getState();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
- anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), any(), any(), any(), any());
doReturn(mForeGroundCall).when(mPhone).getForegroundCall();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
- anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), any(), any(), any(), any());
doReturn(mBackGroundCall).when(mPhone).getBackgroundCall();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
- anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
+ anyInt(), anyInt(), any(), any(), any(), any());
doReturn(mRingingCall).when(mPhone).getRingingCall();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
- mPhone.getPhoneId(),
- mPhone.getSubId(),
- PreciseCallState.PRECISE_CALL_STATE_IDLE,
- PreciseCallState.PRECISE_CALL_STATE_IDLE,
- PreciseCallState.PRECISE_CALL_STATE_IDLE);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
+ ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
+ int phoneId = mPhone.getPhoneId();
+ int subId = mPhone.getSubId();
+ verify(mTelephonyRegistryManager).notifyPreciseCallState(
+ eq(phoneId), eq(subId), captor.capture(), eq(null), eq(null), eq(null));
+ final int[] callStates = captor.getValue();
+ assertEquals(3, callStates.length);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates[/*ringing call*/ 0]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates[/*foreground call*/ 1]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates[/*background call*/ 2]);
doReturn(Call.State.ACTIVE).when(mForeGroundCall).getState();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
- mPhone.getPhoneId(),
- mPhone.getSubId(),
- PreciseCallState.PRECISE_CALL_STATE_IDLE,
- PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
- PreciseCallState.PRECISE_CALL_STATE_IDLE);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
+ ArgumentCaptor<int[]> captor1 = ArgumentCaptor.forClass(int[].class);
+ phoneId = mPhone.getPhoneId();
+ subId = mPhone.getSubId();
+ verify(mTelephonyRegistryManager, times(2)).notifyPreciseCallState(
+ eq(phoneId), eq(subId), captor1.capture(), eq(null), eq(null), eq(null));
+ final int[] callStates1 = captor1.getValue();
+ assertEquals(3, callStates1.length);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates1[/*ringing call*/ 0]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+ callStates1[/*foreground call*/ 1]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates1[/*background call*/ 2]);
doReturn(Call.State.HOLDING).when(mBackGroundCall).getState();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
- mPhone.getPhoneId(),
- mPhone.getSubId(),
- PreciseCallState.PRECISE_CALL_STATE_IDLE,
- PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
- PreciseCallState.PRECISE_CALL_STATE_HOLDING);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
+ ArgumentCaptor<int[]> captor2 = ArgumentCaptor.forClass(int[].class);
+ verify(mTelephonyRegistryManager, times(3)).notifyPreciseCallState(
+ eq(phoneId), eq(subId), captor2.capture(), eq(null), eq(null), eq(null));
+ final int[] callStates2 = captor2.getValue();
+ assertEquals(3, callStates2.length);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates2[/*ringing call*/ 0]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+ callStates2[/*foreground call*/ 1]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_HOLDING,
+ callStates2[/*background call*/ 2]);
doReturn(Call.State.ALERTING).when(mRingingCall).getState();
- mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
+ mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone, null, null, null);
+ ArgumentCaptor<int[]> captor3 = ArgumentCaptor.forClass(int[].class);
+ verify(mTelephonyRegistryManager, times(4)).notifyPreciseCallState(
+ eq(phoneId), eq(subId), captor3.capture(), eq(null), eq(null), eq(null));
+ final int[] callStates3 = captor3.getValue();
+ assertEquals(3, callStates3.length);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_ALERTING,
+ callStates3[/*ringing call*/ 0]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+ callStates3[/*foreground call*/ 1]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_HOLDING,
+ callStates3[/*background call*/ 2]);
+ }
+
+ @Test
+ public void testNotifyPreciseCallStateImsCallInfo() throws Exception {
+ //mock forground/background/ringing call and call state
+ doReturn(Call.State.ACTIVE).when(mImsForeGroundCall).getState();
+ doReturn(Call.State.HOLDING).when(mImsBackGroundCall).getState();
+ doReturn(Call.State.IDLE).when(mImsRingingCall).getState();
+
+ doReturn(mImsForeGroundCall).when(mImsPhone).getForegroundCall();
+ doReturn(mImsBackGroundCall).when(mImsPhone).getBackgroundCall();
+ doReturn(mImsRingingCall).when(mImsPhone).getRingingCall();
+
+ String[] imsCallIds = {null, "1", "2"};
+ int[] imsCallServiceTypes = {ImsCallProfile.SERVICE_TYPE_NONE,
+ ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.SERVICE_TYPE_NORMAL};
+ int[] imsCallTypes = {ImsCallProfile.CALL_TYPE_NONE,
+ ImsCallProfile.CALL_TYPE_VOICE, ImsCallProfile.CALL_TYPE_VT};
+
+ mDefaultPhoneNotifierUT
+ .notifyPreciseCallState(mImsPhone, imsCallIds, imsCallServiceTypes, imsCallTypes);
+ ArgumentCaptor<int[]> callStateCaptor = ArgumentCaptor.forClass(int[].class);
+ ArgumentCaptor<String[]> callIdCaptor = ArgumentCaptor.forClass(String[].class);
+ ArgumentCaptor<int[]> callServiceTypeCaptor = ArgumentCaptor.forClass(int[].class);
+ ArgumentCaptor<int[]> callTypeCaptor = ArgumentCaptor.forClass(int[].class);
+ int phoneId = mImsPhone.getPhoneId();
+ int subId = mImsPhone.getSubId();
verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
- mPhone.getPhoneId(),
- mPhone.getSubId(),
- PreciseCallState.PRECISE_CALL_STATE_ALERTING,
- PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
- PreciseCallState.PRECISE_CALL_STATE_HOLDING);
+ eq(phoneId), eq(subId), callStateCaptor.capture(), callIdCaptor.capture(),
+ callServiceTypeCaptor.capture(), callTypeCaptor.capture());
+ final int[] callStates = callStateCaptor.getValue();
+ final String[] callIds = callIdCaptor.getValue();
+ final int[] callServiceTypes = callServiceTypeCaptor.getValue();
+ final int[] callTypes = callTypeCaptor.getValue();
+ assertEquals(3, callStates.length);
+ assertEquals(3, callIds.length);
+ assertEquals(3, callServiceTypes.length);
+ assertEquals(3, callServiceTypes.length);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ callStates[/*ringing call*/ 0]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+ callStates[/*foreground call*/ 1]);
+ assertEquals(PreciseCallState.PRECISE_CALL_STATE_HOLDING,
+ callStates[/*background call*/ 2]);
+
+ assertEquals("1", callIds[/*foreground call*/ 1]);
+ assertEquals("2", callIds[/*background call*/ 2]);
+ assertEquals(null, callIds[/*ringing call*/ 0]);
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NORMAL,
+ callServiceTypes[/*foreground call*/ 1]);
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NORMAL,
+ callServiceTypes[/*background call*/ 2]);
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NONE,
+ callServiceTypes[/*ringing call*/ 0]);
+ assertEquals(ImsCallProfile.CALL_TYPE_VOICE,
+ callTypes[/*foreground call*/ 1]);
+ assertEquals(ImsCallProfile.CALL_TYPE_VT,
+ callTypes[/*background call*/ 2]);
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NONE,
+ callServiceTypes[/*ringing call*/ 0]);
}
@Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java
new file mode 100644
index 0000000..f729b80
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityLte;
+import android.telephony.LteVopsSupportInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DisplayInfoControllerTest extends TelephonyTest {
+
+ private static final int PHONE_ID = 0;
+ private static final String MCC = "600";
+ private static final String MNC = "01";
+ private static final String NUMERIC = MCC + MNC;
+ private static final String NETWORK = "TestNet";
+
+ private DisplayInfoController mDic;
+ private ServiceStateTracker mSst;
+ private ServiceStateTrackerTestHandler mSstHandler;
+ private SignalStrengthController mSsc;
+ private PersistableBundle mBundle;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
+ private class ServiceStateTrackerTestHandler extends HandlerThread {
+ private ServiceStateTrackerTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mSsc = new SignalStrengthController(mPhone);
+ doReturn(mSsc).when(mPhone).getSignalStrengthController();
+ doReturn(new ServiceState()).when(mPhone).getServiceState();
+ doReturn(NUMERIC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(PHONE_ID));
+ doReturn(NETWORK).when(mTelephonyManager).getSimOperatorNameForPhone(eq(PHONE_ID));
+
+ // Capture listener registered for ServiceStateTracker to emulate the carrier config
+ // change notification used later. In this test, it's the second one. The first one
+ // comes from RatRatcheter.
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener>
+ listenerArgumentCaptor = ArgumentCaptor.forClass(
+ CarrierConfigManager.CarrierConfigChangeListener.class);
+ mSst = new ServiceStateTracker(mPhone, mSimulatedCommands);
+ verify(mCarrierConfigManager, atLeast(2)).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(1);
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("DisplayInfoControllerTest setup!");
+ super.setUp(getClass().getSimpleName());
+
+ doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ mSstHandler = new ServiceStateTrackerTestHandler(getClass().getSimpleName());
+ mSstHandler.start();
+ waitUntilReady();
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSst.removeCallbacksAndMessages(null);
+ mSst = null;
+ mSstHandler.quit();
+ mSstHandler.join();
+ mSstHandler = null;
+ mBundle = null;
+ mCarrierConfigChangeListener = null;
+ super.tearDown();
+ }
+
+ private void sendCarrierConfigUpdate() {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, TelephonyManager.UNKNOWN_CARRIER_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ }
+
+ private static String getPlmnFromCellIdentity(final CellIdentity ci) {
+ if (ci == null || ci instanceof CellIdentityCdma) return "";
+
+ final String mcc = ci.getMccString();
+ final String mnc = ci.getMncString();
+
+ if (TextUtils.isEmpty(mcc) || TextUtils.isEmpty(mnc)) return "";
+
+ return mcc + mnc;
+ }
+
+ private void changeRegState(int state) {
+ int voiceRat = TelephonyManager.NETWORK_TYPE_LTE;
+ int dataRat = TelephonyManager.NETWORK_TYPE_LTE;
+ CellIdentityLte cid =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000,
+ MCC, MNC, NETWORK, NETWORK, Collections.emptyList(), null);
+ LteVopsSupportInfo lteVopsSupportInfo =
+ new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ state, dataRat, 0, false, null, cid, getPlmnFromCellIdentity(cid), 1, false, false,
+ false, lteVopsSupportInfo);
+ mSst.mPollingContext[0] = 3;
+ final String[] oldOpNamesResult = new String[] {"test", "test", NUMERIC};
+ mSst.sendMessage(
+ mSst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_OPERATOR,
+ new AsyncResult(mSst.mPollingContext, oldOpNamesResult, null)));
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ // update data reg state to be in service
+ mSst.sendMessage(
+ mSst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(mSst.mPollingContext, dataResult, null)));
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ state, voiceRat, 0, false, null, cid, getPlmnFromCellIdentity(cid), false, 0, 0, 0);
+ mSst.sendMessage(
+ mSst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(mSst.mPollingContext, voiceResult, null)));
+ waitForLastHandlerAction(mSstHandler.getThreadHandler());
+ }
+
+ @Test
+ public void testIsRoamingOverride_NonRoamingOperator() {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[] {NUMERIC});
+ sendCarrierConfigUpdate();
+
+ changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ ServiceState ss = mSst.getServiceState();
+
+ assertFalse(ss.getRoaming()); // home
+
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ mDic = new DisplayInfoController(mPhone);
+ mDic.updateTelephonyDisplayInfo();
+ TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+ assertFalse(tdi.isRoaming());
+ }
+
+ @Test
+ public void testIsRoamingOverride_ForceHomeNetwork() {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ mBundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, true);
+ sendCarrierConfigUpdate();
+
+ changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ ServiceState ss = mSst.getServiceState();
+
+ assertFalse(ss.getRoaming()); // home
+
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ mDic = new DisplayInfoController(mPhone);
+ mDic.updateTelephonyDisplayInfo();
+ TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+ assertFalse(tdi.isRoaming());
+ }
+
+ @Test
+ public void testIsRoamingOverride_RoamingOperator() {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY, new String[] {"60101"});
+ sendCarrierConfigUpdate();
+
+ changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ ServiceState ss1 = mSst.getServiceState();
+
+ assertTrue(ss1.getRoaming()); // roam
+
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ mDic = new DisplayInfoController(mPhone);
+ mDic.updateTelephonyDisplayInfo();
+ TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+ assertTrue(tdi.isRoaming());
+ }
+
+ @Test
+ public void testIsRoamingOverride_NonRoamingGsmOperator() {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY,
+ new String[] {NUMERIC});
+ sendCarrierConfigUpdate();
+
+ changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ ServiceState ss = mSst.getServiceState();
+
+ assertFalse(ss.getRoaming()); // home
+
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ mDic = new DisplayInfoController(mPhone);
+ mDic.updateTelephonyDisplayInfo();
+ TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+ assertFalse(tdi.isRoaming());
+ }
+
+ @Test
+ public void testIsRoamingOverride_RoamingGsmOperator() {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, new String[] {NUMERIC});
+ sendCarrierConfigUpdate();
+
+ changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ ServiceState ss1 = mSst.getServiceState();
+
+ assertTrue(ss1.getRoaming()); // roam
+
+ doReturn(mSst).when(mPhone).getServiceStateTracker();
+ mDic = new DisplayInfoController(mPhone);
+ mDic.updateTelephonyDisplayInfo();
+ TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+ assertTrue(tdi.isRoaming());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 782f78e..3be8509 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -24,6 +24,7 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.BaseColumns;
import android.provider.Telephony;
import android.telephony.SubscriptionManager;
@@ -123,6 +124,10 @@
+ Telephony.SimInfo.COLUMN_PORT_INDEX + " INTEGER DEFAULT -1,"
+ Telephony.SimInfo.COLUMN_USAGE_SETTING + " INTEGER DEFAULT "
+ SubscriptionManager.USAGE_SETTING_UNKNOWN
+ + "," + Telephony.SimInfo.COLUMN_TP_MESSAGE_REF
+ + " INTEGER DEFAULT -1,"
+ + Telephony.SimInfo.COLUMN_USER_HANDLE + " INTEGER DEFAULT "
+ + UserHandle.USER_NULL
+ ");";
}
@@ -143,6 +148,7 @@
@Override
public Uri insert(Uri uri, ContentValues values) {
+ Log.d(TAG, "insert. values=" + values);
SQLiteDatabase db = mDbHelper.getWritableDatabase();
long id = db.insert("siminfo", null, values);
return ContentUris.withAppendedId(Telephony.SimInfo.CONTENT_URI, id);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
new file mode 100644
index 0000000..2c48158
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.telephony.uicc.AdnRecord;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class FdnUtilsTest {
+
+ private ArrayList<AdnRecord> initializeFdnList() {
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ AdnRecord adnRecord = new AdnRecord(null, null);
+ // By default, every sim card holds 15 empty FDN records
+ int fdnListSize = 15;
+ for (int i = 0; i < fdnListSize; i++) {
+ fdnList.add(adnRecord);
+ }
+ return fdnList;
+ }
+
+ @Test
+ public void fdnListIsNull_returnsFalse() {
+ assertFalse(FdnUtils.isFDN( "123456789", "US", null));
+ }
+
+ @Test
+ public void fdnListIsEmpty_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+ }
+
+ @Test
+ public void fdnListHasOnlyDefaultRecords_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+
+ assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+ }
+
+ @Test
+ public void fdnListHasRecordWithEmptyNumberStr_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "");
+ fdnList.add(1, adnRecord);
+
+ assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrInFdnList_returnsTrue() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "123456789");
+ fdnList.add(2, adnRecord);
+
+ assertTrue(FdnUtils.isFDN( "123456789", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrNotInFdnList_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "111111111");
+ fdnList.add(3, adnRecord);
+
+ assertFalse(FdnUtils.isFDN("123456788", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrIsNull_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "111111111");
+ fdnList.add(4, adnRecord);
+
+ assertFalse(FdnUtils.isFDN( null, "US", fdnList));
+ }
+
+ @Test
+ public void fdnEntryFirstSubStringOfDialStr_returnsTrue() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "123");
+ fdnList.add(5, adnRecord);
+
+ assertTrue(FdnUtils.isFDN( "12345", "US", fdnList));
+ }
+
+ @Test
+ public void fdnEntrySubStringOfDialStr_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "123");
+ fdnList.add(5, adnRecord);
+
+ assertFalse(FdnUtils.isFDN("612345", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrFirstSubStringOfFdnEntry_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "12345");
+ fdnList.add(5, adnRecord);
+
+ assertFalse(FdnUtils.isFDN("123", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrSubStringOfFdnEntry_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "612345");
+ fdnList.add(5, adnRecord);
+
+ assertFalse(FdnUtils.isFDN("123", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrWithoutCountryCode_returnsTrue() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "+16502910000");
+ fdnList.add(6, adnRecord);
+
+ assertTrue(FdnUtils.isFDN( "6502910000", "US", fdnList));
+ }
+
+ @Test
+ public void dialStrWithCountryCode_returnsTrue() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "6502910000");
+ fdnList.add(6, adnRecord);
+
+ assertTrue(FdnUtils.isFDN("+16502910000", "US", fdnList));
+ }
+
+ @Test
+ public void defaultCountryIsoIsEmpty_returnsTrue() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "650");
+ fdnList.add(6, adnRecord);
+
+ assertTrue(FdnUtils.isFDN("+16502910000", "", fdnList));
+ }
+
+ @Test
+ public void defaultCountryIsoIsEmpty_returnsFalse() {
+ ArrayList<AdnRecord> fdnList = initializeFdnList();
+ AdnRecord adnRecord = new AdnRecord(null, "+1650");
+ fdnList.add(6, adnRecord);
+
+ assertFalse(FdnUtils.isFDN("6502910000", "", fdnList));
+ }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index 33a8e62..2fdff9e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -41,6 +41,7 @@
import androidx.test.filters.FlakyTest;
import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import org.junit.After;
import org.junit.Assert;
@@ -50,6 +51,9 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class GsmCdmaCallTrackerTest extends TelephonyTest {
@@ -63,12 +67,16 @@
// Mocked classes
private GsmCdmaConnection mConnection;
private Handler mHandler;
+ private DomainSelectionResolver mDomainSelectionResolver;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mConnection = mock(GsmCdmaConnection.class);
mHandler = mock(Handler.class);
+ mDomainSelectionResolver = mock(DomainSelectionResolver.class);
+ doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
mSimulatedCommands.setRadioPower(true, null);
mPhone.mCi = this.mSimulatedCommands;
@@ -83,6 +91,7 @@
@After
public void tearDown() throws Exception {
mCTUT = null;
+ DomainSelectionResolver.setDomainSelectionResolver(null);
super.tearDown();
}
@@ -109,7 +118,9 @@
processAllMessages();
} catch(Exception ex) {
ex.printStackTrace();
- Assert.fail("unexpected exception thrown"+ex.getMessage()+ex.getStackTrace());
+ StringWriter exString = new StringWriter();
+ ex.printStackTrace(new PrintWriter(exString));
+ Assert.fail("unexpected exception thrown" + exString);
}
assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index fe89a8e..8292e84 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -295,4 +295,21 @@
connection.update(mDC);
assertNull(connection.getForwardedNumber());
}
+
+ /**
+ * Verifies that the mappings for CallFailCause.NO_VALID_SIM,
+ * CallFailCause.LOCAL_NETWORK_NO_SERVICE, and CallFailCause.LOCAL_SERVICE_UNAVAILABLE are as
+ * expected.
+ */
+ @Test @SmallTest
+ public void testNoSimNoServiceMapping() {
+ connection = new GsmCdmaConnection(mPhone, "12345", mCT, null,
+ new DialArgs.Builder().build());
+ assertEquals(DisconnectCause.ICC_ERROR,
+ connection.disconnectCauseFromCode(CallFailCause.NO_VALID_SIM));
+ assertEquals(DisconnectCause.OUT_OF_SERVICE,
+ connection.disconnectCauseFromCode(CallFailCause.LOCAL_NETWORK_NO_SERVICE));
+ assertEquals(DisconnectCause.OUT_OF_SERVICE,
+ connection.disconnectCauseFromCode(CallFailCause.LOCAL_SERVICE_UNAVAILABLE));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 9e45cc3..c5f20e3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -19,9 +19,14 @@
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
import static com.android.internal.telephony.Phone.EVENT_ICC_CHANGED;
+import static com.android.internal.telephony.Phone.EVENT_IMS_DEREGISTRATION_TRIGGERED;
+import static com.android.internal.telephony.Phone.EVENT_RADIO_AVAILABLE;
+import static com.android.internal.telephony.Phone.EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE;
import static com.android.internal.telephony.Phone.EVENT_SRVCC_STATE_CHANGED;
import static com.android.internal.telephony.Phone.EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.test.SimulatedCommands.FAKE_IMEI;
+import static com.android.internal.telephony.test.SimulatedCommands.FAKE_IMEISV;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -49,13 +54,18 @@
import android.content.Intent;
import android.content.SharedPreferences;
+import android.hardware.radio.modem.ImeiInfo;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
import android.preference.PreferenceManager;
+import android.provider.DeviceConfig;
import android.telecom.VideoProfile;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
@@ -64,21 +74,31 @@
import android.telephony.CellIdentityGsm;
import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import androidx.test.filters.FlakyTest;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccVmNotSupportedException;
import com.android.internal.telephony.uicc.UiccController;
@@ -101,16 +121,24 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class GsmCdmaPhoneTest extends TelephonyTest {
+ private static final String LOG_TAG = "GsmCdmaPhoneTest";
private static final String TEST_EMERGENCY_NUMBER = "555";
// Mocked classes
private Handler mTestHandler;
private UiccSlot mUiccSlot;
private CommandsInterface mMockCi;
+ private AdnRecordCache adnRecordCache;
+ private DomainSelectionResolver mDomainSelectionResolver;
//mPhoneUnderTest
private GsmCdmaPhone mPhoneUT;
+ // Ideally we would use TestableDeviceConfig, but that's not doable because the Settings
+ // app is not currently debuggable. For now, we use the real device config and ensure that
+ // we reset the cellular_security namespace property to its pre-test value after every test.
+ private DeviceConfig.Properties mPreTestProperties;
+
private static final int EVENT_EMERGENCY_CALLBACK_MODE_EXIT = 1;
private static final int EVENT_EMERGENCY_CALL_TOGGLE = 2;
private static final int EVENT_SET_ICC_LOCK_ENABLED = 3;
@@ -134,12 +162,18 @@
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
+ mPreTestProperties = DeviceConfig.getProperties(
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE);
mTestHandler = Mockito.mock(Handler.class);
mUiccSlot = Mockito.mock(UiccSlot.class);
mUiccPort = Mockito.mock(UiccPort.class);
mMockCi = Mockito.mock(CommandsInterface.class);
+ adnRecordCache = Mockito.mock(AdnRecordCache.class);
+ mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
doReturn(false).when(mSST).isDeviceShuttingDown();
doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
+ doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
mPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
@@ -157,6 +191,14 @@
public void tearDown() throws Exception {
mPhoneUT.removeCallbacksAndMessages(null);
mPhoneUT = null;
+ DomainSelectionResolver.setDomainSelectionResolver(null);
+ try {
+ DeviceConfig.setProperties(mPreTestProperties);
+ } catch (DeviceConfig.BadConfigException e) {
+ Log.e(LOG_TAG,
+ "Failed to reset DeviceConfig to pre-test state. Test results may be impacted. "
+ + e.getMessage());
+ }
super.tearDown();
}
@@ -953,7 +995,7 @@
verify(mTelephonyManager).setBasebandVersionForPhone(eq(mPhoneUT.getPhoneId()),
nullable(String.class));
// IMEI
- assertEquals(SimulatedCommands.FAKE_IMEI, mPhoneUT.getImei());
+ assertEquals(FAKE_IMEI, mPhoneUT.getImei());
// IMEISV
assertEquals(SimulatedCommands.FAKE_IMEISV, mPhoneUT.getDeviceSvn());
// radio capability
@@ -979,7 +1021,7 @@
verify(mTelephonyManager, times(2)).setBasebandVersionForPhone(eq(mPhoneUT.getPhoneId()),
nullable(String.class));
// device identity
- assertEquals(SimulatedCommands.FAKE_IMEI, mPhoneUT.getImei());
+ assertEquals(FAKE_IMEI, mPhoneUT.getImei());
assertEquals(SimulatedCommands.FAKE_IMEISV, mPhoneUT.getDeviceSvn());
assertEquals(SimulatedCommands.FAKE_ESN, mPhoneUT.getEsn());
assertEquals(SimulatedCommands.FAKE_MEID, mPhoneUT.getMeid());
@@ -1050,14 +1092,14 @@
getVoiceCallForwardingFlag();
// invalid subId
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionController).
- getSubIdUsingPhoneId(anyInt());
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
+ .getSubId(anyInt());
assertEquals(false, mPhoneUT.getCallForwardingIndicator());
// valid subId, sharedPreference not present
int subId1 = 0;
int subId2 = 1;
- doReturn(subId1).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ doReturn(subId1).when(mSubscriptionManagerService).getSubId(anyInt());
assertEquals(false, mPhoneUT.getCallForwardingIndicator());
// old sharedPreference present
@@ -1079,7 +1121,7 @@
assertEquals(true, mPhoneUT.getCallForwardingIndicator());
// check for another subId
- doReturn(subId2).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ doReturn(subId2).when(mSubscriptionManagerService).getSubId(anyInt());
assertEquals(false, mPhoneUT.getCallForwardingIndicator());
// set value for the new subId in sharedPreference
@@ -1088,7 +1130,7 @@
assertEquals(true, mPhoneUT.getCallForwardingIndicator());
// switching back to previous subId, stored value should still be available
- doReturn(subId1).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ doReturn(subId1).when(mSubscriptionManagerService).getSubId(anyInt());
assertEquals(true, mPhoneUT.getCallForwardingIndicator());
// cleanup
@@ -1121,6 +1163,7 @@
@SmallTest
public void testGetEmptyIccCard() {
doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
+ doReturn(null).when(mUiccController).getUiccSlotForPhone(anyInt());
IccCard iccCard = mPhoneUT.getIccCard();
@@ -1247,7 +1290,8 @@
Message.obtain(mPhoneUT, EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED,
new AsyncResult(null, true, null)).sendToTarget();
processAllMessages();
- verify(mSubscriptionController, never()).getSubInfoForIccId(any());
+
+ verify(mSubscriptionManagerService, never()).getAllSubInfoList(anyString(), anyString());
// Have IccId defined. But expected value and current value are the same. So no RIL command
// should be sent.
@@ -1255,7 +1299,8 @@
doReturn(iccId).when(mUiccSlot).getIccId(anyInt());
Message.obtain(mPhoneUT, EVENT_ICC_CHANGED, null).sendToTarget();
processAllMessages();
- verify(mSubscriptionController).getSubInfoForIccId(iccId);
+ verify(mSubscriptionManagerService).getAllSubInfoList(anyString(),
+ nullable(String.class));
verify(mMockCi, never()).enableUiccApplications(anyBoolean(), any());
}
@@ -1291,11 +1336,12 @@
public void testSetRadioPower() throws Exception {
mPhoneUT.setRadioPower(false);
verify(mSST).setRadioPowerForReason(false, false, false, false,
- Phone.RADIO_POWER_REASON_USER);
+ TelephonyManager.RADIO_POWER_REASON_USER);
// Turn on radio for emergency call.
mPhoneUT.setRadioPower(true, true, false, true);
- verify(mSST).setRadioPowerForReason(true, true, false, true, Phone.RADIO_POWER_REASON_USER);
+ verify(mSST).setRadioPowerForReason(true, true, false, true,
+ TelephonyManager.RADIO_POWER_REASON_USER);
}
@Test
@@ -1303,12 +1349,12 @@
public void testSetRadioPowerOnForTestEmergencyCall() {
mPhoneUT.setRadioPower(false);
verify(mSST).setRadioPowerForReason(false, false, false, false,
- Phone.RADIO_POWER_REASON_USER);
+ TelephonyManager.RADIO_POWER_REASON_USER);
mPhoneUT.setRadioPowerOnForTestEmergencyCall(false);
verify(mSST).clearAllRadioOffReasons();
verify(mSST).setRadioPowerForReason(eq(true), eq(false), anyBoolean(), eq(false),
- eq(Phone.RADIO_POWER_REASON_USER));
+ eq(TelephonyManager.RADIO_POWER_REASON_USER));
}
@Test
@@ -1424,8 +1470,8 @@
@Test
public void testEventCarrierConfigChanged() {
- doReturn(null).when(mSubscriptionController).getSubscriptionProperty(anyInt(),
- eq(SubscriptionManager.NR_ADVANCED_CALLING_ENABLED));
+ doReturn(null).when(mSubscriptionManagerService).getSubscriptionProperty(anyInt(),
+ eq(SubscriptionManager.NR_ADVANCED_CALLING_ENABLED), anyString(), anyString());
mPhoneUT.mCi = mMockCi;
mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
@@ -1494,10 +1540,10 @@
@SmallTest
public void testLoadAllowedNetworksFromSubscriptionDatabase_loadTheNullValue_isLoadedTrue() {
int subId = 1;
- doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(anyInt());
- doReturn(null).when(mSubscriptionController).getSubscriptionProperty(anyInt(),
- eq(SubscriptionManager.ALLOWED_NETWORK_TYPES));
+ doReturn(null).when(mSubscriptionManagerService).getSubscriptionProperty(anyInt(),
+ eq(SubscriptionManager.ALLOWED_NETWORK_TYPES), anyString(), anyString());
mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
@@ -1508,17 +1554,94 @@
@SmallTest
public void testLoadAllowedNetworksFromSubscriptionDatabase_subIdNotValid_isLoadedFalse() {
int subId = -1;
- doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(anyInt());
- when(mSubscriptionController.getSubscriptionProperty(anyInt(),
- eq(SubscriptionManager.ALLOWED_NETWORK_TYPES))).thenReturn(null);
-
+ when(mSubscriptionManagerService.getSubscriptionProperty(anyInt(),
+ eq(SubscriptionManager.ALLOWED_NETWORK_TYPES), anyString(), anyString()))
+ .thenReturn(null);
mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
assertEquals(false, mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
}
+ @Test
+ public void testLoadAllowedNetworksFromSubscriptionDatabase_allValidData() {
+ int subId = 1;
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(anyInt());
+
+ // 13 == TelephonyManager.NETWORK_TYPE_LTE
+ // NR_BITMASK == 4096 == 1 << (13 - 1)
+ String validSerializedNetworkMap = "user=4096,power=4096,carrier=4096,enable_2g=4096";
+ SubscriptionInfoInternal si = new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setAllowedNetworkTypesForReasons(validSerializedNetworkMap)
+ .build();
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(1));
+
+ assertFalse(mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
+ mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
+ assertTrue(mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
+
+ for (int i = 0; i < 4; ++i) {
+ assertEquals(TelephonyManager.NETWORK_TYPE_BITMASK_LTE,
+ mPhoneUT.getAllowedNetworkTypes(i));
+ }
+ }
+
+ @Test
+ public void testLoadAllowedNetworksFromSubscriptionDatabase_invalidKeys() {
+ int subId = 1;
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(anyInt());
+
+ // 13 == TelephonyManager.NETWORK_TYPE_LTE
+ // NR_BITMASK == 4096 == 1 << (13 - 1)
+ String validSerializedNetworkMap =
+ "user=4096,power=4096,carrier=4096,enable_2g=4096,-1=4096";
+ SubscriptionInfoInternal si = new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setAllowedNetworkTypesForReasons(validSerializedNetworkMap)
+ .build();
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(1));
+
+ assertFalse(mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
+ mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
+ assertTrue(mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
+
+ for (int i = 0; i < 4; ++i) {
+ assertEquals(TelephonyManager.NETWORK_TYPE_BITMASK_LTE,
+ mPhoneUT.getAllowedNetworkTypes(i));
+ }
+ }
+
+ @Test
+ public void testLoadAllowedNetworksFromSubscriptionDatabase_invalidValues() {
+ int subId = 1;
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(anyInt());
+
+ // 19 == TelephonyManager.NETWORK_TYPE_NR
+ // NR_BITMASK == 524288 == 1 << 19
+ String validSerializedNetworkMap = "user=4096,power=4096,carrier=4096,enable_2g=-1";
+ SubscriptionInfoInternal si = new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setAllowedNetworkTypesForReasons(validSerializedNetworkMap)
+ .build();
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(1));
+
+ mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
+
+ for (int i = 0; i < 3; ++i) {
+ assertEquals(TelephonyManager.NETWORK_TYPE_BITMASK_LTE,
+ mPhoneUT.getAllowedNetworkTypes(i));
+ }
+
+ long defaultAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE);
+ assertEquals(defaultAllowedNetworkTypes, mPhoneUT.getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G));
+
+ }
+
/**
* Verifies that an emergency call placed on a SIM which does NOT explicitly define a number as
* an emergency call will still be placed as an emergency call.
@@ -1599,7 +1722,7 @@
doReturn(true).when(mTelephonyManager).isEmergencyNumber(anyString());
doReturn(isEmergencyPerDialedSim).when(mEmergencyNumberTracker).isEmergencyNumber(
- anyString(), anyBoolean());
+ anyString());
mPhoneUT.setImsPhone(mImsPhone);
}
@@ -1650,11 +1773,25 @@
}
- private SubscriptionInfo makeSubscriptionInfo(boolean isOpportunistic, int usageSetting) {
- return new SubscriptionInfo(
- 1, "xxxxxxxxx", 1, "Android Test", "Android Test", 0, 0, "8675309", 0,
- null, "001", "01", "us", true, null, null, 0, isOpportunistic, null, false,
- 1, 1, 0, null, null, true, 0, usageSetting);
+ private SubscriptionInfoInternal makeSubscriptionInfoInternal(
+ boolean isOpportunistic, int usageSetting) {
+ return new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setIccId("xxxxxxxxx")
+ .setSimSlotIndex(1)
+ .setDisplayName("Android Test")
+ .setDisplayName("Android Test")
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+ .setNumber("8675309")
+ .setMcc("001")
+ .setMnc("01")
+ .setCountryIso("us")
+ .setEmbedded(1)
+ .setOpportunistic(isOpportunistic ? 1 : 0)
+ .setCarrierId(1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING)
+ .setUsageSetting(usageSetting)
+ .build();
}
@Test
@@ -1663,10 +1800,10 @@
setupUsageSettingResources();
mPhoneUT.mCi = mMockCi;
- final SubscriptionInfo si = makeSubscriptionInfo(
+ final SubscriptionInfoInternal si = makeSubscriptionInfoInternal(
false, SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
- doReturn(si).when(mSubscriptionController).getSubscriptionInfo(anyInt());
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
mPhoneUT.updateUsageSetting();
processAllMessages();
@@ -1687,9 +1824,9 @@
setupUsageSettingResources();
mPhoneUT.mCi = mMockCi;
- final SubscriptionInfo si = makeSubscriptionInfo(
+ final SubscriptionInfoInternal si = makeSubscriptionInfoInternal(
true, SubscriptionManager.USAGE_SETTING_DEFAULT);
- doReturn(si).when(mSubscriptionController).getSubscriptionInfo(anyInt());
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
mPhoneUT.updateUsageSetting();
processAllMessages();
@@ -1710,11 +1847,11 @@
setupUsageSettingResources();
mPhoneUT.mCi = mMockCi;
- final SubscriptionInfo si = makeSubscriptionInfo(
+ final SubscriptionInfoInternal si = makeSubscriptionInfoInternal(
false, SubscriptionManager.USAGE_SETTING_DEFAULT);
assertNotNull(si);
- doReturn(si).when(mSubscriptionController).getSubscriptionInfo(anyInt());
+ doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
mPhoneUT.updateUsageSetting();
processAllMessages();
@@ -1726,4 +1863,716 @@
processAllMessages();
verify(mMockCi, never()).setUsageSetting(any(), anyInt());
}
+
+ public void fdnCheckSetup() {
+ // FDN check setup
+ mPhoneUT.mCi = mMockCi;
+ doReturn(adnRecordCache).when(mSimRecords).getAdnCache();
+ doReturn(mUiccProfile).when(mUiccController).getUiccProfileForPhone(anyInt());
+ doReturn(true).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+ doReturn(true).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+ }
+
+ @Test
+ public void testGetCallForwardingOption_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*#21");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.getCallForwardingOption(CommandsInterface.CF_REASON_UNCONDITIONAL, message);
+ processAllMessages();
+ verify(mMockCi).queryCallForwardStatus(eq(CommandsInterface.CF_REASON_UNCONDITIONAL),
+ eq(CommandsInterface.SERVICE_CLASS_VOICE), any(), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.getCallForwardingOption(CommandsInterface.CF_REASON_UNCONDITIONAL, message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testSetCallForwardingOption_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "**21");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.setCallForwardingOption(CommandsInterface.CF_ACTION_REGISTRATION,
+ CommandsInterface.CF_REASON_UNCONDITIONAL, "123", 0,
+ message);
+ processAllMessages();
+ verify(mMockCi).setCallForward(eq(CommandsInterface.CF_ACTION_REGISTRATION),
+ eq(CommandsInterface.CF_REASON_UNCONDITIONAL),
+ eq(CommandsInterface.SERVICE_CLASS_VOICE), eq("123"), eq(0), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.setCallForwardingOption(CommandsInterface.CF_ACTION_REGISTRATION,
+ CommandsInterface.CF_REASON_UNCONDITIONAL, "", 0, message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testGetCallBarring_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*#330");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, "", message,
+ CommandsInterface.SERVICE_CLASS_VOICE);
+ processAllMessages();
+ verify(mMockCi).queryFacilityLock(eq(CommandsInterface.CB_FACILITY_BA_ALL), any(),
+ eq(CommandsInterface.SERVICE_CLASS_VOICE), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, "", message,
+ CommandsInterface.SERVICE_CLASS_VOICE);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testSetCallBarring_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*330");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, true, "",
+ message, CommandsInterface.SERVICE_CLASS_VOICE);
+ processAllMessages();
+ verify(mMockCi).setFacilityLock(eq(CommandsInterface.CB_FACILITY_BA_ALL),
+ eq(true), any(), eq(CommandsInterface.SERVICE_CLASS_VOICE), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, true, "",
+ message, CommandsInterface.SERVICE_CLASS_VOICE);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testChangeCallBarringPassword_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "**03*330");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.changeCallBarringPassword(CommandsInterface.CB_FACILITY_BA_ALL, "",
+ "", message);
+ processAllMessages();
+ verify(mMockCi).changeBarringPassword(eq(CommandsInterface.CB_FACILITY_BA_ALL), any(),
+ any(), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.changeCallBarringPassword(CommandsInterface.CB_FACILITY_BA_ALL, "",
+ "", message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testGetOutgoingCallerIdDisplay_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*#31");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.getOutgoingCallerIdDisplay(message);
+ processAllMessages();
+ verify(mMockCi).getCLIR(any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.getOutgoingCallerIdDisplay(message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testSetOutgoingCallerIdDisplay_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*31");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, message);
+ processAllMessages();
+ verify(mMockCi).setCLIR(eq(CommandsInterface.CLIR_SUPPRESSION), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testQueryCLIP_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*#30");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.queryCLIP(message);
+ processAllMessages();
+ verify(mMockCi).queryCLIP(any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.queryCLIP(message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testGetCallWaiting_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*#43");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.getCallWaiting(message);
+ processAllMessages();
+ verify(mMockCi).queryCallWaiting(eq(CommandsInterface.SERVICE_CLASS_NONE), any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.getCallWaiting(message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testSetCallWaiting_FdnCheck() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+ Message message = Message.obtain(mTestHandler);
+
+ // FDN check success - no exception is returned
+ AdnRecord adnRecord = new AdnRecord(null, "*43");
+ fdnList.add(0, adnRecord);
+ mPhoneUT.setCallWaiting(true, CommandsInterface.SERVICE_CLASS_VOICE, message);
+ processAllMessages();
+ verify(mMockCi).setCallWaiting(eq(true), eq(CommandsInterface.SERVICE_CLASS_VOICE),
+ any());
+ // FDN check failure - returns CommandException in onComplete
+ fdnList.remove(0);
+ mPhoneUT.setCallWaiting(true, CommandsInterface.SERVICE_CLASS_VOICE, message);
+ processAllMessages();
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(((CommandException) ar.exception).getCommandError(),
+ CommandException.Error.FDN_CHECK_FAILURE);
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testDial_fdnCheck() throws Exception {
+ // dial setup
+ mSST.mSS = mServiceState;
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+ mCT.mForegroundCall = mGsmCdmaCall;
+ mCT.mBackgroundCall = mGsmCdmaCall;
+ mCT.mRingingCall = mGsmCdmaCall;
+ doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();
+ replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+ // FDN check success - no exception is returned
+ AdnRecord dialRecord = new AdnRecord(null, "1234567890");
+ fdnList.add(0, dialRecord);
+ Connection connection = mPhoneUT.dial("1234567890",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+ verify(mCT).dialGsm(eq("1234567890"), any(PhoneInternalInterface.DialArgs.class));
+
+ // FDN check failure - returns CallStateException
+ fdnList.remove(0);
+ try {
+ connection = mPhoneUT.dial("1234567890",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+ fail("Expected CallStateException with ERROR_FDN_BLOCKED thrown.");
+ } catch (CallStateException e) {
+ assertEquals(CallStateException.ERROR_FDN_BLOCKED, e.getError());
+ }
+
+ // clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void testHandleNullCipherAndIntegrityEnabled_radioFeatureUnsupported() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.TRUE.toString(),
+ false);
+ mPhoneUT.mCi = mMockCi;
+ assertFalse(mPhoneUT.isNullCipherAndIntegritySupported());
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_RADIO_AVAILABLE,
+ new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+ processAllMessages();
+
+ verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
+ any(Message.class));
+
+ // Some ephemeral error occurred in the modem, but the feature was supported
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE,
+ new AsyncResult(null, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED))));
+ processAllMessages();
+ assertFalse(mPhoneUT.isNullCipherAndIntegritySupported());
+ }
+
+ @Test
+ public void testHandleNullCipherAndIntegrityEnabled_radioSupportsFeature() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.TRUE.toString(),
+ false);
+ mPhoneUT.mCi = mMockCi;
+ assertFalse(mPhoneUT.isNullCipherAndIntegritySupported());
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_RADIO_AVAILABLE,
+ new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+ processAllMessages();
+
+ verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
+ any(Message.class));
+
+ // Some ephemeral error occurred in the modem, but the feature was supported
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE,
+ new AsyncResult(null, null, null)));
+ processAllMessages();
+ assertTrue(mPhoneUT.isNullCipherAndIntegritySupported());
+ }
+
+ @Test
+ public void testHandleNullCipherAndIntegrityEnabled_featureFlagOn() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.TRUE.toString(),
+ false);
+ mPhoneUT.mCi = mMockCi;
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_RADIO_AVAILABLE,
+ new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+ processAllMessages();
+
+ verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testHandleNullCipherAndIntegrityEnabled_featureFlagOff() {
+ mPhoneUT.mCi = mMockCi;
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.FALSE.toString(),
+ false);
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_RADIO_AVAILABLE,
+ new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+ processAllMessages();
+
+ verify(mMockCi, times(0)).setNullCipherAndIntegrityEnabled(anyBoolean(),
+ any(Message.class));
+ }
+
+ public void fdnCheckCleanup() {
+ doReturn(false).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+ doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+ }
+
+ @Test
+ @SmallTest
+ public void testTriggerImsDeregistration() throws Exception {
+ replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_IMS_DEREGISTRATION_TRIGGERED,
+ new AsyncResult(null,
+ new int[]{ImsRegistrationImplBase.REASON_SIM_REFRESH}, null)));
+ processAllMessages();
+
+ verify(mImsPhone, times(1)).triggerImsDeregistration(
+ eq(ImsRegistrationImplBase.REASON_SIM_REFRESH));
+ }
+
+ @Test
+ public void testDomainSelectionEmergencyCallCs() throws CallStateException {
+ setupEmergencyCallScenario(false /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+ false /* isEmergencyOnDialedSim */);
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_CS);
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(extras)
+ .build();
+ mPhoneUT.dial(TEST_EMERGENCY_NUMBER, dialArgs);
+
+ verify(mCT).dialGsm(anyString(), any(PhoneInternalInterface.DialArgs.class));
+ }
+
+ @Test
+ public void testDomainSelectionEmergencyCallPs() throws CallStateException {
+ setupEmergencyCallScenario(false /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+ false /* isEmergencyOnDialedSim */);
+
+ doReturn(false).when(mImsPhone).isImsAvailable();
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_PS);
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(extras)
+ .build();
+ mPhoneUT.dial(TEST_EMERGENCY_NUMBER, dialArgs);
+
+ verify(mImsPhone).dial(anyString(), any(PhoneInternalInterface.DialArgs.class));
+
+ extras = dialArgs.intentExtras;
+
+ assertFalse(extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
+ }
+
+ @Test
+ public void testDomainSelectionEmergencyCallNon3GppPs() throws CallStateException {
+ setupEmergencyCallScenario(false /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+ false /* isEmergencyOnDialedSim */);
+
+ doReturn(false).when(mImsPhone).isImsAvailable();
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, PhoneConstants.DOMAIN_NON_3GPP_PS);
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(extras)
+ .build();
+ mPhoneUT.dial(TEST_EMERGENCY_NUMBER, dialArgs);
+
+ verify(mImsPhone).dial(anyString(), any(PhoneInternalInterface.DialArgs.class));
+
+ extras = dialArgs.intentExtras;
+
+ assertTrue(extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
+ assertEquals(String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN),
+ extras.getString(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
+ }
+
+ @Test
+ public void testDomainSelectionDialCs() throws Exception {
+ doReturn(true).when(mImsPhone).isImsAvailable();
+ doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
+ doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+ doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
+ doReturn(true).when(mImsPhone).isVoiceOverCellularImsEnabled();
+ doReturn(true).when(mImsPhone).isUtEnabled();
+
+ replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_CS);
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(extras)
+ .build();
+ Connection connection = mPhoneUT.dial("1234567890", dialArgs);
+ verify(mCT).dialGsm(eq("1234567890"), any(PhoneInternalInterface.DialArgs.class));
+ }
+
+ @Test
+ public void testDomainSelectionDialPs() throws Exception {
+ mSST.mSS = mServiceState;
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+
+ mCT.mForegroundCall = mGsmCdmaCall;
+ mCT.mBackgroundCall = mGsmCdmaCall;
+ mCT.mRingingCall = mGsmCdmaCall;
+ doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();
+
+ replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+ Bundle extras = new Bundle();
+ extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_PS);
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(extras)
+ .build();
+ Connection connection = mPhoneUT.dial("1234567890", dialArgs);
+ verify(mImsPhone).dial(eq("1234567890"), any(PhoneInternalInterface.DialArgs.class));
+ }
+
+ @Test
+ public void getImeiType_primary() {
+ Message message = mPhoneUT.obtainMessage(Phone.EVENT_GET_DEVICE_IMEI_DONE);
+ ImeiInfo imeiInfo = new ImeiInfo();
+ imeiInfo.imei = FAKE_IMEI;
+ imeiInfo.svn = FAKE_IMEISV;
+ imeiInfo.type = ImeiInfo.ImeiType.PRIMARY;
+ AsyncResult.forMessage(message, imeiInfo, null);
+ mPhoneUT.handleMessage(message);
+ assertEquals(Phone.IMEI_TYPE_PRIMARY, mPhoneUT.getImeiType());
+ assertEquals(FAKE_IMEI, mPhoneUT.getImei());
+ }
+
+ @Test
+ public void getImeiType_Secondary() {
+ Message message = mPhoneUT.obtainMessage(Phone.EVENT_GET_DEVICE_IMEI_DONE);
+ ImeiInfo imeiInfo = new ImeiInfo();
+ imeiInfo.imei = FAKE_IMEI;
+ imeiInfo.svn = FAKE_IMEISV;
+ imeiInfo.type = ImeiInfo.ImeiType.SECONDARY;
+ AsyncResult.forMessage(message, imeiInfo, null);
+ mPhoneUT.handleMessage(message);
+ assertEquals(Phone.IMEI_TYPE_SECONDARY, mPhoneUT.getImeiType());
+ assertEquals(FAKE_IMEI, mPhoneUT.getImei());
+ }
+
+ @Test
+ public void getImei() {
+ assertTrue(mPhoneUT.isPhoneTypeGsm());
+ Message message = mPhoneUT.obtainMessage(Phone.EVENT_RADIO_AVAILABLE);
+ mPhoneUT.handleMessage(message);
+ verify(mSimulatedCommandsVerifier, times(2)).getImei(nullable(Message.class));
+ }
+
+ @Test
+ public void testSetAllowedNetworkTypes_admin2gRestrictionHonored() throws Exception {
+ // circumvent loading/saving to sim db. it's not behavior under test.
+ TelephonyManager.setupISubForTest(Mockito.mock(SubscriptionManagerService.class));
+ TelephonyManager.enableServiceHandleCaching();
+ mPhoneUT.loadAllowedNetworksFromSubscriptionDatabase();
+
+ // 2g is disabled by admin
+ UserManager userManagerMock = mContext.getSystemService(UserManager.class);
+ when(userManagerMock.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(true);
+ mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+
+ // carrier requests 2g to be enabled
+ mPhoneUT.setAllowedNetworkTypes(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+ TelephonyManager.NETWORK_CLASS_BITMASK_2G, null);
+
+ // Assert that 2g was not passed as an allowed network type to the modem
+ ArgumentCaptor<Integer> captureBitMask = ArgumentCaptor.forClass(Integer.class);
+ // One call for the admin restriction update, another by this test
+ verify(mSimulatedCommandsVerifier, times(2)).setAllowedNetworkTypesBitmap(
+ captureBitMask.capture(),
+ nullable(Message.class));
+ assertEquals(0, captureBitMask.getValue() & TelephonyManager.NETWORK_CLASS_BITMASK_2G);
+ }
+
+ @Test
+ @SmallTest
+ public void testEcbm() throws Exception {
+ assertFalse(mPhoneUT.isInEcm());
+
+ mPhoneUT.handleMessage(mPhoneUT.obtainMessage(
+ GsmCdmaPhone.EVENT_EMERGENCY_CALLBACK_MODE_ENTER));
+
+ assertTrue(mPhoneUT.isInEcm());
+ verifyEcbmIntentWasSent(1 /*times*/, true /*inEcm*/);
+ // verify that wakeLock is acquired in ECM
+ assertTrue(mPhoneUT.getWakeLock().isHeld());
+
+ boolean isTestingEmergencyCallbackMode = true;
+ replaceInstance(GsmCdmaPhone.class, "mIsTestingEmergencyCallbackMode", mPhoneUT,
+ isTestingEmergencyCallbackMode);
+ mPhoneUT.exitEmergencyCallbackMode();
+ processAllMessages();
+
+ assertFalse(mPhoneUT.isInEcm());
+ verifyEcbmIntentWasSent(2/*times*/, false /*inEcm*/);
+ // verify wakeLock released
+ assertFalse(mPhoneUT.getWakeLock().isHeld());
+ }
+
+ private void verifyEcbmIntentWasSent(int times, boolean isInEcm) throws Exception {
+ // verify ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeast(times)).sendStickyBroadcastAsUser(intentArgumentCaptor.capture(),
+ any());
+
+ Intent intent = intentArgumentCaptor.getValue();
+ assertNotNull(intent);
+ assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, intent.getAction());
+ assertEquals(isInEcm, intent.getBooleanExtra(
+ TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false));
+ }
+
+ @Test
+ @SmallTest
+ public void testEcbmWhenDomainSelectionEnabled() throws Exception {
+ DomainSelectionResolver dsResolver = Mockito.mock(DomainSelectionResolver.class);
+ doReturn(true).when(dsResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(dsResolver);
+
+ mPhoneUT.handleMessage(mPhoneUT.obtainMessage(
+ GsmCdmaPhone.EVENT_EMERGENCY_CALLBACK_MODE_ENTER));
+
+ boolean isTestingEmergencyCallbackMode = true;
+ replaceInstance(GsmCdmaPhone.class, "mIsTestingEmergencyCallbackMode", mPhoneUT,
+ isTestingEmergencyCallbackMode);
+ mPhoneUT.exitEmergencyCallbackMode();
+ processAllMessages();
+
+ verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testEcbmOnModemResetForNonGsmPhone() throws Exception {
+ switchToCdma();
+ assertFalse(mPhoneUT.isInEcm());
+
+ mPhoneUT.handleMessage(mPhoneUT.obtainMessage(
+ GsmCdmaPhone.EVENT_EMERGENCY_CALLBACK_MODE_ENTER));
+
+ assertTrue(mPhoneUT.isInEcm());
+
+ Message m = mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_MODEM_RESET);
+ AsyncResult.forMessage(m);
+ mPhoneUT.handleMessage(m);
+
+ assertFalse(mPhoneUT.isInEcm());
+ verifyEcbmIntentWasSent(2 /*times*/, false /*inEcm*/);
+ }
+
+ @Test
+ @SmallTest
+ public void testEcbmOnModemResetWhenDomainSelectionEnabled() throws Exception {
+ DomainSelectionResolver dsResolver = Mockito.mock(DomainSelectionResolver.class);
+ doReturn(true).when(dsResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(dsResolver);
+
+ EmergencyStateTracker est = Mockito.mock(EmergencyStateTracker.class);
+ doReturn(true).when(est).isInEcm();
+ replaceInstance(EmergencyStateTracker.class, "INSTANCE", null, est);
+
+ GsmCdmaPhone spyPhone = spy(mPhoneUT);
+ doReturn(true).when(spyPhone).isInEcm();
+ mPhoneUT.handleMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_MODEM_RESET));
+
+ verify(est).exitEmergencyCallbackMode();
+ }
+
+ @Test
+ public void testGetUserHandle() {
+ UserHandle userHandle = new UserHandle(123);
+ doReturn(userHandle).when(mSubscriptionManager).getSubscriptionUserHandle(anyInt());
+ assertEquals(userHandle, mPhoneUT.getUserHandle());
+
+ doReturn(null).when(mSubscriptionManager).getSubscriptionUserHandle(anyInt());
+ assertNull(mPhoneUT.getUserHandle());
+
+ doThrow(IllegalArgumentException.class).when(mSubscriptionManager)
+ .getSubscriptionUserHandle(anyInt());
+ assertNull(mPhoneUT.getUserHandle());
+ }
+
+ @Test
+ public void testResetNetworkSelectionModeOnSimSwap() {
+ // Set current network selection manual mode.
+ mSimulatedCommands.setNetworkSelectionModeManual("123", 0, null);
+ clearInvocations(mSimulatedCommandsVerifier);
+
+ // SIM loaded.
+ Intent simLoadedIntent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ simLoadedIntent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
+ simLoadedIntent.putExtra(TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_LOADED);
+ mContext.sendBroadcast(simLoadedIntent);
+
+ processAllFutureMessages();
+ // Verify set network selection mode to be AUTO
+ verify(mSimulatedCommandsVerifier).getNetworkSelectionMode(any(Message.class));
+ verify(mSimulatedCommandsVerifier).setNetworkSelectionModeAutomatic(any(Message.class));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
index 67d6e5c..4012e98 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
@@ -18,6 +18,15 @@
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.os.AsyncResult;
import android.os.Message;
import android.testing.AndroidTestingRunner;
@@ -25,13 +34,7 @@
import androidx.test.filters.SmallTest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import org.junit.After;
import org.junit.Before;
@@ -50,6 +53,8 @@
// Mocked classes
private SmsPermissions mMockSmsPermissions;
+ protected EmergencyNumberTracker mEmergencyNumberTracker2;
+ protected IccSmsInterfaceManager.PhoneFactoryProxy mPhoneFactoryProxy;
@Before
public void setUp() throws Exception {
@@ -57,6 +62,12 @@
mMockSmsPermissions = mock(SmsPermissions.class);
mIccSmsInterfaceManager = new IccSmsInterfaceManager(mPhone, mContext, mAppOpsManager,
mSmsDispatchersController, mMockSmsPermissions);
+
+ mPhoneFactoryProxy = mock(IccSmsInterfaceManager.PhoneFactoryProxy.class);
+ mIccSmsInterfaceManager.setPhoneFactoryProxy(mPhoneFactoryProxy);
+
+ mEmergencyNumberTracker2 = mock(EmergencyNumberTracker.class);
+ doReturn(mEmergencyNumberTracker2).when(mPhone2).getEmergencyNumberTracker();
}
@After
@@ -144,4 +155,22 @@
fail("getSmscLatch.await interrupted");
}
}
+
+ @Test
+ public void testNotifyIfOutgoingEmergencySmsWithDualSim() {
+ //Replicate Dual-SIM:
+ Phone [] phones = {mPhone, mPhone2};
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(phones);
+ doReturn(1).when(mPhone).getPhoneId();
+ doReturn(2).when(mPhone2).getPhoneId();
+
+ //Replicate behavior when a number is an emergency number
+ // on the secondary SIM but not on the default SIM:
+ when(mPhone.getEmergencyNumberTracker().getEmergencyNumber(any())).thenReturn(null);
+ when(mEmergencyNumberTracker2.getEmergencyNumber(any()))
+ .thenReturn(getTestEmergencyNumber());
+
+ mIccSmsInterfaceManager.notifyIfOutgoingEmergencySms("1234");
+ verify(mEmergencyNumberTracker2).getEmergencyNumber("1234");
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index 6251560..1c44772 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -27,11 +27,15 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
import android.telephony.SmsMessage;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -63,7 +67,9 @@
private FeatureConnector.Listener<ImsManager> mImsManagerListener;
private HashMap<String, Object> mTrackerData;
private ImsSmsDispatcher mImsSmsDispatcher;
+ PersistableBundle mBundle = new PersistableBundle();
private static final int SUB_0 = 0;
+ private static final String TAG = "ImsSmsDispatcherTest";
@Before
public void setUp() throws Exception {
@@ -86,6 +92,7 @@
when(mSmsDispatchersController.isIms()).thenReturn(true);
mTrackerData = new HashMap<>(1);
when(mSmsTracker.getData()).thenReturn(mTrackerData);
+ verify(mSmsDispatchersController).setImsManager(mImsManager);
}
@After
@@ -96,6 +103,85 @@
super.tearDown();
}
+ /**
+ * Send Memory Availability Notification and verify that the token is correct.
+ */
+ @Test
+ @SmallTest
+ public void testOnMemoryAvailable() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ //Send SMMA
+ mImsSmsDispatcher.onMemoryAvailable();
+ assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+ verify(mImsManager).onMemoryAvailable(eq(token + 1));
+ }
+
+ /**
+ * Receive SEND_STATUS_ERROR_RETRY with onMemoryAvailableResult Api and check if
+ * sending SMMA Notification is retried once
+ */
+ @Test
+ @SmallTest
+ public void testOnMemoryAvailableResultErrorRetry() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ //Send SMMA
+ mImsSmsDispatcher.onMemoryAvailable();
+ assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+ verify(mImsManager).onMemoryAvailable(eq(token + 1));
+ // Retry over IMS
+ mImsSmsDispatcher.getSmsListener().onMemoryAvailableResult(token + 1,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, SmsResponse.NO_ERROR_CODE);
+ waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+ processAllMessages();
+ verify(mImsManager).onMemoryAvailable(eq(token + 2));
+ //2nd Failure should not retry
+ mImsSmsDispatcher.getSmsListener().onMemoryAvailableResult(token + 2,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, SmsResponse.NO_ERROR_CODE);
+ waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+ processAllMessages();
+ verify(mImsManager, times(0)).onMemoryAvailable(eq(token + 3));
+
+ }
+ /**
+ * Receive SEND_STATUS_OK with onMemoryAvailableResult Api and check if
+ * sending SMMA Notification behaviour is correct
+ */
+ @Test
+ @SmallTest
+ public void testOnMemoryAvailableResultSuccess() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ //Send SMMA
+ mImsSmsDispatcher.onMemoryAvailable();
+ assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+ verify(mImsManager).onMemoryAvailable(eq(token + 1));
+ // Retry over IMS
+ mImsSmsDispatcher.getSmsListener().onMemoryAvailableResult(token + 1,
+ ImsSmsImplBase.SEND_STATUS_OK, SmsResponse.NO_ERROR_CODE);
+ waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+ processAllMessages();
+ verify(mImsManager, times(0)).onMemoryAvailable(eq(token + 2));
+
+ }
+ /**
+ * Receive SEND_STATUS_ERROR with onMemoryAvailableResult Api and check if
+ * sending SMMA Notification behaviour is correct
+ */
+ @Test
+ @SmallTest
+ public void testOnMemoryAvailableResultError() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ //Send SMMA
+ mImsSmsDispatcher.onMemoryAvailable();
+ assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+ verify(mImsManager).onMemoryAvailable(eq(token + 1));
+ // Retry over IMS
+ mImsSmsDispatcher.getSmsListener().onMemoryAvailableResult(token + 1,
+ ImsSmsImplBase.SEND_STATUS_ERROR, SmsResponse.NO_ERROR_CODE);
+ waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+ processAllMessages();
+ verify(mImsManager, times(0)).onMemoryAvailable(eq(token + 2));
+
+ }
/**
* Send an SMS and verify that the token and PDU is correct.
*/
@@ -154,6 +240,13 @@
@SmallTest
public void testErrorImsRetry() throws Exception {
int token = mImsSmsDispatcher.mNextToken.get();
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT,
+ 2000);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT, 3);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_COUNT_INT, 3);
mTrackerData.put("pdu", com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null,
"+15555551212", "Test", false).encodedMessage);
when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
@@ -225,4 +318,161 @@
ImsSmsImplBase.SEND_STATUS_ERROR, 0, 41);
verify(mSmsTracker).onFailed(any(Context.class), anyInt(), eq(41));
}
+
+ @Test
+ public void testSendSmswithMessageRef() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ int messageRef = mImsSmsDispatcher.nextMessageRef();
+ if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += 1;
+ }
+
+ when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+ mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+ null, null, null, null, false,
+ -1, false, -1, false, 0);
+ verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(false), (byte[]) any());
+ }
+
+ @Test
+ public void testFallbackGsmRetrywithMessageRef() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ int messageRef = mImsSmsDispatcher.nextMessageRef();
+ if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += 1;
+ }
+
+ when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+ mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+ null, null, null, null, false,
+ -1, false, -1, false, 0);
+ verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(false), (byte[]) any());
+
+ mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 1, messageRef,
+ ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, 0, SmsResponse.NO_ERROR_CODE);
+ processAllMessages();
+
+ ArgumentCaptor<SMSDispatcher.SmsTracker> captor =
+ ArgumentCaptor.forClass(SMSDispatcher.SmsTracker.class);
+ verify(mSmsDispatchersController).sendRetrySms(captor.capture());
+ if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ assertTrue(messageRef + 1 == captor.getValue().mMessageRef);
+ } else {
+ assertTrue(messageRef == captor.getValue().mMessageRef);
+ }
+ }
+
+ @Test
+ public void testErrorImsRetrywithMessageRef() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ int messageRef = mImsSmsDispatcher.nextMessageRef();
+ if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += 1;
+ }
+
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT,
+ 2000);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT, 3);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_COUNT_INT, 3);
+ when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+ mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+ null, null, null, null, false,
+ -1, false, -1, false, 0);
+ verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(false), (byte[]) any());
+
+ // Retry over IMS
+ mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 1, messageRef,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, 0, SmsResponse.NO_ERROR_CODE);
+ waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+ processAllMessages();
+
+ // Make sure tpmr value is same and retry bit set
+ ArgumentCaptor<byte[]> byteCaptor = ArgumentCaptor.forClass(byte[].class);
+ verify(mImsManager).sendSms(eq(token + 2), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(true), byteCaptor.capture());
+ byte[] pdu = byteCaptor.getValue();
+ assertEquals(messageRef, pdu[1]);
+ assertEquals(0x04, (pdu[0] & 0x04));
+ }
+
+ @Test
+ public void testErrorImsRetrywithRetryConfig() throws Exception {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ int messageRef = mImsSmsDispatcher.nextMessageRef();
+ if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += 1;
+ }
+
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT,
+ 3000);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT, 2);
+ mContextFixture.getCarrierConfigBundle().putInt(CarrierConfigManager.ImsSms
+ .KEY_SMS_MAX_RETRY_COUNT_INT, 3);
+ when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+ mImsSmsDispatcher.sendText("+15555551212", null, "Retry test",
+ null, null, null, null, false,
+ -1, false, -1, false, 0);
+ verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(false), (byte[]) any());
+ assertEquals(2, mImsSmsDispatcher.getMaxRetryCountOverIms());
+ assertEquals(3, mImsSmsDispatcher.getMaxSmsRetryCount());
+ assertEquals(3000, mImsSmsDispatcher.getSmsRetryDelayValue());
+ // Retry over IMS
+ mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 1, messageRef,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, 0, SmsResponse.NO_ERROR_CODE);
+ waitForMs(mImsSmsDispatcher.getSmsRetryDelayValue() + 200);
+ processAllMessages();
+
+ // Make sure tpmr value is same and retry bit set
+ ArgumentCaptor<byte[]> byteCaptor = ArgumentCaptor.forClass(byte[].class);
+ verify(mImsManager).sendSms(eq(token + 2), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(true), byteCaptor.capture());
+ byte[] pdu = byteCaptor.getValue();
+ assertEquals(messageRef, pdu[1]);
+ assertEquals(0x04, (pdu[0] & 0x04));
+ // Retry over IMS for the second time
+ mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 2, messageRef,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, 0, SmsResponse.NO_ERROR_CODE);
+ waitForMs(mImsSmsDispatcher.getSmsRetryDelayValue() + 200);
+ processAllMessages();
+ // Make sure tpmr value is same and retry bit set
+ ArgumentCaptor<byte[]> byteCaptor2 = ArgumentCaptor.forClass(byte[].class);
+ verify(mImsManager).sendSms(eq(token + 3), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+ nullable(String.class), eq(true), byteCaptor2.capture());
+ byte[] pdu2 = byteCaptor2.getValue();
+ assertEquals(messageRef, pdu2[1]);
+ assertEquals(0x04, (pdu2[0] & 0x04));
+
+ mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 3, messageRef,
+ ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, 0, SmsResponse.NO_ERROR_CODE);
+ waitForMs(mImsSmsDispatcher.getSmsRetryDelayValue() + 200);
+ processAllMessages();
+ // Make sure tpmr value is same and retry bit set
+ ArgumentCaptor<SMSDispatcher.SmsTracker> captor =
+ ArgumentCaptor.forClass(SMSDispatcher.SmsTracker.class);
+ // Ensure GsmSmsDispatcher calls sendSms
+ verify(mSmsDispatchersController).sendRetrySms(captor.capture());
+
+ assertNotNull(captor.getValue());
+ assertTrue(captor.getValue().mRetryCount > 0);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
index b11b94e..d251cf5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
@@ -24,8 +24,6 @@
import android.database.MatrixCursor;
import android.test.suitebuilder.annotation.SmallTest;
-import androidx.test.InstrumentationRegistry;
-
import com.android.internal.util.HexDump;
import org.junit.After;
@@ -34,7 +32,7 @@
import java.util.Arrays;
-public class InboundSmsTrackerTest {
+public class InboundSmsTrackerTest extends TelephonyTest {
InboundSmsTracker mInboundSmsTracker;
private static final byte[] FAKE_PDU = new byte[]{1, 2, 3};
@@ -50,7 +48,8 @@
@Before
public void setUp() throws Exception {
- mInboundSmsTracker = new InboundSmsTracker(InstrumentationRegistry.getContext(),
+ super.setUp(getClass().getSimpleName());
+ mInboundSmsTracker = new InboundSmsTracker(mContext,
FAKE_PDU, FAKE_TIMESTAMP, FAKE_DEST_PORT, false,
FAKE_ADDRESS, FAKE_DISPLAY_ADDRESS, FAKE_REFERENCE_NUMBER, FAKE_SEQUENCE_NUMBER,
FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY, false /* isClass0 */, FAKE_SUBID,
@@ -58,8 +57,9 @@
}
@After
- public void tearDown() {
+ public void tearDown() throws Exception {
mInboundSmsTracker = null;
+ super.tearDown();
}
public static MatrixCursor createFakeCursor() {
@@ -108,8 +108,7 @@
@SmallTest
public void testInitializationFromDb() {
Cursor cursor = createFakeCursor();
- mInboundSmsTracker = new InboundSmsTracker(InstrumentationRegistry.getContext(),
- cursor, false);
+ mInboundSmsTracker = new InboundSmsTracker(mContext, cursor, false);
cursor.close();
testInitialization();
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index bbb4639..f4c19d9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -33,6 +34,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -55,7 +57,8 @@
import androidx.test.InstrumentationRegistry;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
+import com.android.internal.telephony.data.DataSettingsManager;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Assert;
@@ -75,92 +78,183 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class MultiSimSettingControllerTest extends TelephonyTest {
- private static final int SINGLE_SIM = 1;
private static final int DUAL_SIM = 2;
+ private static final String PHONE_PACKAGE = "com.android.internal.telephony";
private MultiSimSettingController mMultiSimSettingControllerUT;
private Phone[] mPhones;
private ParcelUuid mGroupUuid1 = new ParcelUuid(UUID.randomUUID());
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
// Mocked classes
- private SubscriptionController mSubControllerMock;
private Phone mPhoneMock1;
private Phone mPhoneMock2;
- private DataEnabledSettings mDataEnabledSettingsMock1;
- private DataEnabledSettings mDataEnabledSettingsMock2;
+ private DataSettingsManager mDataSettingsManagerMock1;
+ private DataSettingsManager mDataSettingsManagerMock2;
private CommandsInterface mMockCi;
+ private final SubscriptionInfoInternal[] mSubInfo = new SubscriptionInfoInternal[10];
- private final SubscriptionInfo mSubInfo1 = new SubscriptionInfo(1, "subInfo1 IccId", 0,
- "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
- "156", false, null, null);
+ private void initializeSubs() {
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setIccId("subInfo1 IccId")
+ .setSimSlotIndex(0)
+ .setDisplayName("T-mobile")
+ .setCarrierName("T-mobile")
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+ .setIconTint(255)
+ .setNumber("12345")
+ .setMcc("310")
+ .setMnc("260")
+ .setCountryIso("us")
+ .build();
- private final SubscriptionInfo mSubInfo2 = new SubscriptionInfo(2, "subInfo2 IccId", 1,
- "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
- "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
- TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
+ mSubInfo[2] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setId(2)
+ .setIccId("subInfo2 IccId")
+ .setSimSlotIndex(1)
+ .setGroupUuid(mGroupUuid1.toString())
+ .build();
- private final SubscriptionInfo mSubInfo3 = new SubscriptionInfo(3, "subInfo3 IccId", -1,
- "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
- "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
- TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
+ mSubInfo[3] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setId(3)
+ .setIccId("subInfo3 IccId")
+ .setSimSlotIndex(-1)
+ .setGroupUuid(mGroupUuid1.toString())
+ .build();
- private final SubscriptionInfo mSubInfo4 = new SubscriptionInfo(4, "subInfo4 IccId", -1,
- "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
- "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
- TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
+ mSubInfo[4] = new SubscriptionInfoInternal.Builder(
+ mSubInfo[1])
+ .setId(4)
+ .setIccId("subInfo4 IccId")
+ .setSimSlotIndex(-1)
+ .setGroupUuid(mGroupUuid1.toString())
+ .build();
+ }
+
+ private void setSimSlotIndex(int subId, int simSlotIndex) {
+ mSubInfo[subId] = new SubscriptionInfoInternal.Builder(mSubInfo[subId])
+ .setSimSlotIndex(simSlotIndex).build();
+ }
+
+ private void sendCarrierConfigChanged(int phoneId, int subId) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+ }
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mSubControllerMock = mock(SubscriptionController.class);
+ initializeSubs();
mPhoneMock1 = mock(Phone.class);
mPhoneMock2 = mock(Phone.class);
- mDataEnabledSettingsMock1 = mock(DataEnabledSettings.class);
- mDataEnabledSettingsMock2 = mock(DataEnabledSettings.class);
+ mDataSettingsManagerMock1 = mock(DataSettingsManager.class);
+ mDataSettingsManagerMock2 = mock(DataSettingsManager.class);
mMockCi = mock(CommandsInterface.class);
+
+ doReturn(mSubscriptionManagerService).when(mIBinder).queryLocalInterface(anyString());
+ doReturn(mPhone).when(mPhone).getImsPhone();
+ mServiceManagerMockedServices.put("isub", mIBinder);
+
+ doReturn(mSubscriptionManagerService).when(mIBinder)
+ .queryLocalInterface(anyString());
+
// Default configuration:
// DSDS device.
// Sub 1 is the default sub.
// Sub 1 is in slot 0; sub 2 is in slot 1.
doReturn(DUAL_SIM).when(mTelephonyManager).getPhoneCount();
doReturn(DUAL_SIM).when(mTelephonyManager).getActiveModemCount();
- doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
- doReturn(1).when(mSubControllerMock).getDefaultVoiceSubId();
- doReturn(1).when(mSubControllerMock).getDefaultSmsSubId();
- doReturn(true).when(mSubControllerMock).isActiveSubId(1);
- doReturn(true).when(mSubControllerMock).isActiveSubId(2);
- doReturn(0).when(mSubControllerMock).getPhoneId(1);
- doReturn(1).when(mSubControllerMock).getPhoneId(2);
- doReturn(true).when(mSubControllerMock).isOpportunistic(5);
- doReturn(1).when(mPhoneMock1).getSubId();
- doReturn(2).when(mPhoneMock2).getSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultDataSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultVoiceSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultSmsSubId();
mPhoneMock1.mCi = mSimulatedCommands;
mPhoneMock2.mCi = mSimulatedCommands;
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo2);
- doReturn(infoList).when(mSubControllerMock)
- .getActiveSubscriptionInfoList(anyString(), nullable(String.class));
- doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
mPhones = new Phone[] {mPhoneMock1, mPhoneMock2};
- doReturn(mDataEnabledSettingsMock1).when(mPhoneMock1).getDataEnabledSettings();
- doReturn(mDataEnabledSettingsMock2).when(mPhoneMock2).getDataEnabledSettings();
+ doReturn(mDataSettingsManagerMock1).when(mPhoneMock1).getDataSettingsManager();
+ doReturn(mDataSettingsManagerMock2).when(mPhoneMock2).getDataSettingsManager();
- doReturn(Arrays.asList(mSubInfo1)).when(mSubControllerMock).getSubInfo(
- eq(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1), any());
- doReturn(Arrays.asList(mSubInfo2)).when(mSubControllerMock).getSubInfo(
- eq(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2), any());
- doReturn(Arrays.asList(mSubInfo3)).when(mSubControllerMock).getSubInfo(
- eq(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 3), any());
- doReturn(Arrays.asList(mSubInfo4)).when(mSubControllerMock).getSubInfo(
- eq(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 4), any());
+ doAnswer(invocation -> {
+ final int subId = (int) invocation.getArguments()[0];
+ if (subId < 0 || subId >= mSubInfo.length) return null;
+ return mSubInfo[subId].toSubscriptionInfo();
+ }).when(mSubscriptionManagerService).getSubscriptionInfo(anyInt());
+
+ doAnswer(invocation -> {
+ final int subId = (int) invocation.getArguments()[0];
+ if (subId < 0 || subId >= mSubInfo.length) return null;
+ return mSubInfo[subId];
+ }).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+ doAnswer(invocation -> {
+ List<SubscriptionInfo> subscriptionInfoList = new ArrayList<>();
+ for (int i = 1; i < mSubInfo.length; i++) {
+ if (mSubInfo[i] != null && mSubInfo[i].isActive()) {
+ subscriptionInfoList.add(mSubInfo[i].toSubscriptionInfo());
+ }
+ }
+ return subscriptionInfoList;
+ }).when(mSubscriptionManagerService).getActiveSubscriptionInfoList(
+ anyString(), nullable(String.class));
+
+ doAnswer(invocation -> {
+ final boolean visibleOnly = (boolean) invocation.getArguments()[0];
+ List<Integer> subIdList = new ArrayList<>();
+ for (int i = 1; i < mSubInfo.length; i++) {
+ if (mSubInfo[i] != null && mSubInfo[i].isActive()
+ && (!visibleOnly || mSubInfo[i].isVisible())) {
+ subIdList.add(i);
+ }
+ }
+ return subIdList.stream().mapToInt(i -> i).toArray();
+ }).when(mSubscriptionManagerService).getActiveSubIdList(anyBoolean());
+
+ doAnswer(invocation -> {
+ final String uuid = (String) invocation.getArguments()[1];
+ List<SubscriptionInfo> subscriptionInfoList = new ArrayList<>();
+ for (int i = 1; i < mSubInfo.length; i++) {
+ if (mSubInfo[i] != null && mSubInfo[i].getGroupUuid().equals(uuid)) {
+ subscriptionInfoList.add(mSubInfo[i].toSubscriptionInfo());
+ }
+ }
+ return subscriptionInfoList;
+ }).when(mSubscriptionManagerService).getSubscriptionsInGroup(
+ any(), anyString(), nullable(String.class));
+
+ doAnswer(invocation -> {
+ final int subId = (int) invocation.getArguments()[0];
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return SubscriptionManager.INVALID_PHONE_INDEX;
+ }
+ if (mSubInfo[subId] == null) return SubscriptionManager.INVALID_PHONE_INDEX;
+ return mSubInfo[subId].getSimSlotIndex();
+ }).when(mSubscriptionManagerService).getPhoneId(anyInt());
+
+ doAnswer(invocation -> {
+ for (int i = 1; i < mSubInfo.length; i++) {
+ if (mSubInfo[i] != null && mSubInfo[i].getSimSlotIndex() == 0) return i;
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }).when(mPhoneMock1).getSubId();
+
+ doAnswer(invocation -> {
+ for (int i = 1; i < mSubInfo.length; i++) {
+ if (mSubInfo[i] != null && mSubInfo[i].getSimSlotIndex() == 1) return i;
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }).when(mPhoneMock2).getSubId();
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubControllerMock);
- mMultiSimSettingControllerUT = new MultiSimSettingController(mContext, mSubControllerMock);
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+ mMultiSimSettingControllerUT = new MultiSimSettingController(mContext);
processAllMessages();
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
+ assertNotNull(mCarrierConfigChangeListener);
}
@After
@@ -171,57 +265,55 @@
super.tearDown();
}
+ private void markSubscriptionInactive(int subId) {
+ setSimSlotIndex(subId, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ }
+
@Test
@SmallTest
public void testSubInfoChangeBeforeAllSubReady() throws Exception {
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultDataSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultVoiceSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultSmsSubId();
// Mark sub 2 as inactive.
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ markSubscriptionInactive(2);
// Mark subscription ready as false. The below sub info change should be ignored.
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultSmsSubId(anyInt());
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(0, 1);
processAllMessages();
// Sub 1 should be default sub silently.
- verify(mSubControllerMock).setDefaultDataSubId(1);
- verify(mSubControllerMock).setDefaultVoiceSubId(1);
- verify(mSubControllerMock).setDefaultSmsSubId(1);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(1);
+ verify(mSubscriptionManagerService).setDefaultVoiceSubId(1);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(1);
verifyDismissIntentSent();
}
@Test
public void testSubInfoChangeAfterRadioUnavailable() throws Exception {
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
// Ensure all subscription loaded only updates state once
- clearInvocations(mSubControllerMock);
+ clearInvocations(mSubscriptionManagerService);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultSmsSubId(anyInt());
// Notify radio unavailable.
replaceInstance(BaseCommands.class, "mState", mSimulatedCommands,
@@ -230,81 +322,62 @@
MultiSimSettingController.EVENT_RADIO_STATE_CHANGED).sendToTarget();
// Mark all subs as inactive.
- doReturn(false).when(mSubControllerMock).isActiveSubId(1);
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock1).getSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = new ArrayList<>();
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
- clearInvocations(mSubControllerMock);
+ markSubscriptionInactive(1);
+ markSubscriptionInactive(2);
+ clearInvocations(mSubscriptionManagerService);
// The below sub info change should be ignored.
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
- verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultSmsSubId(anyInt());
// Send all sub ready notification
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
processAllMessages();
// Everything should be set to invalid since nothing is active.
- verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- verify(mSubControllerMock)
+ verify(mSubscriptionManagerService).setDefaultDataSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubscriptionManagerService)
.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
@Test
@SmallTest
public void testSingleActiveDsds() throws Exception {
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultDataSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultVoiceSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubscriptionManagerService)
.getDefaultSmsSubId();
// Mark sub 2 as inactive.
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ markSubscriptionInactive(2);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(0, 1);
processAllMessages();
verifyDismissIntentSent();
clearInvocations(mContext);
// Sub 1 should be default sub silently.
// Sub 1 switches to sub 2 in the same slot.
- doReturn(false).when(mSubControllerMock).isActiveSubId(1);
- doReturn(true).when(mSubControllerMock).isActiveSubId(2);
- doReturn(0).when(mSubControllerMock).getPhoneId(2);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
- doReturn(2).when(mPhoneMock1).getSubId();
- infoList = Arrays.asList(mSubInfo2);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ markSubscriptionInactive(1);
+ setSimSlotIndex(2, 0);
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 2);
+ sendCarrierConfigChanged(0, 2);
processAllMessages();
// Sub 1 should be default sub silently.
- verify(mSubControllerMock).setDefaultDataSubId(2);
- verify(mSubControllerMock).setDefaultVoiceSubId(2);
- verify(mSubControllerMock).setDefaultSmsSubId(2);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(2);
+ verify(mSubscriptionManagerService).setDefaultVoiceSubId(2);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(2);
verifyDismissIntentSent();
}
@@ -312,37 +385,26 @@
@SmallTest
public void testActivatingSecondSub() throws Exception {
// Mark sub 2 as inactive.
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ markSubscriptionInactive(2);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(0, 1);
processAllMessages();
// Sub 1 should be default sub silently.
- verify(mSubControllerMock).setDefaultDataSubId(1);
- verify(mSubControllerMock).setDefaultVoiceSubId(1);
- verify(mSubControllerMock).setDefaultSmsSubId(1);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(1);
+ verify(mSubscriptionManagerService).setDefaultVoiceSubId(1);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(1);
verifyDismissIntentSent();
// Mark sub 2 as active in phone[1].
- clearInvocations(mSubControllerMock);
+ setSimSlotIndex(2, 1);
+ clearInvocations(mSubscriptionManagerService);
clearInvocations(mContext);
- doReturn(true).when(mSubControllerMock).isActiveSubId(2);
- doReturn(1).when(mSubControllerMock).getPhoneId(2);
- doReturn(2).when(mPhoneMock2).getSubId();
- infoList = Arrays.asList(mSubInfo1, mSubInfo2);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ mSubInfo[2] = new SubscriptionInfoInternal.Builder().setId(2).setSimSlotIndex(1).build();
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
// Intent should be broadcast to ask default data selection.
@@ -354,18 +416,11 @@
clearInvocations(mContext);
// Switch from sub 2 to sub 3 in phone[1]. This should again trigger default data selection
// dialog.
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(true).when(mSubControllerMock).isActiveSubId(3);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(1).when(mSubControllerMock).getPhoneId(3);
- doReturn(3).when(mPhoneMock2).getSubId();
- infoList = Arrays.asList(mSubInfo1, mSubInfo3);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ markSubscriptionInactive(2);
+ setSimSlotIndex(3, 1);
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3);
+ sendCarrierConfigChanged(1, 3);
processAllMessages();
// Intent should be broadcast to ask default data selection.
@@ -377,47 +432,46 @@
@Test
@SmallTest
- public void testSimpleDsds() {
+ public void testSimpleDsds() throws Exception {
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
// After initialization, sub 2 should have mobile data off.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
// Enable on non-default sub should trigger setDefaultDataSubId.
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
processAllMessages();
- verify(mSubControllerMock).setDefaultDataSubId(2);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(2);
// Changing default data to sub 2 should trigger disabling data on sub 1.
- doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
processAllMessages();
- verify(mDataEnabledSettingsMock1).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock1).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
- doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
- doReturn(1).when(mSubControllerMock).getDefaultSmsSubId();
- doReturn(2).when(mSubControllerMock).getDefaultVoiceSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultDataSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultSmsSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultVoiceSubId();
// Taking out SIM 1.
- clearInvocations(mSubControllerMock);
- doReturn(false).when(mSubControllerMock).isActiveSubId(1);
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo2);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ clearInvocations(mSubscriptionManagerService);
+ markSubscriptionInactive(1);
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(
- 1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
- verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
+ sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ processAllMessages();
+
+ verify(mSubscriptionManagerService).setDefaultDataSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
// Verify intent sent to select sub 2 as default for all types.
Intent intent = captureBroadcastIntent();
@@ -429,21 +483,21 @@
@Test
@SmallTest
- public void testSimpleDsdsFirstBoot() {
+ public void testSimpleDsdsFirstBoot() throws Exception {
// at first boot default is not set
- doReturn(-1).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(-1).when(mSubscriptionManagerService).getDefaultDataSubId();
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
// After initialization, sub 2 should have mobile data off.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock1).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock1).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
// as a result of the above calls, update new values to be returned
doReturn(false).when(mPhoneMock1).isUserDataEnabled();
@@ -455,18 +509,21 @@
intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1));
// Setting default data should not trigger any more setDataEnabled().
- doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
processAllMessages();
- verify(mDataEnabledSettingsMock1, times(1)).setDataEnabled(anyInt(), anyBoolean());
- verify(mDataEnabledSettingsMock2, times(1)).setDataEnabled(anyInt(), anyBoolean());
+ verify(mDataSettingsManagerMock1, times(1))
+ .setDataEnabled(anyInt(), anyBoolean(), anyString());
+ verify(mDataSettingsManagerMock2, times(1))
+ .setDataEnabled(anyInt(), anyBoolean(), anyString());
}
@Test
@SmallTest
- public void testSimpleDsdsInSuW() {
+ public void testSimpleDsdsInSuW() throws Exception {
// at first boot default is not set
- doReturn(-1).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(-1).when(mSubscriptionManagerService).getDefaultDataSubId();
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
@@ -475,13 +532,13 @@
Settings.Global.DEVICE_PROVISIONED, 0);
// After initialization, sub 2 should have mobile data off.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock1).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock1).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
// as a result of the above calls, update new values to be returned
doReturn(false).when(mPhoneMock1).isUserDataEnabled();
@@ -493,22 +550,20 @@
@Test
@SmallTest
- public void testDsdsGrouping() {
- doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+ public void testDsdsGrouping() throws Exception {
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
doReturn(false).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 2, true);
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 2, false);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
// Create subscription grouping.
- doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(2);
- doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(3);
- doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(4);
- doReturn(Arrays.asList(mSubInfo2, mSubInfo3, mSubInfo4)).when(mSubControllerMock)
+ doReturn(Arrays.asList(mSubInfo[2].toSubscriptionInfo(), mSubInfo[3].toSubscriptionInfo(),
+ mSubInfo[4].toSubscriptionInfo())).when(mSubscriptionManagerService)
.getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
processAllMessages();
@@ -521,16 +576,16 @@
mContext, Settings.Global.DATA_ROAMING, 3, true));
assertFalse(GlobalSettingsHelper.getBoolean(
mContext, Settings.Global.DATA_ROAMING, 4, true));
- verify(mSubControllerMock).setDataRoaming(/*enable*/0, /*subId*/2);
+ verify(mSubscriptionManagerService).setDataRoaming(/*enable*/0, /*subId*/2);
// No user selection needed, no intent should be sent.
verify(mContext, never()).sendBroadcast(any());
// Making sub 1 default data sub should result in disabling data on sub 2, 3, 4.
- doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
processAllMessages();
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, false);
processAllMessages();
assertFalse(GlobalSettingsHelper.getBoolean(
@@ -542,22 +597,20 @@
// Switch within group (from sub 2 to sub 3).
// Default data and default sms should become subscription 3.
- clearInvocations(mSubControllerMock);
- doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
- doReturn(2).when(mSubControllerMock).getDefaultSmsSubId();
- doReturn(1).when(mSubControllerMock).getDefaultVoiceSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ clearInvocations(mSubscriptionManagerService);
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultSmsSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultVoiceSubId();
+ setSimSlotIndex(3, 1);
+ markSubscriptionInactive(2);
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3);
+ sendCarrierConfigChanged(1, 3);
processAllMessages();
- verify(mSubControllerMock).setDefaultDataSubId(3);
- verify(mSubControllerMock).setDefaultSmsSubId(3);
- verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
+ verify(mSubscriptionManagerService).setDefaultDataSubId(3);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(3);
+ verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
// No user selection needed, no intent should be sent.
verify(mContext, never()).sendBroadcast(any());
}
@@ -565,8 +618,8 @@
@Test
@SmallTest
public void testCbrs() throws Exception {
- replaceInstance(SubscriptionInfo.class, "mIsOpportunistic", mSubInfo1, true);
- doReturn(true).when(mSubControllerMock).isOpportunistic(1);
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder(mSubInfo[1]).setOpportunistic(1).build();
+
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 2, false);
@@ -574,31 +627,31 @@
// Notify subscriptions ready. Sub 2 should become the default. But shouldn't turn off
// data of oppt sub 1.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mSubControllerMock).setDefaultDataSubId(2);
- verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
- anyInt(), anyBoolean());
+ verify(mSubscriptionManagerService).setDefaultDataSubId(2);
+ verify(mDataSettingsManagerMock1, never()).setDataEnabled(
+ anyInt(), anyBoolean(), anyString());
verifyDismissIntentSent();
- clearInvocations(mSubControllerMock);
- clearInvocations(mDataEnabledSettingsMock1);
- clearInvocations(mDataEnabledSettingsMock2);
- doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+ clearInvocations(mSubscriptionManagerService);
+ clearInvocations(mDataSettingsManagerMock1);
+ clearInvocations(mDataSettingsManagerMock2);
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
// Toggle data on sub 1 or sub 2. Nothing should happen as they are independent.
mMultiSimSettingControllerUT.notifyUserDataEnabled(1, false);
mMultiSimSettingControllerUT.notifyUserDataEnabled(1, true);
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, false);
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
- eq(TelephonyManager.DATA_ENABLED_REASON_USER), anyBoolean());
- verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ verify(mDataSettingsManagerMock1, never()).setDataEnabled(
+ eq(TelephonyManager.DATA_ENABLED_REASON_USER), anyBoolean(), anyString());
+ verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+ eq(TelephonyManager.DATA_ENABLED_REASON_USER), eq(false), anyString());
}
private void verifyDismissIntentSent() {
@@ -612,9 +665,8 @@
@SmallTest
public void testGroupedCbrs() throws Exception {
// Mark sub 1 as opportunistic.
- replaceInstance(SubscriptionInfo.class, "mIsOpportunistic", mSubInfo1, true);
- replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
- doReturn(true).when(mSubControllerMock).isOpportunistic(1);
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setOpportunistic(1).setGroupUuid(mGroupUuid1.toString()).build();
// Make opportunistic sub 1 and sub 2 data enabled.
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
@@ -622,21 +674,23 @@
// Notify subscriptions ready. Sub 2 should become the default, as sub 1 is opportunistic.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mSubControllerMock).setDefaultDataSubId(2);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(2);
// Mark sub 2 as data off.
doReturn(false).when(mPhoneMock2).isUserDataEnabled();
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 2, false);
// Group sub 1 with sub 2.
- doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
+ doReturn(Arrays.asList(mSubInfo[1].toSubscriptionInfo(), mSubInfo[2].toSubscriptionInfo()))
+ .when(mSubscriptionManagerService)
.getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
processAllMessages();
// This should result in setting sync.
- verify(mDataEnabledSettingsMock1).setUserDataEnabled(false, false);
+ verify(mDataSettingsManagerMock1).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+ false, PHONE_PACKAGE);
assertFalse(GlobalSettingsHelper.getBoolean(
mContext, Settings.Global.DATA_ROAMING, 1, true));
@@ -645,7 +699,8 @@
// Turning data on on sub 2. Sub 1 should also be turned on.
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
processAllMessages();
- verify(mDataEnabledSettingsMock1).setUserDataEnabled(true, false);
+ verify(mDataSettingsManagerMock1).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+ true, PHONE_PACKAGE);
verifyDismissIntentSent();
}
@@ -653,39 +708,29 @@
@SmallTest
public void testGroupedPrimaryRemoved() throws Exception {
// Create subscription grouping of subs 1 and 2.
- replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
- doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(1);
- doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(2);
- doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
- .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setGroupUuid(mGroupUuid1.toString()).build();
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
// Defaults not touched, sub 1 is already default.
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
// Take out SIM 1.
- clearInvocations(mSubControllerMock);
- doReturn(false).when(mSubControllerMock).isActiveSubId(1);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock1).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo2);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ clearInvocations(mSubscriptionManagerService);
+ markSubscriptionInactive(1);
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(
- 0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ sendCarrierConfigChanged(0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
// Sub 2 should be made the default sub silently.
- verify(mSubControllerMock).setDefaultDataSubId(2);
- verify(mSubControllerMock).setDefaultVoiceSubId(2);
- verify(mSubControllerMock).setDefaultSmsSubId(2);
+ verify(mSubscriptionManagerService).setDefaultDataSubId(2);
+ verify(mSubscriptionManagerService).setDefaultVoiceSubId(2);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(2);
verifyDismissIntentSent();
}
@@ -698,33 +743,37 @@
@Test
@SmallTest
public void testGroupedPrimarySubscriptions() throws Exception {
- doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultDataSubId();
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(false).when(mPhoneMock2).isUserDataEnabled();
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, true);
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.DATA_ROAMING, 1, false);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
// Create subscription grouping.
- replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
- doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
- .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setGroupUuid(mGroupUuid1.toString()).build();
+ doReturn(Arrays.asList(mSubInfo[1].toSubscriptionInfo(), mSubInfo[2].toSubscriptionInfo()))
+ .when(mSubscriptionManagerService).getSubscriptionsInGroup(any(), anyString(),
+ nullable(String.class));
mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
processAllMessages();
// This should result in setting sync.
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(true, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+ true, PHONE_PACKAGE);
assertFalse(GlobalSettingsHelper.getBoolean(
mContext, Settings.Global.DATA_ROAMING, 2, true));
- verify(mSubControllerMock).setDataRoaming(/*enable*/0, /*subId*/1);
+ verify(mSubscriptionManagerService).setDataRoaming(/*enable*/0, /*subId*/1);
// Turning off user data on sub 1.
doReturn(false).when(mPhoneMock1).isUserDataEnabled();
mMultiSimSettingControllerUT.notifyUserDataEnabled(1, false);
processAllMessages();
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(false, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+ false, PHONE_PACKAGE);
}
@Test
@@ -732,39 +781,34 @@
public void testCarrierConfigLoading() throws Exception {
doReturn(true).when(mPhoneMock1).isUserDataEnabled();
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
+ mSubInfo[2] = new SubscriptionInfoInternal.Builder(mSubInfo[2]).setGroupUuid("").build();
+ mSubInfo[3] = new SubscriptionInfoInternal.Builder(mSubInfo[3]).setGroupUuid("").build();
// Sub 2 should have mobile data off, but it shouldn't happen until carrier configs are
// loaded on both subscriptions.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
processAllMessages();
- verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+ sendCarrierConfigChanged(0, 1);
processAllMessages();
- verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
// Switch from sub 2 to sub 3 in phone[1].
- clearInvocations(mSubControllerMock);
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(true).when(mSubControllerMock).isActiveSubId(3);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(1).when(mSubControllerMock).getPhoneId(3);
- doReturn(3).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ clearInvocations(mSubscriptionManagerService);
+ markSubscriptionInactive(2);
+ setSimSlotIndex(3, 1);
// Nothing should happen until carrier config change is notified on sub 3.
mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
processAllMessages();
verify(mContext, never()).sendBroadcast(any());
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3);
+ sendCarrierConfigChanged(1, 3);
processAllMessages();
// Intent should be broadcast to ask default data selection.
Intent intent = captureBroadcastIntent();
@@ -778,20 +822,14 @@
// b/146446143
public void testGroupChangeOnInactiveSub_shouldNotMarkAsDefaultDataSub() throws Exception {
// Make sub1 and sub3 as active sub.
- doReturn(false).when(mSubControllerMock).isActiveSubId(2);
- doReturn(true).when(mSubControllerMock).isActiveSubId(3);
- doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
- doReturn(1).when(mSubControllerMock).getPhoneId(3);
- doReturn(3).when(mPhoneMock2).getSubId();
- List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo3);
- doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
- nullable(String.class));
- doReturn(new int[]{1, 3}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
- doReturn(Arrays.asList(mSubInfo2, mSubInfo3, mSubInfo4)).when(mSubControllerMock)
+ markSubscriptionInactive(2);
+ setSimSlotIndex(3, 1);
+ doReturn(Arrays.asList(mSubInfo[2].toSubscriptionInfo(), mSubInfo[3].toSubscriptionInfo(),
+ mSubInfo[4].toSubscriptionInfo())).when(mSubscriptionManagerService)
.getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
// Sub 3 and sub 2's mobile data are enabled, and sub 3 is the default data sub.
- doReturn(3).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(3).when(mSubscriptionManagerService).getDefaultDataSubId();
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, false);
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 2, true);
GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 3, true);
@@ -800,16 +838,17 @@
// Sub 2 should have mobile data off, but it shouldn't happen until carrier configs are
// loaded on both subscriptions.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 3);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 3);
processAllMessages();
// Mark sub3 as oppt and notify grouping
- doReturn(true).when(mSubControllerMock).isOpportunistic(3);
+ mSubInfo[3] = new SubscriptionInfoInternal.Builder(mSubInfo[3]).setOpportunistic(1).build();
+ setSimSlotIndex(3, 0);
mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
processAllMessages();
// Shouldn't mark sub 2 as default data, as sub 2 is in active.
- verify(mSubControllerMock, never()).setDefaultDataSubId(2);
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(2);
}
@Test
@@ -821,27 +860,25 @@
doReturn(true).when(mPhoneMock2).isUserDataEnabled();
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
processAllMessages();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(0, 1);
// Notify carrier config change on phone1 without specifying subId.
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
// Nothing should happen as carrier config is not ready for sub 2.
- verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
// Still notify carrier config without specifying subId2, but this time subController
// and CarrierConfigManager have subId 2 active and ready.
- doReturn(new int[] {2}).when(mSubControllerMock).getSubId(1);
+ doReturn(2).when(mSubscriptionManagerService).getSubId(1);
CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService(
mContext.CARRIER_CONFIG_SERVICE);
doReturn(new PersistableBundle()).when(cm).getConfigForSubId(2);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
// This time user data should be disabled on phone1.
- verify(mDataEnabledSettingsMock2).setDataEnabled(
- TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataSettingsManagerMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
}
@Test
@@ -884,14 +921,14 @@
@Test
@SmallTest
public void testVoiceDataSmsAutoFallback() throws Exception {
- doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
+ doReturn(1).when(mSubscriptionManagerService).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(2, 3);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(2, 2);
processAllMessages();
- verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mSubControllerMock, never()).getActiveSubInfoCountMax();
- doReturn(2).when(mSubControllerMock).getActiveSubInfoCountMax();
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService, never()).getActiveSubInfoCountMax();
+ doReturn(2).when(mSubscriptionManagerService).getActiveSubInfoCountMax();
mPhoneMock1.mCi = mMockCi;
mPhoneMock2.mCi = mMockCi;
doReturn(TelephonyManager.RADIO_POWER_ON).when(mMockCi).getRadioState();
@@ -903,10 +940,39 @@
doReturn(true).when(resources).getBoolean(
com.android.internal.R.bool.config_voice_data_sms_auto_fallback);
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1);
- mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, 1);
+ sendCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mSubControllerMock).getActiveSubInfoCountMax();
- verify(mSubControllerMock).setDefaultDataSubId(anyInt());
+ verify(mSubscriptionManagerService).getActiveSubInfoCountMax();
+ verify(mSubscriptionManagerService).setDefaultDataSubId(anyInt());
+ }
+
+ @Test
+ public void onSubscriptionGroupChanged_hasActiveSubNotPartOfGroup() {
+ // sub1 and sub2 are active subs already
+ // Create a subscription group with only sub2
+ doReturn(Arrays.asList(mSubInfo[2].toSubscriptionInfo())).when(mSubscriptionManagerService)
+ .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
+ mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
+ processAllMessages();
+ // Default data is not modified as sub1 is active sub not part of this groupUuid
+ verify(mSubscriptionManagerService, never()).setDefaultDataSubId(anyInt());
+ }
+
+ @Test
+ public void onSubscriptionGroupChanged_allActiveSubArePartOfGroup() throws Exception {
+ doReturn(3).when(mSubscriptionManagerService).getDefaultDataSubId();
+ // Create subscription grouping of subs 1 and 2.
+ mSubInfo[1] = new SubscriptionInfoInternal.Builder(mSubInfo[1])
+ .setGroupUuid(mGroupUuid1.toString()).build();
+ GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, true);
+ doReturn(Arrays.asList(mSubInfo[1].toSubscriptionInfo(), mSubInfo[2].toSubscriptionInfo()))
+ .when(mSubscriptionManagerService)
+ .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
+
+ mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
+ processAllMessages();
+ // Default data is set to sub1
+ verify(mSubscriptionManagerService).syncGroupedSetting(1);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
index e2c32a4..8dc8ea7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
@@ -18,21 +18,31 @@
import static junit.framework.Assert.assertEquals;
+import android.compat.testing.PlatformCompatChangeRule;
import android.os.Parcel;
import android.telephony.AccessNetworkConstants;
import android.telephony.CellIdentityLte;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import java.util.Arrays;
/** Unit tests for {@link NetworkRegistrationInfo}. */
public class NetworkRegistrationInfoTest {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Test
@SmallTest
public void testParcel() {
@@ -68,4 +78,65 @@
assertEquals("12345", new NetworkRegistrationInfo.Builder()
.setRegisteredPlmn("12345").build().getRegisteredPlmn());
}
+
+ @Test
+ @SmallTest
+ public void testSetRoamingType() {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setAvailableServices(Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+ .setCellIdentity(new CellIdentityLte())
+ .setRegisteredPlmn("12345")
+ .build();
+ nri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_HOME, nri.getRegistrationState());
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING,
+ nri.getNetworkRegistrationState());
+ }
+
+ @Test
+ @DisableCompatChanges({NetworkRegistrationInfo.RETURN_REGISTRATION_STATE_EMERGENCY})
+ public void testReturnRegistrationStateEmergencyDisabled() {
+ // LTE
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_DENIED, nri.getRegistrationState());
+
+ // NR
+ nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .build();
+
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ nri.getRegistrationState());
+ }
+
+ @Test
+ @EnableCompatChanges({NetworkRegistrationInfo.RETURN_REGISTRATION_STATE_EMERGENCY})
+ public void testReturnRegistrationStateEmergencyEnabled() {
+ // LTE
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY,
+ nri.getRegistrationState());
+
+ // NR
+ nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .build();
+
+ assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY,
+ nri.getRegistrationState());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 0230645..0c57e82 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -20,11 +20,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.content.Intent;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IPowerManager;
@@ -34,18 +35,16 @@
import android.os.PowerManager;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
import android.telephony.PhysicalChannelConfig;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import com.android.internal.telephony.dataconnection.DataConnection;
+import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
@@ -53,6 +52,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -61,27 +61,9 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class NetworkTypeControllerTest extends TelephonyTest {
- // private constants copied over from NetworkTypeController
- private static final int EVENT_DATA_RAT_CHANGED = 2;
- private static final int EVENT_NR_STATE_CHANGED = 3;
- private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
- private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5;
- private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
- private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
- private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
- private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
- private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
- private static final int EVENT_INITIALIZE = 12;
- private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
- private static final int EVENT_PCO_DATA_CHANGED = 14;
-
private NetworkTypeController mNetworkTypeController;
private PersistableBundle mBundle;
-
- // Mocked classes
- DataConnection mDataConnection;
- ApnSetting mApnSetting;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private IState getCurrentState() throws Exception {
Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
@@ -95,34 +77,40 @@
method.invoke(mNetworkTypeController);
}
- private void broadcastCarrierConfigs() {
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- mContext.sendBroadcast(intent);
+ private void sendCarrierConfigChanged() {
+ if (mCarrierConfigChangeListener != null) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mPhone.getPhoneId(),
+ mPhone.getSubId(), TelephonyManager.UNKNOWN_CARRIER_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ }
processAllMessages();
}
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mDataConnection = mock(DataConnection.class);
- mApnSetting = mock(ApnSetting.class);
mBundle = mContextFixture.getCarrierConfigBundle();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:5G_Plus,connected:5G,not_restricted_rrc_idle:5G,"
+ "not_restricted_rrc_con:5G");
- broadcastCarrierConfigs();
+ mBundle.putInt(CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ sendCarrierConfigChanged();
replaceInstance(Handler.class, "mLooper", mDisplayInfoController, Looper.myLooper());
doReturn(RadioAccessFamily.getRafFromNetworkType(
TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA)).when(
mPhone).getCachedAllowedNetworkTypesBitmask();
- doReturn(false).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
doReturn(new int[] {0}).when(mServiceState).getCellBandwidths();
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
processAllMessages();
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
}
@After
@@ -170,7 +158,6 @@
@Test
public void testUpdateOverrideNetworkTypeNrSa() throws Exception {
// not NR
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -185,7 +172,6 @@
anyInt(), anyInt());
updateOverrideNetworkType();
-
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
}
@@ -193,7 +179,6 @@
@Test
public void testUpdateOverrideNetworkTypeNrSaMmwave() throws Exception {
// not NR
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -209,7 +194,6 @@
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
updateOverrideNetworkType();
-
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
}
@@ -238,7 +222,7 @@
doReturn("test_patternShowAdvanced").when(mServiceState).getOperatorAlphaLongRaw();
mBundle.putString(CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING,
".*_patternShowAdvanced");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
mNetworkTypeController.getOverrideNetworkType());
@@ -263,9 +247,7 @@
@Test
public void testTransitionToCurrentStateLegacy() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_HSPAP).when(mServiceState).getDataNetworkType();
-
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
}
@@ -273,10 +255,8 @@
@Test
public void testTransitionToCurrentStateRestricted() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
-
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("restricted", getCurrentState().getName());
}
@@ -284,11 +264,10 @@
@Test
public void testTransitionToCurrentStateIdle() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -300,11 +279,11 @@
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
processAllMessages();
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -316,28 +295,25 @@
doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
processAllMessages();
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@Test
public void testTransitionToCurrentStateLteConnected() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
@@ -348,14 +324,14 @@
doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
processAllMessages();
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
@@ -368,27 +344,24 @@
doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
processAllMessages();
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
@Test
public void testTransitionToCurrentStateNrConnected() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -396,11 +369,10 @@
@Test
public void testTransitionToCurrentStateNrConnectedMmwave() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -409,7 +381,6 @@
public void testTransitionToCurrentStateNrConnectedMmwaveWithAdditionalBandAndNoMmwave()
throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
@@ -421,9 +392,10 @@
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
lastPhysicalChannelConfigList.add(physicalChannelConfig);
doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -432,7 +404,6 @@
public void testTransitionToCurrentStateNrConnectedWithNoAdditionalBandAndNoMmwave()
throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
@@ -444,9 +415,10 @@
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
lastPhysicalChannelConfigList.add(physicalChannelConfig);
doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -454,13 +426,11 @@
@Test
public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapable() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -469,48 +439,17 @@
public void testTransitionToCurrentStateNrConnectedWithPcoAndNoNrAdvancedCapable()
throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
- int cid = 1;
- byte[] contents = new byte[]{0};
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
- doReturn(mApnSetting).when(mDataConnection).getApnSetting();
- doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
-
- mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
- new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
- processAllMessages();
- assertEquals("connected", getCurrentState().getName());
- }
-
- @Test
- public void testTransitionToCurrentStateNrConnectedWithPcoLength4AndNoNrAdvancedCapable()
- throws Exception {
- assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
- doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
- int cid = 1;
- byte[] contents = new byte[]{31, 1, 84, 0};
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
- doReturn(mApnSetting).when(mDataConnection).getApnSetting();
- doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
-
-
- mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
- new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+ ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+ verify(mDataNetworkController).registerDataNetworkControllerCallback(
+ dataNetworkControllerCallbackCaptor.capture());
+ DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+ callback.onNrAdvancedCapableByPcoChanged(false);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -519,23 +458,17 @@
public void testTransitionToCurrentStateNrConnectedWithWrongPcoAndNoNrAdvancedCapable()
throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
- int cid = 1;
- byte[] contents = new byte[]{1};
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
- doReturn(mApnSetting).when(mDataConnection).getApnSetting();
- doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF00);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
-
- mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
- new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+ ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+ verify(mDataNetworkController).registerDataNetworkControllerCallback(
+ dataNetworkControllerCallbackCaptor.capture());
+ DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+ callback.onNrAdvancedCapableByPcoChanged(false);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -544,42 +477,17 @@
public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapableAndPco()
throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- int cid = 1;
- byte[] contents = new byte[]{1};
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
- doReturn(mApnSetting).when(mDataConnection).getApnSetting();
- doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
- new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
- processAllMessages();
- assertEquals("connected_mmwave", getCurrentState().getName());
- }
-
- @Test
- public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapableAndPcoLength4()
- throws Exception {
- assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
- doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- int cid = 1;
- byte[] contents = new byte[]{31, 1, 84, 1};
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
- doReturn(mApnSetting).when(mDataConnection).getApnSetting();
- doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
- broadcastCarrierConfigs();
-
- mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
- new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+ ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+ verify(mDataNetworkController).registerDataNetworkControllerCallback(
+ dataNetworkControllerCallbackCaptor.capture());
+ DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+ callback.onNrAdvancedCapableByPcoChanged(true);
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -587,10 +495,9 @@
@Test
public void testEventDataRatChanged() throws Exception {
testTransitionToCurrentStateLegacy();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_DATA_RAT_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -600,7 +507,7 @@
testTransitionToCurrentStateNrConnected();
doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("restricted", getCurrentState().getName());
}
@@ -611,9 +518,8 @@
testTransitionToCurrentStateNrConnectedMmwave();
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("connected", getCurrentState().getName());
}
@@ -623,23 +529,20 @@
testTransitionToCurrentStateNrConnected();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("connected_mmwave", getCurrentState().getName());
}
@Test
public void testNrPhysicalChannelChangeFromNrConnectedMmwaveToLteConnected() throws Exception {
testTransitionToCurrentStateNrConnectedMmwave();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
- processAllMessages();
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
+ new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
@@ -651,19 +554,15 @@
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
processAllMessages();
testTransitionToCurrentStateNrConnectedMmwave();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
-
@Test
public void testUsingUserDataForRrcDetection_FromNrConnectedMmwaveToLteConnected()
throws Exception {
@@ -672,34 +571,30 @@
doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
processAllMessages();
testTransitionToCurrentStateNrConnectedMmwave();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
}
@Test
public void testEventPhysicalChannelChangeFromLteToLteCaInLegacyState() throws Exception {
testTransitionToCurrentStateLegacy();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
mNetworkTypeController.getOverrideNetworkType());
}
@@ -710,14 +605,13 @@
mBundle = mContextFixture.getCarrierConfigBundle();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:5G_Plus,connected:5G");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
// Transition to LTE connected state
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -726,9 +620,9 @@
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
mNetworkTypeController.getOverrideNetworkType());
}
@@ -739,14 +633,13 @@
mBundle = mContextFixture.getCarrierConfigBundle();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:5G_Plus,connected:5G");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
// Transition to idle state
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -755,9 +648,9 @@
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
mNetworkTypeController.getOverrideNetworkType());
}
@@ -766,11 +659,10 @@
public void testEventPhysicalLinkStatusChanged() throws Exception {
testTransitionToCurrentStateLteConnected();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
processAllMessages();
-
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -784,10 +676,9 @@
testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
-
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -799,15 +690,14 @@
doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
processAllMessages();
testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
processAllMessages();
-
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -817,7 +707,7 @@
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED,
+ mNetworkTypeController.sendMessage(5 /* EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED */,
new AsyncResult(null, false, null));
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -831,9 +721,7 @@
mNetworkTypeController.getOverrideNetworkType());
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
-
- mNetworkTypeController.sendMessage(EVENT_RADIO_OFF_OR_UNAVAILABLE);
+ mNetworkTypeController.sendMessage(9 /* EVENT_RADIO_OFF_OR_UNAVAILABLE */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
@@ -850,7 +738,7 @@
TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)).when(
mPhone).getCachedAllowedNetworkTypesBitmask();
- mNetworkTypeController.sendMessage(EVENT_PREFERRED_NETWORK_MODE_CHANGED);
+ mNetworkTypeController.sendMessage(10 /* EVENT_PREFERRED_NETWORK_MODE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
@@ -858,11 +746,10 @@
@Test
public void testPrimaryTimerExpire() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -870,13 +757,13 @@
// should trigger 10 second timer
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// timer expires
moveTimeForward(10 * 1000);
@@ -885,23 +772,22 @@
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testPrimaryTimerDeviceIdleMode() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
IPowerManager powerManager = mock(IPowerManager.class);
PowerManager pm = new PowerManager(mContext, powerManager, mock(IThermalService.class),
new Handler(Looper.myLooper()));
doReturn(pm).when(mContext).getSystemService(Context.POWER_SERVICE);
doReturn(true).when(powerManager).isDeviceIdleMode();
- mNetworkTypeController.sendMessage(17 /*EVENT_DEVICE_IDLE_MODE_CHANGED*/);
+ mNetworkTypeController.sendMessage(12 /* EVENT_DEVICE_IDLE_MODE_CHANGED */);
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -909,22 +795,21 @@
// should not trigger timer
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testPrimaryTimerReset() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -932,17 +817,17 @@
// trigger 10 second timer after disconnecting from NR
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// reconnect to NR in the middle of the timer
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
// timer expires
moveTimeForward(10 * 1000);
@@ -952,16 +837,15 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testPrimaryTimerReset_theNetworkModeWithoutNr() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -975,24 +859,23 @@
// trigger 10 second timer after disconnecting from NR, and then it does the timer reset
// since the network mode without the NR capability.
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
// timer should be reset.
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testPrimaryTimerExpireMmwave() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
@@ -1000,13 +883,13 @@
// should trigger 10 second timer
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// timer expires
moveTimeForward(10 * 1000);
@@ -1015,17 +898,16 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testPrimaryTimerResetMmwave() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
@@ -1033,17 +915,17 @@
// trigger 10 second timer after disconnecting from NR
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// reconnect to NR in the middle of the timer
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
// timer expires
moveTimeForward(10 * 1000);
@@ -1053,18 +935,17 @@
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testSecondaryTimerExpire() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
"connected,any,30");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -1072,13 +953,13 @@
// should trigger 10 second primary timer
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -1088,7 +969,7 @@
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -1097,18 +978,17 @@
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testSecondaryTimerReset() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
"connected,any,30");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -1116,13 +996,13 @@
// should trigger 10 second primary timer
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -1132,11 +1012,11 @@
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// reconnect to NR in the middle of the timer
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -1146,19 +1026,18 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testSecondaryTimerExpireMmwave() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
"connected_mmwave,any,30");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
@@ -1166,13 +1045,13 @@
// should trigger 10 second primary timer
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -1182,7 +1061,7 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -1191,19 +1070,18 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testSecondaryTimerResetMmwave() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
"connected_mmwave,any,30");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
@@ -1211,13 +1089,13 @@
// should trigger 10 second primary timer
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -1227,11 +1105,11 @@
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// reconnect to NR in the middle of the timer
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -1241,19 +1119,18 @@
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
public void testNrTimerResetIn3g() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
"connected_mmwave,any,30");
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
assertEquals("connected_mmwave", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
@@ -1261,13 +1138,13 @@
// should trigger 10 second primary timer
doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
- mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
- assertTrue(mNetworkTypeController.is5GHysteresisActive());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
// rat is UMTS, should stop timer
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
@@ -1276,21 +1153,97 @@
.build();
doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(anyInt(), anyInt());
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
- mNetworkTypeController.sendMessage(EVENT_DATA_RAT_CHANGED);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
- private void setPhysicalLinkStatus(Boolean state) {
+ @Test
+ public void testNrTimerResetWhenConnected() throws Exception {
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,30");
+ sendCarrierConfigChanged();
+
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
+ new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // should trigger 10 second primary timer
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // rat is NR, should stop timer
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(anyInt(), anyInt());
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ @Test
+ public void testNrTimerResetWhenConnectedAdvanced() throws Exception {
+ testTransitionToCurrentStateNrConnectedMmwave();
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,30");
+ sendCarrierConfigChanged();
+
+ // should trigger 10 second primary timer
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // not advanced, should not stop timer
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ private void setPhysicalLinkStatus(boolean state) {
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
- // If PhysicalChannelConfigList is empty, PhysicalLinkStatus is DcController
- // .PHYSICAL_LINK_NOT_ACTIVE
- // If PhysicalChannelConfigList is not empty, PhysicalLinkStatus is DcController
- // .PHYSICAL_LINK_ACTIVE
+ // If PhysicalChannelConfigList is empty, PhysicalLinkStatus is
+ // DataCallResponse.LINK_STATUS_DORMANT
+ // If PhysicalChannelConfigList is not empty, PhysicalLinkStatus is
+ // DataCallResponse.LINK_STATUS_ACTIVE
if (state) {
PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
@@ -1305,14 +1258,13 @@
@Test
public void testTransitionToCurrentStateNrConnectedWithLowBandwidth() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
doReturn(new int[] {19999}).when(mServiceState).getCellBandwidths();
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -1320,12 +1272,43 @@
@Test
public void testTransitionToCurrentStateNrConnectedWithHighBandwidth() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- doReturn(new int[] {20001}).when(mServiceState).getCellBandwidths();
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellBandwidthDownlinkKhz(20001)
+ .build());
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
+
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithHighBandwidthIncludingLte()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellBandwidthDownlinkKhz(20000)
+ .build());
+ lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCellBandwidthDownlinkKhz(10000)
+ .build());
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL,
+ true);
+ sendCarrierConfigChanged();
mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
processAllMessages();
@@ -1335,14 +1318,13 @@
@Test
public void testNrAdvancedDisabledWhileRoaming() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putBoolean(CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, false);
- broadcastCarrierConfigs();
+ sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java
index 2460dba..05bcc5f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java
@@ -29,8 +29,17 @@
public class NitzDataTest {
@Test
- public void testParse_dateOutsideAllowedRange() {
- assertNull(NitzData.parse("38/06/20,00:00:00+0"));
+ public void testParse_invalidYear() {
+ assertNull(NitzData.parse("00/06/20,00:00:00+0,0"));
+ }
+
+ @Test
+ public void testParse_beyond2038() {
+ NitzData nitz = NitzData.parse("40/06/20,01:02:03+4,1");
+ assertEquals(createUnixEpochTime(2040, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(4 * 15), nitz.getLocalOffsetMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(60), nitz.getDstAdjustmentMillis().longValue());
+ assertNull(nitz.getEmulatorHostTimeZone());
}
@Test
@@ -38,7 +47,7 @@
// "yy/mm/dd,hh:mm:ss(+/-)tz[,dt[,tzid]]"
// No tz field.
- assertNull(NitzData.parse("38/06/20,00:00:00"));
+ assertNull(NitzData.parse("22/06/20,00:00:00"));
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 5771326..700a246 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -138,6 +138,27 @@
@Test
@SmallTest
+ public void testConfigureAndGetMaxActiveVoiceSubscriptions() throws Exception {
+ init(2);
+ assertEquals(1, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
+
+ PhoneCapability dualActiveVoiceSubCapability = new PhoneCapability.Builder(
+ PhoneCapability.DEFAULT_DSDS_CAPABILITY)
+ .setMaxActiveVoiceSubscriptions(2)
+ .build();
+
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).getPhoneCapability(captor.capture());
+ Message msg = captor.getValue();
+ AsyncResult.forMessage(msg, dualActiveVoiceSubCapability, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertEquals(2, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
+ }
+
+ @Test
+ @SmallTest
public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception {
init(1);
assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
@@ -199,9 +220,9 @@
// Verify clearSubInfoRecord() and onSlotActiveStatusChange() are called for second phone,
// and not for the first one
- verify(mSubscriptionController).clearSubInfoRecord(1);
+ verify(mSubscriptionManagerService).markSubscriptionsInactive(1);
verify(mMockCi1).onSlotActiveStatusChange(anyBoolean());
- verify(mSubscriptionController, never()).clearSubInfoRecord(0);
+ verify(mSubscriptionManagerService, never()).markSubscriptionsInactive(0);
verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
// Verify onPhoneRemoved() gets called on MultiSimSettingController phone
@@ -229,28 +250,28 @@
ex.) object.set( 2 ) --> next call to object.get() will return 2
*/
- // setup mocks for VOICE mSubscriptionController. getter/setter
+ // setup mocks for VOICE mSubscriptionManagerService. getter/setter
doAnswer(invocation -> {
Integer value = (Integer) invocation.getArguments()[0];
- Mockito.when(mSubscriptionController.getDefaultVoiceSubId()).thenReturn(value);
+ Mockito.when(mSubscriptionManagerService.getDefaultVoiceSubId()).thenReturn(value);
return null;
- }).when(mSubscriptionController).setDefaultVoiceSubId(anyInt());
+ }).when(mSubscriptionManagerService).setDefaultVoiceSubId(anyInt());
+
// start off the phone stat with 1 active sim. reset values for new test.
init(1);
- mSubscriptionController.setDefaultVoiceSubId(startingDefaultSubscriptionId);
-
- // assert the mSubscriptionController registers the change
- assertEquals(startingDefaultSubscriptionId, mSubscriptionController.getDefaultVoiceSubId());
+ mSubscriptionManagerService.setDefaultVoiceSubId(startingDefaultSubscriptionId);
+ assertEquals(startingDefaultSubscriptionId,
+ mSubscriptionManagerService.getDefaultVoiceSubId());
// Perform the switch to DSDS mode and ensure all existing checks are not altered
testSwitchFromSingleToDualSimModeNoReboot();
// VOICE check
assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID /* No CALL Preference value */,
- mSubscriptionController.getDefaultVoiceSubId()); // Now, when the user goes to
- // place a CALL, they will be prompted on which sim to use.
+ mSubscriptionManagerService.getDefaultVoiceSubId());
+ // Now, when the user goes to place a CALL, they will be prompted on which sim to use.
}
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneFactoryTest.java
deleted file mode 100644
index d1aea03..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneFactoryTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import static org.junit.Assert.fail;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
-
-public class PhoneFactoryTest {
- @Test
- @SmallTest
- public void testBeforeMakePhone() {
- try {
- PhoneFactory.getDefaultPhone();
- fail("Expecting IllegalStateException");
- } catch (IllegalStateException e) {
- }
-
- try {
- PhoneFactory.getPhone(0);
- fail("Expecting IllegalStateException");
- } catch (IllegalStateException e) {
- }
-
- try {
- PhoneFactory.getPhones();
- fail("Expecting IllegalStateException");
- } catch (IllegalStateException e) {
- }
-
- try {
- PhoneFactory.getNetworkFactory(0);
- fail("Expecting IllegalStateException");
- } catch (IllegalStateException e) {
- }
- }
-
- //todo: add test for makeDefaultPhone(). will need some refactoring in PhoneFactory.
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index b7d2913..2f3bbf7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -562,6 +562,17 @@
assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
}
+ @Test
+ public void testFormatNumber_lowerCase() {
+ assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "us"));
+ assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "us"));
+ assertEquals("011 86 10 8888 0000",
+ PhoneNumberUtils.formatNumber("011861088880000", "us"));
+ assertEquals("010 8888 0000", PhoneNumberUtils.formatNumber("01088880000", "cn"));
+ // formatNumber doesn't format alpha numbers, but keep them as they are.
+ assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "us"));
+ }
+
/**
* Tests ability to format phone numbers from Japan using the international format when the
* current country is not Japan.
@@ -570,6 +581,9 @@
@Test
public void testFormatJapanInternational() {
assertEquals("+81 90-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "US"));
+
+ // Again with lower case country code
+ assertEquals("+81 90-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "us"));
}
/**
@@ -582,8 +596,15 @@
assertEquals("090-6657-0660", PhoneNumberUtils.formatNumber("09066570660", "JP"));
assertEquals("090-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "JP"));
+ // Again with lower case country code
+ assertEquals("090-6657-0660", PhoneNumberUtils.formatNumber("09066570660", "jp"));
+ assertEquals("090-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "jp"));
+
+
// US number should still be internationally formatted
assertEquals("+1 650-555-1212", PhoneNumberUtils.formatNumber("+16505551212", "JP"));
+ // Again with lower case country code
+ assertEquals("+1 650-555-1212", PhoneNumberUtils.formatNumber("+16505551212", "jp"));
}
@SmallTest
@@ -600,6 +621,20 @@
assertEquals("**650 2910000", PhoneNumberUtils.formatNumber("**650 2910000", "US"));
}
+ // Same as testFormatNumber_LeadingStarAndHash but using lower case country code.
+ @Test
+ public void testFormatNumber_LeadingStarAndHash_countryCodeLowerCase() {
+ // Numbers with a leading '*' or '#' should be left unchanged.
+ assertEquals("*650 2910000", PhoneNumberUtils.formatNumber("*650 2910000", "us"));
+ assertEquals("#650 2910000", PhoneNumberUtils.formatNumber("#650 2910000", "us"));
+ assertEquals("*#650 2910000", PhoneNumberUtils.formatNumber("*#650 2910000", "us"));
+ assertEquals("#*650 2910000", PhoneNumberUtils.formatNumber("#*650 2910000", "us"));
+ assertEquals("#650*2910000", PhoneNumberUtils.formatNumber("#650*2910000", "us"));
+ assertEquals("#650*2910000", PhoneNumberUtils.formatNumber("#650*2910000", "us"));
+ assertEquals("##650 2910000", PhoneNumberUtils.formatNumber("##650 2910000", "us"));
+ assertEquals("**650 2910000", PhoneNumberUtils.formatNumber("**650 2910000", "us"));
+ }
+
@SmallTest
@Test
public void testNormalizeNumber() {
@@ -641,59 +676,63 @@
PhoneNumberUtils.formatNumber("011861088880000", "", "GB"));
}
+ // Same as testFormatDailabeNumber but using lower case country code.
+ @Test
+ public void testFormatDailabeNumber_countryCodeLowerCase() {
+ // Using the phoneNumberE164's country code
+ assertEquals("(650) 291-0000",
+ PhoneNumberUtils.formatNumber("6502910000", "+16502910000", "cn"));
+ // Using the default country code for a phone number containing the IDD
+ assertEquals("011 86 10 8888 0000",
+ PhoneNumberUtils.formatNumber("011861088880000", "+861088880000", "us"));
+ assertEquals("00 86 10 8888 0000",
+ PhoneNumberUtils.formatNumber("00861088880000", "+861088880000", "gb"));
+ assertEquals("+86 10 8888 0000",
+ PhoneNumberUtils.formatNumber("+861088880000", "+861088880000", "gb"));
+ // Wrong default country, so no formatting is done
+ assertEquals("011861088880000",
+ PhoneNumberUtils.formatNumber("011861088880000", "+861088880000", "gb"));
+ // The phoneNumberE164 is null
+ assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("6502910000", null, "us"));
+ // The given number has a country code.
+ assertEquals("+1 650-291-0000", PhoneNumberUtils.formatNumber("+16502910000", null, "cn"));
+ // The given number was formatted.
+ assertEquals("650-291-0000", PhoneNumberUtils.formatNumber("650-291-0000", null, "us"));
+ // A valid Polish number should be formatted.
+ assertEquals("506 128 687", PhoneNumberUtils.formatNumber("506128687", null, "pl"));
+ // An invalid Polish number should be left as it is. Note Poland doesn't use '0' as a
+ // national prefix; therefore, the leading '0' makes the number invalid.
+ assertEquals("0506128687", PhoneNumberUtils.formatNumber("0506128687", null, "pl"));
+ // Wrong default country, so no formatting is done
+ assertEquals("011861088880000",
+ PhoneNumberUtils.formatNumber("011861088880000", "", "gb"));
+ }
+
@FlakyTest
@Test
@Ignore
public void testIsEmergencyNumber() {
- // There are two parallel sets of tests here: one for the
- // regular isEmergencyNumber() method, and the other for
- // isPotentialEmergencyNumber().
- //
// (The difference is that isEmergencyNumber() will return true
// only if the specified number exactly matches an actual
- // emergency number, but isPotentialEmergencyNumber() will
- // return true if the specified number simply starts with the
- // same digits as any actual emergency number.)
+ // emergency number
// Tests for isEmergencyNumber():
- assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
- assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("911"));
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("112"));
// The next two numbers are not valid phone numbers in the US,
// so do not count as emergency numbers (but they *are* "potential"
// emergency numbers; see below.)
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
- assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345"));
// A valid mobile phone number from Singapore shouldn't be classified as an emergency number
// in Singapore, as 911 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234"));
// A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
// in Brazil, as 112 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567"));
// A valid local phone number from Brazil shouldn't be classified as an emergency number in
// Brazil.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
-
- // Tests for isPotentialEmergencyNumber():
- // These first two are obviously emergency numbers:
- assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("911", "US"));
- assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("112", "US"));
- // The next two numbers are not valid phone numbers in the US, but can be used to trick the
- // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
- // addressing that, they are also classified as "potential" emergency numbers in the US.
- assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US"));
- assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US"));
-
- // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
- // in Singapore, as 911 is not an emergency number there.
- // This test fails on devices that have ecclist property preloaded with 911.
- // assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
-
- // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
- // in Brazil, as 112 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR"));
- // A valid local phone number from Brazil shouldn't be classified as an emergency number in
- // Brazil.
- assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "BR"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345"));
}
@SmallTest
@@ -768,6 +807,22 @@
assertFalse(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "AU"));
}
+ // Same as testIsInternational but using lower case country code.
+ @Test
+ public void testIsInternational_countryCodeLowerCase() {
+ assertFalse(PhoneNumberUtils.isInternationalNumber("", "us"));
+ assertFalse(PhoneNumberUtils.isInternationalNumber(null, "us"));
+ assertFalse(PhoneNumberUtils.isInternationalNumber("+16505551212", "us"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "uk"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "jp"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("+86 10 8888 0000", "us"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("001-541-754-3010", "de"));
+ assertFalse(PhoneNumberUtils.isInternationalNumber("001-541-754-3010", "us"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("01161396694916", "us"));
+ assertTrue(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "us"));
+ assertFalse(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "au"));
+ }
+
@SmallTest
@Test
public void testIsUriNumber() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
index 8f97571..c10dea9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
@@ -23,7 +23,7 @@
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
-
+import android.text.style.TtsSpan;
import org.junit.Ignore;
import org.junit.Test;
@@ -42,6 +42,8 @@
textWatcher.afterTextChanged(number);
assertEquals(formatted1, number.toString());
assertEquals(formatted1.length(), Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
+
// Append one chars
final char appendChar = '6';
final String formatted2 = "(650) 123-456";
@@ -53,6 +55,13 @@
textWatcher.afterTextChanged(number);
assertEquals(formatted2, number.toString());
assertEquals(formatted2.length(), Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
+ }
+
+ private void assertSingleTtsSpan(Editable s)
+ {
+ int numTtsSpans = (s.getSpans(0, s.length(), TtsSpan.class)).length;
+ assertEquals(1, numTtsSpans);
}
@Test @SmallTest
@@ -70,6 +79,8 @@
textWatcher.afterTextChanged(number);
assertEquals(result1, number.toString());
assertEquals(result1.length(), Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
+
// Remove last 5 chars
final String result2 = "650-123";
textWatcher.beforeTextChanged(number, number.length() - 4, 4, 0);
@@ -79,6 +90,7 @@
textWatcher.afterTextChanged(number);
assertEquals(result2, number.toString());
assertEquals(result2.length(), Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
}
@Test @SmallTest
@@ -97,6 +109,7 @@
assertEquals(expected1, number.toString());
// the cursor should still at the right of '1'
assertEquals(5, Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
// Insert multiple chars
final String expected2 = "(650) 145-6723";
@@ -108,6 +121,7 @@
assertEquals(expected2, number.toString());
// the cursor should be still at the right of '7'
assertEquals(12, Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
}
@Test @SmallTest
@@ -188,6 +202,7 @@
assertEquals(expected2, number.toString());
// the cursor should still at the right of '4'
assertEquals(expected2.length(), Selection.getSelectionEnd(number));
+ assertSingleTtsSpan(number);
}
@Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index 606bcec..00634a0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -16,13 +16,17 @@
package com.android.internal.telephony;
import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.telephony.TelephonyManager.APPTYPE_ISIM;
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -31,17 +35,32 @@
import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telephony.uicc.IsimUiccRecords;
+import com.android.internal.telephony.uicc.SIMRecords;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccProfile;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.List;
public class PhoneSubInfoControllerTest extends TelephonyTest {
private static final String FEATURE_ID = "myfeatureId";
+ private static final String PSI_SMSC_TEL1 = "tel:+91123456789";
+ private static final String PSI_SMSC_SIP1 = "sip:+1234567890@abc.pc.operetor1.com;user=phone";
+ private static final String PSI_SMSC_TEL2 = "tel:+91987654321";
+ private static final String PSI_SMSC_SIP2 = "sip:+19876543210@dcf.pc.operetor2.com;user=phone";
private PhoneSubInfoController mPhoneSubInfoControllerUT;
private AppOpsManager mAppOsMgr;
@@ -57,18 +76,15 @@
PropertyInvalidatedCache.disableForTestMode();
/* mPhone -> PhoneId: 0 -> SubId:0
mSecondPhone -> PhoneId:1 -> SubId: 1*/
- doReturn(0).when(mSubscriptionController).getPhoneId(eq(0));
- doReturn(1).when(mSubscriptionController).getPhoneId(eq(1));
+ doReturn(0).when(mSubscriptionManagerService).getPhoneId(eq(0));
+ doReturn(1).when(mSubscriptionManagerService).getPhoneId(eq(1));
doReturn(2).when(mTelephonyManager).getPhoneCount();
doReturn(2).when(mTelephonyManager).getActiveModemCount();
- doReturn(true).when(mSubscriptionController).isActiveSubId(0, TAG, FEATURE_ID);
- doReturn(true).when(mSubscriptionController).isActiveSubId(1, TAG, FEATURE_ID);
+ doReturn(true).when(mSubscriptionManagerService).isActiveSubId(0, TAG, FEATURE_ID);
+ doReturn(true).when(mSubscriptionManagerService).isActiveSubId(1, TAG, FEATURE_ID);
doReturn(new int[]{0, 1}).when(mSubscriptionManager)
.getCompleteActiveSubscriptionIdList();
- mServiceManagerMockedServices.put("isub", mSubscriptionController);
- doReturn(mSubscriptionController).when(mSubscriptionController)
- .queryLocalInterface(anyString());
doReturn(mContext).when(mSecondPhone).getContext();
mAppOsMgr = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -922,4 +938,357 @@
assertEquals("VM_SIM_1", mPhoneSubInfoControllerUT
.getVoiceMailAlphaTagForSubscriber(1, TAG, FEATURE_ID));
}
-}
+
+ private void setUpInitials() {
+ UiccPort uiccPort1 = Mockito.mock(UiccPort.class);
+ UiccProfile uiccProfile1 = Mockito.mock(UiccProfile.class);
+ UiccCardApplication uiccCardApplication1 = Mockito.mock(UiccCardApplication.class);
+ SIMRecords simRecords1 = Mockito.mock(SIMRecords.class);
+ IsimUiccRecords isimUiccRecords1 = Mockito.mock(IsimUiccRecords.class);
+
+ doReturn(uiccPort1).when(mPhone).getUiccPort();
+ doReturn(uiccProfile1).when(uiccPort1).getUiccProfile();
+ doReturn(uiccCardApplication1).when(uiccProfile1).getApplicationByType(anyInt());
+ doReturn(simRecords1).when(uiccCardApplication1).getIccRecords();
+ doReturn(isimUiccRecords1).when(uiccCardApplication1).getIccRecords();
+ doReturn(PSI_SMSC_TEL1).when(simRecords1).getSmscIdentity();
+ doReturn(PSI_SMSC_TEL1).when(isimUiccRecords1).getSmscIdentity();
+
+ doReturn(mUiccPort).when(mSecondPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+ doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
+ doReturn(PSI_SMSC_TEL2).when(mSimRecords).getSmscIdentity();
+ doReturn(PSI_SMSC_TEL2).when(mIsimUiccRecords).getSmscIdentity();
+ }
+
+ @Test
+ public void testGetSmscIdentityForTelUri() {
+ try {
+ setUpInitials();
+ assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_USIM).toString());
+ assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_USIM).toString());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testGetSmscIdentityForSipUri() {
+ try {
+ UiccPort uiccPort1 = Mockito.mock(UiccPort.class);
+ UiccProfile uiccProfile1 = Mockito.mock(UiccProfile.class);
+ UiccCardApplication uiccCardApplication1 = Mockito.mock(UiccCardApplication.class);
+ SIMRecords simRecords1 = Mockito.mock(SIMRecords.class);
+ IsimUiccRecords isimUiccRecords1 = Mockito.mock(IsimUiccRecords.class);
+
+ doReturn(uiccPort1).when(mPhone).getUiccPort();
+ doReturn(uiccProfile1).when(uiccPort1).getUiccProfile();
+ doReturn(uiccCardApplication1).when(uiccProfile1).getApplicationByType(anyInt());
+ doReturn(simRecords1).when(uiccCardApplication1).getIccRecords();
+ doReturn(isimUiccRecords1).when(uiccCardApplication1).getIccRecords();
+ doReturn(PSI_SMSC_SIP1).when(simRecords1).getSmscIdentity();
+ doReturn(PSI_SMSC_SIP1).when(isimUiccRecords1).getSmscIdentity();
+
+ doReturn(mUiccPort).when(mSecondPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+ doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
+ doReturn(PSI_SMSC_SIP2).when(mSimRecords).getSmscIdentity();
+ doReturn(PSI_SMSC_SIP2).when(mIsimUiccRecords).getSmscIdentity();
+
+ assertEquals(PSI_SMSC_SIP1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_SIP1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_USIM).toString());
+ assertEquals(PSI_SMSC_SIP2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_SIP2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_USIM).toString());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testGetSmscIdentityWithOutPermissions() {
+ setUpInitials();
+
+ //case 1: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
+ mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+ try {
+ mPhoneSubInfoControllerUT.getSmscIdentity(0, APPTYPE_ISIM);
+ Assert.fail("expected Security Exception Thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("getSmscIdentity"));
+ }
+
+ try {
+ mPhoneSubInfoControllerUT.getSmscIdentity(1, APPTYPE_ISIM);
+ Assert.fail("expected Security Exception Thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("getSmscIdentity"));
+ }
+
+ try {
+ mPhoneSubInfoControllerUT.getSmscIdentity(0, APPTYPE_USIM);
+ Assert.fail("expected Security Exception Thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("getSmscIdentity"));
+ }
+
+ try {
+ mPhoneSubInfoControllerUT.getSmscIdentity(1, APPTYPE_USIM);
+ Assert.fail("expected Security Exception Thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("getSmscIdentity"));
+ }
+
+ //case 2: no READ_PRIVILEGED_PHONE_STATE
+ mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
+ eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
+ nullable(String.class));
+
+ try {
+ assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+ .getSmscIdentity(0, APPTYPE_USIM).toString());
+ assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_ISIM).toString());
+ assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+ .getSmscIdentity(1, APPTYPE_USIM).toString());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testGetSimServiceTable() throws RemoteException {
+ String refSst = "1234567";
+ doReturn(mUiccPort).when(mPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ assertEquals(refSst, resultSst);
+ }
+
+ @Test
+ public void testGetSimServiceTableEmpty() throws RemoteException {
+ String refSst = null;
+ doReturn(mUiccPort).when(mPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ assertEquals(refSst, resultSst);
+ }
+
+ @Test
+ public void testGetSstWhenNoUiccPort() throws RemoteException {
+ String refSst = "1234567";
+ doReturn(null).when(mPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ assertEquals(null, resultSst);
+ }
+
+ @Test
+ public void testGetSstWhenNoUiccProfile() throws RemoteException {
+ String refSst = "1234567";
+ doReturn(mUiccPort).when(mPhone).getUiccPort();
+ doReturn(null).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ assertEquals(null, resultSst);
+ }
+
+ @Test
+ public void testGetSstWhenNoUiccApplication() throws RemoteException {
+ String refSst = "1234567";
+ doReturn(mUiccPort).when(mPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(null).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ assertEquals(null, resultSst);
+ }
+
+ @Test
+ public void testGetSimServiceTableWithOutPermissions() throws RemoteException {
+ String refSst = "1234567";
+ doReturn(mUiccPort).when(mPhone).getUiccPort();
+ doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+ doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+ doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+ mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+ try {
+ mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+ Assert.fail("expected Security Exception Thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("getSimServiceTable"));
+ }
+
+ mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+ assertEquals(refSst, mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt()));
+ }
+
+ @Test
+ public void getPrivateUserIdentity() {
+ String refImpi = "1234567890@example.com";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpi).when(mIsimUiccRecords).getIsimImpi();
+
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER), anyInt(), eq(TAG),
+ eq(FEATURE_ID), nullable(String.class));
+
+ String impi = mPhoneSubInfoControllerUT.getImsPrivateUserIdentity(0, TAG, FEATURE_ID);
+ assertEquals(refImpi, impi);
+ }
+
+ @Test
+ public void getPrivateUserIdentity_NoPermission() {
+ String refImpi = "1234567890@example.com";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpi).when(mIsimUiccRecords).getIsimImpi();
+
+ try {
+ mPhoneSubInfoControllerUT.getImsPrivateUserIdentity(0, TAG, FEATURE_ID);
+ fail();
+ } catch (Exception ex) {
+ assertTrue(ex instanceof SecurityException);
+ assertTrue(ex.getMessage().contains("No permissions to the caller"));
+ }
+ }
+
+ @Test
+ public void getPrivateUserIdentity_InValidSubIdCheck() {
+ String refImpi = "1234567890@example.com";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpi).when(mIsimUiccRecords).getIsimImpi();
+
+ try {
+ mPhoneSubInfoControllerUT.getImsPrivateUserIdentity(-1, TAG, FEATURE_ID);
+ fail();
+ } catch (Exception ex) {
+ assertTrue(ex instanceof IllegalArgumentException);
+ assertTrue(ex.getMessage().contains("Invalid SubscriptionID"));
+ }
+ }
+
+ @Test
+ public void getImsPublicUserIdentities() {
+ String[] refImpuArray = new String[3];
+ refImpuArray[0] = "012345678";
+ refImpuArray[1] = "sip:test@verify.com";
+ refImpuArray[2] = "tel:+91987754324";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
+
+ List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
+ FEATURE_ID);
+
+ assertNotNull(impuList);
+ assertEquals(refImpuArray.length, impuList.size());
+ assertEquals(impuList.get(0).toString(), refImpuArray[0]);
+ assertEquals(impuList.get(1).toString(), refImpuArray[1]);
+ assertEquals(impuList.get(2).toString(), refImpuArray[2]);
+ }
+
+ @Test
+ public void getImsPublicUserIdentities_InvalidImpu() {
+ String[] refImpuArray = new String[3];
+ refImpuArray[0] = null;
+ refImpuArray[2] = "";
+ refImpuArray[2] = "tel:+91987754324";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
+ List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
+ FEATURE_ID);
+ assertNotNull(impuList);
+ // Null or Empty string cannot be converted to URI
+ assertEquals(refImpuArray.length - 2, impuList.size());
+ }
+
+ @Test
+ public void getImsPublicUserIdentities_IsimNotLoadedError() {
+ doReturn(null).when(mPhone).getIsimRecords();
+
+ try {
+ mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, FEATURE_ID);
+ fail();
+ } catch (Exception ex) {
+ assertTrue(ex instanceof IllegalStateException);
+ assertTrue(ex.getMessage().contains("ISIM is not loaded"));
+ }
+ }
+
+ @Test
+ public void getImsPublicUserIdentities_InValidSubIdCheck() {
+ try {
+ mPhoneSubInfoControllerUT.getImsPublicUserIdentities(-1, TAG, FEATURE_ID);
+ fail();
+ } catch (Exception ex) {
+ assertTrue(ex instanceof IllegalArgumentException);
+ assertTrue(ex.getMessage().contains("Invalid SubscriptionID"));
+ }
+ }
+
+ @Test
+ public void getImsPublicUserIdentities_NoReadPrivilegedPermission() {
+ mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+ String[] refImpuArray = new String[3];
+ refImpuArray[0] = "012345678";
+ refImpuArray[1] = "sip:test@verify.com";
+ refImpuArray[2] = "tel:+91987754324";
+ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+ doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
+
+ List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
+ FEATURE_ID);
+
+ assertNotNull(impuList);
+ assertEquals(refImpuArray.length, impuList.size());
+ assertEquals(impuList.get(0).toString(), refImpuArray[0]);
+ assertEquals(impuList.get(1).toString(), refImpuArray[1]);
+ assertEquals(impuList.get(2).toString(), refImpuArray[2]);
+ mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+ }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
index 21514ef..65ab664 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
@@ -19,14 +19,16 @@
import static android.telephony.RadioAccessFamily.RAF_GSM;
import static android.telephony.RadioAccessFamily.RAF_LTE;
+import static com.android.internal.telephony.ProxyController.EVENT_FINISH_RC_RESPONSE;
import static com.android.internal.telephony.ProxyController.EVENT_MULTI_SIM_CONFIG_CHANGED;
import static com.android.internal.telephony.ProxyController.EVENT_START_RC_RESPONSE;
+import static com.android.internal.telephony.ProxyController.EVENT_TIMEOUT;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.os.AsyncResult;
@@ -47,14 +49,11 @@
@TestableLooper.RunWithLooper
public class ProxyControllerTest extends TelephonyTest {
// Mocked classes
- Phone mPhone2;
-
ProxyController mProxyController;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mPhone2 = mock(Phone.class);
replaceInstance(ProxyController.class, "sProxyController", null, null);
mProxyController = ProxyController.getInstance(mContext);
}
@@ -101,12 +100,139 @@
rafs[1] = new RadioAccessFamily(1, RAF_GSM | RAF_LTE);
mProxyController.setRadioCapability(rafs);
- Message.obtain(mProxyController.mHandler, EVENT_START_RC_RESPONSE,
- new AsyncResult(null, null,
- new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)))
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_START_RC_RESPONSE,
+ new AsyncResult(
+ null,
+ null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)))
.sendToTarget();
processAllMessages();
assertFalse(mProxyController.isWakeLockHeld());
}
+
+ @Test
+ @SmallTest
+ public void testWithNonPermanentExceptionOnRCResponse_WithExceptionOnFinishResponse()
+ throws Exception {
+ int activeModemCount = 2;
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone2});
+ doReturn(activeModemCount).when(mTelephonyManager).getPhoneCount();
+ doReturn(RAF_GSM | RAF_LTE).when(mPhone).getRadioAccessFamily();
+ doReturn(RAF_GSM).when(mPhone2).getRadioAccessFamily();
+
+ Message.obtain(mProxyController.mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED).sendToTarget();
+ processAllMessages();
+ verify(mPhone2).registerForRadioCapabilityChanged(any(), anyInt(), any());
+
+ RadioAccessFamily[] rafs = new RadioAccessFamily[activeModemCount];
+ rafs[0] = new RadioAccessFamily(0, RAF_GSM);
+ rafs[1] = new RadioAccessFamily(1, RAF_GSM | RAF_LTE);
+ mProxyController.setRadioCapability(rafs);
+
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_START_RC_RESPONSE,
+ new AsyncResult(
+ null,
+ null,
+ new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE)))
+ .sendToTarget();
+ processAllMessages();
+ assertTrue(mProxyController.isWakeLockHeld());
+ onFinishResponseWithException();
+ }
+
+ @Test
+ @SmallTest
+ public void testWithNonPermanentExceptionOnRCResponse_WithoutExceptionOnFinishResponse()
+ throws Exception {
+ int activeModemCount = 2;
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone2});
+ doReturn(activeModemCount).when(mTelephonyManager).getPhoneCount();
+ doReturn(RAF_GSM | RAF_LTE).when(mPhone).getRadioAccessFamily();
+ doReturn(RAF_GSM).when(mPhone2).getRadioAccessFamily();
+
+ Message.obtain(mProxyController.mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED).sendToTarget();
+ processAllMessages();
+ verify(mPhone2).registerForRadioCapabilityChanged(any(), anyInt(), any());
+
+ RadioAccessFamily[] rafs = new RadioAccessFamily[activeModemCount];
+ rafs[0] = new RadioAccessFamily(0, RAF_GSM);
+ rafs[1] = new RadioAccessFamily(1, RAF_GSM | RAF_LTE);
+ mProxyController.setRadioCapability(rafs);
+
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_START_RC_RESPONSE,
+ new AsyncResult(null, null, null))
+ .sendToTarget();
+ processAllMessages();
+ assertTrue(mProxyController.isWakeLockHeld());
+ onFinishResponseWithoutException();
+ }
+
+ @Test
+ @SmallTest
+ public void testOnRCResponseTimeout_WithExceptionOnFinishResponse() throws Exception {
+ int activeModemCount = 2;
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone2});
+ doReturn(activeModemCount).when(mTelephonyManager).getPhoneCount();
+ doReturn(RAF_GSM | RAF_LTE).when(mPhone).getRadioAccessFamily();
+ doReturn(RAF_GSM).when(mPhone2).getRadioAccessFamily();
+
+ Message.obtain(mProxyController.mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED).sendToTarget();
+ processAllMessages();
+ verify(mPhone2).registerForRadioCapabilityChanged(any(), anyInt(), any());
+
+ RadioAccessFamily[] rafs = new RadioAccessFamily[activeModemCount];
+ rafs[0] = new RadioAccessFamily(0, RAF_GSM);
+ rafs[1] = new RadioAccessFamily(1, RAF_GSM | RAF_LTE);
+ mProxyController.setRadioCapability(rafs);
+
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_TIMEOUT,
+ new AsyncResult(
+ null,
+ null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)))
+ .sendToTarget();
+ processAllMessages();
+ onFinishResponseWithException();
+ }
+
+ private void onFinishResponseWithException() throws Exception {
+ replaceInstance(
+ ProxyController.class, "mRadioAccessFamilyStatusCounter", mProxyController, 1);
+ replaceInstance(ProxyController.class, "mTransactionFailed", mProxyController, true);
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_FINISH_RC_RESPONSE,
+ new AsyncResult(
+ null,
+ null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)))
+ .sendToTarget();
+ processAllMessages();
+ assertTrue(mProxyController.isWakeLockHeld());
+ }
+
+ private void onFinishResponseWithoutException() throws Exception {
+ replaceInstance(
+ ProxyController.class, "mRadioAccessFamilyStatusCounter", mProxyController, 1);
+ replaceInstance(ProxyController.class, "mTransactionFailed", mProxyController, false);
+ replaceInstance(
+ ProxyController.class, "mRadioCapabilitySessionId", mProxyController, 123456);
+ Message.obtain(
+ mProxyController.mHandler,
+ EVENT_FINISH_RC_RESPONSE,
+ new AsyncResult(
+ null, new RadioCapability(0, 123456, 0, 0, "test_modem", 0), null))
+ .sendToTarget();
+ processAllMessages();
+ assertFalse(mProxyController.isWakeLockHeld());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 311fe20..2396d1d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -16,6 +16,12 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE;
@@ -27,6 +33,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DATA_REGISTRATION_STATE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DELETE_SMS_ON_SIM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IDENTITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IMEI;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENABLE_UICC_APPLICATIONS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION;
@@ -90,6 +97,7 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -120,12 +128,14 @@
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.LinkAddress;
+import android.os.AsyncResult;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.WorkSource;
import android.service.carrier.CarrierIdentifier;
import android.telephony.AccessNetworkConstants;
@@ -172,6 +182,7 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -183,8 +194,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@@ -205,14 +218,16 @@
private RadioDataProxy mDataProxy;
private RadioNetworkProxy mNetworkProxy;
private RadioSimProxy mSimProxy;
+ private RadioModemProxy mRadioModemProxy;
- private HalVersion mRadioVersionV10 = new HalVersion(1, 0);
- private HalVersion mRadioVersionV11 = new HalVersion(1, 1);
- private HalVersion mRadioVersionV12 = new HalVersion(1, 2);
- private HalVersion mRadioVersionV13 = new HalVersion(1, 3);
- private HalVersion mRadioVersionV14 = new HalVersion(1, 4);
- private HalVersion mRadioVersionV15 = new HalVersion(1, 5);
- private HalVersion mRadioVersionV16 = new HalVersion(1, 6);
+ private Map<Integer, HalVersion> mHalVersionV10 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV11 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV12 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV13 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV14 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV15 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV16 = new HashMap<>();
+ private Map<Integer, HalVersion> mHalVersionV21 = new HashMap<>();
private RIL mRILInstance;
private RIL mRILUnderTest;
@@ -300,6 +315,7 @@
mDataProxy = mock(RadioDataProxy.class);
mNetworkProxy = mock(RadioNetworkProxy.class);
mSimProxy = mock(RadioSimProxy.class);
+ mRadioModemProxy = mock(RadioModemProxy.class);
try {
TelephonyDevController.create();
} catch (RuntimeException e) {
@@ -316,10 +332,11 @@
doReturn(powerManager).when(context).getSystemService(Context.POWER_SERVICE);
doReturn(new ApplicationInfo()).when(context).getApplicationInfo();
SparseArray<RadioServiceProxy> proxies = new SparseArray<>();
- proxies.put(RIL.RADIO_SERVICE, null);
- proxies.put(RIL.DATA_SERVICE, mDataProxy);
- proxies.put(RIL.NETWORK_SERVICE, mNetworkProxy);
- proxies.put(RIL.SIM_SERVICE, mSimProxy);
+ proxies.put(HAL_SERVICE_RADIO, null);
+ proxies.put(HAL_SERVICE_DATA, mDataProxy);
+ proxies.put(HAL_SERVICE_NETWORK, mNetworkProxy);
+ proxies.put(HAL_SERVICE_SIM, mSimProxy);
+ proxies.put(HAL_SERVICE_MODEM, mRadioModemProxy);
mRILInstance = new RIL(context,
RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE),
Phone.PREFERRED_CDMA_SUBSCRIPTION, 0, proxies);
@@ -331,11 +348,24 @@
eq(RadioNetworkProxy.class), any());
doReturn(mSimProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioSimProxy.class),
any());
+ doReturn(mRadioModemProxy).when(mRILUnderTest).getRadioServiceProxy(
+ eq(RadioModemProxy.class), any());
doReturn(false).when(mDataProxy).isEmpty();
doReturn(false).when(mNetworkProxy).isEmpty();
doReturn(false).when(mSimProxy).isEmpty();
+ doReturn(false).when(mRadioModemProxy).isEmpty();
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV10);
+ for (int service = RIL.MIN_SERVICE_IDX; service <= RIL.MAX_SERVICE_IDX; service++) {
+ mHalVersionV10.put(service, new HalVersion(1, 0));
+ mHalVersionV11.put(service, new HalVersion(1, 1));
+ mHalVersionV12.put(service, new HalVersion(1, 2));
+ mHalVersionV13.put(service, new HalVersion(1, 3));
+ mHalVersionV14.put(service, new HalVersion(1, 4));
+ mHalVersionV15.put(service, new HalVersion(1, 5));
+ mHalVersionV16.put(service, new HalVersion(1, 6));
+ mHalVersionV21.put(service, new HalVersion(2, 1));
+ }
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV10);
} catch (Exception e) {
}
}
@@ -493,7 +523,7 @@
// Make radio version 1.5 to support the operation.
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
@@ -531,7 +561,7 @@
// Make radio version 1.5 to support the operation.
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
@@ -711,7 +741,7 @@
public void testStartNetworkScanWithUnsupportedResponse() throws Exception {
// Use Radio HAL v1.5
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
NetworkScanRequest nsr = getNetworkScanRequestForTesting();
@@ -738,7 +768,7 @@
public void testGetVoiceRegistrationStateWithUnsupportedResponse() throws Exception {
// Use Radio HAL v1.5
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
mRILUnderTest.getVoiceRegistrationState(obtainMessage());
@@ -773,7 +803,7 @@
public void testGetDataRegistrationStateWithUnsupportedResponse() throws Exception {
// Use Radio HAL v1.5
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
@@ -818,7 +848,7 @@
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
@@ -861,7 +891,7 @@
public void testSendSMS_1_6() throws Exception {
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
String smscPdu = "smscPdu";
@@ -893,7 +923,7 @@
public void testSendSMSExpectMore_1_6() throws Exception {
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
String smscPdu = "smscPdu";
@@ -912,7 +942,7 @@
public void testSendCdmaSMS_1_6() throws Exception {
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
@@ -928,7 +958,7 @@
public void testSendCdmaSMSExpectMore_1_6() throws Exception {
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
@@ -1251,7 +1281,7 @@
@Test
public void testIccCloseLogicalChannel() throws Exception {
int channel = 1;
- mRILUnderTest.iccCloseLogicalChannel(channel, obtainMessage());
+ mRILUnderTest.iccCloseLogicalChannel(channel, false, obtainMessage());
verify(mRadioProxy).iccCloseLogicalChannel(mSerialNumberCaptor.capture(), eq(channel));
verifyRILResponse(
mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SIM_CLOSE_CHANNEL);
@@ -1475,7 +1505,7 @@
// Make radio version 1.5 to support the operation.
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
mRILUnderTest.getBarringInfo(obtainMessage());
@@ -2838,7 +2868,7 @@
// Make radio version 1.5 to support the operation.
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
mRILUnderTest.enableUiccApplications(false, obtainMessage());
@@ -2855,7 +2885,7 @@
// Make radio version 1.5 to support the operation.
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
} catch (Exception e) {
}
mRILUnderTest.areUiccApplicationsEnabled(obtainMessage());
@@ -2902,7 +2932,7 @@
public void testGetSlicingConfig() throws Exception {
// Use Radio HAL v1.6
try {
- replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
} catch (Exception e) {
}
mRILUnderTest.getSlicingConfig(obtainMessage());
@@ -2910,4 +2940,31 @@
verifyRILResponse_1_6(
mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_SLICING_CONFIG);
}
+
+ @Test
+ public void getImei() throws RemoteException {
+ try {
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV21);
+ } catch (Exception e) {
+ fail();
+ }
+ mRILUnderTest.getImei(obtainMessage());
+ verify(mRadioModemProxy, atLeast(1)).getImei(mSerialNumberCaptor.capture());
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DEVICE_IMEI);
+ }
+
+ @Test
+ public void getImeiNotSupported() {
+ try {
+ replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
+ } catch (Exception e) {
+ fail();
+ }
+ Message message = obtainMessage();
+ mRILUnderTest.getImei(message);
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertEquals(null, ar.result);
+ Assert.assertNotNull(ar.exception.getMessage());
+ Assert.assertEquals("REQUEST_NOT_SUPPORTED", ar.exception.getMessage());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
index 6cbb1da..45acc40 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
@@ -18,6 +18,11 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
import android.os.PersistableBundle;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
@@ -31,6 +36,7 @@
import org.junit.Test;
import java.util.Arrays;
+import java.util.concurrent.Executor;
/** Tests for RatRatcheter. */
public class RatRatcheterTest extends TelephonyTest {
@@ -41,6 +47,7 @@
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
+ doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
mServiceState = new ServiceState();
}
@@ -135,6 +142,7 @@
ServiceState newSS = new ServiceState();
mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
mBundle.putStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES,
new String[]{"14,19"});
@@ -153,6 +161,7 @@
ServiceState newSS = new ServiceState();
mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
mBundle.putStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, new String[]{});
setNetworkRegistrationInfo(oldSS, TelephonyManager.NETWORK_TYPE_LTE_CA);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
deleted file mode 100644
index 80cd9f1..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.test.suitebuilder.annotation.MediumTest;
-import com.android.internal.telephony.TestPhoneNotifier;
-import com.android.internal.telephony.gsm.SmsMessage;
-import com.android.internal.telephony.test.SimulatedCommands;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-import com.android.internal.telephony.uicc.IccUtils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-
-import java.util.Iterator;
-
-/**
- * {@hide}
- */
-public class SMSDispatcherTest extends AndroidTestCase {
- @MediumTest
- public void testCMT1() throws Exception {
- SmsMessage sms;
- SmsHeader header;
-
- String[] lines = new String[2];
-
- lines[0] = "+CMT: ,158";
- lines[1] = "07914140279510F6440A8111110301003BF56080426101748A8C0B05040B"
- + "8423F000035502010106276170706C69636174696F6E2F766E642E776170"
- + "2E6D6D732D6D65737361676500AF848D0185B4848C8298524F347839776F"
- + "7547514D4141424C3641414141536741415A4B554141414141008D908918"
- + "802B31363530323438363137392F545950453D504C4D4E008A808E028000"
- + "88058103093A8083687474703A2F2F36";
-
- sms = SmsMessage.newFromCMT(lines);
- header = sms.getUserDataHeader();
- assertNotNull(header);
- assertNotNull(sms.getUserData());
- assertNotNull(header.concatRef);
- assertEquals(header.concatRef.refNumber, 85);
- assertEquals(header.concatRef.msgCount, 2);
- assertEquals(header.concatRef.seqNumber, 1);
- assertEquals(header.concatRef.isEightBits, true);
- assertNotNull(header.portAddrs);
- assertEquals(header.portAddrs.destPort, 2948);
- assertEquals(header.portAddrs.origPort, 9200);
- assertEquals(header.portAddrs.areEightBits, false);
- }
-
- @MediumTest
- public void testCMT2() throws Exception {
- SmsMessage sms;
- SmsHeader header;
-
- String[] lines = new String[2];
-
- lines[0] = "+CMT: ,77";
- lines[1] = "07914140279510F6440A8111110301003BF56080426101848A3B0B05040B8423F"
- + "00003550202362E3130322E3137312E3135302F524F347839776F7547514D4141"
- + "424C3641414141536741415A4B55414141414100";
-
- sms = SmsMessage.newFromCMT(lines);
- header = sms.getUserDataHeader();
- assertNotNull(header);
- assertNotNull(sms.getUserData());
- assertNotNull(header.concatRef);
- assertEquals(header.concatRef.refNumber, 85);
- assertEquals(header.concatRef.msgCount, 2);
- assertEquals(header.concatRef.seqNumber, 2);
- assertEquals(header.concatRef.isEightBits, true);
- assertNotNull(header.portAddrs);
- assertEquals(header.portAddrs.destPort, 2948);
- assertEquals(header.portAddrs.origPort, 9200);
- assertEquals(header.portAddrs.areEightBits, false);
- }
-
- @MediumTest
- public void testEfRecord() throws Exception {
- SmsMessage sms;
-
- String s = "03029111000c9194981492631000f269206190022000a053e4534a05358bd3"
- + "69f05804259da0219418a40641536a110a0aea408080604028180e888462c1"
- + "50341c0f484432a1542c174c46b3e1743c9f9068442a994ea8946ac56ab95e"
- + "b0986c46abd96eb89c6ec7ebf97ec0a070482c1a8fc8a472c96c3a9fd0a874"
- + "4aad5aafd8ac76cbed7abfe0b0784c2e9bcfe8b47acd6ebbdff0b87c4eafdb"
- + "eff8bc7ecfeffbffffffffffffffffffffffffffff";
- byte[] data = IccUtils.hexStringToBytes(s);
-
- sms = SmsMessage.createFromEfRecord(1, data);
- assertNotNull(sms.getMessageBody());
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 7d9891d..846b48e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -92,7 +93,10 @@
import com.android.internal.R;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.metrics.ServiceStateStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccRecords;
@@ -104,12 +108,14 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.Executor;
public class ServiceStateTrackerTest extends TelephonyTest {
// Mocked classes
@@ -118,9 +124,12 @@
private NetworkService mIwlanNetworkService;
private INetworkService.Stub mIwlanNetworkServiceStub;
private SubscriptionInfo mSubInfo;
+
+ private SubscriptionInfoInternal mSubInfoInternal;
private ServiceStateStats mServiceStateStats;
private CellularNetworkService mCellularNetworkService;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
// SST now delegates all signal strength operations to SSC
// Add Mock SSC as the dependency to avoid NPE
@@ -178,7 +187,18 @@
mSsc = new SignalStrengthController(mPhone);
doReturn(mSsc).when(mPhone).getSignalStrengthController();
+ // Capture listener registered for ServiceStateTracker to emulate the carrier config
+ // change notification used later. In this test, it's the third one. The first one
+ // comes from RatRatcheter and the second one comes from SignalStrengthController.
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener>
+ listenerArgumentCaptor =
+ ArgumentCaptor.forClass(
+ CarrierConfigManager.CarrierConfigChangeListener.class);
sst = new ServiceStateTracker(mPhone, mSimulatedCommands);
+ verify(mCarrierConfigManager, atLeast(3)).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(2);
+
sst.setServiceStateStats(mServiceStateStats);
doReturn(sst).when(mPhone).getServiceStateTracker();
setReady(true);
@@ -225,8 +245,14 @@
mIwlanNetworkService = Mockito.mock(NetworkService.class);
mIwlanNetworkServiceStub = Mockito.mock(INetworkService.Stub.class);
mSubInfo = Mockito.mock(SubscriptionInfo.class);
+ mSubInfoInternal = new SubscriptionInfoInternal.Builder().setId(1).build();
mServiceStateStats = Mockito.mock(ServiceStateStats.class);
+ mContextFixture.putResource(R.string.kg_text_message_separator, " \u2014 ");
+
+ doReturn(mSubInfoInternal).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(anyInt());
+ doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
mContextFixture.putResource(R.string.config_wwan_network_service_package,
"com.android.phone");
mContextFixture.putResource(R.string.config_wlan_network_service_package,
@@ -234,12 +260,14 @@
doReturn(mIwlanNetworkServiceStub).when(mIwlanNetworkServiceStub).asBinder();
addNetworkService();
- doReturn(true).when(mDcTracker).areAllDataDisconnected();
+ doReturn(true).when(mDataNetworkController).areAllDataDisconnected();
doReturn(new ServiceState()).when(mPhone).getServiceState();
replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
mBundle.putStringArray(
CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
@@ -259,7 +287,6 @@
int dds = SubscriptionManager.getDefaultDataSubscriptionId();
doReturn(dds).when(mPhone).getSubId();
- doReturn(true).when(mPhone).areAllDataDisconnected();
doReturn(true).when(mPackageManager)
.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
@@ -269,6 +296,11 @@
waitUntilReady();
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ // Voice radio tech change will always trigger an update of
+ // phone object irrespective of this config
+ mContextFixture.putBooleanResource(
+ com.android.internal.R.bool.config_switch_phone_on_voice_reg_state_change, false);
+
// Override SPN related resource
mContextFixture.putResource(
com.android.internal.R.string.lockscreen_carrier_default,
@@ -334,9 +366,7 @@
30 /* SIGNAL_STRENGTH_GREAT */
});
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
- mContext.sendBroadcast(intent);
+ sendCarrierConfigUpdate(PHONE_ID);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
logd("ServiceStateTrackerTest -Setup!");
@@ -380,6 +410,58 @@
}
@Test
+ public void testSetRadioPowerWaitForAllDataDisconnected() throws Exception {
+ // Set up DSDS environment
+ GsmCdmaPhone phone2 = Mockito.mock(GsmCdmaPhone.class);
+ DataNetworkController dataNetworkController_phone2 =
+ Mockito.mock(DataNetworkController.class);
+ mPhones = new Phone[] {mPhone, phone2};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ doReturn(dataNetworkController_phone2).when(phone2).getDataNetworkController();
+ doReturn(mSST).when(phone2).getServiceStateTracker();
+ doReturn(false).when(mDataNetworkController).areAllDataDisconnected();
+ doReturn(false).when(dataNetworkController_phone2).areAllDataDisconnected();
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(phone2).getSubId();
+
+ // Start with radio on
+ sst.setRadioPower(true);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+ // Turn on APM and verify that both subs are waiting for all data disconnected
+ sst.setRadioPower(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ verify(mDataNetworkController).tearDownAllDataNetworks(
+ eq(3 /* TEAR_DOWN_REASON_AIRPLANE_MODE_ON */));
+ verify(dataNetworkController_phone2, never()).tearDownAllDataNetworks(anyInt());
+ ArgumentCaptor<DataNetworkController.DataNetworkControllerCallback> callback1 =
+ ArgumentCaptor.forClass(DataNetworkController.DataNetworkControllerCallback.class);
+ ArgumentCaptor<DataNetworkController.DataNetworkControllerCallback> callback2 =
+ ArgumentCaptor.forClass(DataNetworkController.DataNetworkControllerCallback.class);
+ verify(mDataNetworkController, times(1)).registerDataNetworkControllerCallback(
+ callback1.capture());
+ verify(dataNetworkController_phone2, times(1)).registerDataNetworkControllerCallback(
+ callback2.capture());
+
+ // Data disconnected on sub 2, still waiting for data disconnected on sub 1
+ doReturn(true).when(dataNetworkController_phone2).areAllDataDisconnected();
+ callback2.getValue().onAnyDataNetworkExistingChanged(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ verify(dataNetworkController_phone2, times(1)).unregisterDataNetworkControllerCallback(
+ any());
+
+ // Data disconnected on sub 1, radio should power off now
+ doReturn(true).when(mDataNetworkController).areAllDataDisconnected();
+ callback1.getValue().onAnyDataNetworkExistingChanged(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ verify(mDataNetworkController, times(1)).unregisterDataNetworkControllerCallback(any());
+ assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+ }
+
+ @Test
@SmallTest
public void testSetRadioPowerOnForEmergencyCall() {
// Turn off radio first.
@@ -412,30 +494,37 @@
@Test
@MediumTest
public void testSetRadioPowerForReason() {
+ testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL);
+ testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE);
+ testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER);
+ }
+
+ private void testSetRadioPowerForReason(int reason) {
// Radio does not turn on if off for other reason and not emergency call.
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
assertTrue(sst.getRadioPowerOffReasons().isEmpty());
- sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
- assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ sst.setRadioPowerForReason(false, false, false, false, reason);
+ assertTrue(sst.getRadioPowerOffReasons().contains(reason));
assertTrue(sst.getRadioPowerOffReasons().size() == 1);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
- sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_USER);
- assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ sst.setRadioPowerForReason(true, false, false, false,
+ TelephonyManager.RADIO_POWER_REASON_USER);
+ assertTrue(sst.getRadioPowerOffReasons().contains(reason));
assertTrue(sst.getRadioPowerOffReasons().size() == 1);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
// Radio power state reason is removed and radio turns on if turned on for same reason it
// had been turned off for.
- sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
+ sst.setRadioPowerForReason(true, false, false, false, reason);
assertTrue(sst.getRadioPowerOffReasons().isEmpty());
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
// Turn radio off, then successfully turn radio on for emergency call.
- sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
- assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ sst.setRadioPowerForReason(false, false, false, false, reason);
+ assertTrue(sst.getRadioPowerOffReasons().contains(reason));
assertTrue(sst.getRadioPowerOffReasons().size() == 1);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
@@ -447,33 +536,83 @@
@Test
@MediumTest
- public void testSetRadioPowerFromCarrier() {
+ public void testSetRadioPowerForMultipleReasons() {
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+ assertTrue(sst.getRadioPowerOffReasons().isEmpty());
+
+ // Turn off radio
+ turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_USER, 1);
+ turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL, 2);
+ turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER, 3);
+ turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE, 4);
+
+ // Turn on radio
+ turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE, 3,
+ TelephonyManager.RADIO_POWER_OFF);
+ turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL, 2,
+ TelephonyManager.RADIO_POWER_OFF);
+ turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER, 1,
+ TelephonyManager.RADIO_POWER_OFF);
+ turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_USER, 0,
+ TelephonyManager.RADIO_POWER_ON);
+ }
+
+ private void turnRadioOffForReason(int reason, int powerOffReasonSize) {
+ sst.setRadioPowerForReason(false, false, false, false, reason);
+ assertTrue(sst.getRadioPowerOffReasons().contains(reason));
+ assertTrue(sst.getRadioPowerOffReasons().size() == powerOffReasonSize);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+ }
+
+ private void turnRadioOnForReason(int reason, int powerOffReasonSize,
+ int expectedRadioPowerState) {
+ sst.setRadioPowerForReason(true, false, false, false, reason);
+ assertFalse(sst.getRadioPowerOffReasons().contains(reason));
+ assertTrue(sst.getRadioPowerOffReasons().size() == powerOffReasonSize);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == expectedRadioPowerState);
+ }
+
+ @Test
+ @MediumTest
+ public void testSetRadioPowerForReasonCarrier() {
// Carrier disable radio power
- sst.setRadioPowerFromCarrier(false);
+ sst.setRadioPowerForReason(false, false, false, false,
+ TelephonyManager.RADIO_POWER_REASON_CARRIER);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertFalse(mSimulatedCommands.getRadioState()
== TelephonyManager.RADIO_POWER_ON);
- assertTrue(sst.getDesiredPowerState());
+ assertFalse(sst.getDesiredPowerState());
assertFalse(sst.getPowerStateFromCarrier());
+ assertTrue(sst.getRadioPowerOffReasons().contains(
+ TelephonyManager.RADIO_POWER_REASON_CARRIER));
+ assertTrue(sst.getRadioPowerOffReasons().size() == 1);
// User toggle radio power will not overrides carrier settings
sst.setRadioPower(true);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertFalse(mSimulatedCommands.getRadioState()
== TelephonyManager.RADIO_POWER_ON);
- assertTrue(sst.getDesiredPowerState());
+ assertFalse(sst.getDesiredPowerState());
assertFalse(sst.getPowerStateFromCarrier());
+ assertTrue(sst.getRadioPowerOffReasons().contains(
+ TelephonyManager.RADIO_POWER_REASON_CARRIER));
+ assertTrue(sst.getRadioPowerOffReasons().size() == 1);
// Carrier re-enable radio power
- sst.setRadioPowerFromCarrier(true);
+ sst.setRadioPowerForReason(true, false, false, false,
+ TelephonyManager.RADIO_POWER_REASON_CARRIER);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
assertTrue(sst.getDesiredPowerState());
assertTrue(sst.getPowerStateFromCarrier());
+ assertTrue(sst.getRadioPowerOffReasons().isEmpty());
// User toggle radio power off (airplane mode) and set carrier on
sst.setRadioPower(false);
- sst.setRadioPowerFromCarrier(true);
+ sst.setRadioPowerForReason(true, false, false, false,
+ TelephonyManager.RADIO_POWER_REASON_CARRIER);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
assertFalse(mSimulatedCommands.getRadioState()
== TelephonyManager.RADIO_POWER_ON);
@@ -696,15 +835,10 @@
verify(mPhone, times(1)).notifyServiceStateChanged(any(ServiceState.class));
}
- private void sendCarrierConfigUpdate() {
- CarrierConfigManager mockConfigManager = Mockito.mock(CarrierConfigManager.class);
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(mockConfigManager);
- when(mockConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
-
- Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, PHONE_ID);
- mContext.sendBroadcast(intent);
+ private void sendCarrierConfigUpdate(int phoneId) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(phoneId,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
}
@@ -780,13 +914,15 @@
@Test
@MediumTest
public void testUpdatePhoneType() {
+ String brandOverride = "spn from brand override";
+ doReturn(brandOverride).when(mUiccProfile).getOperatorBrandOverride();
doReturn(false).when(mPhone).isPhoneTypeGsm();
doReturn(true).when(mPhone).isPhoneTypeCdmaLte();
doReturn(CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM).when(mCdmaSSM).
getCdmaSubscriptionSource();
- logd("Calling updatePhoneType");
// switch to CDMA
+ logd("Calling updatePhoneType");
sst.updatePhoneType();
ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -831,7 +967,6 @@
mSimulatedCommands.setVoiceRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
mSimulatedCommands.setDataRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
-
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
// verify if registered handler has message posted to it
@@ -1013,7 +1148,6 @@
mSimulatedCommands.notifyNetworkStateChanged();
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
// verify if registered handler has message posted to it
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
@@ -1137,7 +1271,7 @@
@Test
@MediumTest
- public void testRegisterForVoiceRegStateOrRatChange() {
+ public void testRegisterForVoiceRegStateOrRatChange() throws Exception {
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.setDomain(NetworkRegistrationInfo.DOMAIN_CS)
@@ -1145,14 +1279,17 @@
.build();
sst.mSS.addNetworkRegistrationInfo(nri);
+ sst.mSS.setState(ServiceState.STATE_IN_SERVICE);
sst.registerForVoiceRegStateOrRatChanged(mTestHandler, EVENT_VOICE_RAT_CHANGED, null);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
// Verify if message was posted to handler and value of result
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+ verify(mTestHandler)
+ .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
assertEquals(EVENT_VOICE_RAT_CHANGED, messageArgumentCaptor.getValue().what);
+
assertEquals(new Pair<Integer, Integer>(ServiceState.STATE_IN_SERVICE,
ServiceState.RIL_RADIO_TECHNOLOGY_LTE),
((AsyncResult)messageArgumentCaptor.getValue().obj).result);
@@ -1247,6 +1384,7 @@
mSimulatedCommands.setDataRegState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
+ waitForDelayedHandlerAction(mSSTTestHandler.getThreadHandler(), 500, 200);
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
// verify if registered handler has message posted to it
@@ -1398,14 +1536,11 @@
@Test
@SmallTest
- public void testSetPsNotifications() {
+ public void testSetPsNotifications() throws Exception {
int subId = 1;
sst.mSubId = subId;
doReturn(subId).when(mSubInfo).getSubscriptionId();
- doReturn(mSubInfo).when(mSubscriptionController).getActiveSubscriptionInfo(
- anyInt(), anyString(), nullable(String.class));
-
final NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mContextFixture.putBooleanResource(
@@ -1416,6 +1551,9 @@
when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
mContextFixture.putResource(com.android.internal.R.string.RestrictedOnDataTitle, "test1");
+ // Make sure getState() condition returns in service, without this at logs at times found to
+ // be out of service
+ sst.mSS.setState(ServiceState.STATE_IN_SERVICE);
sst.setNotification(ServiceStateTracker.PS_ENABLED);
ArgumentCaptor<Notification> notificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
@@ -1431,12 +1569,10 @@
@Test
@SmallTest
- public void testSetCsNotifications() {
+ public void testSetCsNotifications() throws Exception {
int subId = 1;
sst.mSubId = subId;
doReturn(subId).when(mSubInfo).getSubscriptionId();
- doReturn(mSubInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfo(anyInt(), anyString(), nullable(String.class));
final NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -1449,6 +1585,9 @@
mContextFixture.putResource(com.android.internal.R.string.RestrictedOnAllVoiceTitle,
"test2");
+ // Make sure getState() condition returns in service, without this at logs at times found to
+ // be out of service
+ sst.mSS.setState(ServiceState.STATE_IN_SERVICE);
sst.setNotification(ServiceStateTracker.CS_ENABLED);
ArgumentCaptor<Notification> notificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
@@ -1464,12 +1603,10 @@
@Test
@SmallTest
- public void testSetCsNormalNotifications() {
+ public void testSetCsNormalNotifications() throws Exception {
int subId = 1;
sst.mSubId = subId;
doReturn(subId).when(mSubInfo).getSubscriptionId();
- doReturn(mSubInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfo(anyInt(), anyString(), nullable(String.class));
final NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -1481,6 +1618,9 @@
when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
mContextFixture.putResource(com.android.internal.R.string.RestrictedOnNormalTitle, "test3");
+ // Make sure getState() condition returns in service, without this at logs at times found to
+ // be out of service
+ sst.mSS.setState(ServiceState.STATE_IN_SERVICE);
sst.setNotification(ServiceStateTracker.CS_NORMAL_ENABLED);
ArgumentCaptor<Notification> notificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
@@ -1496,12 +1636,10 @@
@Test
@SmallTest
- public void testSetCsEmergencyNotifications() {
+ public void testSetCsEmergencyNotifications() throws Exception {
int subId = 1;
sst.mSubId = subId;
doReturn(subId).when(mSubInfo).getSubscriptionId();
- doReturn(mSubInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfo(anyInt(), anyString(), nullable(String.class));
final NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -1514,6 +1652,9 @@
mContextFixture.putResource(com.android.internal.R.string.RestrictedOnEmergencyTitle,
"test4");
+ // Make sure mIsEmergencyOnly should be true, when setNotification notification type is
+ // CS_EMERGENCY_ENABLED notification
+ sst.mSS.setEmergencyOnly(true);
sst.setNotification(ServiceStateTracker.CS_EMERGENCY_ENABLED);
ArgumentCaptor<Notification> notificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
@@ -1536,6 +1677,9 @@
int otherSubId = 2;
sst.mSubId = otherSubId;
doReturn(subId).when(mSubInfo).getSubscriptionId();
+ doReturn(new SubscriptionInfoInternal.Builder().setId(2).setOpportunistic(1)
+ .setGroupUuid("fb767ff3-a395-4a53-bb17-3f853ae0eb87").build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
final NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -1716,29 +1860,6 @@
assertEquals(TelephonyManager.RADIO_POWER_UNAVAILABLE, mSimulatedCommands.getRadioState());
}
- @Test
- @SmallTest
- public void testImsRegisteredDelayShutDown() throws Exception {
- doReturn(false).when(mPhone).isUsingNewDataStack();
- doReturn(true).when(mPhone).isPhoneTypeGsm();
- mContextFixture.putIntResource(
- com.android.internal.R.integer.config_delay_for_ims_dereg_millis, 1000 /*ms*/);
- sst.setImsRegistrationState(true);
- mSimulatedCommands.setRadioPowerFailResponse(false);
- sst.setRadioPower(true);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
- // Turn off the radio and ensure radio power is still on
- assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
- sst.setRadioPower(false);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-
- // Now set IMS reg state to false and ensure we see the modem move to power off.
- sst.setImsRegistrationState(false);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
- }
@Test
@SmallTest
@@ -1759,33 +1880,6 @@
@Test
@SmallTest
- public void testImsRegisteredDelayShutDownTimeout() throws Exception {
- doReturn(false).when(mPhone).isUsingNewDataStack();
- doReturn(true).when(mPhone).isPhoneTypeGsm();
- mContextFixture.putIntResource(
- com.android.internal.R.integer.config_delay_for_ims_dereg_millis, 1000 /*ms*/);
- sst.setImsRegistrationState(true);
- mSimulatedCommands.setRadioPowerFailResponse(false);
- sst.setRadioPower(true);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
- // Turn off the radio and ensure radio power is still on
- assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
- sst.setRadioPower(false);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-
- // Ensure that if we never turn deregister for IMS, we still eventually see radio state
- // move to off.
- // Timeout for IMS reg + some extra time to remove race conditions
- waitForDelayedHandlerAction(mSSTTestHandler.getThreadHandler(),
- sst.getRadioPowerOffDelayTimeoutForImsRegistration() + 1000, 1000);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
- }
-
- @Test
- @SmallTest
public void testImsRegisteredAPMOnOffToggle() throws Exception {
doReturn(true).when(mPhone).isPhoneTypeGsm();
mContextFixture.putIntResource(
@@ -1814,12 +1908,6 @@
@SmallTest
public void testSetTimeFromNITZStr_withoutAge() throws Exception {
{
- // Mock sending incorrect nitz str from RIL
- mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0");
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- verify(mNitzStateMachine, times(0)).handleNitzReceived(any());
- }
- {
// Mock sending correct nitz str from RIL with a zero ageMs
String nitzStr = "15/06/20,00:00:00+0";
NitzData expectedNitzData = NitzData.parse(nitzStr);
@@ -1844,13 +1932,6 @@
@SmallTest
public void testSetTimeFromNITZStr_withAge() throws Exception {
{
- // Mock sending incorrect nitz str from RIL with a non-zero ageMs
- long ageMs = 60 * 1000;
- mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0", ageMs);
- waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
- verify(mNitzStateMachine, times(0)).handleNitzReceived(any());
- }
- {
// Mock sending correct nitz str from RIL with a non-zero ageMs
String nitzStr = "21/08/15,00:00:00+0";
long ageMs = 60 * 1000;
@@ -2191,16 +2272,16 @@
}
@Test
- public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
+ public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() {
// LTE Cell with bandwidth = 10000
CellIdentityLte cellIdentity10 =
new CellIdentityLte(1, 1, 1, 1, new int[] {1, 2}, 10000, "1", "1", "test",
"tst", Collections.emptyList(), null);
sendRegStateUpdateForLteCellId(cellIdentity10);
- assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+ assertArrayEquals(new int[]{10000}, sst.mSS.getCellBandwidths());
sendPhyChanConfigChange(new int[] {10000, 5000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
- assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ assertArrayEquals(new int[]{10000, 5000}, sst.mSS.getCellBandwidths());
}
@Test
@@ -2212,7 +2293,43 @@
}
@Test
- public void testPhyChanBandwidthResetsOnOos() throws Exception {
+ public void testNotifyServiceStateChangedOnPhysicalConfigUpdates() {
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL,
+ true);
+
+ // LTE Cell with bandwidth = 10000
+ CellIdentityLte cellIdentity11 =
+ new CellIdentityLte(1, 1, 1, 1, new int[] {1, 2}, 10000, "1", "1", "test",
+ "tst", Collections.emptyList(), null);
+
+ sendRegStateUpdateForLteCellId(cellIdentity11);
+
+ // Notify Service State changed for BandWidth Change
+ sendPhyChanConfigChange(new int[] {10000, 40000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
+ verify(mPhone, times(1)).notifyServiceStateChanged(any(ServiceState.class));
+
+ // Notify Service State changed for NR Connection Status: LTE -> NR
+ // with context id update
+ when(mPhone.getDataNetworkController().isInternetNetwork(eq(3))).thenReturn(true);
+ sendPhyChanConfigChange(new int[] {10000, 40000}, TelephonyManager.NETWORK_TYPE_NR, 1,
+ new int[][]{{0, 1}, {2, 3}});
+ verify(mPhone, times(2)).notifyServiceStateChanged(any(ServiceState.class));
+
+ // Service State changed is not notified since no change
+ when(mPhone.getDataNetworkController().isInternetNetwork(eq(3))).thenReturn(true);
+ sendPhyChanConfigChange(new int[] {10000, 40000}, TelephonyManager.NETWORK_TYPE_NR, 1,
+ new int[][]{{0, 1}, {2, 3}});
+ verify(mPhone, times(2)).notifyServiceStateChanged(any(ServiceState.class));
+
+ // Notify Service State changed for NR Connection Status: NR -> LTE
+ when(mPhone.getDataNetworkController().isInternetNetwork(eq(3))).thenReturn(true);
+ sendPhyChanConfigChange(new int[] {10000, 40000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
+ verify(mPhone, times(3)).notifyServiceStateChanged(any(ServiceState.class));
+ }
+
+ @Test
+ public void testPhyChanBandwidthResetsOnOos() {
testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
LteVopsSupportInfo lteVopsSupportInfo =
new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
@@ -2246,7 +2363,7 @@
sendPhyChanConfigChange(new int[] {10000, 5000}, TelephonyManager.NETWORK_TYPE_NR, 0);
sendRegStateUpdateForNrCellId(nrCi);
- assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ assertArrayEquals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths());
}
/**
@@ -2293,6 +2410,76 @@
assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, sst.mSS.getRilDataRadioTechnology());
}
+ private void changeRegStateWithIwlanOperatorNumeric(int psState, int psRat, int wlanState,
+ String[] opNamesResult, boolean passCellId) {
+ LteVopsSupportInfo lteVopsSupportInfo =
+ new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+ LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+ CellIdentityLte cellIdentity =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
+ "tst", Collections.emptyList(), null);
+ if (!passCellId) {
+ cellIdentity = null;
+ }
+ sst.mPollingContext[0] = 3;
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_OPERATOR,
+ new AsyncResult(sst.mPollingContext, opNamesResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+
+ // PS WWAN
+ NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ psState, psRat, 0, false, null, cellIdentity,
+ getPlmnFromCellIdentity(cellIdentity),
+ 1, false, false, false, lteVopsSupportInfo);
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ // PS WLAN
+ 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);
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_IWLAN_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataIwlanResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(opNamesResult[0], sst.getServiceState().getOperatorAlpha());
+ assertEquals(opNamesResult[2], sst.getServiceState().getOperatorNumeric());
+ verify(mLocaleTracker).updateOperatorNumeric(eq(opNamesResult[2]));
+ }
+
+ /**
+ * Ensure that LocaleTracker is not updated with mcc when only IWLAN is in service
+ */
+ @Test
+ public void testLocaleTrackerUpdateWithIWLANInService() {
+ // 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);
+ changeRegStateWithIwlanOperatorNumeric(NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, OpNamesResult, true);
+ verify(mLocaleTracker).updateOperatorNumeric(eq(OpNamesResult[2]));
+ changeRegStateWithIwlanOperatorNumeric(
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, OpNamesResult, true);
+ /* cellId based mccmnc */
+ verify(mLocaleTracker).updateOperatorNumeric(eq("00101"));
+ changeRegStateWithIwlanOperatorNumeric(
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, OpNamesResult, false);
+ verify(mLocaleTracker).updateOperatorNumeric(eq(""));
+ }
+
@Test
public void testGetServiceProviderNameWithBrandOverride() {
String brandOverride = "spn from brand override";
@@ -2358,7 +2545,7 @@
@Test
@SmallTest
- public void testGetMdn() throws Exception {
+ public void testGetMdn() {
doReturn(false).when(mPhone).isPhoneTypeGsm();
doReturn(false).when(mPhone).isPhoneTypeCdma();
doReturn(true).when(mPhone).isPhoneTypeCdmaLte();
@@ -2659,13 +2846,20 @@
@Test
public void testUpdateSpnDisplay_spnNotEmptyAndCrossSimCallingEnabled_showSpnOnly() {
// GSM phone
-
doReturn(true).when(mPhone).isPhoneTypeGsm();
+ final CellIdentityLte cellIdentityLte =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
+ "tst", Collections.emptyList(), null);
// In Service
ServiceState ss = new ServiceState();
ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+ //To By Pass RatRacheter
+ ss.addNetworkRegistrationInfo(makeNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ cellIdentityLte, true));
sst.mSS = ss;
// cross-sim-calling is enable
@@ -2694,15 +2888,24 @@
public void testUpdateSpnDisplay_spnNotEmptyAndWifiCallingEnabled_showSpnOnly() {
// GSM phone
doReturn(true).when(mPhone).isPhoneTypeGsm();
+ final CellIdentityLte cellIdentityLte =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
+ "tst", Collections.emptyList(), null);
// In Service
ServiceState ss = new ServiceState();
ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+ //To By Pass RatRacheter
+ ss.addNetworkRegistrationInfo(makeNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ cellIdentityLte, true));
sst.mSS = ss;
// wifi-calling is enabled
doReturn(true).when(mPhone).isWifiCallingEnabled();
+ doReturn(true).when(mPhone).isImsRegistered();
// update the spn
sst.updateSpnDisplay();
@@ -2722,7 +2925,7 @@
public void testUpdateSpnDisplay_spnEmptyAndWifiCallingEnabled_showPlmnOnly() {
// set empty service provider name
mBundle.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING, "");
- sendCarrierConfigUpdate();
+ sendCarrierConfigUpdate(PHONE_ID);
// GSM phone
doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -2735,6 +2938,7 @@
// wifi-calling is enabled
doReturn(true).when(mPhone).isWifiCallingEnabled();
+ doReturn(true).when(mPhone).isImsRegistered();
// update the spn
sst.updateSpnDisplay();
@@ -2801,7 +3005,7 @@
public void testUpdateSpnDisplayLegacy_WlanServiceNoWifiCalling_displayOOS() {
mBundle.putBoolean(
CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false);
- sendCarrierConfigUpdate();
+ sendCarrierConfigUpdate(PHONE_ID);
// GSM phone
doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -2933,6 +3137,7 @@
@Test
public void testGetCombinedRegState() {
doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
// If voice/data out of service, return out of service.
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
@@ -2972,4 +3177,35 @@
doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
assertEquals(ServiceState.STATE_EMERGENCY_ONLY, sst.getCombinedRegState(mServiceState));
}
+
+
+ @Test
+ public void testOnChangingPreferredOnIwlan() throws Exception {
+ Field callbackField =
+ ServiceStateTracker.class.getDeclaredField("mAccessNetworksManagerCallback");
+ callbackField.setAccessible(true);
+ AccessNetworksManager.AccessNetworksManagerCallback accessNetworksManagerCallback =
+ (AccessNetworksManager.AccessNetworksManagerCallback) callbackField.get(sst);
+
+ when(mAccessNetworksManager.isAnyApnOnIwlan()).thenReturn(false);
+
+ // Start state: Cell data only LTE + IWLAN
+ CellIdentityLte cellIdentity =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
+ "tst", Collections.emptyList(), null);
+ changeRegStateWithIwlan(
+ // WWAN
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, cellIdentity,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, TelephonyManager.NETWORK_TYPE_LTE,
+ // WLAN
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_IWLAN);
+ assertFalse(sst.mSS.isIwlanPreferred());
+
+ when(mAccessNetworksManager.isAnyApnOnIwlan()).thenReturn(true);
+ accessNetworksManagerCallback.onPreferredTransportChanged(0);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ assertTrue(sst.mSS.isIwlanPreferred());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
index 583c22b..b2f118d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
@@ -16,19 +16,25 @@
package com.android.internal.telephony;
+import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP;
import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
+import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP;
import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -44,14 +50,18 @@
import android.telephony.SignalStrength;
import android.telephony.SignalStrengthUpdateRequest;
import android.telephony.SignalThresholdInfo;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.MediumTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.util.ArrayUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -59,12 +69,11 @@
import java.util.Map;
/**
- * Unit test for {@link SignalStrengthUpdateRequest}.
+ * Unit test for {@link SignalStrengthController}.
*/
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class SignalStrengthControllerTest extends TelephonyTest {
-
private static final String TAG = "SignalStrengthControllerTest";
private static final int ACTIVE_SUB_ID = 0;
@@ -86,24 +95,48 @@
private SignalStrengthController mSsc;
private PersistableBundle mBundle;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
@Before
public void setUp() throws Exception {
super.setUp(this.getClass().getSimpleName());
mHandler = Mockito.mock(Handler.class);
when(mPhone.getSubId()).thenReturn(ACTIVE_SUB_ID);
- mSsc = new SignalStrengthController(mPhone);
- replaceInstance(Handler.class, "mLooper", mHandler, mSsc.getLooper());
- replaceInstance(Phone.class, "mLooper", mPhone, mSsc.getLooper());
+ // Config a fixed supported RAN/MeasurementTypes to make the test more stable
mBundle = mContextFixture.getCarrierConfigBundle();
+ // Support GERAN with RSSI
+ mBundle.putIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -109, /* SIGNAL_STRENGTH_POOR */
+ -103, /* SIGNAL_STRENGTH_MODERATE */
+ -97, /* SIGNAL_STRENGTH_GOOD */
+ -89, /* SIGNAL_STRENGTH_GREAT */
+ });
+ mBundle.putInt(CarrierConfigManager.KEY_GERAN_RSSI_HYSTERESIS_DB_INT, 6);
+ // Support EUTRAN with RSRP
+ mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
+ 1 /* USE_RSRP */);
+ mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -115, /* SIGNAL_STRENGTH_POOR */
+ -105, /* SIGNAL_STRENGTH_MODERATE */
+ -95, /* SIGNAL_STRENGTH_GOOD */
+ -85, /* SIGNAL_STRENGTH_GREAT */
+ });
+ mBundle.putInt(CarrierConfigManager.KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT, 3);
+ // Support NR with SSRSRP
+ mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+ 1 /* USE_SSRSRP */);
mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
new int[] {
-110, /* SIGNAL_STRENGTH_POOR */
-90, /* SIGNAL_STRENGTH_MODERATE */
-80, /* SIGNAL_STRENGTH_GOOD */
- -65, /* SIGNAL_STRENGTH_GREAT */
+ -64, /* SIGNAL_STRENGTH_GREAT */
});
+ mBundle.putInt(CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT, 1);
+ // By default, NR with SSRSRQ and SSSINR is not supported
mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
new int[] {
-31, /* SIGNAL_STRENGTH_POOR */
@@ -118,7 +151,20 @@
15, /* SIGNAL_STRENGTH_GOOD */
30 /* SIGNAL_STRENGTH_GREAT */
});
+
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+ mSsc = new SignalStrengthController(mPhone);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
+
+ replaceInstance(Handler.class, "mLooper", mHandler, mSsc.getLooper());
+ replaceInstance(Phone.class, "mLooper", mPhone, mSsc.getLooper());
+
processAllMessages();
+ reset(mSimulatedCommandsVerifier);
}
@After
@@ -475,6 +521,212 @@
}
@Test
+ public void testSetMinimumHysteresisDb_FromThresholdDelta() {
+ final int[] consolidatedThresholdList = new int[] {-120, -116, -113, -112};
+
+ SignalThresholdInfo info =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[] {-113}, true)
+ .setHysteresisDb(2)
+ .build();
+ SignalStrengthUpdateRequest request =
+ createTestSignalStrengthUpdateRequest(
+ info,
+ false /* shouldReportWhileIdle*/,
+ false /* shouldReportSystemWhileIdle */);
+ mSsc.setSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+
+ int minHysteresis =
+ mSsc.getMinimumHysteresisDb(true,
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ SIGNAL_MEASUREMENT_TYPE_RSSI,
+ consolidatedThresholdList);
+ assertEquals(1, minHysteresis);
+ mSsc.clearSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+ }
+
+ @Test
+ public void testSetMinimumHysteresisDb_FromSignalThresholdRequest() {
+ final int[] consolidatedThresholdList = new int[] {-120, -116, -112, -108};
+
+ SignalThresholdInfo info =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setThresholds(new int[] {-113}, true)
+ .setHysteresisDb(3)
+ .build();
+ SignalStrengthUpdateRequest request =
+ createTestSignalStrengthUpdateRequest(
+ info,
+ false /* shouldReportWhileIdle*/,
+ false /* shouldReportSystemWhileIdle */);
+ mSsc.setSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+
+ int minHysteresis =
+ mSsc.getMinimumHysteresisDb(true,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ SIGNAL_MEASUREMENT_TYPE_RSRP,
+ consolidatedThresholdList);
+ assertEquals(3, minHysteresis);
+
+ mSsc.clearSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+ }
+
+ @Test
+ public void testSetMinimumHysteresisDb_FromCarrierConfig() {
+ final int[] consolidatedThresholdList = new int[] {-120, -115, -108, -103};
+
+ SignalThresholdInfo info =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setThresholds(new int[] {-113}, true)
+ .setHysteresisDb(6)
+ .build();
+ SignalStrengthUpdateRequest request =
+ createTestSignalStrengthUpdateRequest(
+ info,
+ false /* shouldReportWhileIdle*/,
+ false /* shouldReportSystemWhileIdle */);
+ mSsc.setSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+
+ int minHysteresis =
+ mSsc.getMinimumHysteresisDb(true,
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ consolidatedThresholdList);
+ assertEquals(1, minHysteresis);
+ mSsc.clearSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+ }
+
+ @Test
+ public void testSetHysteresisDb_WithCarrierConfigValue() {
+ when(mPhone.isDeviceIdle()).thenReturn(true);
+ when(mPhone.getSubId()).thenReturn(ACTIVE_SUB_ID);
+
+ mBundle.putInt(CarrierConfigManager.KEY_GERAN_RSSI_HYSTERESIS_DB_INT, 5);
+ mBundle.putInt(CarrierConfigManager.KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT, 3);
+ mBundle.putInt(CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT, 2);
+ sendCarrierConfigUpdate();
+
+ ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mSimulatedCommandsVerifier, atLeastOnce())
+ .setSignalStrengthReportingCriteria(signalThresholdInfoCaptor.capture(), isNull());
+ List<SignalThresholdInfo> capturedInfos = signalThresholdInfoCaptor.getAllValues().get(0);
+ assertThat(capturedInfos).isNotEmpty();
+
+ for (SignalThresholdInfo signalThresholdInfo : capturedInfos) {
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_RSRP) {
+ assertEquals(3, signalThresholdInfo.getHysteresisDb());
+ }
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_RSSI) {
+ assertEquals(5, signalThresholdInfo.getHysteresisDb());
+ }
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_SSRSRP) {
+ assertEquals(2, signalThresholdInfo.getHysteresisDb());
+ }
+ }
+ reset(mSimulatedCommandsVerifier);
+ }
+
+ @Test
+ public void testSetHysteresisDb_BetweenCarrierConfigSignalThresholdInfoThresholdDelta() {
+ SignalThresholdInfo info =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setThresholds(new int[] {-116}, true)
+ .setHysteresisDb(3)
+ .build();
+ SignalStrengthUpdateRequest request =
+ createTestSignalStrengthUpdateRequest(
+ info,
+ false /* shouldReportWhileIdle*/,
+ false /* shouldReportSystemWhileIdle */);
+ mSsc.setSignalStrengthUpdateRequest(
+ ACTIVE_SUB_ID, CALLING_UID, request, Message.obtain(mHandler));
+ processAllMessages();
+
+ reset(mSimulatedCommandsVerifier);
+ when(mPhone.isDeviceIdle()).thenReturn(false);
+ when(mPhone.getSubId()).thenReturn(ACTIVE_SUB_ID);
+ mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -113, /* SIGNAL_STRENGTH_POOR */
+ -107, /* SIGNAL_STRENGTH_MODERATE */
+ -100, /* SIGNAL_STRENGTH_GOOD */
+ -95, /* SIGNAL_STRENGTH_GREAT */
+ });
+
+ mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+ 1 /* USE_SSRSRP */);
+ mBundle.putInt(CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT, 4);
+ sendCarrierConfigUpdate();
+
+ ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mSimulatedCommandsVerifier, atLeastOnce())
+ .setSignalStrengthReportingCriteria(signalThresholdInfoCaptor.capture(), isNull());
+ List<SignalThresholdInfo> capturedInfos = signalThresholdInfoCaptor.getAllValues().get(0);
+ assertThat(capturedInfos).isNotEmpty();
+
+ for (SignalThresholdInfo signalThresholdInfo : capturedInfos) {
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_SSRSRP) {
+ assertEquals(4,
+ mBundle.getInt(CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT));
+ assertEquals(3, signalThresholdInfo.getHysteresisDb());
+ }
+ }
+ }
+
+ @Test
+ public void testSetHysteresisDb_WithInvalidCarrierConfigValue() {
+ when(mPhone.isDeviceIdle()).thenReturn(true);
+ when(mPhone.getSubId()).thenReturn(ACTIVE_SUB_ID);
+
+ mBundle.putInt(CarrierConfigManager.KEY_GERAN_RSSI_HYSTERESIS_DB_INT, -4);
+ mBundle.putInt(CarrierConfigManager.KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT, -5);
+ mBundle.putInt(CarrierConfigManager.KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT, -2);
+ sendCarrierConfigUpdate();
+
+ ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mSimulatedCommandsVerifier, atLeastOnce())
+ .setSignalStrengthReportingCriteria(signalThresholdInfoCaptor.capture(), isNull());
+ List<SignalThresholdInfo> capturedInfos = signalThresholdInfoCaptor.getAllValues().get(0);
+ assertThat(capturedInfos).isNotEmpty();
+
+ for (SignalThresholdInfo signalThresholdInfo : capturedInfos) {
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_RSRP) {
+ assertEquals(2, signalThresholdInfo.getHysteresisDb());
+ }
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_RSSI) {
+ assertEquals(2, signalThresholdInfo.getHysteresisDb());
+ }
+ if (signalThresholdInfo.getSignalMeasurementType() == SIGNAL_MEASUREMENT_TYPE_SSRSRP) {
+ assertEquals(2, signalThresholdInfo.getHysteresisDb());
+ }
+ }
+ reset(mSimulatedCommandsVerifier);
+ }
+
+ @Test
public void testLteSignalStrengthReportingCriteria_convertRssnrUnitFromTenDbToDB() {
SignalStrength ss = new SignalStrength(
new CellSignalStrengthCdma(),
@@ -654,15 +906,76 @@
assertEquals(mSsc.getSignalStrength().getLevel(), CellSignalStrength.SIGNAL_STRENGTH_GOOD);
}
+ /**
+ * Verify on both high-power and idle modes. All SignalThresholdInfo should be disabled if the
+ * threshold array is empty when calling CI#setSignalStrengthReportingCriteria.
+ */
+ @Test
+ public void consolidateAndSetReportingCriteria_allEmptyThresholdShouldBeDisabled() {
+ // Firstly, test on high-power mode
+ when(mPhone.isDeviceIdle()).thenReturn(false);
+ SignalThresholdInfo info = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setThresholds(new int[]{-112}, true /* isSystem */)
+ .build();
+ SignalStrengthUpdateRequest request = createTestSignalStrengthUpdateRequest(
+ info,
+ true /* shouldReportWhileIdle*/,
+ false /* shouldReportSystemWhileIdle */
+ );
+
+ mSsc.setSignalStrengthUpdateRequest(ACTIVE_SUB_ID, CALLING_UID,
+ request, Message.obtain(mHandler));
+ processAllMessages();
+
+ // Expect 3 non-empty thresholds (GERAN/RSSI, EUTRAN/RSRP, NR/SSRSRP)
+ // if Radio HAL ver is >= 1.5
+ verifyAllEmptyThresholdAreDisabledWhenSetSignalStrengthReportingCriteria(
+ 3 /*expectedNonEmptyThreshold*/);
+
+ // Then, test when device turns into idle mode in which all system thresholds are emptied
+ // (shouldReportSystemWhileIdle is false)
+ reset(mSimulatedCommandsVerifier);
+ when(mPhone.isDeviceIdle()).thenReturn(true);
+ mSsc.onDeviceIdleStateChanged(true /* isDeviceIdle */);
+ processAllMessages();
+
+ // Expect 1 non-empty threshold left (EUTRAN/RSRP set by the SignalStrengthUpdateRequest)
+ verifyAllEmptyThresholdAreDisabledWhenSetSignalStrengthReportingCriteria(
+ 1 /*expectedNonEmptyThreshold*/);
+ }
+
+ private void verifyAllEmptyThresholdAreDisabledWhenSetSignalStrengthReportingCriteria(
+ int expectedNonEmptyThreshold) {
+ ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mSimulatedCommandsVerifier).setSignalStrengthReportingCriteria(
+ signalThresholdInfoCaptor.capture(), isNull());
+ List<SignalThresholdInfo> capturedInfos = signalThresholdInfoCaptor.getAllValues().get(0);
+ assertThat(capturedInfos).isNotEmpty();
+ int actualNonEmptyThreshold = 0;
+ for (SignalThresholdInfo signalThresholdInfo: capturedInfos) {
+ if (ArrayUtils.isEmpty(signalThresholdInfo.getThresholds())) {
+ assertThat(signalThresholdInfo.isEnabled()).isFalse();
+ } else {
+ actualNonEmptyThreshold++;
+ }
+ }
+ // Only check on RADIO hal 1.5 and above to make it less flaky
+ if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+ assertThat(expectedNonEmptyThreshold).isEqualTo(actualNonEmptyThreshold);
+ }
+ }
+
private void sendCarrierConfigUpdate() {
CarrierConfigManager mockConfigManager = Mockito.mock(CarrierConfigManager.class);
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mockConfigManager);
when(mockConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
- Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, PHONE_ID);
- mContext.sendBroadcast(intent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(PHONE_ID, ACTIVE_SUB_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
}
@@ -697,4 +1010,4 @@
}
return builder.build();
}
-}
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
index f15845c..96184c5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
@@ -98,7 +98,7 @@
new CellSignalStrengthWcdma(-94, 4, -102, -5),
new CellSignalStrengthTdscdma(-95, 2, -103),
new CellSignalStrengthLte(-85, -91, -6, -10, 1, 12, 1),
- new CellSignalStrengthNr(-91, -6, 3, 1, NrCqiReport, -80, -7, 4));
+ new CellSignalStrengthNr(-91, -6, 3, 1, NrCqiReport, -80, -7, 4, 1));
assertParcelingIsLossless(s);
PersistableBundle bundle = new PersistableBundle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
index b282c55..e22d7ab 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
@@ -42,69 +42,94 @@
public class SignalThresholdInfoTest extends TestCase {
private static final int HYSTERESIS_DB = 2;
private static final int HYSTERESIS_MS = 30;
- private static final int[] SSRSRP_THRESHOLDS = new int[]{-120, -100, -80, -60};
+ private static final int[] SSRSRP_THRESHOLDS = new int[] {-120, -100, -80, -60};
// Map of SignalMeasurementType to invalid thresholds edge values.
// Each invalid value will be constructed with a thresholds array to test separately.
- private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP = Map.of(
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
- List.of(SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
- List.of(SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
- List.of(SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
- List.of(SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
- List.of(SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
- List.of(SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
- List.of(SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
- List.of(SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
- SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1)
- );
+ private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP =
+ Map.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ List.of(
+ SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+ List.of(
+ SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ List.of(
+ SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ List.of(
+ SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ List.of(
+ SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ List.of(
+ SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ List.of(
+ SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
+ List.of(
+ SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+ List.of(
+ SignalThresholdInfo.SIGNAL_ECNO_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_ECNO_MAX_VALUE + 1));
// Map of RAN to allowed SignalMeasurementType set.
// RAN/TYPE pair will be used to verify the validation of the combo
- private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP = Map.of(
- AccessNetworkConstants.AccessNetworkType.GERAN,
- Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
- AccessNetworkConstants.AccessNetworkType.CDMA2000,
- Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
- AccessNetworkConstants.AccessNetworkType.UTRAN,
- Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP),
- AccessNetworkConstants.AccessNetworkType.EUTRAN,
- Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
- AccessNetworkConstants.AccessNetworkType.NGRAN,
- Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
- );
+ private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP =
+ Map.of(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ Set.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO),
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ Set.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ Set.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR));
// Deliberately picking up the max/min value in each range to test the edge cases
- private final int[] mRssiThresholds = new int[]{-113, -103, -97, -51};
- private final int[] mRscpThresholds = new int[]{-120, -105, -95, -25};
- private final int[] mRsrpThresholds = new int[]{-140, -118, -108, -44};
- private final int[] mRsrqThresholds = new int[]{-34, -17, -14, 3};
- private final int[] mRssnrThresholds = new int[]{-20, 10, 20, 30};
- private final int[] mSsrsrpThresholds = new int[]{-140, -118, -98, -44};
- private final int[] mSsrsrqThresholds = new int[]{-43, -17, -14, 20};
- private final int[] mSssinrThresholds = new int[]{-23, -16, -10, 40};
+ private final int[] mRssiThresholds = new int[] {-113, -103, -97, -51};
+ private final int[] mRscpThresholds = new int[] {-120, -105, -95, -25};
+ private final int[] mRsrpThresholds = new int[] {-140, -118, -108, -44};
+ private final int[] mRsrqThresholds = new int[] {-34, -17, -14, 3};
+ private final int[] mRssnrThresholds = new int[] {-20, 10, 20, 30};
+ private final int[] mSsrsrpThresholds = new int[] {-140, -118, -98, -44};
+ private final int[] mSsrsrqThresholds = new int[] {-43, -17, -14, 20};
+ private final int[] mSssinrThresholds = new int[] {-23, -16, -10, 40};
+ private final int[] mEcnoThresholds = new int[] {-24, -16, -8, 1};
- private final int[][] mThresholds = {mRssiThresholds, mRscpThresholds, mRsrpThresholds,
- mRsrqThresholds, mRssnrThresholds, mSsrsrpThresholds, mSsrsrqThresholds,
- mSssinrThresholds};
+ private final int[][] mThresholds = {
+ mRssiThresholds,
+ mRscpThresholds,
+ mRsrpThresholds,
+ mRsrqThresholds,
+ mRssnrThresholds,
+ mSsrsrpThresholds,
+ mSsrsrqThresholds,
+ mSssinrThresholds,
+ mEcnoThresholds
+ };
@Test
@SmallTest
@@ -120,12 +145,14 @@
.setIsEnabled(false)
.build();
- assertEquals(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ assertEquals(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
signalThresholdInfo.getSignalMeasurementType());
assertEquals(HYSTERESIS_MS, signalThresholdInfo.getHysteresisMs());
assertEquals(HYSTERESIS_DB, signalThresholdInfo.getHysteresisDb());
- assertEquals(Arrays.toString(SSRSRP_THRESHOLDS), Arrays.toString(
- signalThresholdInfo.getThresholds()));
+ assertEquals(
+ Arrays.toString(SSRSRP_THRESHOLDS),
+ Arrays.toString(signalThresholdInfo.getThresholds()));
assertFalse(signalThresholdInfo.isEnabled());
}
@@ -160,68 +187,127 @@
@SmallTest
public void testGetSignalThresholdInfo() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
- .setHysteresisMs(0)
- .setHysteresisDb(0)
- .setThresholds(new int[]{}, true /*isSystem*/)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
- .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRssiThresholds)
- .setIsEnabled(false)
- .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(0)
+ .setHysteresisDb(0)
+ .setThresholds(new int[] {}, true /*isSystem*/)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build());
- assertThat(stList.get(0).getThresholds()).isEqualTo(new int[]{});
- assertThat(stList.get(1).getSignalMeasurementType()).isEqualTo(
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
+ assertThat(stList.get(0).getThresholds()).isEqualTo(new int[] {});
+ assertThat(stList.get(1).getSignalMeasurementType())
+ .isEqualTo(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
assertThat(stList.get(1).getThresholds()).isEqualTo(mRssiThresholds);
+ assertThat(stList.get(0).getHysteresisDb())
+ .isEqualTo(SignalThresholdInfo.HYSTERESIS_DB_MINIMUM);
+ assertThat(stList.get(1).getHysteresisDb())
+ .isEqualTo(HYSTERESIS_DB);
}
@Test
@SmallTest
public void testEqualsSignalThresholdInfo() {
- final int[] dummyThresholds = new int[]{-100, -90, -70, -60};
- final int[] dummyThreholdsDisordered = new int[]{-60, -90, -100, -70};
- SignalThresholdInfo st1 = new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(1).setSignalMeasurementType(1)
- .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRssiThresholds).setIsEnabled(false)
- .build();
- SignalThresholdInfo st2 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(2)
- .setSignalMeasurementType(2).setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
- .build();
- SignalThresholdInfo st3 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
- .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThresholds).setIsEnabled(false)
- .build();
- SignalThresholdInfo st4 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
- .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
- .build();
- SignalThresholdInfo st5 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
- .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThreholdsDisordered)
- .setIsEnabled(false).build();
+ final int[] dummyThresholds = new int[] {-100, -90, -70, -60};
+ final int[] dummyThresholdsDisordered = new int[] {-60, -90, -100, -70};
+ SignalThresholdInfo st1 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st2 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(2)
+ .setSignalMeasurementType(2)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st3 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(dummyThresholds)
+ .setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st4 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st5 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(dummyThresholdsDisordered)
+ .setIsEnabled(false)
+ .build();
- //Return true if all SignalThresholdInfo values match.
+ // Return true if all SignalThresholdInfo values match.
assertTrue(st1.equals(st1));
assertFalse(st1.equals(st2));
assertFalse(st1.equals(st3));
assertTrue(st1.equals(st4));
- //Threshold values ordering doesn't matter
+ // Threshold values ordering doesn't matter
assertTrue(st3.equals(st5));
- //Return false if the object of argument is other than SignalThresholdInfo.
+ // Return false if the object of argument is other than SignalThresholdInfo.
assertFalse(st1.equals(new String("test")));
}
@Test
@SmallTest
+ public void testHysteresisDbSettings_WithValidRange() {
+ SignalThresholdInfo st1 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[] {}, true)
+ .build();
+ SignalThresholdInfo st2 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[] {}, true)
+ .setHysteresisDb(3)
+ .build();
+ SignalThresholdInfo st3 =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[] {}, true)
+ .setHysteresisDb(1)
+ .build();
+ assertThat(st1.getHysteresisDb()).isEqualTo(HYSTERESIS_DB);
+ assertThat(st2.getHysteresisDb()).isEqualTo(3);
+ assertThat(st3.getHysteresisDb()).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
public void testBuilderWithValidParameters() {
ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithPublicFields();
@@ -229,7 +315,7 @@
SignalThresholdInfo st = stList.get(i);
assertThat(st.getThresholds()).isEqualTo(mThresholds[i]);
assertThat(st.getHysteresisMs()).isEqualTo(SignalThresholdInfo.HYSTERESIS_MS_DISABLED);
- assertThat(st.getHysteresisDb()).isEqualTo(SignalThresholdInfo.HYSTERESIS_DB_DISABLED);
+ assertThat(st.getHysteresisDb()).isEqualTo(HYSTERESIS_DB);
assertFalse(st.isEnabled());
}
}
@@ -238,33 +324,46 @@
@SmallTest
public void testBuilderWithInvalidParameter() {
// Invalid signal measurement type
- int[] invalidSignalMeasurementTypes = new int[]{-1, 0, 9};
+ int[] invalidSignalMeasurementTypes = new int[] {-1, 0, 9};
for (int signalMeasurementType : invalidSignalMeasurementTypes) {
buildWithInvalidParameterThrowException(
- AccessNetworkConstants.AccessNetworkType.GERAN, signalMeasurementType,
- new int[]{-1});
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ signalMeasurementType,
+ new int[] {-1}, 2);
}
// Null thresholds array
- buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, null);
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ null, 0);
// Empty thresholds
- buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
- SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, new int[]{});
-
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ new int[] {}, 5);
// Too long thresholds array
- buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
- new int[]{-100, -90, -70, -60, -58});
+ new int[] {-100, -90, -70, -60, -58}, 3);
+
+ // Test Hysteresis Db invalid Range
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ new int[] {-100, -90, -70, -60}, -1);
// Thresholds value out of range
for (int signalMeasurementType : INVALID_THRESHOLDS_MAP.keySet()) {
List<Integer> invalidThresholds = INVALID_THRESHOLDS_MAP.get(signalMeasurementType);
for (int threshold : invalidThresholds) {
- buildWithInvalidParameterThrowException(getValidRan(signalMeasurementType),
- signalMeasurementType, new int[]{threshold});
+ buildWithInvalidParameterThrowException(
+ getValidRan(signalMeasurementType),
+ signalMeasurementType,
+ new int[] {threshold}, 1);
}
}
@@ -272,21 +371,23 @@
for (int ran : VALID_RAN_TO_MEASUREMENT_TYPE_MAP.keySet()) {
Set validTypes = VALID_RAN_TO_MEASUREMENT_TYPE_MAP.get(ran);
for (int type = SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
- type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR; type++) {
+ type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO;
+ type++) {
if (!validTypes.contains(type)) {
- buildWithInvalidParameterThrowException(ran, type, new int[]{-1});
+ buildWithInvalidParameterThrowException(ran, type, new int[] {-1}, 2);
}
}
}
}
- private void buildWithInvalidParameterThrowException(int ran, int signalMeasurementType,
- int[] thresholds) {
+ private void buildWithInvalidParameterThrowException(
+ int ran, int signalMeasurementType, int[] thresholds, int hysteresisDb) {
try {
new SignalThresholdInfo.Builder()
.setRadioAccessNetworkType(ran)
.setSignalMeasurementType(signalMeasurementType)
.setThresholds(thresholds)
+ .setHysteresisDb(hysteresisDb)
.build();
fail("exception expected");
} catch (IllegalArgumentException | NullPointerException expected) {
@@ -296,68 +397,90 @@
private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithAllFields() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
- .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRssiThresholds).setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRscpThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRsrpThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRsrqThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mRssnrThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mSsrsrpThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mSsrsrqThresholds)
- .setIsEnabled(false)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
- .setHysteresisMs(HYSTERESIS_MS)
- .setHysteresisDb(HYSTERESIS_DB)
- .setThresholds(mSssinrThresholds)
- .setIsEnabled(false)
- .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRscpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssnrThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSssinrThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mEcnoThresholds)
+ .setIsEnabled(false)
+ .build());
return stList;
}
@@ -365,46 +488,63 @@
private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithPublicFields() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
- .setThresholds(mRssiThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
- .setThresholds(mRscpThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
- .setThresholds(mRsrpThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
- .setThresholds(mRsrqThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
- .setThresholds(mRssnrThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
- .setThresholds(mSsrsrpThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
- .setThresholds(mSsrsrqThresholds)
- .build());
- stList.add(new SignalThresholdInfo.Builder()
- .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
- .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
- .setThresholds(mSssinrThresholds)
- .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(mRssiThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setThresholds(mRscpThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setThresholds(mRsrpThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setThresholds(mRsrqThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setThresholds(mRssnrThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setThresholds(mSsrsrpThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setThresholds(mSsrsrqThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setThresholds(mSssinrThresholds)
+ .build());
+ stList.add(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO)
+ .setThresholds(mEcnoThresholds)
+ .build());
return stList;
}
@@ -418,6 +558,7 @@
case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
return AccessNetworkConstants.AccessNetworkType.GERAN;
case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO:
return AccessNetworkConstants.AccessNetworkType.UTRAN;
case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
index 0bacc1d..48406de 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
@@ -42,7 +42,6 @@
List<SmsRawData> records = sms.getAllMessagesFromIccEfForSubscriber(
preferredSmsSubscription, ActivityThread.currentPackageName());
assertNotNull(records);
- assertTrue(records.size() >= 0);
int firstNullIndex = -1;
int firstValidIndex = -1;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index e5ac907..1e4c939 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -21,6 +21,7 @@
import android.hardware.radio.V1_0.DataRegStateResult;
import android.hardware.radio.V1_0.SetupDataCallResult;
import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.hardware.radio.modem.ImeiInfo;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
import android.os.AsyncResult;
@@ -66,6 +67,7 @@
import com.android.internal.telephony.RILUtils;
import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.SmsResponse;
+import com.android.internal.telephony.SrvccConnection;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
@@ -181,12 +183,22 @@
private boolean mDcSuccess = true;
private SetupDataCallResult mSetupDataCallResult;
private boolean mIsRadioPowerFailResponse = false;
+ private boolean mIsReportSmsMemoryStatusFailResponse = false;
public boolean mSetRadioPowerForEmergencyCall;
public boolean mSetRadioPowerAsSelectedPhoneForEmergencyCall;
+ public boolean mCallWaitActivated = false;
+ private SrvccConnection[] mSrvccConnections;
+
// mode for Icc Sim Authentication
private int mAuthenticationMode;
+
+ private int[] mImsRegistrationInfo = new int[4];
+
+ private boolean mN1ModeEnabled = false;
+ private boolean mVonrEnabled = false;
+
//***** Constructor
public
SimulatedCommands() {
@@ -1298,10 +1310,19 @@
@Override
public void reportSmsMemoryStatus(boolean available, Message result) {
- resultSuccess(result, null);
+ if (!mIsReportSmsMemoryStatusFailResponse) {
+ resultSuccess(result, null);
+ } else {
+ CommandException ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
+ resultFail(result, null, ex);
+ }
SimulatedCommandsVerifier.getInstance().reportSmsMemoryStatus(available, result);
}
+ public void setReportSmsMemoryStatusFailResponse(boolean fail) {
+ mIsReportSmsMemoryStatusFailResponse = fail;
+ }
+
@Override
public void reportStkServiceIsRunning(Message result) {
resultSuccess(result, null);
@@ -1422,6 +1443,14 @@
@Override
public void queryCallWaiting(int serviceClass, Message response) {
+ if (response != null && serviceClass == SERVICE_CLASS_NONE) {
+ int[] r = new int[2];
+ r[0] = (mCallWaitActivated ? 1 : 0);
+ r[1] = (mCallWaitActivated ? SERVICE_CLASS_VOICE : SERVICE_CLASS_NONE);
+ resultSuccess(response, r);
+ return;
+ }
+
unimplemented(response);
}
@@ -1430,11 +1459,15 @@
* @param serviceClass is a sum of SERVICE_CLASS_*
* @param response is callback message
*/
-
@Override
public void setCallWaiting(boolean enable, int serviceClass,
Message response) {
- unimplemented(response);
+ if ((serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+ mCallWaitActivated = enable;
+ }
+ if (response != null) {
+ resultSuccess(response, null);
+ }
}
/**
@@ -1467,12 +1500,17 @@
}
@Override
- public void setNetworkSelectionModeAutomatic(Message result) {unimplemented(result);}
+ public void setNetworkSelectionModeAutomatic(Message result) {
+ SimulatedCommandsVerifier.getInstance().setNetworkSelectionModeAutomatic(result);
+ mMockNetworkSelectionMode = 0;
+ }
@Override
public void exitEmergencyCallbackMode(Message result) {unimplemented(result);}
@Override
public void setNetworkSelectionModeManual(String operatorNumeric, int ran, Message result) {
- unimplemented(result);
+ SimulatedCommandsVerifier.getInstance().setNetworkSelectionModeManual(
+ operatorNumeric, ran, result);
+ mMockNetworkSelectionMode = 1;
}
/**
@@ -1489,10 +1527,13 @@
getNetworkSelectionModeCallCount.incrementAndGet();
int ret[] = new int[1];
- ret[0] = 0;
+ ret[0] = mMockNetworkSelectionMode;
resultSuccess(result, ret);
}
+ /** 0 for automatic selection and a 1 for manual selection. */
+ private int mMockNetworkSelectionMode = 0;
+
private final AtomicInteger getNetworkSelectionModeCallCount = new AtomicInteger(0);
@VisibleForTesting
@@ -1782,6 +1823,16 @@
}
@Override
+ public void getImei(Message response) {
+ SimulatedCommandsVerifier.getInstance().getImei(response);
+ ImeiInfo imeiInfo = new ImeiInfo();
+ imeiInfo.imei = FAKE_IMEI;
+ imeiInfo.svn = FAKE_IMEISV;
+ imeiInfo.type = ImeiInfo.ImeiType.SECONDARY;
+ resultSuccess(response, imeiInfo);
+ }
+
+ @Override
public void
getCDMASubscription(Message result) {
String ret[] = new String[5];
@@ -1881,8 +1932,8 @@
@Override
public void setCdmaBroadcastActivation(boolean activate, Message response) {
- unimplemented(response);
-
+ SimulatedCommandsVerifier.getInstance().setCdmaBroadcastActivation(activate, response);
+ resultSuccess(response, null);
}
@Override
@@ -1893,7 +1944,8 @@
@Override
public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message response) {
- unimplemented(response);
+ SimulatedCommandsVerifier.getInstance().setCdmaBroadcastConfig(configs, response);
+ resultSuccess(response, null);
}
public void forceDataDormancy(Message response) {
@@ -1903,7 +1955,8 @@
@Override
public void setGsmBroadcastActivation(boolean activate, Message response) {
- unimplemented(response);
+ SimulatedCommandsVerifier.getInstance().setGsmBroadcastActivation(activate, response);
+ resultSuccess(response, null);
}
@@ -1911,7 +1964,7 @@
public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
SimulatedCommandsVerifier.getInstance().setGsmBroadcastConfig(config, response);
if (mSendSetGsmBroadcastConfigResponse) {
- unimplemented(response);
+ resultSuccess(response, null);
}
}
@@ -2067,12 +2120,28 @@
@Override
public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
+ SimulatedCommandsVerifier.getInstance().setInitialAttachApn(dataProfile, isRoaming, result);
+ resultSuccess(result, null);
}
@Override
public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
+ SimulatedCommandsVerifier.getInstance().setDataProfile(dps, isRoaming, result);
+ resultSuccess(result, null);
}
+ @Override
+ public void startHandover(Message result, int callId) {
+ SimulatedCommandsVerifier.getInstance().startHandover(result, callId);
+ resultSuccess(result, null);
+ };
+
+ @Override
+ public void cancelHandover(Message result, int callId) {
+ SimulatedCommandsVerifier.getInstance().cancelHandover(result, callId);
+ resultSuccess(result, null);
+ };
+
public void setImsRegistrationState(int[] regState) {
mImsRegState = regState;
}
@@ -2109,19 +2178,18 @@
}
@Override
- public void iccCloseLogicalChannel(int channel, Message response) {
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response) {
unimplemented(response);
}
@Override
public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
- int p1, int p2, int p3, String data,
- Message response) {
+ int p1, int p2, int p3, String data, boolean isEs10Command, Message response) {
SimulatedCommandsVerifier.getInstance().iccTransmitApduLogicalChannel(channel, cla,
- instruction, p1, p2, p3, data, response);
- if(mIccIoResultForApduLogicalChannel!=null) {
+ instruction, p1, p2, p3, data, isEs10Command, response);
+ if (mIccIoResultForApduLogicalChannel != null) {
resultSuccess(response, mIccIoResultForApduLogicalChannel);
- }else {
+ } else {
resultFail(response, null, new RuntimeException("IccIoResult not set"));
}
}
@@ -2348,6 +2416,18 @@
}
@Override
+ public void registerForNotAvailable(Handler h, int what, Object obj) {
+ SimulatedCommandsVerifier.getInstance().registerForNotAvailable(h, what, obj);
+ super.registerForNotAvailable(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForNotAvailable(Handler h) {
+ SimulatedCommandsVerifier.getInstance().unregisterForNotAvailable(h);
+ super.unregisterForNotAvailable(h);
+ }
+
+ @Override
public void registerForModemReset(Handler h, int what, Object obj) {
SimulatedCommandsVerifier.getInstance().registerForModemReset(h, what, obj);
super.registerForModemReset(h, what, obj);
@@ -2368,6 +2448,9 @@
@Override
public void setSignalStrengthReportingCriteria(List<SignalThresholdInfo> signalThresholdInfos,
Message result) {
+ SimulatedCommandsVerifier.getInstance().setSignalStrengthReportingCriteria(
+ signalThresholdInfos, result);
+ resultSuccess(result, null);
}
@Override
@@ -2517,4 +2600,44 @@
PcoData response = new PcoData(cid, bearerProto, pcoId, contents);
mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
+
+ @Override
+ public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
+ mSrvccConnections = srvccConnections;
+ }
+
+ public SrvccConnection[] getSrvccConnections() {
+ return mSrvccConnections;
+ }
+
+ @Override
+ public void updateImsRegistrationInfo(int regState,
+ int imsRadioTech, int suggestedAction, int capabilities, Message result) {
+ mImsRegistrationInfo[0] = regState;
+ mImsRegistrationInfo[1] = imsRadioTech;
+ mImsRegistrationInfo[2] = suggestedAction;
+ mImsRegistrationInfo[3] = capabilities;
+ }
+
+ public int[] getImsRegistrationInfo() {
+ return mImsRegistrationInfo;
+ }
+
+ @Override
+ public void setN1ModeEnabled(boolean enable, Message result) {
+ mN1ModeEnabled = enable;
+ }
+
+ public boolean isN1ModeEnabled() {
+ return mN1ModeEnabled;
+ }
+
+ @Override
+ public void isVoNrEnabled(Message message, WorkSource workSource) {
+ resultSuccess(message, (Object) mVonrEnabled);
+ }
+
+ public void setVonrEnabled(boolean vonrEnable) {
+ mVonrEnabled = vonrEnable;
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index f93e9e1..4d1c104 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -1160,6 +1160,11 @@
}
@Override
+ public void getImei(Message response) {
+
+ }
+
+ @Override
public void getCDMASubscription(Message response) {
}
@@ -1299,13 +1304,14 @@
}
@Override
- public void iccCloseLogicalChannel(int channel, Message response) {
+ public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response) {
}
@Override
public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1,
- int p2, int p3, String data, Message response) {
+ int p2, int p3, String data,
+ boolean isEs10Command, Message response) {
}
@@ -1535,4 +1541,12 @@
@Override
public void unregisterForSlicingConfigChanged(Handler h) {
}
+
+ @Override
+ public void startHandover(Message result, int callId) {
+ }
+
+ @Override
+ public void cancelHandover(Message result, int callId) {
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
new file mode 100644
index 0000000..09c4173
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.pm.PackageManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
+import com.android.internal.telephony.uicc.IccConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SmsControllerTest extends TelephonyTest {
+
+ // Mocked classes
+ private AdnRecordCache mAdnRecordCache;
+
+ // SmsController under test
+ private SmsController mSmsControllerUT;
+ private final String smscAddrStr = "+1206313004";
+ private String mCallingPackage;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mAdnRecordCache = Mockito.mock(AdnRecordCache.class);
+ mSmsControllerUT = new SmsController(mContext);
+ mCallingPackage = mContext.getOpPackageName();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mAdnRecordCache = null;
+ super.tearDown();
+ }
+
+ private void fdnCheckSetup() {
+ // FDN check setup
+ doReturn(mAdnRecordCache).when(mSimRecords).getAdnCache();
+ doReturn(mUiccProfile).when(mUiccController).getUiccProfileForPhone(anyInt());
+ doReturn(true).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+ doReturn(true).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+ doReturn(false).when(mTelephonyManager).isEmergencyNumber(anyString());
+ doReturn("us").when(mTelephonyManager).getSimCountryIso();
+ doReturn(smscAddrStr).when(mIccSmsInterfaceManager).getSmscAddressFromIccEf(anyString());
+ }
+
+ private void fdnCheckCleanup() {
+ doReturn(false).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+ doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+ }
+
+ @Test
+ public void isNumberBlockedByFdn_fdnListHasBothDestAddrAndSmscAddr() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+ // FDN list has both destination addr and smsc addr
+ AdnRecord smscAddrRecord = new AdnRecord(null, smscAddrStr);
+ AdnRecord destAddrRecord = new AdnRecord(null, "1234");
+ fdnList.add(0, smscAddrRecord);
+ fdnList.add(1, destAddrRecord);
+
+ // Returns false as list contains both dest addr and smsc addr
+ assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+ mCallingPackage));
+
+ // Clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void isNumberBlockedByFdn_fdnListHasDestAddr() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+ // FDN list has only destination addr
+ AdnRecord destAddrRecord = new AdnRecord(null, "1234");
+ fdnList.add(0, destAddrRecord);
+
+ // Returns true as list does not contain smsc addr
+ assertTrue(mSmsControllerUT.isNumberBlockedByFDN(1, "1234", mCallingPackage));
+
+ // Clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void isNumberBlockedByFdn_fdnListHasSmscAddr() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+ // FDN list has both destination addr and smsc addr
+ AdnRecord smscAddrRecord = new AdnRecord(null, smscAddrStr);
+ fdnList.add(0, smscAddrRecord);
+
+ // Returns true as list does not contain dest addr
+ assertTrue(mSmsControllerUT.isNumberBlockedByFDN(1, "1234", mCallingPackage));
+
+ // Clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void isNumberBlockedByFdn_destAddrIsEmergencyNumber() {
+ // FDN check setup
+ fdnCheckSetup();
+ ArrayList<AdnRecord> fdnList = new ArrayList<>();
+ doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(anyString());
+ // Returns false as dest addr is emergency number
+ assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+ mCallingPackage));
+
+ // Clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void isNumberBlockedByFdn_fdnDisabled() {
+ // FDN check setup
+ fdnCheckSetup();
+
+ doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+ // Returns false as fdn is not enabled
+ assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+ mCallingPackage));
+
+ // Clean up
+ fdnCheckCleanup();
+ }
+
+ @Test
+ public void sendVisualVoicemailSmsForSubscriber_phoneIsNotInEcm() {
+ assertFalse(mPhone.isInEcm());
+ int subId = 1;
+ doReturn(true).when(mSubscriptionManager)
+ .isSubscriptionAssociatedWithUser(eq(subId), any());
+
+ mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
+ subId, null, 0, null, null);
+ verify(mIccSmsInterfaceManager).sendTextWithSelfPermissions(any(),
+ any(), any(), any(), any(), any(), any(), eq(false), eq(true));
+ }
+
+ @Test
+ public void sendVisualVoicemailSmsForSubscriber_phoneIsInEcm() {
+ doReturn(true).when(mPhone).isInEcm();
+
+ mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
+ 1, null, 0, null, null);
+ verify(mIccSmsInterfaceManager, never()).sendTextWithSelfPermissions(any(),
+ any(), any(), any(), any(), any(), any(), eq(false), eq(true));
+
+ doReturn(false).when(mPhone).isInEcm();
+ }
+
+ @Test
+ public void sendsendTextForSubscriberTest() {
+ int subId = 1;
+ doReturn(true).when(mSubscriptionManager)
+ .isSubscriptionAssociatedWithUser(eq(subId), any());
+
+ mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
+ null, "text", null, null, false, 0L, true, true);
+ verify(mIccSmsInterfaceManager, Mockito.times(1))
+ .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+ }
+
+ @Test
+ public void sendTextForSubscriberTest_InteractAcrossUsers() {
+ int subId = 1;
+ // Sending text to subscriber should not fail when the caller has the
+ // INTERACT_ACROSS_USERS_FULL permission.
+ doReturn(false).when(mSubscriptionManager)
+ .isSubscriptionAssociatedWithUser(eq(subId), any());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL));
+
+ mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
+ null, "text", null, null, false, 0L, true, true);
+ verify(mIccSmsInterfaceManager, Mockito.times(1))
+ .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+ }
+
+ @Test
+ public void sendTextForSubscriberTestFail() {
+ int subId = 1;
+ // Sending text to subscriber should fail when the caller does not have the
+ // INTERACT_ACROSS_USERS_FULL permission and is not associated with the subscription.
+ doReturn(false).when(mSubscriptionManager)
+ .isSubscriptionAssociatedWithUser(eq(subId), any());
+ doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL));
+
+ mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
+ null, "text", null, null, false, 0L, true, true);
+ verify(mIccSmsInterfaceManager, Mockito.times(0))
+ .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+ }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 6ef8508..b073cd4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -19,6 +19,8 @@
import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -28,13 +30,24 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Looper;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.test.FlakyTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -42,36 +55,140 @@
import android.testing.TestableLooper;
import android.util.Singleton;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
+import com.android.internal.telephony.domainselection.EmergencySmsDomainSelectionConnection;
+import com.android.internal.telephony.domainselection.SmsDomainSelectionConnection;
+import com.android.internal.telephony.uicc.IccUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.concurrent.CompletableFuture;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class SmsDispatchersControllerTest extends TelephonyTest {
+ /**
+ * Inherits the SmsDispatchersController to verify the protected methods.
+ */
+ private static class TestSmsDispatchersController extends SmsDispatchersController {
+ TestSmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
+ SmsUsageMonitor usageMonitor, Looper looper) {
+ super(phone, storageMonitor, usageMonitor, looper);
+ }
+
+ public DomainSelectionConnectionHolder testGetDomainSelectionConnectionHolder(
+ boolean emergency) {
+ return getDomainSelectionConnectionHolder(emergency);
+ }
+
+ public void testSendData(String callingPackage, String destAddr, String scAddr,
+ int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean isForVvm) {
+ sendData(callingPackage, destAddr, scAddr,
+ destPort, data, sentIntent, deliveryIntent, isForVvm);
+ }
+
+ public void testSendMultipartText(String destAddr, String scAddr,
+ ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
+ ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+ boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
+ long messageId) {
+ sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents, messageUri,
+ callingPkg, persistMessage, priority, expectMore, validityPeriod, messageId);
+ }
+ }
+
+ /**
+ * Inherits the SMSDispatcher to verify the abstract or protected methods.
+ */
+ protected abstract static class TestSmsDispatcher extends SMSDispatcher {
+ public TestSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+ super(phone, smsDispatchersController);
+ }
+
+ @Override
+ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean isForVvm) {
+ super.sendData(callingPackage, destAddr, scAddr, destPort,
+ data, sentIntent, deliveryIntent, isForVvm);
+ }
+
+ @Override
+ public void sendSms(SmsTracker tracker) {
+ }
+
+ @Override
+ public String getFormat() {
+ return SmsConstants.FORMAT_3GPP;
+ }
+ }
+
+ /**
+ * Inherits the SMSDispatcher to verify the protected methods.
+ */
+ protected static class TestImsSmsDispatcher extends ImsSmsDispatcher {
+ public TestImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController,
+ FeatureConnectorFactory factory) {
+ super(phone, smsDispatchersController, factory);
+ }
+
+ @Override
+ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean isForVvm) {
+ super.sendData(callingPackage, destAddr, scAddr, destPort,
+ data, sentIntent, deliveryIntent, isForVvm);
+ }
+
+ @Override
+ public String getFormat() {
+ return SmsConstants.FORMAT_3GPP;
+ }
+ }
+
+ private static final String ACTION_TEST_SMS_SENT = "TEST_SMS_SENT";
+
// Mocked classes
private SMSDispatcher.SmsTracker mTracker;
+ private PendingIntent mSentIntent;
+ private TestImsSmsDispatcher mImsSmsDispatcher;
+ private TestSmsDispatcher mGsmSmsDispatcher;
+ private TestSmsDispatcher mCdmaSmsDispatcher;
+ private SmsDomainSelectionConnection mSmsDsc;
+ private EmergencySmsDomainSelectionConnection mEmergencySmsDsc;
- private SmsDispatchersController mSmsDispatchersController;
+ private TestSmsDispatchersController mSmsDispatchersController;
private boolean mInjectionCallbackTriggered = false;
+ private CompletableFuture<Integer> mDscFuture;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mTracker = mock(SMSDispatcher.SmsTracker.class);
setupMockPackagePermissionChecks();
-
- mSmsDispatchersController = new SmsDispatchersController(mPhone, mSmsStorageMonitor,
- mSmsUsageMonitor);
+ mSmsDispatchersController = new TestSmsDispatchersController(mPhone, mSmsStorageMonitor,
+ mSmsUsageMonitor, mTestableLooper.getLooper());
+ setUpDomainSelectionConnectionAsNotSupported();
processAllMessages();
}
@After
public void tearDown() throws Exception {
+ mImsSmsDispatcher = null;
+ mGsmSmsDispatcher = null;
+ mCdmaSmsDispatcher = null;
+ mSmsDsc = null;
+ mEmergencySmsDsc = null;
+ mDscFuture = null;
mSmsDispatchersController.dispose();
mSmsDispatchersController = null;
super.tearDown();
@@ -91,6 +208,30 @@
assertTrue(mSmsDispatchersController.isIms());
}
+ @Test @SmallTest
+ public void testReportSmsMemoryStatus() throws Exception {
+ int eventReportMemoryStatusDone = 3;
+ SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+ Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
+ ImsSmsDispatcher mImsSmsDispatcher = Mockito.mock(ImsSmsDispatcher.class);
+ mSmsDispatchersController.setImsSmsDispatcher(mImsSmsDispatcher);
+ mSmsDispatchersController.reportSmsMemoryStatus(result);
+ AsyncResult ar = (AsyncResult) result.obj;
+ verify(mImsSmsDispatcher).onMemoryAvailable();
+ assertNull(ar.exception);
+ }
+
+ @Test @SmallTest
+ public void testReportSmsMemoryStatusFailure() throws Exception {
+ int eventReportMemoryStatusDone = 3;
+ SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+ Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
+ mSmsDispatchersController.setImsSmsDispatcher(null);
+ mSmsDispatchersController.reportSmsMemoryStatus(result);
+ AsyncResult ar = (AsyncResult) result.obj;
+ assertNotNull(ar.exception);
+ }
+
@Test @SmallTest @FlakyTest
public void testSendImsGmsTest() throws Exception {
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
@@ -179,6 +320,225 @@
assertEquals(true, mInjectionCallbackTriggered);
}
+ @Test @SmallTest
+ public void testSendImsGmsTestWithSmsc() {
+ IccSmsInterfaceManager iccSmsInterfaceManager = Mockito.mock(IccSmsInterfaceManager.class);
+ when(mPhone.getIccSmsInterfaceManager()).thenReturn(iccSmsInterfaceManager);
+ when(iccSmsInterfaceManager.getSmscAddressFromIccEf("com.android.messaging"))
+ .thenReturn("222");
+ switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
+
+ mSmsDispatchersController.sendText("111", null /*scAddr*/, TAG,
+ null, null, null, "com.android.messaging",
+ false, -1, false, -1, false, 0L);
+ byte[] smscbyte = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
+ "222");
+ String smsc = IccUtils.bytesToHexString(smscbyte);
+ verify(mSimulatedCommandsVerifier).sendImsGsmSms(eq(smsc), anyString(),
+ anyInt(), anyInt(), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testSendDataWhenDomainPs() throws Exception {
+ sendDataWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendDataWhenDomainCsAndCdma() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_CDMA);
+ sendDataWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, true);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendDataWhenDomainCsAndGsm() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ sendDataWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendTextWhenDomainPs() throws Exception {
+ sendTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendTextWhenDomainCsAndCdma() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_CDMA);
+ sendTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, true);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendTextWhenDomainCsAndGsm() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ sendTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartTextWhenDomainPs() throws Exception {
+ sendMultipartTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartTextWhenDomainCsAndCdma() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_CDMA);
+ sendMultipartTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, true);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartTextWhenDomainCsAndGsm() throws Exception {
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ sendMultipartTextWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendRetrySmsWhenDomainPs() throws Exception {
+ sendRetrySmsWithDomainSelection(NetworkRegistrationInfo.DOMAIN_PS,
+ PhoneConstants.PHONE_TYPE_GSM, SmsConstants.FORMAT_3GPP);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendRetrySmsWhenDomainCsAndCdma() throws Exception {
+ sendRetrySmsWithDomainSelection(NetworkRegistrationInfo.DOMAIN_CS,
+ PhoneConstants.PHONE_TYPE_CDMA, SmsConstants.FORMAT_3GPP2);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendRetrySmsWhenDomainCsAndGsm() throws Exception {
+ sendRetrySmsWithDomainSelection(NetworkRegistrationInfo.DOMAIN_CS,
+ PhoneConstants.PHONE_TYPE_GSM, SmsConstants.FORMAT_3GPP);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendRetrySmsWhenImsAlreadyUsedAndCdma() throws Exception {
+ sendRetrySmsWhenImsAlreadyUsed(PhoneConstants.PHONE_TYPE_CDMA, SmsConstants.FORMAT_3GPP2);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendRetrySmsWhenImsAlreadyUsedAndGsm() throws Exception {
+ sendRetrySmsWhenImsAlreadyUsed(PhoneConstants.PHONE_TYPE_GSM, SmsConstants.FORMAT_3GPP);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendEmergencyTextWhenDomainPs() throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
+ "test-app", false, 0, false, 10, false, 1L, false);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(true);
+ verify(mEmergencySmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isEmergency());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+ processAllMessages();
+
+ verify(mEmergencySmsDsc).finishSelection();
+ verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
+ any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
+ eq(1L), eq(false));
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyDomainSelectionTerminated() throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+ "test-app", false, 0, false, 10, false, 1L, false);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> captor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+ verify(mSmsDsc).requestDomainSelection(any(), captor.capture());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback = captor.getValue();
+ assertNotNull(callback);
+
+ mSmsDispatchersController.post(() -> {
+ callback.onSelectionTerminated(DisconnectCause.LOCAL);
+ });
+ processAllMessages();
+
+ verify(mSmsDsc, never()).finishSelection();
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+
+ // We can use the IntentReceiver for receiving the sent result, but it can be reported as
+ // a flaky test since sometimes broadcasts can take a long time if the system is under load.
+ // At this point, we couldn't use the PendingIntent as a mock because it's a final class
+ // so this test checks the method in the IActivityManager when the PendingIntent#send(int)
+ // is called.
+ verify(mIActivityManager).sendIntentSender(any(), any(), any(),
+ eq(SmsManager.RESULT_ERROR_GENERIC_FAILURE), any(), any(), any(), any(), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendTextContinuously() throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+ "test-app", false, 0, false, 10, false, 1L, false);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+ "test-app", false, 0, false, 10, false, 1L, false);
+
+ verify(mSmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(2, holder.getPendingRequests().size());
+
+ mDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+ processAllMessages();
+
+ verify(mSmsDsc).finishSelection();
+ verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"),
+ eq(mSentIntent), any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10),
+ eq(false), eq(1L), eq(false));
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
private void switchImsSmsFormat(int phoneType) {
mSimulatedCommands.setImsRegistrationState(new int[]{1, phoneType});
mSimulatedCommands.notifyImsNetworkStateChanged();
@@ -186,4 +546,249 @@
processAllMessages();
assertTrue(mSmsDispatchersController.isIms());
}
+
+ @Test
+ public void testSetImsManager() {
+ ImsManager imsManager = mock(ImsManager.class);
+ assertTrue(mSmsDispatchersController.setImsManager(imsManager));
+ }
+
+ private void setUpDomainSelectionConnectionAsNotSupported() {
+ mSmsDispatchersController.setDomainSelectionResolverProxy(
+ new SmsDispatchersController.DomainSelectionResolverProxy() {
+ @Override
+ @Nullable
+ public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+ @DomainSelectionService.SelectorType int selectorType,
+ boolean isEmergency) {
+ return null;
+ }
+
+ @Override
+ public boolean isDomainSelectionSupported() {
+ return false;
+ }
+ });
+ }
+
+ private void setUpDomainSelectionConnection() {
+ mEmergencySmsDsc = Mockito.mock(EmergencySmsDomainSelectionConnection.class);
+ mSmsDsc = Mockito.mock(SmsDomainSelectionConnection.class);
+ mSmsDispatchersController.setDomainSelectionResolverProxy(
+ new SmsDispatchersController.DomainSelectionResolverProxy() {
+ @Override
+ @Nullable
+ public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+ @DomainSelectionService.SelectorType int selectorType,
+ boolean isEmergency) {
+ return isEmergency ? mEmergencySmsDsc : mSmsDsc;
+ }
+
+ @Override
+ public boolean isDomainSelectionSupported() {
+ return true;
+ }
+ });
+
+ mDscFuture = new CompletableFuture<>();
+ when(mSmsDsc.requestDomainSelection(
+ any(DomainSelectionService.SelectionAttributes.class),
+ any(DomainSelectionConnection.DomainSelectionConnectionCallback.class)))
+ .thenReturn(mDscFuture);
+ when(mEmergencySmsDsc.requestDomainSelection(
+ any(DomainSelectionService.SelectionAttributes.class),
+ any(DomainSelectionConnection.DomainSelectionConnectionCallback.class)))
+ .thenReturn(mDscFuture);
+ }
+
+ private void setUpSmsDispatchers() throws Exception {
+ mImsSmsDispatcher = Mockito.mock(TestImsSmsDispatcher.class);
+ mGsmSmsDispatcher = Mockito.mock(TestSmsDispatcher.class);
+ mCdmaSmsDispatcher = Mockito.mock(TestSmsDispatcher.class);
+
+ replaceInstance(SmsDispatchersController.class, "mImsSmsDispatcher",
+ mSmsDispatchersController, mImsSmsDispatcher);
+ replaceInstance(SmsDispatchersController.class, "mGsmDispatcher",
+ mSmsDispatchersController, mGsmSmsDispatcher);
+ replaceInstance(SmsDispatchersController.class, "mCdmaDispatcher",
+ mSmsDispatchersController, mCdmaSmsDispatcher);
+
+ when(mTelephonyManager.isEmergencyNumber(eq("911"))).thenReturn(true);
+
+ mSentIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
+ new Intent(ACTION_TEST_SMS_SENT), PendingIntent.FLAG_MUTABLE
+ | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT);
+ }
+
+ private void sendDataWithDomainSelection(@NetworkRegistrationInfo.Domain int domain,
+ boolean isCdmaMo) throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ byte[] data = new byte[] { 0x01 };
+ mSmsDispatchersController.testSendData(
+ "test-app", "1111", "2222", 8080, data, mSentIntent, null, false);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ verify(mSmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mDscFuture.complete(domain);
+ processAllMessages();
+
+ verify(mSmsDsc).finishSelection();
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ verify(mImsSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
+ eq(data), eq(mSentIntent), any(), eq(false));
+ } else if (isCdmaMo) {
+ verify(mCdmaSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
+ eq(data), eq(mSentIntent), any(), eq(false));
+ } else {
+ verify(mGsmSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
+ eq(data), eq(mSentIntent), any(), eq(false));
+ }
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
+ private void sendTextWithDomainSelection(@NetworkRegistrationInfo.Domain int domain,
+ boolean isCdmaMo) throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+ "test-app", false, 0, false, 10, false, 1L, false);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ verify(mSmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mDscFuture.complete(domain);
+ processAllMessages();
+
+ verify(mSmsDsc).finishSelection();
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
+ any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
+ eq(1L), eq(false));
+ } else if (isCdmaMo) {
+ verify(mCdmaSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
+ any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
+ eq(1L), eq(false));
+ } else {
+ verify(mGsmSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
+ any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
+ eq(1L), eq(false));
+ }
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
+ private void sendMultipartTextWithDomainSelection(@NetworkRegistrationInfo.Domain int domain,
+ boolean isCdmaMo) throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+
+ ArrayList<String> parts = new ArrayList<>();
+ ArrayList<PendingIntent> sentIntents = new ArrayList<>();
+ ArrayList<PendingIntent> deliveryIntents = new ArrayList<>();
+ mSmsDispatchersController.testSendMultipartText("1111", "2222", parts, sentIntents,
+ deliveryIntents, null, "test-app", false, 0, false, 10, 1L);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ verify(mSmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mDscFuture.complete(domain);
+ processAllMessages();
+
+ verify(mSmsDsc).finishSelection();
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ verify(mImsSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
+ eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
+ eq(false), eq(10), eq(1L));
+ } else if (isCdmaMo) {
+ verify(mCdmaSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
+ eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
+ eq(false), eq(10), eq(1L));
+ } else {
+ verify(mGsmSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
+ eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
+ eq(false), eq(10), eq(1L));
+ }
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
+ private void sendRetrySmsWithDomainSelection(@NetworkRegistrationInfo.Domain int domain,
+ int phoneType, String smsFormat) throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+ when(mPhone.getPhoneType()).thenReturn(phoneType);
+ when(mImsSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP);
+ when(mCdmaSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP2);
+ when(mGsmSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP);
+ replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker, smsFormat);
+
+ mSmsDispatchersController.sendRetrySms(mTracker);
+
+ SmsDispatchersController.DomainSelectionConnectionHolder holder =
+ mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+ verify(mSmsDsc).requestDomainSelection(any(), any());
+ assertNotNull(holder);
+ assertNotNull(holder.getConnection());
+ assertTrue(holder.isDomainSelectionRequested());
+ assertEquals(1, holder.getPendingRequests().size());
+
+ mDscFuture.complete(domain);
+ processAllMessages();
+
+ verify(mSmsDsc).finishSelection();
+ if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
+ verify(mImsSmsDispatcher).sendSms(eq(mTracker));
+ } else if (SmsConstants.FORMAT_3GPP2.equals(smsFormat)) {
+ verify(mCdmaSmsDispatcher).sendSms(eq(mTracker));
+ } else {
+ verify(mGsmSmsDispatcher).sendSms(eq(mTracker));
+ }
+ assertNull(holder.getConnection());
+ assertFalse(holder.isDomainSelectionRequested());
+ assertEquals(0, holder.getPendingRequests().size());
+ }
+
+ private void sendRetrySmsWhenImsAlreadyUsed(int phoneType, String smsFormat) throws Exception {
+ setUpDomainSelectionConnection();
+ setUpSmsDispatchers();
+ when(mPhone.getPhoneType()).thenReturn(phoneType);
+ when(mImsSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP);
+ when(mCdmaSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP2);
+ when(mGsmSmsDispatcher.getFormat()).thenReturn(SmsConstants.FORMAT_3GPP);
+ replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker, smsFormat);
+ mTracker.mUsesImsServiceForIms = true;
+
+ mSmsDispatchersController.sendRetrySms(mTracker);
+
+ verify(mSmsDsc, never()).requestDomainSelection(any(), any());
+
+ if (SmsConstants.FORMAT_3GPP2.equals(smsFormat)) {
+ verify(mCdmaSmsDispatcher).sendSms(eq(mTracker));
+ } else {
+ verify(mGsmSmsDispatcher).sendSms(eq(mTracker));
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
index 2a7f35b..5057eea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
@@ -78,7 +78,7 @@
}
@Override
- public boolean isCallerDefaultSmsPackage(String packageName) {
+ public boolean isCallerDefaultSmsPackage(String packageName, int uid) {
return mCallerIsDefaultSmsPackage;
}
};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
index 4b22f5f..b6775eb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
@@ -16,12 +16,18 @@
package com.android.internal.telephony;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Message;
import android.provider.Telephony;
+import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -31,17 +37,24 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class SmsStorageMonitorTest extends TelephonyTest {
+ private static final int MAX_RETRIES = 1;
+ private static final int RETRY_DELAY = 200; // 200 millis
+
private SmsStorageMonitor mSmsStorageMonitor;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mSmsStorageMonitor = new SmsStorageMonitor(mPhone);
+ mSmsStorageMonitor.setMaxRetries(MAX_RETRIES);
+ mSmsStorageMonitor.setRetryDelayInMillis(RETRY_DELAY);
processAllMessages();
}
@@ -99,4 +112,89 @@
verify(mSimulatedCommandsVerifier).reportSmsMemoryStatus(eq(true), any(Message.class));
}
-}
\ No newline at end of file
+
+ @Test @MediumTest
+ public void testRetrySmsMemoryStatus() {
+ mSimulatedCommands.setReportSmsMemoryStatusFailResponse(true);
+
+ // Send DEVICE_STORAGE_FULL
+ mContextFixture.getTestDouble().sendBroadcast(
+ new Intent(Intent.ACTION_DEVICE_STORAGE_FULL));
+ processAllMessages();
+
+ // Wait until retrying is done.
+ for (int i = 0; i < MAX_RETRIES; i++) {
+ waitForMs(RETRY_DELAY);
+ processAllMessages();
+ }
+
+ verify(mSimulatedCommandsVerifier, times(1 + MAX_RETRIES))
+ .reportSmsMemoryStatus(eq(false), any(Message.class));
+ assertFalse(mSmsStorageMonitor.isStorageAvailable());
+
+ mSimulatedCommands.setReportSmsMemoryStatusFailResponse(false);
+
+ // Notify radio on
+ mSimulatedCommands.notifyRadioOn();
+ processAllMessages();
+
+ verify(mSimulatedCommandsVerifier, times(2 + MAX_RETRIES))
+ .reportSmsMemoryStatus(eq(false), any(Message.class));
+ }
+
+ @Test @SmallTest
+ public void testReportSmsMemoryStatusToIms() {
+ Resources mockResources = Mockito.mock(Resources.class);
+ doReturn(mockResources).when(mContext).getResources();
+ doReturn(true).when(mockResources).getBoolean(anyInt());
+ doReturn(true).when(mIccSmsInterfaceManager.mDispatchersController).isIms();
+
+ mSimulatedCommands.notifyRadioOn();
+ processAllMessages();
+
+ verify(mSimulatedCommandsVerifier, never()).reportSmsMemoryStatus(anyBoolean(),
+ any(Message.class));
+
+ // Send DEVICE_STORAGE_FULL
+ mContextFixture.getTestDouble().sendBroadcast(
+ new Intent(Intent.ACTION_DEVICE_STORAGE_FULL));
+ processAllMessages();
+
+ verify(mSimulatedCommandsVerifier).reportSmsMemoryStatus(eq(false), any(Message.class));
+ assertFalse(mSmsStorageMonitor.isStorageAvailable());
+
+ mSimulatedCommands.notifyRadioOn();
+ processAllMessages();
+
+ verify(mSimulatedCommandsVerifier).reportSmsMemoryStatus(eq(false), any(Message.class));
+
+ // Send DEVICE_STORAGE_NOT_FULL
+ mContextFixture.getTestDouble().sendBroadcast(
+ new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL));
+ processAllMessages();
+
+ verify(mIccSmsInterfaceManager.mDispatchersController)
+ .reportSmsMemoryStatus(any(Message.class));
+ }
+
+ @Test @SmallTest
+ public void testReportSmsMemoryStatusDuringRetry() {
+ mSimulatedCommands.setReportSmsMemoryStatusFailResponse(true);
+
+ // Send DEVICE_STORAGE_FULL
+ mContextFixture.getTestDouble().sendBroadcast(
+ new Intent(Intent.ACTION_DEVICE_STORAGE_FULL));
+ processAllMessages();
+
+ mSimulatedCommands.setReportSmsMemoryStatusFailResponse(false);
+
+ // Send DEVICE_STORAGE_NOT_FULL
+ mContextFixture.getTestDouble().sendBroadcast(
+ new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL));
+ processAllMessages();
+
+ verify(mSimulatedCommandsVerifier).reportSmsMemoryStatus(eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier).reportSmsMemoryStatus(eq(true), any(Message.class));
+ assertTrue(mSmsStorageMonitor.isStorageAvailable());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
deleted file mode 100644
index 6a4c9c2..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ /dev/null
@@ -1,2057 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony;
-
-import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION;
-
-import static com.android.internal.telephony.uicc.IccCardStatus.CardState.CARDSTATE_PRESENT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.UiccPortInfo;
-import android.telephony.UiccSlotInfo;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.uicc.IccCardStatus;
-import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccSlot;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-public class SubscriptionControllerTest extends TelephonyTest {
- private static final int SINGLE_SIM = 1;
- private static final int DUAL_SIM = 2;
- private static final int FAKE_SUBID = 123;
- private String mCallingPackage;
- private String mCallingFeature;
- private SubscriptionController mSubscriptionControllerUT;
- private MockContentResolver mMockContentResolver;
- private FakeTelephonyProvider mFakeTelephonyProvider;
- private PersistableBundle mCarrierConfigs;
-
- // Mocked classes
- private UiccSlot mUiccSlot;
- private ITelephonyRegistry.Stub mTelephonyRegistryMock;
- private MultiSimSettingController mMultiSimSettingControllerMock;
- private ISetOpportunisticDataCallback mSetOpptDataCallback;
- private Handler mHandler;
- private SubscriptionInfo mMockSubscriptionInfo;
-
- private static final String MAC_ADDRESS_PREFIX = "mac_";
- private static final String DISPLAY_NAME_PREFIX = "my_phone_";
-
- private static final String UNAVAILABLE_ICCID = "";
- private static final String UNAVAILABLE_NUMBER = "";
- private static final String DISPLAY_NUMBER = "123456";
- private static final String DISPLAY_NAME = "testing_display_name";
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mUiccSlot = mock(UiccSlot.class);
- mTelephonyRegistryMock = mock(ITelephonyRegistry.Stub.class);
- mMultiSimSettingControllerMock = mock(MultiSimSettingController.class);
- mSetOpptDataCallback = mock(ISetOpportunisticDataCallback.class);
- mHandler = mock(Handler.class);
- mMockSubscriptionInfo = mock(SubscriptionInfo.class);
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
- doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
- doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
- mMockContentResolver = (MockContentResolver) mContext.getContentResolver();
- mFakeTelephonyProvider = new FakeTelephonyProvider();
- mMockContentResolver.addProvider(SubscriptionManager.CONTENT_URI.getAuthority(),
- mFakeTelephonyProvider);
- replaceInstance(SubscriptionController.class, "sInstance", null, null);
- replaceInstance(MultiSimSettingController.class, "sInstance", null,
- mMultiSimSettingControllerMock);
-
- mSubscriptionControllerUT = SubscriptionController.init(mContext);
- mCallingPackage = mContext.getOpPackageName();
- mCallingFeature = mContext.getAttributionTag();
-
- doReturn(1).when(mProxyController).getMaxRafSupported();
-
- // Carrier Config
- mCarrierConfigs = mContextFixture.getCarrierConfigBundle();
-
- mContextFixture.putIntArrayResource(com.android.internal.R.array.sim_colors, new int[]{5});
- setupMocksForTelephonyPermissions(Build.VERSION_CODES.R);
- }
-
- @After
- public void tearDown() throws Exception {
- mContextFixture.addCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- /* Should clear fake content provider and resolver here */
- mContext.getContentResolver().delete(SubscriptionManager.CONTENT_URI, null, null);
-
- /* Clear sub info in mSubscriptionControllerUT since they will otherwise be persistent
- * between each test case. */
- if (mSubscriptionControllerUT != null) {
- mSubscriptionControllerUT.clearSubInfo();
- mSubscriptionControllerUT.resetStaticMembers();
- mSubscriptionControllerUT = null;
- }
-
- /* Clear settings for default voice/data/sms sub ID */
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-
- mCallingPackage = null;
- mCallingFeature = null;
- mMockContentResolver = null;
- mFakeTelephonyProvider = null;
- mCarrierConfigs = null;
- super.tearDown();
- }
-
- @Test @SmallTest
- public void testInsertSim() {
- //verify there is no sim inserted in the SubscriptionManager
- assertEquals(0, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage,
- mCallingFeature));
-
- int slotID = 0;
- //insert one Subscription Info
- mSubscriptionControllerUT.addSubInfoRecord("test", slotID);
-
- //verify there is one sim
- assertEquals(1, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage,
- mCallingFeature));
-
- //sanity for slot id and sub id
- List<SubscriptionInfo> mSubList = mSubscriptionControllerUT
- .getActiveSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertTrue(mSubList != null && mSubList.size() > 0);
- for (int i = 0; i < mSubList.size(); i++) {
- assertTrue(SubscriptionManager.isValidSubscriptionId(
- mSubList.get(i).getSubscriptionId()));
- assertTrue(SubscriptionManager.isValidSlotIndex(mSubList.get(i).getSimSlotIndex()));
- }
- }
-
- @Test @SmallTest
- public void testUsageSettingProperty() {
- testInsertSim();
- /* Get SUB ID */
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(subIds != null && subIds.length != 0);
- final int subId = subIds[0];
-
- /* Getting, there is no direct getter function for each fields of property */
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- // assertEquals(SubscriptionManager.USAGE_SETTING_UNKNOWN, subInfo.getUsageSetting());
-
- assertThrows(IllegalArgumentException.class,
- () -> mSubscriptionControllerUT.setUsageSetting(
- SubscriptionManager.USAGE_SETTING_UNKNOWN,
- subId,
- mCallingPackage));
-
- assertThrows(IllegalArgumentException.class,
- () -> mSubscriptionControllerUT.setUsageSetting(
- SubscriptionManager.USAGE_SETTING_DEFAULT,
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- mCallingPackage));
-
- mSubscriptionControllerUT.setUsageSetting(
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- subId,
- mCallingPackage);
-
- subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
- assertEquals(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC, subInfo.getUsageSetting());
- }
-
- @Test @SmallTest
- public void testChangeSIMProperty() {
- int dataRoaming = 1;
- int iconTint = 1;
- String disName = "TESTING";
- String disNum = "12345";
- boolean isOpportunistic = true;
-
- testInsertSim();
- /* Get SUB ID */
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(subIds != null && subIds.length != 0);
- int subID = subIds[0];
-
- /* Getting, there is no direct getter function for each fields of property */
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subID, mCallingPackage, mCallingFeature);
-
- /* Setting */
- mSubscriptionControllerUT.setDisplayNameUsingSrc(disName, subID,
- SubscriptionManager.NAME_SOURCE_USER_INPUT);
- mSubscriptionControllerUT.setDataRoaming(dataRoaming, subID);
- mSubscriptionControllerUT.setDisplayNumber(disNum, subID);
- mSubscriptionControllerUT.setIconTint(iconTint, subID);
- mSubscriptionControllerUT.setOpportunistic(isOpportunistic, subID, mCallingPackage);
-
- subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subID, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(dataRoaming, subInfo.getDataRoaming());
- assertEquals(disName, subInfo.getDisplayName());
- assertEquals(iconTint, subInfo.getIconTint());
- assertEquals(disNum, subInfo.getNumber());
- assertEquals(isOpportunistic, subInfo.isOpportunistic());
-
- /* verify broadcast intent */
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
- assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
- captorIntent.getValue().getAction());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc() {
- testInsertSim();
-
- /* Get SUB ID */
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(subIds != null && subIds.length != 0);
- int subID = subIds[0];
-
- /* Setting */
- String disName = "TESTING";
- int nameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(disName, subID, nameSource);
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subID, mCallingPackage, mCallingFeature);
- assertNotNull(subInfo);
- assertEquals(disName, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
-
- /* verify broadcast intent */
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
- assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
- captorIntent.getValue().getAction());
-
- }
-
- private void setSimEmbedded(boolean isEmbedded) throws Exception {
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, isEmbedded ? 1 : 0);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + getFirstSubId(),
- null);
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithEmbeddedSim()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- setSimEmbedded(true);
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- String newDisplayName = "display_name_pnn";
- int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_PNN;
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithConfigIsNull()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- setSimEmbedded(false);
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- String newDisplayName = "display_name_spn";
- int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
- when(mCarrierConfigManager.getConfigForSubId(subId)).thenReturn(null);
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithCarrierNameOverride()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- setSimEmbedded(false);
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
- String newDisplayName = "display_name_spn";
- mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithSpnAndCarrierName()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- setSimEmbedded(false);
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
- String carrierName = "testing_carrier_name";
- String newDisplayName = "display_name_spn";
- when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
- when(mUiccProfile.getServiceProviderName()).thenReturn(null);
- mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
- mCarrierConfigs.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING, carrierName);
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourcePnnToNameSourceCarrierId()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_SIM_PNN;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- String newDisplayName = "display_name_carrier_id";
- int newNameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
- when(mPhone.getPlmn()).thenReturn("testing_pnn");
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testSetGetDisplayNameSrc_updateNameSourceUserInputToNameSourceSpn()
- throws Exception {
- testInsertSim();
-
- // Set values of DB
- int subId = getFirstSubId();
- int nameSource = SubscriptionManager.NAME_SOURCE_USER_INPUT;
- mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
-
- // Update with new value
- String newDisplayName = "display_name_spn";
- int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
-
- // Save to DB after updated
- mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subInfo);
- assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
- assertEquals(nameSource, subInfo.getNameSource());
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_pnnIsNotNull_returnTrue() {
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_PNN);
- when(mPhone.getPlmn()).thenReturn("testing_pnn");
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_spnIsNotNull_returnTrue() {
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_SPN);
- when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
- when(mUiccProfile.getServiceProviderName()).thenReturn("testing_spn");
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_simIsEmbedded_returnTrue() {
- when(mMockSubscriptionInfo.isEmbedded()).thenReturn(true);
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_carrierConfigIsNull_returnTrue() {
- when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
- when(mCarrierConfigManager.getConfigForSubId(FAKE_SUBID)).thenReturn(null);
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_carrierNameOverrideIsTrue_returnTrue() {
- when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
- mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testIsExistingNameSourceStillValid_spnIsNullAndCarrierNameIsNotNull_returnTrue() {
- when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
- when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
- when(mMockSubscriptionInfo.getNameSource())
- .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
- when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
- when(mUiccProfile.getServiceProviderName()).thenReturn(null);
- mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
- mCarrierConfigs.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING,
- "testing_carrier_name");
-
- assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
- }
-
- @Test @SmallTest
- public void testCleanUpSIM() {
- testInsertSim();
- assertFalse(mSubscriptionControllerUT.isActiveSubId(2));
- mSubscriptionControllerUT.clearSubInfo();
- assertFalse(mSubscriptionControllerUT.isActiveSubId(1));
- assertEquals(SubscriptionManager.SIM_NOT_INSERTED,
- mSubscriptionControllerUT.getSlotIndex(1));
- }
-
- @Test @SmallTest
- public void testDefaultSubIdOnSingleSimDevice() {
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultDataSubId());
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultSmsSubId());
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultSmsSubId());
- /* insert one sim */
- testInsertSim();
- // if support single sim, sms/data/voice default sub should be the same
- assertEquals(1, mSubscriptionControllerUT.getDefaultSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultDataSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultSmsSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultVoiceSubId());
- }
-
- @Test @SmallTest
- public void testSetGetMCCMNC() {
- testInsertSim();
- String mCcMncVERIZON = "310004";
- mSubscriptionControllerUT.setMccMnc(mCcMncVERIZON, 1);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(1, mCallingPackage, mCallingFeature);
- assertNotNull(subInfo);
- assertEquals(Integer.parseInt(mCcMncVERIZON.substring(0, 3)), subInfo.getMcc());
- assertEquals(Integer.parseInt(mCcMncVERIZON.substring(3)), subInfo.getMnc());
-
- /* verify broadcast intent */
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
- assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
- captorIntent.getValue().getAction());
- }
-
- @Test @SmallTest
- public void testSetGetCarrierId() {
- testInsertSim();
- int carrierId = 1234;
- mSubscriptionControllerUT.setCarrierId(carrierId, 1);
-
- SubscriptionInfo subInfo = mSubscriptionControllerUT
- .getActiveSubscriptionInfo(1, mCallingPackage, mCallingFeature);
- assertNotNull(subInfo);
- assertEquals(carrierId, subInfo.getCarrierId());
-
- /* verify broadcast intent */
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
- assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
- captorIntent.getValue().getAction());
- }
-
- @Test
- @SmallTest
- public void testSetDefaultDataSubId() throws Exception {
- doReturn(1).when(mPhone).getSubId();
-
- mSubscriptionControllerUT.setDefaultDataSubId(1);
-
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, times(1)).sendStickyBroadcastAsUser(
- captorIntent.capture(), eq(UserHandle.ALL));
-
- Intent intent = captorIntent.getValue();
- assertEquals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED, intent.getAction());
-
- Bundle b = intent.getExtras();
-
- assertTrue(b.containsKey(PhoneConstants.SUBSCRIPTION_KEY));
- assertEquals(1, b.getInt(PhoneConstants.SUBSCRIPTION_KEY));
- }
-
- @Test
- @SmallTest
- public void testMigrateImsSettings() throws Exception {
- testInsertSim();
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(subIds != null && subIds.length != 0);
- int subID = subIds[0];
-
- // Set default void subId.
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
- subID);
-
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ENHANCED_4G_MODE_ENABLED,
- 1);
-
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.VT_IMS_ENABLED,
- 0);
-
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ENABLED,
- 1);
-
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_MODE,
- 2);
-
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ROAMING_MODE,
- 3);
-
- mSubscriptionControllerUT.migrateImsSettings();
-
- // Global settings should be all set.
- assertEquals(-1, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ENHANCED_4G_MODE_ENABLED));
-
- assertEquals(-1, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.VT_IMS_ENABLED));
-
- assertEquals(-1, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ENABLED));
-
- assertEquals(-1, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_MODE));
-
- assertEquals(-1, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ROAMING_MODE));
-
- // The values should be migrated to its DB.
- assertEquals("1", mSubscriptionControllerUT.getSubscriptionProperty(
- subID,
- SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
- mCallingPackage,
- mCallingFeature));
-
- assertEquals("0", mSubscriptionControllerUT.getSubscriptionProperty(
- subID,
- SubscriptionManager.VT_IMS_ENABLED,
- mCallingPackage,
- mCallingFeature));
-
- assertEquals("1", mSubscriptionControllerUT.getSubscriptionProperty(
- subID,
- SubscriptionManager.WFC_IMS_ENABLED,
- mCallingPackage,
- mCallingFeature));
-
- assertEquals("2", mSubscriptionControllerUT.getSubscriptionProperty(
- subID,
- SubscriptionManager.WFC_IMS_MODE,
- mCallingPackage,
- mCallingFeature));
-
- assertEquals("3", mSubscriptionControllerUT.getSubscriptionProperty(
- subID,
- SubscriptionManager.WFC_IMS_ROAMING_MODE,
- mCallingPackage,
- mCallingFeature));
- }
-
- @Test
- @SmallTest
- public void testSkipMigrateImsSettings() throws Exception {
-
- // Set default invalid subId.
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
- -1);
-
- int enhanced4gModeEnabled = 1;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ENHANCED_4G_MODE_ENABLED,
- enhanced4gModeEnabled);
-
- int vtImsEnabled = 0;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.VT_IMS_ENABLED,
- vtImsEnabled);
-
- int wfcImsEnabled = 1;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ENABLED,
- wfcImsEnabled);
-
- int wfcImsMode = 2;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_MODE,
- wfcImsMode);
-
- int wfcImsRoamingMode = 3;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ROAMING_MODE,
- wfcImsRoamingMode);
-
- mSubscriptionControllerUT.migrateImsSettings();
-
- // Migration should be skipped because subId was invalid
- assertEquals(enhanced4gModeEnabled, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ENHANCED_4G_MODE_ENABLED));
-
- assertEquals(vtImsEnabled, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.VT_IMS_ENABLED));
-
- assertEquals(wfcImsEnabled, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ENABLED));
-
- assertEquals(wfcImsMode, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_MODE));
-
- assertEquals(wfcImsRoamingMode, Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WFC_IMS_ROAMING_MODE));
- }
-
- @Test
- @SmallTest
- public void testOpptSubInfoListChanged() throws Exception {
- registerMockTelephonyRegistry();
- verify(mTelephonyRegistryManager, times(0))
- .notifyOpportunisticSubscriptionInfoChanged();
-
- testInsertSim();
- mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
-
- // Neither sub1 or sub2 are opportunistic. So getOpportunisticSubscriptions
- // should return empty list and no callback triggered.
- List<SubscriptionInfo> opptSubList = mSubscriptionControllerUT
- .getOpportunisticSubscriptions(mCallingPackage, mCallingFeature);
-
- assertTrue(opptSubList.isEmpty());
- verify(mTelephonyRegistryManager, times(0))
- .notifyOpportunisticSubscriptionInfoChanged();
-
- // Setting sub2 as opportunistic should trigger callback.
- mSubscriptionControllerUT.setOpportunistic(true, 2, mCallingPackage);
-
- verify(mTelephonyRegistryManager, times(1))
- .notifyOpportunisticSubscriptionInfoChanged();
- opptSubList = mSubscriptionControllerUT
- .getOpportunisticSubscriptions(mCallingPackage, mCallingFeature);
- assertEquals(1, opptSubList.size());
- assertEquals("test2", opptSubList.get(0).getIccId());
-
- // Changing non-opportunistic sub1 shouldn't trigger callback.
- mSubscriptionControllerUT.setDisplayNameUsingSrc("DisplayName", 1,
- SubscriptionManager.NAME_SOURCE_SIM_SPN);
- verify(mTelephonyRegistryManager, times(1))
- .notifyOpportunisticSubscriptionInfoChanged();
-
- mSubscriptionControllerUT.setDisplayNameUsingSrc("DisplayName", 2,
- SubscriptionManager.NAME_SOURCE_SIM_SPN);
- verify(mTelephonyRegistryManager, times(2))
- .notifyOpportunisticSubscriptionInfoChanged();
- }
-
- @Test @SmallTest
- public void testInsertRemoteSim() {
- makeThisDeviceMultiSimCapable();
- mContextFixture.addSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-
- // verify there are no sim's in the system.
- assertEquals(0, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage,
- mCallingFeature));
-
- addAndVerifyRemoteSimAddition(1, 0);
- }
-
- private void addAndVerifyRemoteSimAddition(int num, int numOfCurrentSubs) {
- // Verify the number of current subs in the system
- assertEquals(numOfCurrentSubs,
- mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage, mCallingFeature));
-
- // if there are current subs in the system, get that info
- List<SubscriptionInfo> mSubList;
- ArrayList<String> macAddresses = new ArrayList<>();
- ArrayList<String> displayNames = new ArrayList<>();
- if (numOfCurrentSubs > 0) {
- mSubList = mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
- assertNotNull(mSubList);
- assertEquals(numOfCurrentSubs, mSubList.size());
- for (SubscriptionInfo info : mSubList) {
- assertNotNull(info.getIccId());
- assertNotNull(info.getDisplayName());
- macAddresses.add(info.getIccId());
- displayNames.add(info.getDisplayName().toString());
- }
- }
-
- // To add more subs, we need to create macAddresses + displaynames.
- for (int i = 0; i < num; i++) {
- macAddresses.add(MAC_ADDRESS_PREFIX + (numOfCurrentSubs + i));
- displayNames.add(DISPLAY_NAME_PREFIX + (numOfCurrentSubs + i));
- }
-
- // Add subs - one at a time and verify the contents in subscription info data structs
- for (int i = 0; i < num; i++) {
- int index = numOfCurrentSubs + i;
- mSubscriptionControllerUT.addSubInfo(macAddresses.get(index), displayNames.get(index),
- SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB,
- SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
-
- // make sure the subscription is added in SubscriptionController data structs
- Map<Integer, ArrayList<Integer>> slotIndexToSubsMap =
- mSubscriptionControllerUT.getSlotIndexToSubIdsMap();
- assertNotNull(slotIndexToSubsMap);
- // Since All remote sim's go to the same slot index, there should only be one entry
- assertEquals(1, slotIndexToSubsMap.size());
-
- // get all the subscriptions available. should be what is just added in this method
- // PLUS the number of subs that already existed before
- int expectedNumOfSubs = numOfCurrentSubs + i + 1;
- ArrayList<Integer> subIdsList =
- slotIndexToSubsMap.get(SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB);
- assertNotNull(subIdsList);
- assertEquals(expectedNumOfSubs, subIdsList.size());
-
- // validate slot index, sub id etc
- mSubList = mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
- assertNotNull(mSubList);
- assertEquals(expectedNumOfSubs, mSubList.size());
-
- // sort on subscription-id which will make sure the previously existing subscriptions
- // are in earlier slots in the array
- mSubList.sort(SUBSCRIPTION_INFO_COMPARATOR);
-
- // Verify the subscription data. Skip the verification for the existing subs.
- for (int j = numOfCurrentSubs; j < mSubList.size(); j++) {
- SubscriptionInfo info = mSubList.get(j);
- assertTrue(SubscriptionManager.isValidSubscriptionId(info.getSubscriptionId()));
- assertEquals(SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB,
- info.getSimSlotIndex());
- assertEquals(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM,
- info.getSubscriptionType());
- assertEquals(macAddresses.get(j), info.getIccId());
- assertEquals(displayNames.get(j), info.getDisplayName());
- }
- }
- }
-
- private static final Comparator<SubscriptionInfo> SUBSCRIPTION_INFO_COMPARATOR =
- Comparator.comparingInt(o -> o.getSubscriptionId());
-
- @Test @SmallTest
- public void testInsertMultipleRemoteSims() {
- makeThisDeviceMultiSimCapable();
- mContextFixture.addSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-
- // verify that there are no subscription info records
- assertEquals(0, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage,
- mCallingFeature));
- Map<Integer, ArrayList<Integer>> slotIndexToSubsMap =
- mSubscriptionControllerUT.getSlotIndexToSubIdsMap();
- assertNotNull(slotIndexToSubsMap);
- assertTrue(slotIndexToSubsMap.isEmpty());
-
- // Add a few subscriptions
- addAndVerifyRemoteSimAddition(4, 0);
- }
-
- @FlakyTest
- @Test @SmallTest
- public void testDefaultSubIdOnMultiSimDevice() {
- makeThisDeviceMultiSimCapable();
-
- // Initially, defaults should be -1
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultDataSubId());
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultSmsSubId());
- assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getDefaultSmsSubId());
-
- // Insert one Remote-Sim.
- testInsertRemoteSim();
-
- // defaults should be set to this newly-inserted subscription
- assertEquals(1, mSubscriptionControllerUT.getDefaultSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultDataSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultSmsSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultVoiceSubId());
-
- // Add a few subscriptions
- addAndVerifyRemoteSimAddition(4, 1);
-
- // defaults should be still be set to the first sub - and unchanged by the addition of
- // the above multiple sims.
- assertEquals(1, mSubscriptionControllerUT.getDefaultSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultDataSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultSmsSubId());
- assertEquals(1, mSubscriptionControllerUT.getDefaultVoiceSubId());
- }
-
- @Test @SmallTest
- public void testRemoveSubscription() {
- makeThisDeviceMultiSimCapable();
-
- /* insert some sims */
- testInsertMultipleRemoteSims();
- assertEquals(1, mSubscriptionControllerUT.getDefaultSubId());
- int[] subIdsArray = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(subIdsArray.length > 0);
- int len = subIdsArray.length;
-
- // remove the first sim - which also is the default sim.
- int result = mSubscriptionControllerUT.removeSubInfo(MAC_ADDRESS_PREFIX + 0,
- SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
-
- assertTrue(result > 0);
- // now check the number of subs left. should be one less than earlier
- int[] newSubIdsArray = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- assertTrue(newSubIdsArray.length > 0);
- assertEquals(len - 1, newSubIdsArray.length);
-
- // now check that there is a new default
- assertNotSame(1, mSubscriptionControllerUT.getDefaultSubId());
- }
-
- private void makeThisDeviceMultiSimCapable() {
- doReturn(10).when(mTelephonyManager).getSimCount();
- }
-
- @Test
- @SmallTest
- public void testSetSubscriptionGroupWithModifyPermission() throws Exception {
- testInsertSim();
- mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
-
- int[] subIdList = new int[] {1, 2};
- try {
- mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- fail("createSubscriptionGroup should fail with no permission.");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- // With modify permission it should succeed.
- mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
- ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, groupId);
-
- // Calling it again should generate a new group ID.
- ParcelUuid newGroupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, newGroupId);
- assertNotEquals(groupId, newGroupId);
- }
-
- @Test
- @SmallTest
- public void testGetSubscriptionProperty() throws Exception {
- testInsertSim();
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.GROUP_UUID, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1, null);
-
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
-
- // should succeed with read phone state permission
- String prop = mSubscriptionControllerUT.getSubscriptionProperty(1,
- SubscriptionManager.CB_EXTREME_THREAT_ALERT, mContext.getOpPackageName(),
- mContext.getAttributionTag());
-
- assertNotEquals(null, prop);
-
- // group UUID requires privileged phone state permission
- prop = mSubscriptionControllerUT.getSubscriptionProperty(1, SubscriptionManager.GROUP_UUID,
- mContext.getOpPackageName(), mContext.getAttributionTag());
- assertEquals(null, prop);
-
- // group UUID should succeed once privileged phone state permission is granted
- setupReadPrivilegePermission();
- prop = mSubscriptionControllerUT.getSubscriptionProperty(1, SubscriptionManager.GROUP_UUID,
- mContext.getOpPackageName(), mContext.getAttributionTag());
- assertNotEquals(null, prop);
- }
-
- @Test
- @SmallTest
- public void testCreateSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
- testInsertSim();
- // Adding a second profile and mark as embedded.
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
-
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
-
- int[] subIdList = new int[] {1, 2};
- // It should fail since it has no permission.
- try {
- mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- fail("createSubscriptionGroup should fail with no permission.");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
- try {
- mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- fail("createSubscriptionGroup should fail with no permission on sub 2.");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
- ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, groupId);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
- mContext.getAttributionTag());
-
- // Put sub3 into slot 1 to make sub2 inactive.
- mContextFixture.addCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE);
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("test3", 1);
- mContextFixture.removeCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE);
- // As sub2 is inactive, it will checks carrier privilege against access rules in the db.
- doReturn(true).when(mSubscriptionManager).canManageSubscription(
- eq(subInfoList.get(1)), anyString());
-
- ParcelUuid newGroupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, newGroupId);
- assertNotEquals(groupId, newGroupId);
- }
-
- @Test
- @SmallTest
- public void testAddSubscriptionIntoGroupWithCarrierPrivilegePermission() throws Exception {
- testInsertSim();
- // Adding a second profile and mark as embedded.
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
-
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
-
- // Create group for sub 1.
- int[] subIdList = new int[] {1};
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
- ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, "packageName1");
-
- // Try to add sub 2 into group of sub 1.
- // Should fail as it doesn't have carrier privilege on sub 2.
- try {
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- new int[] {2}, groupId, "packageName1");
- fail("addSubscriptionsIntoGroup should fail with no permission on sub 2.");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- doReturn(false).when(mTelephonyManager).hasCarrierPrivileges(1);
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
- // Try to add sub 2 into group of sub 1.
- // Should fail as it doesn't have carrier privilege on sub 1.
- try {
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- new int[] {2}, groupId, "packageName2");
- fail("addSubscriptionsIntoGroup should fail with no permission on the group (sub 1).");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(new int[] {2}, groupId, "packageName2");
- List<SubscriptionInfo> infoList = mSubscriptionControllerUT
- .getSubscriptionsInGroup(groupId, "packageName2", "feature2");
- assertEquals(2, infoList.size());
- }
-
- @Test
- @SmallTest
- public void testUpdateSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
- testInsertSim();
- // Adding a second profile and mark as embedded.
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
-
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
-
- int[] subIdList = new int[] {1};
-
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
- doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
-
- ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, "packageName1");
- assertNotEquals(null, groupId);
-
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- new int[] {2}, groupId, "packageName1");
- List<SubscriptionInfo> infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupId, "packageName1", "feature1");
- assertEquals(2, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
- assertEquals(2, infoList.get(1).getSubscriptionId());
-
- mSubscriptionControllerUT.removeSubscriptionsFromGroup(
- new int[] {2}, groupId, "packageName1");
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupId, "packageName1", "feature1");
- assertEquals(1, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
-
- // Make sub 1 inactive.
- mSubscriptionControllerUT.clearSubInfoRecord(0);
-
- try {
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- new int[] {2}, groupId, "packageName2");
- fail("addSubscriptionsIntoGroup should fail with wrong callingPackage name");
- } catch (SecurityException e) {
- // Expected result.
- }
-
- // Adding and removing subscription should still work for packageName1, as it's the group
- // owner who created the group earlier..
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- new int[] {2}, groupId, "packageName1");
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupId, "packageName1", "feature1");
- assertEquals(2, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
- assertEquals(2, infoList.get(1).getSubscriptionId());
-
- mSubscriptionControllerUT.removeSubscriptionsFromGroup(
- new int[] {2}, groupId, "packageName1");
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupId, "packageName1", "feature1");
- assertEquals(1, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
- }
-
- @Test
- @SmallTest
- public void testDisabledSubscriptionGroup() throws Exception {
- registerMockTelephonyRegistry();
-
- testInsertSim();
- // Adding a second profile and mark as embedded.
- mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
-
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- values.put(SubscriptionManager.IS_OPPORTUNISTIC, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
- mSubscriptionControllerUT.notifySubscriptionInfoChanged();
-
- verify(mTelephonyRegistryManager, times(1))
- .notifyOpportunisticSubscriptionInfoChanged();
-
- // Set sub 1 and 2 into same group.
- int[] subIdList = new int[] {1, 2};
- ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, groupId);
-
- mSubscriptionControllerUT.notifySubscriptionInfoChanged();
- verify(mTelephonyRegistryManager, times(2))
- .notifyOpportunisticSubscriptionInfoChanged();
- List<SubscriptionInfo> opptSubList = mSubscriptionControllerUT
- .getOpportunisticSubscriptions(mCallingPackage, mCallingFeature);
- assertEquals(1, opptSubList.size());
- assertEquals(2, opptSubList.get(0).getSubscriptionId());
- assertEquals(false, opptSubList.get(0).isGroupDisabled());
-
- // Unplug SIM 1. This should trigger subscription controller disabling sub 2.
- values = new ContentValues();
- values.put(SubscriptionManager.SIM_SLOT_INDEX, -1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
- mSubscriptionControllerUT.notifySubscriptionInfoChanged();
-
- verify(mTelephonyRegistryManager, times(3))
- .notifyOpportunisticSubscriptionInfoChanged();
- opptSubList = mSubscriptionControllerUT.getOpportunisticSubscriptions(mCallingPackage,
- mCallingFeature);
- assertEquals(1, opptSubList.size());
- assertEquals(2, opptSubList.get(0).getSubscriptionId());
- assertEquals(true, opptSubList.get(0).isGroupDisabled());
- }
-
- @Test
- @SmallTest
- public void testSetSubscriptionGroup() throws Exception {
- testInsertSim();
- // Adding a second profile and mark as embedded.
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- ContentValues values = new ContentValues();
- values.put(SubscriptionManager.IS_EMBEDDED, 1);
- mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
- mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
-
- assertTrue(mSubscriptionControllerUT.isActiveSubId(1));
- assertTrue(mSubscriptionControllerUT.isActiveSubId(2));
- assertTrue(TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, 1,
- mContext.getOpPackageName(), mContext.getAttributionTag(),
- "getSubscriptionsInGroup"));
-
- int[] subIdList = new int[] {1};
- ParcelUuid groupUuid = mSubscriptionControllerUT.createSubscriptionGroup(
- subIdList, mContext.getOpPackageName());
- assertNotEquals(null, groupUuid);
-
- // Sub 1 and sub 2 should be in same group.
- List<SubscriptionInfo> infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag());
- assertNotEquals(null, infoList);
- assertEquals(1, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
-
- subIdList = new int[] {2};
-
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- subIdList, groupUuid, mContext.getOpPackageName());
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid,
- mContext.getOpPackageName(), mContext.getAttributionTag());
- assertEquals(2, infoList.size());
- assertEquals(2, infoList.get(1).getSubscriptionId());
-
- // Remove group of sub 1.
- subIdList = new int[] {1};
- mSubscriptionControllerUT.removeSubscriptionsFromGroup(
- subIdList, groupUuid, mContext.getOpPackageName());
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid,
- mContext.getOpPackageName(), mContext.getAttributionTag());
- assertEquals(1, infoList.size());
- assertEquals(2, infoList.get(0).getSubscriptionId());
-
- // Adding sub 1 into a non-existing UUID, which should be granted.
- groupUuid = new ParcelUuid(UUID.randomUUID());
- mSubscriptionControllerUT.addSubscriptionsIntoGroup(
- subIdList, groupUuid, mContext.getOpPackageName());
- infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid,
- mContext.getOpPackageName(), mContext.getAttributionTag());
- assertEquals(1, infoList.size());
- assertEquals(1, infoList.get(0).getSubscriptionId());
- }
-
- private void registerMockTelephonyRegistry() {
- mServiceManagerMockedServices.put("telephony.registry", mTelephonyRegistryMock);
- doReturn(mTelephonyRegistryMock).when(mTelephonyRegistryMock)
- .queryLocalInterface(anyString());
- }
-
- @Test
- @SmallTest
- public void testEnableDisableSubscriptionSanity() throws Exception {
- testInsertSim();
-
- // Non existing subId.
- assertFalse(mSubscriptionControllerUT.isSubscriptionEnabled(2));
-
- // Test invalid arguments.
- try {
- assertFalse(mSubscriptionControllerUT.isSubscriptionEnabled(-1));
- fail("Should throw IllegalArgumentException with invalid subId.");
- } catch (IllegalArgumentException exception) {
- // Expected.
- }
-
- try {
- mSubscriptionControllerUT.getEnabledSubscriptionId(3);
- fail("Should throw IllegalArgumentException with invalid subId.");
- } catch (IllegalArgumentException exception) {
- // Expected.
- }
- }
-
- @Test
- @SmallTest
- public void testGetActiveSubIdList() throws Exception {
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("123", 1); // sub 1
- mSubscriptionControllerUT.addSubInfoRecord("456", 0); // sub 2
-
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
- // Make sure the return sub ids are sorted by slot index
- assertTrue("active sub ids = " + subIds, Arrays.equals(subIds, new int[]{2, 1}));
- }
-
- @Test
- public void testGetActiveSubscriptionInfoWithNoPermissions() throws Exception {
- // If the calling package does not have the READ_PHONE_STATE permission or carrier
- // privileges then getActiveSubscriptionInfo should throw a SecurityException;
- testInsertSim();
- setupReadPrivilegePermission();
- int subId = getFirstSubId();
- removeReadPrivilegePermission();
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
-
- try {
- mSubscriptionControllerUT.getActiveSubscriptionInfo(subId, mCallingPackage,
- mCallingFeature);
- fail("getActiveSubscriptionInfo should fail when invoked with no permissions");
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void testGetActiveSubscriptionInfoWithReadPhoneState() throws Exception {
- // If the calling package only has the READ_PHONE_STATE permission then
- // getActiveSubscriptionInfo should still return a result but the ICC ID should not be
- // available via getIccId or getCardString.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setIdentifierAccess(false);
- setupReadPrivilegePermission();
- int subId = getFirstSubId();
-
- SubscriptionInfo subscriptionInfo = mSubscriptionControllerUT.getActiveSubscriptionInfo(
- subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertEquals(UNAVAILABLE_ICCID, subscriptionInfo.getIccId());
- assertEquals(UNAVAILABLE_ICCID, subscriptionInfo.getCardString());
- assertEquals(UNAVAILABLE_NUMBER, subscriptionInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionWithPhoneNumberAccess() throws Exception {
- // If the calling package meets any of the requirements for the
- // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
- // in the SubscriptionInfo.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
- setupReadPrivilegePermission();
- int subId = getFirstSubId();
-
- SubscriptionInfo subscriptionInfo = mSubscriptionControllerUT.getActiveSubscriptionInfo(
- subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertEquals(DISPLAY_NUMBER, subscriptionInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoWithCarrierPrivileges() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo.
- testInsertSim();
- setupIdentifierCarrierPrivilegesTest();
- setupReadPrivilegePermission();
- int subId = getFirstSubId();
-
- SubscriptionInfo subscriptionInfo = mSubscriptionControllerUT.getActiveSubscriptionInfo(
- subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertTrue(subscriptionInfo.getIccId().length() > 0);
- assertTrue(subscriptionInfo.getCardString().length() > 0);
- }
-
- @Test
- public void testGetActiveSubscriptionWithPrivilegedPermission() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo.
- testInsertSim();
- int subId = getFirstSubId();
-
- SubscriptionInfo subscriptionInfo = mSubscriptionControllerUT.getActiveSubscriptionInfo(
- subId, mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertTrue(subscriptionInfo.getIccId().length() > 0);
- assertTrue(subscriptionInfo.getCardString().length() > 0);
- }
-
- @Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithNoPermission() throws Exception {
- // If the calling package does not have the READ_PHONE_STATE permission or carrier
- // privileges then getActiveSubscriptionInfoForSimSlotIndex should throw a
- // SecurityException.
- testInsertSim();
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
-
- try {
- mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0, mCallingPackage,
- mCallingFeature);
- fail("getActiveSubscriptionInfoForSimSlotIndex should fail when invoked with no "
- + "permissions");
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithReadPhoneState() throws Exception {
- // If the calling package only has the READ_PHONE_STATE permission then
- // getActiveSubscriptionInfoForSimlSlotIndex should still return the SubscriptionInfo but
- // the ICC ID should not be available via getIccId or getCardString.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setIdentifierAccess(false);
-
- SubscriptionInfo subscriptionInfo =
- mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0,
- mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertEquals(UNAVAILABLE_ICCID, subscriptionInfo.getIccId());
- assertEquals(UNAVAILABLE_ICCID, subscriptionInfo.getCardString());
- assertEquals(UNAVAILABLE_NUMBER, subscriptionInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithPhoneNumberAccess()
- throws Exception {
- // If the calling package meets any of the requirements for the
- // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
- // in the SubscriptionInfo.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
-
- SubscriptionInfo subscriptionInfo =
- mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0,
- mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertEquals(DISPLAY_NUMBER, subscriptionInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithCarrierPrivileges()
- throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo.
- testInsertSim();
- setupIdentifierCarrierPrivilegesTest();
-
- SubscriptionInfo subscriptionInfo =
- mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0,
- mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertTrue(subscriptionInfo.getIccId().length() > 0);
- assertTrue(subscriptionInfo.getCardString().length() > 0);
- }
-
- @Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithPrivilegedPermission()
- throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo.
- testInsertSim();
-
- SubscriptionInfo subscriptionInfo =
- mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0,
- mCallingPackage, mCallingFeature);
-
- assertNotNull(subscriptionInfo);
- assertTrue(subscriptionInfo.getIccId().length() > 0);
- assertTrue(subscriptionInfo.getCardString().length() > 0);
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithNoPermission() throws Exception {
- // If the calling package does not have the READ_PHONE_STATE permission or carrier
- // privileges then getActiveSubscriptionInfoList should return a list with 0 elements.
- testInsertSim();
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertNotNull(subInfoList);
- assertTrue(subInfoList.size() == 0);
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithReadPhoneState() throws Exception {
- // If the calling package only has the READ_PHONE_STATE permission then
- // getActiveSubscriptionInfoList should still return the list of SubscriptionInfo objects
- // but the ICC ID should not be available via getIccId or getCardString.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setIdentifierAccess(false);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertEquals(UNAVAILABLE_ICCID, info.getIccId());
- assertEquals(UNAVAILABLE_ICCID, info.getCardString());
- assertEquals(UNAVAILABLE_NUMBER, info.getNumber());
- }
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithPhoneNumberAccess() throws Exception {
- // If the calling package meets any of the requirements for the
- // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
- // in the SubscriptionInfo.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- SubscriptionInfo subInfo = subInfoList.get(0);
- assertEquals(DISPLAY_NUMBER, subInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithCarrierPrivileges() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
- testInsertSim();
- setupIdentifierCarrierPrivilegesTest();
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertTrue(info.getIccId().length() > 0);
- assertTrue(info.getCardString().length() > 0);
- }
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithCarrierPrivilegesOnOneSubId()
- throws Exception {
- // If an app does not have the READ_PHONE_STATE permission but has carrier privileges on one
- // out of multiple sub IDs then the SubscriptionInfo for that subId should be returned with
- // the ICC ID and phone number.
- testInsertSim();
- doReturn(2).when(mTelephonyManager).getPhoneCount();
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- int firstSubId = getFirstSubId();
- int secondSubId = getSubIdAtIndex(1);
- mSubscriptionControllerUT.setDisplayNumber(DISPLAY_NUMBER, secondSubId);
- setupIdentifierCarrierPrivilegesTest();
- mContextFixture.removeCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
- setCarrierPrivilegesForSubId(false, firstSubId);
- setCarrierPrivilegesForSubId(true, secondSubId);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertEquals(1, subInfoList.size());
- SubscriptionInfo subInfo = subInfoList.get(0);
- assertEquals("test2", subInfo.getIccId());
- assertEquals(DISPLAY_NUMBER, subInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListCheckOrder()
- throws Exception {
- // If an app does not have the READ_PHONE_STATE permission but has carrier privileges on one
- // out of multiple sub IDs then the SubscriptionInfo for that subId should be returned with
- // the ICC ID and phone number.
- testInsertSim();
- doReturn(2).when(mTelephonyManager).getPhoneCount();
- mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
- int firstSubId = getFirstSubId();
- int secondSubId = getSubIdAtIndex(1);
- setupIdentifierCarrierPrivilegesTest();
- setCarrierPrivilegesForSubId(false, firstSubId);
- setCarrierPrivilegesForSubId(true, secondSubId);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertEquals(2, subInfoList.size());
- assertTrue(subInfoList.get(0).getSubscriptionId() < subInfoList.get(1).getSubscriptionId());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithIdentifierAccessWithoutNumberAccess()
- throws Exception {
- // An app with access to device identifiers may not have access to the device phone number
- // (ie an app that passes the device / profile owner check or an app that has been granted
- // the device identifiers appop); this test verifies that an app with identifier access
- // can read the ICC ID but does not receive the phone number.
- testInsertSim();
- setupReadPhoneNumbersTest();
- setIdentifierAccess(true);
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertEquals(1, subInfoList.size());
- SubscriptionInfo subInfo = subInfoList.get(0);
- assertEquals("test", subInfo.getIccId());
- assertEquals(UNAVAILABLE_NUMBER, subInfo.getNumber());
- }
-
- @Test
- public void testGetActiveSubscriptionInfoListWithPrivilegedPermission() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
- testInsertSim();
-
- List<SubscriptionInfo> subInfoList =
- mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
- mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertTrue(info.getIccId().length() > 0);
- assertTrue(info.getCardString().length() > 0);
- }
- }
-
- @Test
- public void testGetSubscriptionsInGroupWithNoPermission() throws Exception {
- // If the calling package does not have the READ_PHONE_STATE permission or carrier
- // privileges then getSubscriptionsInGroup should throw a SecurityException when the
- // READ_PHONE_STATE permission check is performed.
- ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
-
- try {
- mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
- mCallingFeature);
- fail("getSubscriptionsInGroup should fail when invoked with no permissions");
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void testGetSubscriptionsInGroupWithReadPhoneState() throws Exception {
- // If the calling package only has the READ_PHONE_STATE permission then
- // getSubscriptionsInGroup should still return the list of SubscriptionInfo objects
- // but the ICC ID should not be available via getIccId or getCardString.
- ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
- setupReadPhoneNumbersTest();
- setIdentifierAccess(false);
-
- List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupUuid, mCallingPackage, mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertEquals(UNAVAILABLE_ICCID, info.getIccId());
- assertEquals(UNAVAILABLE_ICCID, info.getCardString());
- assertEquals(UNAVAILABLE_NUMBER, info.getNumber());
- }
- }
-
- @Test
- public void testGetSubscriptionInGroupWithPhoneNumberAccess() throws Exception {
- // If the calling package meets any of the requirements for the
- // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
- // in the SubscriptionInfo.
- ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
- setupReadPhoneNumbersTest();
- setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
-
- List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupUuid, mCallingPackage, mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- SubscriptionInfo subInfo = subInfoList.get(0);
- assertEquals(DISPLAY_NUMBER, subInfo.getNumber());
- }
-
- @Test
- public void testGetSubscriptionsInGroupWithCarrierPrivileges() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
- ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
- setupIdentifierCarrierPrivilegesTest();
-
- List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupUuid, mCallingPackage, mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertTrue(info.getIccId().length() > 0);
- assertTrue(info.getCardString().length() > 0);
- }
- }
-
- @Test
- public void testGetSubscriptionsInGroupWithPrivilegedPermission() throws Exception {
- // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
- // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
- ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
-
- List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
- groupUuid, mCallingPackage, mCallingFeature);
-
- assertTrue(subInfoList.size() > 0);
- for (SubscriptionInfo info : subInfoList) {
- assertTrue(info.getIccId().length() > 0);
- assertTrue(info.getCardString().length() > 0);
- }
- }
-
- private ParcelUuid setupGetSubscriptionsInGroupTest() throws Exception {
- testInsertSim();
- int[] subIdList = new int[]{getFirstSubId()};
- ParcelUuid groupUuid = mSubscriptionControllerUT.createSubscriptionGroup(subIdList,
- mCallingPackage);
- assertNotNull(groupUuid);
- return groupUuid;
- }
-
- private void setupReadPhoneNumbersTest() throws Exception {
- mSubscriptionControllerUT.setDisplayNumber(DISPLAY_NUMBER, getFirstSubId());
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
- setupMocksForTelephonyPermissions(Build.VERSION_CODES.R);
- setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
- }
-
- private void setupIdentifierCarrierPrivilegesTest() throws Exception {
- mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
- setupMocksForTelephonyPermissions();
- setIdentifierAccess(false);
- setCarrierPrivileges(true);
- }
-
- private void setupReadPrivilegePermission() throws Exception {
- mContextFixture.addCallingOrSelfPermissionToCurrentPermissions(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- }
- private void removeReadPrivilegePermission() throws Exception {
- mContextFixture.removeCallingOrSelfPermission(
- Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- }
-
- private int getFirstSubId() throws Exception {
- return getSubIdAtIndex(0);
- }
-
- private int getSubIdAtIndex(int index) throws Exception {
- int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibileOnly*/false);
- assertTrue(subIds != null && subIds.length > index);
- return subIds[index];
- }
-
- @Test
- public void testGetEnabledSubscriptionIdSingleSIM() {
- // A single SIM device may have logical slot 0 mapped to physical slot 1
- // (i.e. logical slot -1 mapped to physical slot 0)
- UiccSlotInfo slot0 = getFakeUiccSlotInfo(false, -1, null);
- UiccSlotInfo slot1 = getFakeUiccSlotInfo(true, 0, null);
- UiccSlotInfo [] uiccSlotInfos = {slot0, slot1};
- UiccSlot [] uiccSlots = {mUiccSlot, mUiccSlot};
-
- doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
- doReturn(uiccSlots).when(mUiccController).getUiccSlots();
- assertEquals(2, UiccController.getInstance().getUiccSlots().length);
-
- ContentResolver resolver = mContext.getContentResolver();
- // logical 0 should find physical 1, has settings enabled subscription 0
- Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 1, 0);
-
- int enabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(0);
- assertEquals(0, enabledSubscription);
- }
-
- @Test
- public void testGetEnabledSubscriptionIdDualSIM() {
- doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
- doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
- doReturn(SINGLE_SIM).when(mTelephonyManager).getActiveModemCount();
- // A dual SIM device may have logical slot 0 mapped to physical slot 0
- // (i.e. logical slot 1 mapped to physical slot 1)
- UiccSlotInfo slot0 = getFakeUiccSlotInfo(true, 0, null);
- UiccSlotInfo slot1 = getFakeUiccSlotInfo(true, 1, null);
- UiccSlotInfo [] uiccSlotInfos = {slot0, slot1};
- UiccSlot [] uiccSlots = {mUiccSlot, mUiccSlot};
-
- doReturn(2).when(mTelephonyManager).getPhoneCount();
- doReturn(2).when(mTelephonyManager).getActiveModemCount();
- doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
- doReturn(uiccSlots).when(mUiccController).getUiccSlots();
- assertEquals(2, UiccController.getInstance().getUiccSlots().length);
-
- ContentResolver resolver = mContext.getContentResolver();
- // logical 0 should find physical 0, has settings enabled subscription 0
- Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 0, 0);
- Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 1, 1);
-
- int enabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(0);
- int secondEabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(1);
- assertEquals(0, enabledSubscription);
- assertEquals(1, secondEabledSubscription);
- }
-
-
- private UiccSlotInfo getFakeUiccSlotInfo(boolean active, int logicalSlotIndex, String iccId) {
- return getFakeUiccSlotInfo(active, logicalSlotIndex, "fake card Id", iccId);
- }
-
- private UiccSlotInfo getFakeUiccSlotInfo(
- boolean active, int logicalSlotIndex, String cardId, String iccId) {
- return new UiccSlotInfo(false, cardId,
- UiccSlotInfo.CARD_STATE_INFO_PRESENT, true, true,
- Collections.singletonList(
- new UiccPortInfo(iccId, 0, logicalSlotIndex, active)
- ));
- }
-
- @Test
- @SmallTest
- public void testNameSourcePriority() throws Exception {
- assertTrue(mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_USER_INPUT)
- > mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_CARRIER));
-
- assertTrue(mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_CARRIER)
- > mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_SIM_SPN));
-
- assertTrue(mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_SIM_SPN)
- > mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_SIM_PNN));
-
- assertTrue(mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_SIM_PNN)
- > mSubscriptionControllerUT.getNameSourcePriority(
- SubscriptionManager.NAME_SOURCE_CARRIER_ID));
- }
-
- @Test
- @SmallTest
- public void testGetAvailableSubscriptionList() throws Exception {
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("123", 1); // sub 1
- mSubscriptionControllerUT.addSubInfoRecord("456", 0); // sub 2
-
- List<SubscriptionInfo> infoList = mSubscriptionControllerUT
- .getAvailableSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertEquals(2, infoList.size());
- assertEquals("456", infoList.get(0).getIccId());
- assertEquals("123", infoList.get(1).getIccId());
-
- // Remove "123" from active sim list but have it inserted.
- UiccSlot[] uiccSlots = {mUiccSlot};
- IccCardStatus.CardState cardState = CARDSTATE_PRESENT;
- doReturn(uiccSlots).when(mUiccController).getUiccSlots();
- doReturn(cardState).when(mUiccSlot).getCardState();
- doReturn("123").when(mUiccSlot).getIccId(0); // default port index
- mSubscriptionControllerUT.clearSubInfoRecord(1);
-
- // Active sub list should return 1 now.
- infoList = mSubscriptionControllerUT
- .getActiveSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertEquals(1, infoList.size());
- assertEquals("456", infoList.get(0).getIccId());
-
- // Available sub list should still return two.
- infoList = mSubscriptionControllerUT
- .getAvailableSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertEquals(2, infoList.size());
- assertEquals("123", infoList.get(0).getIccId());
- assertEquals("456", infoList.get(1).getIccId());
- }
-
- @Test
- @SmallTest
- public void testGetAvailableSubscriptionList_withTrailingF() throws Exception {
- // TODO b/123300875 slot index 1 is not expected to be valid
- mSubscriptionControllerUT.addSubInfoRecord("123", 1); // sub 1
- mSubscriptionControllerUT.addSubInfoRecord("456", 0); // sub 2
-
- // Remove "123" from active sim list but have it inserted.
- UiccSlot[] uiccSlots = {mUiccSlot};
- IccCardStatus.CardState cardState = CARDSTATE_PRESENT;
- doReturn(uiccSlots).when(mUiccController).getUiccSlots();
- doReturn(cardState).when(mUiccSlot).getCardState();
- // IccId ends with a 'F' which should be ignored and taking into account.
- doReturn("123F").when(mUiccSlot).getIccId(0); // default port index
-
- mSubscriptionControllerUT.clearSubInfoRecord(1);
-
- // Active sub list should return 1 now.
- List<SubscriptionInfo> infoList = mSubscriptionControllerUT
- .getActiveSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertEquals(1, infoList.size());
- assertEquals("456", infoList.get(0).getIccId());
-
- // Available sub list should still return two.
- infoList = mSubscriptionControllerUT
- .getAvailableSubscriptionInfoList(mCallingPackage, mCallingFeature);
- assertEquals(2, infoList.size());
- assertEquals("123", infoList.get(0).getIccId());
- assertEquals("456", infoList.get(1).getIccId());
- }
-
- @Test
- @SmallTest
- public void testSetPreferredDataSubscriptionId_phoneSwitcherNotInitialized() throws Exception {
- replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, null);
-
- mSubscriptionControllerUT.setPreferredDataSubscriptionId(1, true, mSetOpptDataCallback);
- verify(mSetOpptDataCallback).onComplete(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
- }
-
- @Test
- @SmallTest
- public void testGetPreferredDataSubscriptionId_phoneSwitcherNotInitialized() throws Exception {
- replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, null);
-
- assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- mSubscriptionControllerUT.getPreferredDataSubscriptionId());
- }
-
- @Test
- public void testSetSubscriptionEnabled_disableActivePsim_cardIdWithTrailingF() {
- String iccId = "123F";
- mSubscriptionControllerUT.addSubInfoRecord(iccId, 0);
- mSubscriptionControllerUT.registerForUiccAppsEnabled(mHandler, 0, null, false);
- UiccSlotInfo slot = getFakeUiccSlotInfo(true, 0, iccId + "FF", iccId);
- UiccSlotInfo[] uiccSlotInfos = {slot};
- doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
-
- mSubscriptionControllerUT.setSubscriptionEnabled(false, 1);
- verify(mHandler).sendMessageAtTime(any(), anyLong());
- assertFalse(mSubscriptionControllerUT.getActiveSubscriptionInfo(
- 1, mContext.getOpPackageName(), null).areUiccApplicationsEnabled());
- }
-
- @Test
- @SmallTest
- public void testInsertEmptySubInfoRecord_returnsNull_ifRecordExists() {
- final String mockedIccid = "123456789";
- final int mockedSlotIndex = 1;
-
- assertNotNull(mSubscriptionControllerUT.insertEmptySubInfoRecord(
- mockedIccid, mockedSlotIndex));
- // Insert second time with the same iccid should result in no-op and return null.
- assertNull(mSubscriptionControllerUT.insertEmptySubInfoRecord(
- mockedIccid, mockedSlotIndex));
- assertEquals(
- 1,
- mSubscriptionControllerUT
- .getAllSubInfoList(mCallingPackage, mCallingFeature).size());
- }
-
- @Test
- @SmallTest
- public void testCheckPhoneIdAndIccIdMatch() {
- try {
- testSetSubscriptionGroupWithModifyPermission();
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
-
- mSubscriptionControllerUT.addSubInfoRecord("test3",
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
- assertTrue(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test"));
- assertTrue(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test2"));
- assertFalse(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test3"));
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index 3f85435..3bafe4d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -15,12 +15,11 @@
*/
package com.android.internal.telephony;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import android.telephony.SubscriptionInfo;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.telephony.SubscriptionManager;
import org.junit.After;
import org.junit.Before;
@@ -38,78 +37,63 @@
@Before
public void setUp() throws Exception {
- mSubscriptionInfoUT = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
- "T-mobile", 0, 255, "12345", 0, null, "310", "260", "156", false, null, null);
- mSubscriptionInfoUT.setAssociatedPlmns(EHPLMNS, HPLMNS);
+ mSubscriptionInfoUT = new SubscriptionInfo.Builder()
+ .setId(1)
+ .setIccId("890126042XXXXXXXXXXX")
+ .setSimSlotIndex(0)
+ .setDisplayName("T-mobile")
+ .setCarrierName("T-mobile")
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER_ID)
+ .setIconTint(255)
+ .setNumber("12345")
+ .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE)
+ .setMcc("310")
+ .setMnc("260")
+ .setEhplmns(EHPLMNS)
+ .setHplmns(HPLMNS)
+ .setCountryIso("us")
+ .build();
}
@Test
- @SmallTest
public void testSubProperties() {
- assertEquals(260, mSubscriptionInfoUT.getMnc());
- assertEquals(310, mSubscriptionInfoUT.getMcc());
- assertEquals("12345", mSubscriptionInfoUT.getNumber());
- assertEquals(0, mSubscriptionInfoUT.getDataRoaming());
- assertEquals("T-mobile", mSubscriptionInfoUT.getDisplayName());
- assertEquals("T-mobile", mSubscriptionInfoUT.getCarrierName());
- assertEquals("156", mSubscriptionInfoUT.getCountryIso());
- assertEquals(255, mSubscriptionInfoUT.getIconTint());
- assertEquals(0, mSubscriptionInfoUT.getNameSource());
- assertEquals(1, mSubscriptionInfoUT.getSubscriptionId());
- assertEquals(0, mSubscriptionInfoUT.getSimSlotIndex());
- assertEquals("890126042XXXXXXXXXXX", mSubscriptionInfoUT.getIccId());
+ assertThat(mSubscriptionInfoUT.getMcc()).isEqualTo(310);
+ assertThat(mSubscriptionInfoUT.getMccString()).isEqualTo("310");
+ assertThat(mSubscriptionInfoUT.getMnc()).isEqualTo(260);
+ assertThat(mSubscriptionInfoUT.getMncString()).isEqualTo("260");
+ assertThat(mSubscriptionInfoUT.getNumber()).isEqualTo("12345");
+ assertThat(mSubscriptionInfoUT.getDataRoaming()).isEqualTo(0);
+ assertThat(mSubscriptionInfoUT.getDisplayName().toString()).isEqualTo("T-mobile");
+ assertThat(mSubscriptionInfoUT.getCarrierName().toString()).isEqualTo("T-mobile");
+ assertThat(mSubscriptionInfoUT.getCountryIso()).isEqualTo("us");
+ assertThat(mSubscriptionInfoUT.getIconTint()).isEqualTo(255);
+ assertThat(mSubscriptionInfoUT.getDisplayNameSource()).isEqualTo(0);
+ assertThat(mSubscriptionInfoUT.getSubscriptionId()).isEqualTo(1);
+ assertThat(mSubscriptionInfoUT.getSimSlotIndex()).isEqualTo(0);
+ assertThat(mSubscriptionInfoUT.getIccId()).isEqualTo("890126042XXXXXXXXXXX");
}
@Test
- @SmallTest
- public void testSetGetCarrierName() {
- assertEquals("T-mobile", mSubscriptionInfoUT.getCarrierName());
- mSubscriptionInfoUT.setCarrierName("Verizon");
- assertEquals("Verizon", mSubscriptionInfoUT.getCarrierName());
- }
-
- @Test
- @SmallTest
- public void testSetGetDisplayName() {
- assertEquals("T-mobile", mSubscriptionInfoUT.getDisplayName());
- mSubscriptionInfoUT.setDisplayName("Verizon");
- assertEquals("Verizon", mSubscriptionInfoUT.getDisplayName());
- }
-
- @Test
- @SmallTest
- public void testSetGetIconTint() {
- assertEquals(255, mSubscriptionInfoUT.getIconTint());
- mSubscriptionInfoUT.setIconTint(0);
- assertEquals(0, mSubscriptionInfoUT.getIconTint());
- }
-
- @Test
- @SmallTest
public void testParcelUnparcel() {
Parcel p = Parcel.obtain();
mSubscriptionInfoUT.writeToParcel(p, 0);
p.setDataPosition(0);
SubscriptionInfo copy = SubscriptionInfo.CREATOR.createFromParcel(p);
- assertEquals(mSubscriptionInfoUT, copy);
+ assertThat(mSubscriptionInfoUT).isEqualTo(copy);
}
@Test
- @SmallTest
public void testEquals() {
- SubscriptionInfo copiedInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
- "T-mobile", "T-mobile", 0, 255, "12345", 0, null,
- "310", "260", "156", false, null, null);
- copiedInfo.setAssociatedPlmns(EHPLMNS, HPLMNS);
- SubscriptionInfo differentDisplayName = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
- "AT&T", "T-mobile", 0, 255, "12345", 0, null,
- "310", "260", "156", false, null, null);
- SubscriptionInfo differentSubId = new SubscriptionInfo(2, "890126042XXXXXXXXXXX", 0,
- "AT&T", "T-mobile", 0, 255, "12345", 0, null,
- "310", "260", "156", false, null, null);
+ SubscriptionInfo copiedInfo = new SubscriptionInfo.Builder(mSubscriptionInfoUT).build();
+ SubscriptionInfo differentDisplayName = new SubscriptionInfo.Builder(mSubscriptionInfoUT)
+ .setDisplayName("Different display name")
+ .build();
+ SubscriptionInfo differentSubId = new SubscriptionInfo.Builder(mSubscriptionInfoUT)
+ .setId(1234)
+ .build();
- assertEquals(mSubscriptionInfoUT, copiedInfo);
- assertNotEquals(mSubscriptionInfoUT, differentDisplayName);
- assertNotEquals(mSubscriptionInfoUT, differentSubId);
+ assertThat(mSubscriptionInfoUT).isEqualTo(copiedInfo);
+ assertThat(mSubscriptionInfoUT).isNotEqualTo(differentDisplayName);
+ assertThat(mSubscriptionInfoUT).isNotEqualTo(differentSubId);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
deleted file mode 100644
index ea38e2d..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ /dev/null
@@ -1,1076 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony;
-
-import static android.telephony.SubscriptionManager.UICC_APPLICATIONS_ENABLED;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.UserInfo;
-import android.net.Uri;
-import android.os.Looper;
-import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.service.euicc.EuiccProfileInfo;
-import android.service.euicc.EuiccService;
-import android.service.euicc.GetEuiccProfileInfoListResult;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.UiccAccessRule;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.euicc.EuiccController;
-import com.android.internal.telephony.uicc.IccFileHandler;
-import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccSlot;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class SubscriptionInfoUpdaterTest extends TelephonyTest {
- private static final int FAKE_SUB_ID_1 = 0;
- private static final int FAKE_SUB_ID_2 = 1;
- private static final int FAKE_CARD_ID = 0;
- private static final String FAKE_EID = "89049032000001000000031328322874";
- private static final String FAKE_ICCID_1 = "89012604200000000000";
- private static final String FAKE_MCC_MNC_1 = "123456";
- private static final String FAKE_MCC_MNC_2 = "456789";
- private static final int FAKE_PHONE_ID_1 = 0;
-
- private SubscriptionInfoUpdater mUpdater;
- private IccRecords mIccRecord;
-
- // Mocked classes
- private UserInfo mUserInfo;
- private SubscriptionInfo mSubInfo;
- private ContentProvider mContentProvider;
- private HashMap<String, Object> mSubscriptionContent;
- private IccFileHandler mIccFileHandler;
- private EuiccController mEuiccController;
- private IntentBroadcaster mIntentBroadcaster;
- private IPackageManager mPackageManager;
- private UiccSlot mUiccSlot;
-
- /*Custom ContentProvider */
- private class FakeSubscriptionContentProvider extends MockContentProvider {
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return mContentProvider.update(uri, values, selection, selectionArgs);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mUserInfo = mock(UserInfo.class);
- mSubInfo = mock(SubscriptionInfo.class);
- mContentProvider = mock(ContentProvider.class);
- mSubscriptionContent = mock(HashMap.class);
- mIccFileHandler = mock(IccFileHandler.class);
- mEuiccController = mock(EuiccController.class);
- mIntentBroadcaster = mock(IntentBroadcaster.class);
- mPackageManager = mock(IPackageManager.class);
- mUiccSlot = mock(UiccSlot.class);
-
- replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null, new String[1]);
- replaceInstance(SubscriptionInfoUpdater.class, "sContext", null, null);
- replaceInstance(SubscriptionInfoUpdater.class, "SUPPORTED_MODEM_COUNT", null, 1);
- replaceInstance(SubscriptionInfoUpdater.class, "sSimCardState", null, new int[1]);
- replaceInstance(SubscriptionInfoUpdater.class, "sSimApplicationState", null, new int[1]);
- replaceInstance(SubscriptionInfoUpdater.class, "sIsSubInfoInitialized", null, false);
-
- replaceInstance(EuiccController.class, "sInstance", null, mEuiccController);
- replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
-
- doReturn(true).when(mUiccSlot).isActive();
- doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
- doReturn(1).when(mTelephonyManager).getSimCount();
- doReturn(1).when(mTelephonyManager).getPhoneCount();
- doReturn(1).when(mTelephonyManager).getActiveModemCount();
-
- when(mContentProvider.update(any(), any(), any(), isNull())).thenAnswer(
- new Answer<Integer>() {
- @Override
- public Integer answer(InvocationOnMock invocation) throws Throwable {
- ContentValues values = invocation.getArgument(1);
- for (String key : values.keySet()) {
- mSubscriptionContent.put(key, values.get(key));
- }
- return 1;
- }
- });
-
- doReturn(mUserInfo).when(mIActivityManager).getCurrentUser();
- doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController).getSubId(0);
- doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionManager).getActiveSubscriptionIdList();
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
- doReturn(new int[]{}).when(mSubscriptionController)
- .getActiveSubIdList(/*visibleOnly*/false);
- mIccRecord = mUiccProfile.getIccRecords();
-
- mUpdater =
- new SubscriptionInfoUpdater(Looper.myLooper(), mContext, mSubscriptionController);
- processAllMessages();
-
- assertFalse(mUpdater.isSubInfoInitialized());
- }
-
- @After
- public void tearDown() throws Exception {
- mIccRecord = null;
- mUpdater = null;
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testSimAbsent() throws Exception {
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController)
- .getActiveSubIdList(/*visibleOnly*/false);
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionController, times(1)).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
-
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_ABSENT));
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimAbsentAndInactive() throws Exception {
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController)
- .getActiveSubIdList(/*visibleOnly*/false);
- mUpdater.updateInternalIccStateForInactivePort(FAKE_SUB_ID_1, null);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionController, times(1)).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
-
- // Verify that in the special absent and inactive case, we update subscriptions without
- // broadcasting SIM state change
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, times(0)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_ABSENT));
- verify(mContext, times(0)).sendBroadcast(any(), anyString());
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimUnknown() throws Exception {
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- assertFalse(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionContent, times(0)).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_UNKNOWN));
- verify(mSubscriptionController, times(0)).clearSubInfo();
- verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimNotReady() throws Exception {
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
-
- processAllMessages();
- assertFalse(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionContent, never()).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
- verify(mSubscriptionController, never()).clearSubInfoRecord(FAKE_PHONE_ID_1);
- verify(mSubscriptionController, never()).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimNotReadyEmptyProfile() throws Exception {
- doReturn(mIccCard).when(mPhone).getIccCard();
- doReturn(true).when(mIccCard).isEmptyProfile();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- // Sub info should be cleared and change should be notified.
- verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
- verify(mSubscriptionController).notifySubscriptionInfoChanged();
- // No new sub should be added.
- verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
- verify(mSubscriptionContent, never()).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
- }
-
- @Test
- @SmallTest
- public void testSimNotReadyDisabledUiccApps() throws Exception {
- String iccId = "123456";
- doReturn(mIccCard).when(mPhone).getIccCard();
- doReturn(false).when(mIccCard).isEmptyProfile();
- doReturn(mUiccPort).when(mUiccController).getUiccPort(anyInt());
- doReturn(iccId).when(mUiccPort).getIccId();
- doReturn(mSubInfo).when(mSubscriptionController).getSubInfoForIccId(iccId);
- doReturn(false).when(mSubInfo).areUiccApplicationsEnabled();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- // Sub info should be cleared and change should be notified.
- verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
- verify(mSubscriptionController).notifySubscriptionInfoChanged();
- // No new sub should be added.
- verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
- verify(mSubscriptionContent, never()).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
-
- // When becomes ABSENT, UICC_APPLICATIONS_ENABLED should be reset to true.
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_PHONE_ID_1);
- processAllMessages();
- ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
- eq(SubscriptionManager.ICC_ID + "=\'" + iccId + "\'"), eq(null));
- ContentValues contentValues = valueCapture.getValue();
- assertTrue(contentValues != null && contentValues.getAsBoolean(
- UICC_APPLICATIONS_ENABLED));
- }
-
- @Test
- @SmallTest
- public void testSimRemovedWhileDisablingUiccApps() throws Exception {
- loadSim();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
- processAllMessages();
-
- // UICC_APPLICATIONS_ENABLED should be reset to true.
- ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
- eq(SubscriptionManager.ICC_ID + "=\'" + FAKE_ICCID_1 + "\'"), eq(null));
- ContentValues contentValues = valueCapture.getValue();
- assertTrue(contentValues != null && contentValues.getAsBoolean(
- UICC_APPLICATIONS_ENABLED));
- }
-
- @Test
- @SmallTest
- public void testSimError() throws Exception {
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionContent, times(0)).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR));
- verify(mSubscriptionController, times(0)).clearSubInfo();
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testWrongSimState() throws Exception {
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_IMSI, null, 2);
-
- processAllMessages();
- assertFalse(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionContent, times(0)).put(anyString(), any());
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, times(0)).updateConfigForPhoneId(eq(2),
- eq(IccCardConstants.INTENT_VALUE_ICC_IMSI));
- verify(mSubscriptionController, times(0)).clearSubInfo();
- verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
- }
-
- private void loadSim() {
- doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
- doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- when(mActivityManager.updateMccMncConfiguration(anyString(), anyString())).thenReturn(
- true);
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
-
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
- }
-
- @Test
- @SmallTest
- public void testSimLoaded() throws Exception {
- loadSim();
-
- // verify SIM_STATE_CHANGED broadcast. It should be broadcast twice, once for
- // READ_PHONE_STATE and once for READ_PRIVILEGED_PHONE_STATE
- /* todo: cannot verify as intent is sent using ActivityManagerNative.broadcastStickyIntent()
- * uncomment code below when that is fixed
- */
- /* ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mContext, times(2)).sendBroadcast(intentArgumentCaptor.capture(),
- stringArgumentCaptor.capture());
- assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
- intentArgumentCaptor.getAllValues().get(0).getAction());
- assertEquals(Manifest.permission.READ_PHONE_STATE,
- stringArgumentCaptor.getAllValues().get(0));
- assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
- intentArgumentCaptor.getAllValues().get(1).getAction());
- assertEquals(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
- stringArgumentCaptor.getAllValues().get(1)); */
-
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(1)).setMccMnc(FAKE_MCC_MNC_1, FAKE_SUB_ID_1);
- verify(mSubscriptionController, times(0)).clearSubInfo();
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
-
- // ACTION_USER_UNLOCKED should trigger another SIM_STATE_CHANGED
- Intent intentSimStateChanged = new Intent(Intent.ACTION_USER_UNLOCKED);
- mContext.sendBroadcast(intentSimStateChanged);
- processAllMessages();
-
- // verify SIM_STATE_CHANGED broadcast
- /* todo: cannot verify as intent is sent using ActivityManagerNative.broadcastStickyIntent()
- * uncomment code below when that is fixed
- */
- /* verify(mContext, times(4)).sendBroadcast(intentArgumentCaptor.capture(),
- stringArgumentCaptor.capture());
- assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
- intentArgumentCaptor.getAllValues().get(2).getAction());
- assertEquals(Manifest.permission.READ_PHONE_STATE,
- stringArgumentCaptor.getAllValues().get(2));
- assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
- intentArgumentCaptor.getAllValues().get(3).getAction());
- assertEquals(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
- stringArgumentCaptor.getAllValues().get(3)); */
- }
-
- @Test
- @SmallTest
- public void testSimLoadedEmptyOperatorNumeric() throws Exception {
- doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
- // operator numeric is empty
- doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
- verify(mSubscriptionController, times(0)).clearSubInfo();
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
- }
-
- @Test
- @SmallTest
- public void testSimLockedWithOutIccId() throws Exception {
- /* mock no IccId Info present and try to query IccId
- after IccId query, update subscriptionDB */
- doReturn("98106240020000000000").when(mIccRecord).getFullIccId();
-
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq("98106240020000000000"), eq(FAKE_SUB_ID_1));
-
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(0)).clearSubInfo();
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
- }
-
- @Test
- @SmallTest
- public void testDualSimLoaded() throws Exception {
- // Mock there is two sim cards
- replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone});
- replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
- new String[]{null, null});
- replaceInstance(SubscriptionInfoUpdater.class, "SUPPORTED_MODEM_COUNT", null, 2);
- replaceInstance(SubscriptionInfoUpdater.class, "sSimCardState", null,
- new int[]{0, 0});
- replaceInstance(SubscriptionInfoUpdater.class, "sSimApplicationState", null,
- new int[]{0, 0});
-
- doReturn(new int[]{FAKE_SUB_ID_1, FAKE_SUB_ID_2}).when(mSubscriptionManager)
- .getActiveSubscriptionIdList();
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_1));
- doReturn(FAKE_SUB_ID_2).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_2));
- doReturn(2).when(mTelephonyManager).getPhoneCount();
- doReturn(2).when(mTelephonyManager).getActiveModemCount();
- when(mActivityManager.updateMccMncConfiguration(anyString(), anyString())).thenReturn(
- true);
- doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_1));
- doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
- verify(mSubscriptionController, times(0)).clearSubInfo();
- doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(anyString(), anyInt());
- verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
-
- // Mock sending a sim loaded for SIM 1
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
-
- processAllMessages();
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(anyString(), anyInt());
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(1)).setMccMnc(anyString(), anyInt());
- assertFalse(mUpdater.isSubInfoInitialized());
-
- // Mock sending a sim loaded for SIM 2
- doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_2));
- doReturn(FAKE_SUB_ID_2).when(mSubInfo).getSubscriptionId();
- doReturn("89012604200000000001").when(mIccRecord).getFullIccId();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);
-
- processAllMessages();
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq(FAKE_ICCID_1),
- eq(FAKE_SUB_ID_1));
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000001"),
- eq(FAKE_SUB_ID_2));
- verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_1), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_2), eq(FAKE_SUB_ID_2));
- verify(mSubscriptionController, times(2)).notifySubscriptionInfoChanged();
- assertTrue(mUpdater.isSubInfoInitialized());
- }
-
- @Test
- @SmallTest
- public void testSimLockWithIccId() throws Exception {
- // ICCID will be queried even if it is already available
- doReturn("98106240020000000000").when(mIccRecord).getFullIccId();
-
- replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
- new String[]{FAKE_ICCID_1});
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
-
- processAllMessages();
- assertTrue(mUpdater.isSubInfoInitialized());
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- anyString(), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(0)).clearSubInfo();
- CarrierConfigManager mConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- /* broadcast is done */
- verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
- eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
- }
-
- @Test
- @SmallTest
- public void testUpdateEmbeddedSubscriptions_listSuccess() throws Exception {
- when(mEuiccManager.isEnabled()).thenReturn(true);
- when(mEuiccManager.createForCardId(anyInt())).thenReturn(mEuiccManager);
- when(mEuiccManager.getEid()).thenReturn(FAKE_EID);
-
- EuiccProfileInfo[] euiccProfiles = new EuiccProfileInfo[] {
- new EuiccProfileInfo("1", null /* accessRules */, null /* nickname */),
- new EuiccProfileInfo("3", null /* accessRules */, null /* nickname */),
- };
- when(mEuiccController.blockingGetEuiccProfileInfoList(FAKE_CARD_ID)).thenReturn(
- new GetEuiccProfileInfoListResult(
- EuiccService.RESULT_OK, euiccProfiles, false /* removable */));
-
- List<SubscriptionInfo> subInfoList = new ArrayList<>();
- // 1: not embedded, but has matching iccid with an embedded subscription.
- subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
- null /* accessRules */, null));
- // 2: embedded but no longer present.
- subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
- null /* accessRules */, null));
-
- when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
- new String[] { "1", "3"}, false /* removable */)).thenReturn(subInfoList);
-
- List<Integer> cardIds = new ArrayList<>();
- cardIds.add(FAKE_CARD_ID);
- mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);
- processAllMessages();
-
- // 3 is new and so a new entry should have been created.
- verify(mSubscriptionController).insertEmptySubInfoRecord(
- "3", SubscriptionManager.SIM_NOT_INSERTED);
- // 1 already existed, so no new entries should be created for it.
- verify(mSubscriptionController, times(0)).clearSubInfo();
- verify(mSubscriptionController, never()).insertEmptySubInfoRecord(eq("1"), anyInt());
-
- // Info for 1 and 3 should be updated as active embedded subscriptions.
- ArgumentCaptor<ContentValues> iccid1Values = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), iccid1Values.capture(),
- eq(SubscriptionManager.ICC_ID + "='1'"), isNull());
- assertEquals(1,
- iccid1Values.getValue().getAsInteger(SubscriptionManager.IS_EMBEDDED).intValue());
- ArgumentCaptor<ContentValues> iccid3Values = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), iccid3Values.capture(),
- eq(SubscriptionManager.ICC_ID + "='3'"), isNull());
- assertEquals(1,
- iccid3Values.getValue().getAsInteger(SubscriptionManager.IS_EMBEDDED).intValue());
-
- // 2 should have been removed since it was returned from the cache but was not present
- // in the list provided by the LPA.
- ArgumentCaptor<ContentValues> iccid2Values = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), iccid2Values.capture(),
- eq(SubscriptionManager.ICC_ID + " IN ('2')"), isNull());
- assertEquals(0,
- iccid2Values.getValue().getAsInteger(SubscriptionManager.IS_EMBEDDED).intValue());
- }
-
- @Test
- @SmallTest
- public void testUpdateEmbeddedSubscriptions_listFailure() throws Exception {
- when(mEuiccManager.isEnabled()).thenReturn(true);
- when(mEuiccController.blockingGetEuiccProfileInfoList(FAKE_CARD_ID))
- .thenReturn(new GetEuiccProfileInfoListResult(
- 42, null /* subscriptions */, false /* removable */));
-
- List<SubscriptionInfo> subInfoList = new ArrayList<>();
- // 1: not embedded, but has matching iccid with an embedded subscription.
- subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
- null /* accessRules */, null));
- // 2: embedded.
- subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
- null /* accessRules */, null));
-
- when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
- new String[0], false /* removable */)).thenReturn(subInfoList);
-
- ArrayList<Integer> cardIds = new ArrayList<>(1);
- cardIds.add(FAKE_CARD_ID);
- mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);
-
- // No new entries should be created.
- verify(mSubscriptionController, times(0)).clearSubInfo();
- verify(mSubscriptionController, never()).insertEmptySubInfoRecord(anyString(), anyInt());
-
- // No existing entries should have been updated.
- verify(mContentProvider, never()).update(eq(SubscriptionManager.CONTENT_URI), any(),
- any(), isNull());
- }
-
- @Test
- @SmallTest
- public void testUpdateEmbeddedSubscriptions_emptyToEmpty() throws Exception {
- when(mEuiccManager.isEnabled()).thenReturn(true);
- when(mEuiccController.blockingGetEuiccProfileInfoList(FAKE_CARD_ID))
- .thenReturn(new GetEuiccProfileInfoListResult(
- 42, null /* subscriptions */, true /* removable */));
-
- List<SubscriptionInfo> subInfoList = new ArrayList<>();
- // 1: not embedded.
- subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
- null /* accessRules */, null));
-
- when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
- new String[0], false /* removable */)).thenReturn(subInfoList);
-
- ArrayList<Integer> cardIds = new ArrayList<>(1);
- cardIds.add(FAKE_CARD_ID);
- mUpdater.updateEmbeddedSubscriptions(cardIds, null /* callback */);
-
- // No new entries should be created.
- verify(mSubscriptionController, never()).insertEmptySubInfoRecord(anyString(), anyInt());
-
- // No existing entries should have been updated.
- verify(mContentProvider, never()).update(eq(SubscriptionManager.CONTENT_URI), any(),
- any(), isNull());
- }
-
- @Test
- @SmallTest
- public void testHexIccIdSuffix() throws Exception {
- doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexPrivileged(anyInt());
- verify(mSubscriptionController, times(0)).clearSubInfo();
- doReturn("890126042000000000Ff").when(mIccRecord).getFullIccId();
-
- // Mock sending a sim loaded for SIM 1
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_LOADED, "TESTING", FAKE_SUB_ID_1);
-
- processAllMessages();
-
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("890126042000000000"),
- eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(0)).clearSubInfo();
- }
-
- PersistableBundle getCarrierConfigForSubInfoUpdate(
- boolean isOpportunistic, String groupUuid) {
- PersistableBundle p = new PersistableBundle();
- p.putBoolean(CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, isOpportunistic);
- p.putString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, groupUuid);
- return p;
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigOpportunisticUnchanged() throws Exception {
- final int phoneId = mPhone.getPhoneId();
- String carrierPackageName = "FakeCarrierPackageName";
-
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, new PersistableBundle());
-
- //at each call to updateSubscriptionByCarrierConfig, only carrier certs are updated
- verify(mContentProvider, times(1)).update(any(), any(), any(), any());
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigOpportunisticSetOpportunistic() throws Exception {
- final int phoneId = mPhone.getPhoneId();
- PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate(
- true, "");
- String carrierPackageName = "FakeCarrierPackageName";
-
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(false).when(mSubInfo).isOpportunistic();
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, carrierConfig);
-
- ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider, times(1)).update(
- eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)),
- cvCaptor.capture(), eq(null), eq(null));
- assertEquals(1, cvCaptor.getValue().getAsInteger(
- SubscriptionManager.IS_OPPORTUNISTIC).intValue());
- // 2 updates: isOpportunistic, and carrier certs:
- assertEquals(2, cvCaptor.getValue().size());
- verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigOpportunisticAddToGroup() throws Exception {
- final int phoneId = mPhone.getPhoneId();
- PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate(
- true, "11111111-2222-3333-4444-555555555555");
- String carrierPackageName = "FakeCarrierPackageName";
-
- doReturn(true).when(mSubscriptionController).canPackageManageGroup(
- ParcelUuid.fromString("11111111-2222-3333-4444-555555555555"), carrierPackageName);
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, carrierConfig);
-
- ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider, times(1)).update(
- eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)),
- cvCaptor.capture(), eq(null), eq(null));
- assertEquals(1, cvCaptor.getValue().getAsInteger(
- SubscriptionManager.IS_OPPORTUNISTIC).intValue());
- assertEquals("11111111-2222-3333-4444-555555555555",
- cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID));
- assertEquals(carrierPackageName,
- cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_OWNER));
- // 4 updates: isOpportunistic, groupUuid, groupOwner, and carrier certs:
- assertEquals(4, cvCaptor.getValue().size());
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigOpportunisticRemoveFromGroup() throws Exception {
- final int phoneId = mPhone.getPhoneId();
- PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdate(
- true, "00000000-0000-0000-0000-000000000000");
- String carrierPackageName = "FakeCarrierPackageName";
-
- doReturn(true).when(mSubscriptionController).canPackageManageGroup(
- ParcelUuid.fromString("11111111-2222-3333-4444-555555555555"), carrierPackageName);
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(ParcelUuid.fromString("11111111-2222-3333-4444-555555555555"))
- .when(mSubInfo).getGroupUuid();
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, carrierConfig);
-
- ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider, times(1)).update(
- eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)),
- cvCaptor.capture(), eq(null), eq(null));
- assertEquals(1, cvCaptor.getValue().getAsInteger(
- SubscriptionManager.IS_OPPORTUNISTIC).intValue());
- assertNull(cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID));
- // 3 updates: isOpportunistic, groupUuid, and carrier certs:
- assertEquals(3, cvCaptor.getValue().size());
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigPreferredUsageSettingDataCentric() throws Exception {
- testUpdateFromCarrierConfigPreferredUsageSetting(
- SubscriptionManager.USAGE_SETTING_UNKNOWN,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigPreferredUsageSettingDataCentric2() throws Exception {
- testUpdateFromCarrierConfigPreferredUsageSetting(
- SubscriptionManager.USAGE_SETTING_DEFAULT,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigPreferredUsageSettingDefault() throws Exception {
- testUpdateFromCarrierConfigPreferredUsageSetting(
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DEFAULT,
- SubscriptionManager.USAGE_SETTING_DEFAULT);
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigPreferredUsageSettingNoChange() throws Exception {
- testUpdateFromCarrierConfigPreferredUsageSetting(
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigPreferredUsageSettingInvalid() throws Exception {
- testUpdateFromCarrierConfigPreferredUsageSetting(
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- SubscriptionManager.USAGE_SETTING_UNKNOWN,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
- }
-
- private PersistableBundle getCarrierConfigForSubInfoUpdateUsageSetting(
- @SubscriptionManager.UsageSetting int usageSetting) {
- PersistableBundle p = new PersistableBundle();
- p.putString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
- p.putBoolean(CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
- p.putInt(CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT, usageSetting);
- return p;
- }
-
- private void testUpdateFromCarrierConfigPreferredUsageSetting(
- int initialSetting, int requestedSetting, int expectedSetting) throws Exception {
- final String carrierPackageName = "FakeCarrierPackageName";
- final int phoneId = mPhone.getPhoneId();
-
- // Install fixtures, ensure the test will hit the right code path
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- // Setup overlay
- setupUsageSettingResources();
-
- // Setup subscription
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(null).when(mSubInfo).getGroupUuid();
- doReturn(false).when(mSubInfo).isOpportunistic();
- doReturn(initialSetting).when(mSubInfo).getUsageSetting();
-
- // Get a config bundle for that prefers data centric
- PersistableBundle carrierConfig = getCarrierConfigForSubInfoUpdateUsageSetting(
- requestedSetting);
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, carrierConfig);
-
- ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider, times(1)).update(
- eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)),
- cvCaptor.capture(), eq(null), eq(null));
-
- if (initialSetting != expectedSetting) {
- assertEquals(expectedSetting,
- (int) cvCaptor.getValue().getAsInteger(SubscriptionManager.USAGE_SETTING));
- } else {
- // If the content value was not set, the captor value will be null
- assertNull(cvCaptor.getValue().getAsInteger(SubscriptionManager.USAGE_SETTING));
- }
- }
-
- @Test
- @SmallTest
- public void testUpdateFromCarrierConfigCarrierCertificates() {
- String[] certs = new String[2];
- certs[0] = "d1f1";
- certs[1] = "b5d6";
-
- UiccAccessRule[] carrierConfigAccessRules = new UiccAccessRule[certs.length];
- for (int i = 0; i < certs.length; i++) {
- carrierConfigAccessRules[i] = new UiccAccessRule(
- IccUtils.hexStringToBytes(certs[i]), null, 0);
- }
-
- final int phoneId = mPhone.getPhoneId();
- PersistableBundle carrierConfig = new PersistableBundle();
- carrierConfig.putStringArray(
- CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, certs);
-
- String carrierPackageName = "FakeCarrierPackageName";
-
- doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
- doReturn(mSubInfo).when(mSubscriptionController).getSubscriptionInfo(eq(FAKE_SUB_ID_1));
- doReturn(false).when(mSubInfo).isOpportunistic();
- doReturn(carrierPackageName).when(mTelephonyManager)
- .getCarrierServicePackageNameForLogicalSlot(eq(phoneId));
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- SubscriptionManager.CONTENT_URI.getAuthority(),
- new FakeSubscriptionContentProvider());
-
- mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
- carrierPackageName, carrierConfig);
-
- ArgumentCaptor<ContentValues> cvCaptor = ArgumentCaptor.forClass(ContentValues.class);
- verify(mContentProvider, times(1)).update(
- eq(SubscriptionManager.getUriForSubscriptionId(FAKE_SUB_ID_1)),
- cvCaptor.capture(), eq(null), eq(null));
- assertEquals(carrierConfigAccessRules, UiccAccessRule.decodeRules(cvCaptor.getValue()
- .getAsByteArray(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS)));
- assertEquals(1, cvCaptor.getValue().size());
- verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimReady() throws Exception {
- replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,new String[]{""});
- doReturn(mUiccPort).when(mUiccController).getUiccPort(anyInt());
- doReturn(FAKE_ICCID_1).when(mUiccPort).getIccId();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_READY, "TESTING", FAKE_SUB_ID_1);
- processAllMessages();
-
- verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
- assertTrue(mUpdater.isSubInfoInitialized());
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- @Test
- @SmallTest
- public void testSimReadyAndLoaded() throws Exception {
- replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,new String[]{""});
-
- doReturn(mUiccPort).when(mUiccController).getUiccPort(anyInt());
- doReturn(null).when(mUiccPort).getIccId();
-
- mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_READY, "TESTING", FAKE_SUB_ID_1);
- processAllMessages();
-
- verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(
- eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
-
- loadSim();
-
- SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
- }
-
- private void setupUsageSettingResources() {
- // The most common case, request a voice-centric->data-centric change
- mContextFixture.putIntResource(
- com.android.internal.R.integer.config_default_cellular_usage_setting,
- SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
- mContextFixture.putIntArrayResource(
- com.android.internal.R.array.config_supported_cellular_usage_settings,
- new int[]{
- SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC});
- }
-
- @Test
- @SmallTest
- public void testCalculateUsageSetting() throws Exception {
- setupUsageSettingResources();
- assertEquals(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
- mUpdater.calculateUsageSetting(
- SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC));
-
- // Test that a voice-centric-only device only allows voice-centric configuration
- mContextFixture.putIntArrayResource(
- com.android.internal.R.array.config_supported_cellular_usage_settings,
- new int[]{SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC});
-
- assertEquals(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
- mUpdater.calculateUsageSetting(
- SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
- SubscriptionManager.USAGE_SETTING_DATA_CENTRIC));
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyAdminReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyAdminReceiverTest.java
new file mode 100644
index 0000000..118daa5
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyAdminReceiverTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.UserManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TelephonyAdminReceiverTest extends TelephonyTest {
+
+ private TelephonyAdminReceiver mTelephonyAdminReceiver;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mTelephonyAdminReceiver = new TelephonyAdminReceiver(mContext, mPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void test_nullUserManager() {
+ mUserManager = null;
+ TelephonyAdminReceiver telephonyAdminReceiver = new TelephonyAdminReceiver(mContext,
+ mPhone);
+ assertFalse(telephonyAdminReceiver.isCellular2gDisabled());
+ }
+
+ @Test
+ public void test_nullIntent_noUpdate() {
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+
+ mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+
+ verify(mPhone, never()).sendSubscriptionSettings(anyBoolean());
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+ }
+
+ @Test
+ public void test_userRestrictionsNotChanged_noUpdate() {
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(false);
+
+ mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+
+ verify(mPhone, never()).sendSubscriptionSettings(anyBoolean());
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+ }
+
+ @Test
+ public void test_userRestrictionToggled_shouldUpdate() {
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+ true).thenReturn(false);
+
+ mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+ assertTrue(mTelephonyAdminReceiver.isCellular2gDisabled());
+
+ mContext.sendBroadcast(new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+ assertFalse(mTelephonyAdminReceiver.isCellular2gDisabled());
+ verify(mPhone, times(2)).sendSubscriptionSettings(false);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index 0e6e2f7..a053c56 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.permission.LegacyPermissionManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -530,6 +532,14 @@
}
}
+ @Test
+ public void testCheckSubscriptionAssociatedWithUser_emergencyNumber() {
+ doReturn(true).when(mTelephonyManagerMock).isEmergencyNumber(anyString());
+
+ assertTrue(TelephonyPermissions.checkSubscriptionAssociatedWithUser(mMockContext, SUB_ID,
+ UserHandle.SYSTEM, "911"));
+ }
+
// Put mMockTelephony into service cache so that TELEPHONY_SUPPLIER will get it.
private void setTelephonyMockAsService() throws Exception {
when(mMockTelephonyBinder.queryLocalInterface(anyString())).thenReturn(mMockTelephony);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index dbb371b..35a3186 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -25,7 +25,9 @@
import static android.telephony.TelephonyManager.RADIO_POWER_UNAVAILABLE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,16 +37,22 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.net.LinkProperties;
+import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
+import android.telephony.BarringInfo;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoLte;
import android.telephony.CellLocation;
import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
@@ -62,6 +70,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
+import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -73,6 +82,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -100,6 +110,10 @@
private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
private CellLocation mCellLocation;
+ private List<CellInfo> mCellInfo;
+ private BarringInfo mBarringInfo = null;
+ private CellIdentity mCellIdentityForRegiFail;
+ private int mRegistrationFailReason;
// All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
private static final Set<Integer> READ_PHONE_STATE_EVENTS;
@@ -143,6 +157,8 @@
TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
READ_PRIVILEGED_PHONE_STATE_EVENTS.add(
TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
}
// All events contribute to TelephonyRegistry#isActiveEmergencySessionPermissionRequired
@@ -166,7 +182,10 @@
TelephonyCallback.LinkCapacityEstimateChangedListener,
TelephonyCallback.PhysicalChannelConfigListener,
TelephonyCallback.CellLocationListener,
- TelephonyCallback.ServiceStateListener {
+ TelephonyCallback.ServiceStateListener,
+ TelephonyCallback.CellInfoListener,
+ TelephonyCallback.BarringInfoListener,
+ TelephonyCallback.RegistrationFailedListener {
// This class isn't mockable to get invocation counts because the IBinder is null and
// crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative.
public AtomicInteger invocationCount = new AtomicInteger(0);
@@ -228,6 +247,27 @@
public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs) {
mPhysicalChannelConfigs = configs;
}
+
+ @Override
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ invocationCount.incrementAndGet();
+ mCellInfo = cellInfo;
+ }
+
+ @Override
+ public void onBarringInfoChanged(BarringInfo barringInfo) {
+ invocationCount.incrementAndGet();
+ mBarringInfo = barringInfo;
+ }
+
+ public void onRegistrationFailed(@android.annotation.NonNull CellIdentity cellIdentity,
+ @android.annotation.NonNull String chosenPlmn,
+ @NetworkRegistrationInfo.Domain int domain,
+ int causeCode, int additionalCauseCode) {
+ invocationCount.incrementAndGet();
+ mCellIdentityForRegiFail = cellIdentity;
+ mRegistrationFailReason = causeCode;
+ }
}
private void addTelephonyRegistryService() {
@@ -874,6 +914,170 @@
assertEquals(PHYSICAL_CELL_ID_UNKNOWN, mPhysicalChannelConfigs.get(0).getPhysicalCellId());
}
+ @Test
+ public void testBarringInfoChangedWithLocationFinePermission() throws Exception {
+ checkBarringInfoWithLocationPermission(Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+
+ @Test
+ public void testBarringInfoChangedLocationCoarsePermission() throws Exception {
+ checkBarringInfoWithLocationPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ @Test
+ public void testBarringInfoChangedWithoutLocationPermission() throws Exception {
+ checkBarringInfoWithLocationPermission(null);
+ }
+
+ private void checkBarringInfoWithLocationPermission(String permission) throws Exception {
+ // Return a slotIndex / phoneId of 0 for all sub ids given.
+ doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+ doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(any(UserHandle.class));
+
+ mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+ mContextFixture.addCallingOrSelfPermission("");
+ mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE);
+ if (permission != null) {
+ mContextFixture.addCallingOrSelfPermission(permission);
+ }
+
+ final int subId = 1;
+ int[] events = {TelephonyCallback.EVENT_BARRING_INFO_CHANGED};
+ SparseArray<BarringInfo.BarringServiceInfo> bsi = new SparseArray(1);
+ bsi.set(BarringInfo.BARRING_SERVICE_TYPE_MMTEL_VOICE,
+ new BarringInfo.BarringServiceInfo(
+ BarringInfo.BarringServiceInfo.BARRING_TYPE_CONDITIONAL,
+ false /*isConditionallyBarred*/,
+ 30 /*conditionalBarringFactor*/,
+ 10 /*conditionalBarringTimeSeconds*/));
+ BarringInfo info = new BarringInfo(
+ new CellIdentityLte(777, 333, 12345, 222, 13579), bsi);
+ // 1. Register listener which requires location access.
+ mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ processAllMessages();
+ assertEquals(1, mTelephonyCallback.invocationCount.get());
+ assertNotNull(mBarringInfo);
+
+ // Updating the barring info causes Barring Info to be updated
+ mTelephonyRegistry.notifyBarringInfoChanged(0, subId, info);
+ processAllMessages();
+ assertEquals(2, mTelephonyCallback.invocationCount.get());
+ assertEquals(mBarringInfo
+ .getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_MMTEL_VOICE),
+ info.getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_MMTEL_VOICE));
+ String log = mBarringInfo.toString();
+ assertTrue(log.contains("777"));
+ assertTrue(log.contains("333"));
+ if (permission != null && permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
+ assertTrue(log.contains("12345"));
+ assertTrue(log.contains("222"));
+ assertTrue(log.contains("13579"));
+ } else {
+ assertFalse(log.contains("12345"));
+ assertFalse(log.contains("222"));
+ assertFalse(log.contains("13579"));
+ }
+
+ // Duplicate BarringInfo notifications do not trigger callback
+ mTelephonyRegistry.notifyBarringInfoChanged(0, subId, info);
+ processAllMessages();
+ assertEquals(2, mTelephonyCallback.invocationCount.get());
+
+ mTelephonyRegistry.listenWithEventList(true, true, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, new int[0], true);
+ // 2. Register listener renounces location access.
+ mTelephonyRegistry.listenWithEventList(true, true, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ processAllMessages();
+ // check receiving barring info without location info.
+ assertEquals(3, mTelephonyCallback.invocationCount.get());
+ assertNotNull(mBarringInfo);
+ assertEquals(mBarringInfo
+ .getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_MMTEL_VOICE),
+ info.getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_MMTEL_VOICE));
+ log = mBarringInfo.toString();
+ assertTrue(log.contains("777"));
+ assertTrue(log.contains("333"));
+ assertFalse(log.contains("12345"));
+ assertFalse(log.contains("222"));
+ assertFalse(log.contains("13579"));
+ }
+
+ @Test
+ public void testRegistrationFailedEventWithLocationFinePermission() throws Exception {
+ checkRegistrationFailedEventWithLocationPermission(
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+ @Test
+ public void testRegistrationFailedEventWithLocationCoarsePermission() throws Exception {
+ checkRegistrationFailedEventWithLocationPermission(
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ @Test
+ public void testRegistrationFailedEventWithoutLocationPermission() throws Exception {
+ checkRegistrationFailedEventWithLocationPermission(null);
+ }
+
+ private void checkRegistrationFailedEventWithLocationPermission(String permission)
+ throws Exception {
+ // Return a slotIndex / phoneId of 0 for all sub ids given.
+ doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+ doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(any(UserHandle.class));
+
+ mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+ mContextFixture.addCallingOrSelfPermission("");
+ mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE);
+ if (permission != null) {
+ mContextFixture.addCallingOrSelfPermission(permission);
+ }
+
+ final int subId = 1;
+ int[] events = {TelephonyCallback.EVENT_REGISTRATION_FAILURE};
+ CellIdentity cellIdentity =
+ new CellIdentityLte(777, 333, 12345, 227, 13579);
+
+ // 1. Register listener which requires location access.
+ mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ processAllMessages();
+ int invocationCount = mTelephonyCallback.invocationCount.get();
+ // Updating the RegistrationFailed info to be updated
+ mTelephonyRegistry.notifyRegistrationFailed(
+ 0, subId, cellIdentity, "88888", 1, 333, 22);
+ processAllMessages();
+ assertEquals(invocationCount + 1, mTelephonyCallback.invocationCount.get());
+ if (permission != null && permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
+ assertEquals(cellIdentity, mCellIdentityForRegiFail);
+ } else {
+ assertEquals(cellIdentity.sanitizeLocationInfo(), mCellIdentityForRegiFail);
+ }
+ assertEquals(333, mRegistrationFailReason);
+ mTelephonyRegistry.listenWithEventList(true, true, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, new int[0], true);
+
+ // 2. Register listener which renounces location access.
+ mTelephonyRegistry.listenWithEventList(true, true, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ invocationCount = mTelephonyCallback.invocationCount.get();
+ // Updating the RegistrationFailed info to be updated
+ mTelephonyRegistry.notifyRegistrationFailed(
+ 0, subId, cellIdentity, "88888", 1, 555, 22);
+ processAllMessages();
+ assertEquals(invocationCount + 1, mTelephonyCallback.invocationCount.get());
+ assertEquals(cellIdentity.sanitizeLocationInfo(), mCellIdentityForRegiFail);
+ assertEquals(555, mRegistrationFailReason);
+ }
+
/**
* Test listen to events that require READ_PHONE_STATE permission.
*/
@@ -1011,7 +1215,8 @@
anyString(), any())).thenReturn(true);
TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ false);
// Notify with invalid subId on default phone. Should NOT trigger callback.
mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo);
@@ -1037,10 +1242,12 @@
anyString(), any())).thenReturn(false);
TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ false);
TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo(
TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ false);
// Notify with invalid subId on default phone. Should NOT trigger callback.
mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo);
@@ -1062,10 +1269,12 @@
int[] events = {TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED};
TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ false);
TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo(
TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ false);
TelephonyCallback telephonyCallback2 = new TelephonyCallbackWrapper() {
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfoNotify) {
@@ -1106,12 +1315,9 @@
final int subId = 1;
// Return a slotIndex / phoneId of 0 for subId 1.
- doReturn(new int[] {subId}).when(mSubscriptionController).getSubId(phoneId);
+ doReturn(subId).when(mSubscriptionManagerService).getSubId(phoneId);
doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(subId);
doReturn(phoneId).when(mMockSubInfo).getSimSlotIndex();
- mServiceManagerMockedServices.put("isub", mSubscriptionController);
- doReturn(mSubscriptionController).when(mSubscriptionController)
- .queryLocalInterface(anyString());
UserInfo userInfo = new UserInfo(UserHandle.myUserId(), "" /* name */, 0 /* flags */);
doReturn(userInfo.id).when(mIActivityManager).getCurrentUserId();
@@ -1260,4 +1466,28 @@
assertEquals(1, mTelephonyCallback.invocationCount.get());
}
+
+ @Test @SmallTest
+ public void testCellInfoChanged() {
+ final int subId = 1;
+ final int[] events = {TelephonyCallback.EVENT_CELL_INFO_CHANGED};
+ final List<CellInfo> dummyCellInfo = Arrays.asList(new CellInfoLte());
+
+ mCellInfo = null; // null is an invalid value since the API is NonNull;
+
+ doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+ doReturn(0 /*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(any(UserHandle.class));
+
+ mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ processAllMessages();
+ assertEquals(1, mTelephonyCallback.invocationCount.get());
+ assertNotNull(mCellInfo);
+
+ mTelephonyRegistry.notifyCellInfoForSubscriber(subId, dummyCellInfo);
+ processAllMessages();
+ assertEquals(2, mTelephonyCallback.invocationCount.get());
+ assertEquals(mCellInfo, dummyCellInfo);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 9b54b19..b044814 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -16,7 +16,8 @@
package com.android.internal.telephony;
-import static org.junit.Assert.assertNotNull;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
@@ -33,6 +34,7 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
+import android.app.PropertyInvalidatedCache;
import android.app.usage.NetworkStatsManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -68,6 +70,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Telephony;
+import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
@@ -96,25 +99,27 @@
import com.android.internal.telephony.data.AccessNetworksManager;
import com.android.internal.telephony.data.CellularNetworkValidator;
import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.DataEnabledOverride;
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.data.DataProfileManager;
import com.android.internal.telephony.data.DataRetryManager;
import com.android.internal.telephony.data.DataServiceManager;
import com.android.internal.telephony.data.DataSettingsManager;
import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DataThrottler;
-import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.data.PhoneSwitcher;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
+import com.android.internal.telephony.imsphone.ImsNrSaModeHandler;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.DeviceStateHelper;
import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.MetricsCollector;
import com.android.internal.telephony.metrics.PersistAtomsStorage;
+import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
import com.android.internal.telephony.uicc.IccCardStatus;
@@ -178,6 +183,7 @@
// Mocked classes
protected GsmCdmaPhone mPhone;
+ protected GsmCdmaPhone mPhone2;
protected ImsPhone mImsPhone;
protected ServiceStateTracker mSST;
protected EmergencyNumberTracker mEmergencyNumberTracker;
@@ -185,6 +191,7 @@
protected ImsPhoneCallTracker mImsCT;
protected UiccController mUiccController;
protected UiccProfile mUiccProfile;
+ protected UiccSlot mUiccSlot;
protected CallManager mCallManager;
protected PhoneNotifier mNotifier;
protected TelephonyComponentFactory mTelephonyComponentFactory;
@@ -192,7 +199,6 @@
protected RegistrantList mRegistrantList;
protected IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
protected ImsManager mImsManager;
- protected DcTracker mDcTracker;
protected DataNetworkController mDataNetworkController;
protected DataRetryManager mDataRetryManager;
protected DataSettingsManager mDataSettingsManager;
@@ -202,7 +208,7 @@
protected GsmCdmaCall mGsmCdmaCall;
protected ImsCall mImsCall;
protected ImsEcbm mImsEcbm;
- protected SubscriptionController mSubscriptionController;
+ protected SubscriptionManagerService mSubscriptionManagerService;
protected ServiceState mServiceState;
protected IPackageManager.Stub mMockPackageManager;
protected LegacyPermissionManagerService mMockLegacyPermissionManager;
@@ -216,6 +222,7 @@
protected RuimRecords mRuimRecords;
protected IsimUiccRecords mIsimUiccRecords;
protected ProxyController mProxyController;
+ protected PhoneSwitcher mPhoneSwitcher;
protected Singleton<IActivityManager> mIActivityManagerSingleton;
protected IActivityManager mIActivityManager;
protected IIntentSender mIIntentSender;
@@ -229,6 +236,7 @@
protected CarrierSignalAgent mCarrierSignalAgent;
protected CarrierActionAgent mCarrierActionAgent;
protected ImsExternalCallTracker mImsExternalCallTracker;
+ protected ImsNrSaModeHandler mImsNrSaModeHandler;
protected AppSmsManager mAppSmsManager;
protected IccSmsInterfaceManager mIccSmsInterfaceManager;
protected SmsDispatchersController mSmsDispatchersController;
@@ -237,11 +245,8 @@
protected IntentBroadcaster mIntentBroadcaster;
protected NitzStateMachine mNitzStateMachine;
protected RadioConfig mMockRadioConfig;
- protected SubscriptionInfoUpdater mSubInfoRecordUpdater;
protected LocaleTracker mLocaleTracker;
protected RestrictedState mRestrictedState;
- protected DataEnabledSettings mDataEnabledSettings;
- protected DataEnabledOverride mDataEnabledOverride;
protected PhoneConfigurationManager mPhoneConfigurationManager;
protected CellularNetworkValidator mCellularNetworkValidator;
protected UiccCard mUiccCard;
@@ -254,7 +259,6 @@
protected PersistAtomsStorage mPersistAtomsStorage;
protected MetricsCollector mMetricsCollector;
protected SmsStats mSmsStats;
- protected DataThrottler mDataThrottler;
protected SignalStrength mSignalStrength;
protected WifiManager mWifiManager;
protected WifiInfo mWifiInfo;
@@ -266,11 +270,15 @@
protected CellLocation mCellLocation;
protected DataServiceManager mMockedWwanDataServiceManager;
protected DataServiceManager mMockedWlanDataServiceManager;
+ protected ServiceStateStats mServiceStateStats;
+ protected SatelliteController mSatelliteController;
+ protected DeviceStateHelper mDeviceStateHelper;
// Initialized classes
protected ActivityManager mActivityManager;
protected ImsCallProfile mImsCallProfile;
protected TelephonyManager mTelephonyManager;
+ protected TelecomManager mTelecomManager;
protected TelephonyRegistryManager mTelephonyRegistryManager;
protected SubscriptionManager mSubscriptionManager;
protected EuiccManager mEuiccManager;
@@ -411,6 +419,7 @@
TAG = tag;
enableStrictMode();
mPhone = Mockito.mock(GsmCdmaPhone.class);
+ mPhone2 = Mockito.mock(GsmCdmaPhone.class);
mImsPhone = Mockito.mock(ImsPhone.class);
mSST = Mockito.mock(ServiceStateTracker.class);
mEmergencyNumberTracker = Mockito.mock(EmergencyNumberTracker.class);
@@ -418,6 +427,7 @@
mImsCT = Mockito.mock(ImsPhoneCallTracker.class);
mUiccController = Mockito.mock(UiccController.class);
mUiccProfile = Mockito.mock(UiccProfile.class);
+ mUiccSlot = Mockito.mock(UiccSlot.class);
mCallManager = Mockito.mock(CallManager.class);
mNotifier = Mockito.mock(PhoneNotifier.class);
mTelephonyComponentFactory = Mockito.mock(TelephonyComponentFactory.class);
@@ -425,7 +435,6 @@
mRegistrantList = Mockito.mock(RegistrantList.class);
mIccPhoneBookIntManager = Mockito.mock(IccPhoneBookInterfaceManager.class);
mImsManager = Mockito.mock(ImsManager.class);
- mDcTracker = Mockito.mock(DcTracker.class);
mDataNetworkController = Mockito.mock(DataNetworkController.class);
mDataRetryManager = Mockito.mock(DataRetryManager.class);
mDataSettingsManager = Mockito.mock(DataSettingsManager.class);
@@ -435,7 +444,7 @@
mGsmCdmaCall = Mockito.mock(GsmCdmaCall.class);
mImsCall = Mockito.mock(ImsCall.class);
mImsEcbm = Mockito.mock(ImsEcbm.class);
- mSubscriptionController = Mockito.mock(SubscriptionController.class);
+ mSubscriptionManagerService = Mockito.mock(SubscriptionManagerService.class);
mServiceState = Mockito.mock(ServiceState.class);
mMockPackageManager = Mockito.mock(IPackageManager.Stub.class);
mMockLegacyPermissionManager = Mockito.mock(LegacyPermissionManagerService.class);
@@ -449,6 +458,7 @@
mRuimRecords = Mockito.mock(RuimRecords.class);
mIsimUiccRecords = Mockito.mock(IsimUiccRecords.class);
mProxyController = Mockito.mock(ProxyController.class);
+ mPhoneSwitcher = Mockito.mock(PhoneSwitcher.class);
mIActivityManagerSingleton = Mockito.mock(Singleton.class);
mIActivityManager = Mockito.mock(IActivityManager.class);
mIIntentSender = Mockito.mock(IIntentSender.class);
@@ -462,6 +472,7 @@
mCarrierSignalAgent = Mockito.mock(CarrierSignalAgent.class);
mCarrierActionAgent = Mockito.mock(CarrierActionAgent.class);
mImsExternalCallTracker = Mockito.mock(ImsExternalCallTracker.class);
+ mImsNrSaModeHandler = Mockito.mock(ImsNrSaModeHandler.class);
mAppSmsManager = Mockito.mock(AppSmsManager.class);
mIccSmsInterfaceManager = Mockito.mock(IccSmsInterfaceManager.class);
mSmsDispatchersController = Mockito.mock(SmsDispatchersController.class);
@@ -470,11 +481,8 @@
mIntentBroadcaster = Mockito.mock(IntentBroadcaster.class);
mNitzStateMachine = Mockito.mock(NitzStateMachine.class);
mMockRadioConfig = Mockito.mock(RadioConfig.class);
- mSubInfoRecordUpdater = Mockito.mock(SubscriptionInfoUpdater.class);
mLocaleTracker = Mockito.mock(LocaleTracker.class);
mRestrictedState = Mockito.mock(RestrictedState.class);
- mDataEnabledSettings = Mockito.mock(DataEnabledSettings.class);
- mDataEnabledOverride = Mockito.mock(DataEnabledOverride.class);
mPhoneConfigurationManager = Mockito.mock(PhoneConfigurationManager.class);
mCellularNetworkValidator = Mockito.mock(CellularNetworkValidator.class);
mUiccCard = Mockito.mock(UiccCard.class);
@@ -487,7 +495,6 @@
mPersistAtomsStorage = Mockito.mock(PersistAtomsStorage.class);
mMetricsCollector = Mockito.mock(MetricsCollector.class);
mSmsStats = Mockito.mock(SmsStats.class);
- mDataThrottler = Mockito.mock(DataThrottler.class);
mSignalStrength = Mockito.mock(SignalStrength.class);
mWifiManager = Mockito.mock(WifiManager.class);
mWifiInfo = Mockito.mock(WifiInfo.class);
@@ -499,9 +506,12 @@
mCellLocation = Mockito.mock(CellLocation.class);
mMockedWwanDataServiceManager = Mockito.mock(DataServiceManager.class);
mMockedWlanDataServiceManager = Mockito.mock(DataServiceManager.class);
+ mServiceStateStats = Mockito.mock(ServiceStateStats.class);
+ mSatelliteController = Mockito.mock(SatelliteController.class);
+ mDeviceStateHelper = Mockito.mock(DeviceStateHelper.class);
TelephonyManager.disableServiceHandleCaching();
- SubscriptionController.disableCaching();
+ PropertyInvalidatedCache.disableForTestMode();
// For testing do not allow Log.WTF as it can cause test process to crash
Log.setWtfHandler((tagString, what, system) -> Log.d(TAG, "WTF captured, ignoring. Tag: "
+ tagString + ", exception: " + what));
@@ -524,13 +534,20 @@
Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
+ mServiceManagerMockedServices.put("isub", mSubscriptionManagerService);
+ doReturn(mSubscriptionManagerService).when(mSubscriptionManagerService)
+ .queryLocalInterface(anyString());
+
mPhone.mCi = mSimulatedCommands;
mCT.mCi = mSimulatedCommands;
doReturn(mUiccCard).when(mPhone).getUiccCard();
+ doReturn(mUiccCard).when(mUiccSlot).getUiccCard();
+ doReturn(mUiccCard).when(mUiccController).getUiccCardForPhone(anyInt());
doReturn(mUiccPort).when(mPhone).getUiccPort();
doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mTelecomManager = mContext.getSystemService(TelecomManager.class);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mTelephonyRegistryManager = (TelephonyRegistryManager) mContext.getSystemService(
Context.TELEPHONY_REGISTRY_SERVICE);
@@ -567,8 +584,6 @@
.makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
.makeIccPhoneBookInterfaceManager(nullable(Phone.class));
- doReturn(mDcTracker).when(mTelephonyComponentFactory)
- .makeDcTracker(nullable(Phone.class), anyInt());
doReturn(mDisplayInfoController).when(mTelephonyComponentFactory)
.makeDisplayInfoController(nullable(Phone.class));
doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
@@ -581,6 +596,8 @@
anyInt(), nullable(Object.class));
doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory)
.makeImsExternalCallTracker(nullable(ImsPhone.class));
+ doReturn(mImsNrSaModeHandler).when(mTelephonyComponentFactory)
+ .makeImsNrSaModeHandler(nullable(ImsPhone.class));
doReturn(mAppSmsManager).when(mTelephonyComponentFactory)
.makeAppSmsManager(nullable(Context.class));
doReturn(mCarrierSignalAgent).when(mTelephonyComponentFactory)
@@ -596,8 +613,6 @@
doReturn(mLocaleTracker).when(mTelephonyComponentFactory)
.makeLocaleTracker(nullable(Phone.class), nullable(NitzStateMachine.class),
nullable(Looper.class));
- doReturn(mDataEnabledSettings).when(mTelephonyComponentFactory)
- .makeDataEnabledSettings(nullable(Phone.class));
doReturn(mEriManager).when(mTelephonyComponentFactory)
.makeEriManager(nullable(Phone.class), anyInt());
doReturn(mLinkBandwidthEstimator).when(mTelephonyComponentFactory)
@@ -627,8 +642,6 @@
doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
doReturn(mIccSmsInterfaceManager).when(mPhone).getIccSmsInterfaceManager();
doReturn(mAccessNetworksManager).when(mPhone).getAccessNetworksManager();
- doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
- doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
doReturn(mDataSettingsManager).when(mDataNetworkController).getDataSettingsManager();
doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
doReturn(mDataSettingsManager).when(mPhone).getDataSettingsManager();
@@ -646,7 +659,6 @@
doReturn(mDataProfileManager).when(mDataNetworkController).getDataProfileManager();
doReturn(mDataRetryManager).when(mDataNetworkController).getDataRetryManager();
doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
- doReturn(true).when(mPhone).isUsingNewDataStack();
doReturn(0).when(mPhone).getPhoneId();
//mUiccController
@@ -674,7 +686,8 @@
}
}
}).when(mUiccController).getIccRecords(anyInt(), anyInt());
- doReturn(new UiccSlot[] {}).when(mUiccController).getUiccSlots();
+ doReturn(new UiccSlot[] {mUiccSlot}).when(mUiccController).getUiccSlots();
+ doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
doReturn(mPinStorage).when(mUiccController).getPinStorage();
//UiccCardApplication
@@ -709,14 +722,21 @@
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
.getRilDataRadioTechnology();
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
doReturn(mPhone).when(mCT).getPhone();
doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
doReturn(mPhone).when(mInboundSmsHandler).getPhone();
doReturn(mImsCallProfile).when(mImsCall).getCallProfile();
doReturn(mIBinder).when(mIIntentSender).asBinder();
- doReturn(mIIntentSender).when(mIActivityManager).getIntentSenderWithFeature(anyInt(),
+ doAnswer(invocation -> {
+ Intent[] intents = invocation.getArgument(6);
+ if (intents != null && intents.length > 0) {
+ doReturn(intents[0]).when(mIActivityManager)
+ .getIntentForIntentSender(mIIntentSender);
+ }
+ return mIIntentSender;
+ }).when(mIActivityManager).getIntentSenderWithFeature(anyInt(),
nullable(String.class), nullable(String.class), nullable(IBinder.class),
nullable(String.class), anyInt(), nullable(Intent[].class),
nullable(String[].class), anyInt(), nullable(Bundle.class), anyInt());
@@ -725,6 +745,7 @@
doReturn(TelephonyManager.PHONE_TYPE_GSM).when(mTelephonyManager).getPhoneType();
doReturn(mServiceState).when(mSST).getServiceState();
+ doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
mSST.mSS = mServiceState;
mSST.mRestrictedState = mRestrictedState;
mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
@@ -734,18 +755,10 @@
doReturn(new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN})
.when(mAccessNetworksManager).getAvailableTransports();
- doReturn(new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN})
- .when(mAccessNetworksManager).getAvailableTransports();
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
- .getCurrentTransport(anyInt());
- doReturn(true).when(mDataEnabledSettings).isDataEnabled();
- doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
- doReturn(true).when(mDataEnabledSettings).isInternalDataEnabled();
doReturn(true).when(mDataSettingsManager).isDataEnabled();
doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
anyInt(), anyInt());
- doReturn(RIL.RADIO_HAL_VERSION_2_0).when(mPhone).getHalVersion();
+ doReturn(RIL.RADIO_HAL_VERSION_2_0).when(mPhone).getHalVersion(anyInt());
doReturn(2).when(mSignalStrength).getLevel();
// WiFi
@@ -777,8 +790,6 @@
Settings.Global.putInt(resolver,
Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, 0);
- doReturn(mDataThrottler).when(mDcTracker).getDataThrottler();
- doReturn(-1L).when(mDataThrottler).getRetryTime(anyInt());
doReturn(90).when(mDataConfigManager).getNetworkCapabilityPriority(
eq(NetworkCapabilities.NET_CAPABILITY_EIMS));
@@ -822,6 +833,11 @@
doReturn(null).when(mContext).getFileStreamPath(anyString());
doReturn(mPersistAtomsStorage).when(mMetricsCollector).getAtomsStorage();
doReturn(mWifiManager).when(mContext).getSystemService(eq(Context.WIFI_SERVICE));
+ doReturn(mDeviceStateHelper).when(mMetricsCollector).getDeviceStateHelper();
+ doReturn(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN)
+ .when(mDeviceStateHelper)
+ .getFoldState();
+ doReturn(null).when(mContext).getSystemService(eq(Context.DEVICE_STATE_SERVICE));
//Use reflection to mock singletons
replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
@@ -829,8 +845,10 @@
mTelephonyComponentFactory);
replaceInstance(UiccController.class, "mInstance", null, mUiccController);
replaceInstance(CdmaSubscriptionSourceManager.class, "sInstance", null, mCdmaSSM);
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
+ replaceInstance(SubscriptionManagerService.class, "sInstance", null,
+ mSubscriptionManagerService);
replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
+ replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mPhoneSwitcher);
replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
mIActivityManagerSingleton);
replaceInstance(CdmaSubscriptionSourceManager.class,
@@ -847,7 +865,6 @@
replaceInstance(PhoneFactory.class, "sMadeDefaults", null, true);
replaceInstance(PhoneFactory.class, "sPhone", null, mPhone);
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
- replaceInstance(PhoneFactory.class, "sSubInfoRecordUpdater", null, mSubInfoRecordUpdater);
replaceInstance(RadioConfig.class, "sRadioConfig", null, mMockRadioConfig);
replaceInstance(PhoneConfigurationManager.class, "sInstance", null,
mPhoneConfigurationManager);
@@ -855,13 +872,11 @@
mCellularNetworkValidator);
replaceInstance(MultiSimSettingController.class, "sInstance", null,
mMultiSimSettingController);
- replaceInstance(SubscriptionInfoUpdater.class, "sIsSubInfoInitialized", null, true);
replaceInstance(PhoneFactory.class, "sCommandsInterfaces", null,
new CommandsInterface[] {mSimulatedCommands});
replaceInstance(PhoneFactory.class, "sMetricsCollector", null, mMetricsCollector);
+ replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteController);
- assertNotNull("Failed to set up SubscriptionController singleton",
- SubscriptionController.getInstance());
setReady(false);
// create default TestableLooper for test and add to list of monitored loopers
mTestableLooper = TestableLooper.get(TelephonyTest.this);
@@ -898,7 +913,6 @@
}
restoreInstances();
TelephonyManager.enableServiceHandleCaching();
- SubscriptionController.enableCaching();
mNetworkRegistrationInfo = null;
mActivityManager = null;
@@ -960,14 +974,17 @@
private static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
DeviceConfig.NAMESPACE_PRIVACY + "/"
+ "device_identifier_access_restrictions_disabled";
+ private HashMap<String, String> mFlags = new HashMap<>();
@Override
public Bundle call(String method, String arg, Bundle extras) {
+ logd("FakeSettingsConfigProvider: call called, method: " + method +
+ " request: " + arg + ", args=" + extras);
+ Bundle bundle = new Bundle();
switch (method) {
case Settings.CALL_METHOD_GET_CONFIG: {
switch (arg) {
case PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED: {
- Bundle bundle = new Bundle();
bundle.putString(
PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED,
"0");
@@ -979,6 +996,18 @@
}
break;
}
+ case Settings.CALL_METHOD_LIST_CONFIG:
+ logd("LIST_config: " + mFlags);
+ Bundle result = new Bundle();
+ result.putSerializable(Settings.NameValueTable.VALUE, mFlags);
+ return result;
+ case Settings.CALL_METHOD_SET_ALL_CONFIG:
+ mFlags = (extras != null)
+ ? (HashMap) extras.getSerializable(Settings.CALL_METHOD_FLAGS_KEY)
+ : new HashMap<>();
+ bundle.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN,
+ Settings.SET_ALL_RESULT_SUCCESS);
+ return bundle;
default:
fail("Method not expected: " + method);
}
@@ -1162,7 +1191,7 @@
* Remove a TestableLooper from the list of monitored loopers
* @param looper removed if it does exist
*/
- public void unmonitorTestableLooper(TestableLooper looper) {
+ private void unmonitorTestableLooper(TestableLooper looper) {
if (mTestableLoopers.contains(looper)) {
mTestableLoopers.remove(looper);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken b/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
deleted file mode 100644
index 761dfc7..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import android.telephony.CellInfo;
-
-import java.util.List;
-
-/**
- * Stub class used for unit tests
- */
-
-public class TestPhoneNotifier implements PhoneNotifier {
- public TestPhoneNotifier() {
- }
-
- public void notifyPhoneState(Phone sender) {
- }
-
- public void notifyServiceState(Phone sender) {
- }
-
- public void notifyCellLocation(Phone sender) {
- }
-
- public void notifySignalStrength(Phone sender) {
- }
-
- public void notifyMessageWaitingChanged(Phone sender) {
- }
-
- public void notifyCallForwardingChanged(Phone sender) {
- }
-
- public void notifyDataConnection(Phone sender, String reason, String apnType) {
- }
-
- public void notifyDataConnection(Phone sender, String reason, String apnType,
- PhoneConstants.DataState state) {
- }
-
- public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
- }
-
- public void notifyDataActivity(Phone sender) {
- }
-
- public void notifyOtaspChanged(Phone sender, int otaspMode) {
- }
-
- public void notifyCellInfo(Phone sender, List<CellInfo> cellInfo) {
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
index f09c94e..9f76337 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
@@ -96,7 +96,7 @@
VisualVoicemailSmsFilter.setPhoneAccountHandleConverterForTest(
new PhoneAccountHandleConverter() {
@Override
- public PhoneAccountHandle fromSubId(int subId) {
+ public PhoneAccountHandle fromSubId(int subId, Context context) {
return new PhoneAccountHandle(
new ComponentName("com.android.internal.telephony",
"VisualVoicemailSmsFilterTest"), "foo");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
new file mode 100644
index 0000000..f2c1870
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.cat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.ProxyController;
+import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.test.SimulatedCommands;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccFileHandler;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccProfile;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CATServiceTest extends TelephonyTest {
+
+ private static final String SMS_SENT_ACTION =
+ "com.android.internal.telephony.cat.SMS_SENT_ACTION";
+ private static final String SMS_DELIVERY_ACTION =
+ "com.android.internal.telephony.cat.SMS_DELIVERY_ACTION";
+ //Mocked Classes
+ @Mock
+ private RilMessageDecoder mRilMessageDecoder;
+ private IccFileHandler mIccFileHandler;
+ private SmsController mSmsController;
+ private CommandDetails mCommandDetails;
+ private CatService mCatService;
+ private IccCardStatus mIccCardStatus;
+ private IccIoResult mIccIoResult;
+ private String mData =
+ "D059810301130082028183051353656E64696E672072657175657374202E2E2E0607911989548056780B"
+ + "3051FF05812143F500F6082502700000201115001500BFFF01BA23C2169EA9B02D7A7FBAA0"
+ + "DAABFEE8B8DE9DA06DCD234E";
+ private byte[] mRawdata = IccUtils.hexStringToBytes(mData);
+ private List<ComprehensionTlv> mCtlvs;
+
+ /**
+ * Terminal Response with result code in last 3 bytes = length + SMS_RP_ERROR(0x35)
+ * + ErrorCode(= 41)
+ */
+ private String mTerminalResponseForSmsRpError = "81030113000202828183023529";
+
+ /**
+ * Terminal Response with result code in last 3 bytes = length + NETWORK_UNABLE_TO_PROCESS(0x21)
+ * + ErrorCode(= 41 with 8th bit set to 1)
+ */
+ private String mTerminalResponseForNetworkUnableToProcess = "810301130002028281830221A9";
+
+ /**
+ * Terminal Response with result code in last 2 bytes = length
+ * + TERMINAL_UNABLE_TO_PROCESS(0x20)
+ */
+ private String mTerminalResponseForTerminalUnableToProcess = "810301130002028281830120";
+
+ //Terminal Response with result code(0x00)for delivery success in last 2 bytes
+ private String mTerminalResponseForDeliverySuccess = "810301130002028281830100";
+
+ public CATServiceTest() {
+ super();
+ }
+
+ private IccCardApplicationStatus composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType appType,
+ IccCardApplicationStatus.AppState appState, String aid) {
+ IccCardApplicationStatus mIccCardAppStatus = new IccCardApplicationStatus();
+ mIccCardAppStatus.aid = aid;
+ mIccCardAppStatus.app_type = appType;
+ mIccCardAppStatus.aid = aid;
+ mIccCardAppStatus.app_type = appType;
+ mIccCardAppStatus.app_state = appState;
+ mIccCardAppStatus.pin1 = mIccCardAppStatus.pin2 =
+ IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+ return mIccCardAppStatus;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mRilMessageDecoder = mock(RilMessageDecoder.class);
+ mIccFileHandler = mock(IccFileHandler.class);
+ mSmsController = mock(SmsController.class);
+ mIccCardStatus = mock(IccCardStatus.class);
+ mProxyController = mock(ProxyController.class);
+ mUiccCard = mock(UiccCard.class);
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{umtsApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex =
+ mIccCardStatus.mImsSubscriptionAppIndex =
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ mIccIoResult = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes("FF40"));
+ mSimulatedCommands = mock(SimulatedCommands.class);
+ mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
+ mUiccProfile = new UiccProfile(mContext, mSimulatedCommands, mIccCardStatus,
+ 0 /* phoneId */, mUiccCard, new Object());
+ processAllMessages();
+ logd("Created UiccProfile");
+ processAllMessages();
+ mCatService = CatService.getInstance(mSimulatedCommands, mContext,
+ mUiccProfile, mUiccController.getSlotIdFromPhoneId(0));
+ logd("Created CATService");
+ createCommandDetails();
+ createComprehensionTlvList();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCatService.dispose();
+ mUiccProfile = null;
+ mCatService = null;
+ mCtlvs = null;
+ mProxyController = null;
+ mRilMessageDecoder = null;
+ mCommandDetails = null;
+ mContext = null;
+ mSimulatedCommands = null;
+ mIccCardStatus = null;
+ mIccCard = null;
+ mIccFileHandler = null;
+ mIccIoResult = null;
+ mSmsController = null;
+ super.tearDown();
+ }
+
+ private void createCommandDetails() {
+ mCommandDetails = mock(CommandDetails.class);
+ mCommandDetails.compRequired = true;
+ mCommandDetails.commandNumber = 1;
+ mCommandDetails.typeOfCommand = 19;
+ mCommandDetails.commandQualifier = 0;
+ }
+
+ private void createComprehensionTlvList() {
+ ComprehensionTlv ctlv1 = new ComprehensionTlv(1, false, 3, mRawdata, 4);
+ ComprehensionTlv ctlv2 = new ComprehensionTlv(2, false, 2, mRawdata, 9);
+ ComprehensionTlv ctlv3 = new ComprehensionTlv(5, false, 19, mRawdata, 13);
+ ComprehensionTlv ctlv4 = new ComprehensionTlv(6, false, 7, mRawdata, 34);
+ ComprehensionTlv ctlv5 = new ComprehensionTlv(11, false, 48, mRawdata, 43);
+ mCtlvs = new ArrayList<>();
+ mCtlvs.add(ctlv1);
+ mCtlvs.add(ctlv2);
+ mCtlvs.add(ctlv3);
+ mCtlvs.add(ctlv4);
+ mCtlvs.add(ctlv5);
+ }
+
+ @Test
+ public void testSendSmsCommandParams() throws Exception {
+ ComprehensionTlv ctlv = new ComprehensionTlv(11, false, 48, mRawdata, 43);
+ SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlv);
+ assertNotNull(smsMessage);
+ assertEquals("12345", smsMessage.getRecipientAddress());
+ }
+
+ @Test
+ public void testSendSTKSmsViaCatService() {
+ CommandParams cmdPrms = new CommandParams(mCommandDetails);
+ when(mProxyController.getSmsController()).thenReturn(mSmsController);
+ mCatService.sendStkSms("test", "12345", 1, cmdPrms, mProxyController);
+ verify(mSmsController, Mockito.times(1)).sendTextForSubscriber(anyInt(),
+ anyString(), nullable(String.class), anyString(), nullable(String.class),
+ anyString(), Mockito.anyObject(), any(), eq(false), anyLong(), eq(true), eq(true));
+ }
+
+ @Test
+ public void testprocessSMSEventNotify() throws Exception {
+ CommandParamsFactory cmdPF = CommandParamsFactory.getInstance(mRilMessageDecoder,
+ mIccFileHandler, mContext);
+ assertEquals(false, cmdPF.processSMSEventNotify(mCommandDetails, mCtlvs));
+ }
+
+ @Test
+ public void testSkipFdnCheckforSTKSmsViaCatService() {
+ CommandParams cmdPrms = new CommandParams(mCommandDetails);
+ when(mProxyController.getSmsController()).thenReturn(mSmsController);
+ mCatService.sendStkSms("test", "12345", 1, cmdPrms, mProxyController);
+ verify(mSmsController, Mockito.times(0)).isNumberBlockedByFDN(1, "12345",
+ "com.android.internal.telephony");
+ }
+
+ //Create and assign a PendingResult object in BroadcastReceiver with which resultCode is updated
+ private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver, int resultCode) {
+ BroadcastReceiver.PendingResult pendingResult =
+ new BroadcastReceiver.PendingResult(resultCode,
+ "resultData",
+ /* resultExtras= */ null,
+ BroadcastReceiver.PendingResult.TYPE_UNREGISTERED,
+ /* ordered= */ true,
+ /* sticky= */ false,
+ /* token= */ null,
+ UserHandle.myUserId(),
+ /* flags= */ 0);
+ receiver.setPendingResult(pendingResult);
+ }
+
+ @Test
+ public void testSendTerminalResponseForSendSuccess() {
+ setBroadcastReceiverPendingResult(mCatService.mSmsBroadcastReceiver, Activity.RESULT_OK);
+ Intent intent = new Intent(SMS_SENT_ACTION).putExtra("cmdDetails", mCommandDetails);
+ intent.putExtra("ims", true);
+ mContext.sendOrderedBroadcast(intent, null, mCatService.mSmsBroadcastReceiver, null,
+ Activity.RESULT_OK, null, null);
+ processAllMessages();
+ verify(mSimulatedCommands, never()).sendTerminalResponse(
+ any(), any());
+ }
+
+ @Test
+ public void testSendTerminalResponseForSendSmsRpError() {
+ setBroadcastReceiverPendingResult(mCatService.mSmsBroadcastReceiver,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ Intent intent = new Intent(SMS_SENT_ACTION).putExtra("cmdDetails", mCommandDetails);
+ intent.putExtra("ims", true);
+ intent.putExtra("errorCode", 41);
+ mContext.sendOrderedBroadcast(intent, null, mCatService.mSmsBroadcastReceiver, null,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE, null, null);
+ processAllMessages();
+ //Verify if the command is encoded with correct Result byte as per TS 101.267
+ verify(mSimulatedCommands, atLeastOnce()).sendTerminalResponse(
+ eq(mTerminalResponseForSmsRpError), any());
+ }
+
+ @Test
+ public void testSendTerminalResponseForSendSmsNetworkError() {
+ setBroadcastReceiverPendingResult(mCatService.mSmsBroadcastReceiver,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ Intent intent = new Intent(SMS_SENT_ACTION).putExtra("cmdDetails", mCommandDetails);
+ intent.putExtra("ims", false);
+ intent.putExtra("errorCode", 41);
+ mContext.sendOrderedBroadcast(intent, null, mCatService.mSmsBroadcastReceiver, null,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE, null, null);
+ processAllMessages();
+ //Verify if the command is encoded with correct Result byte as per TS 101.267
+ verify(mSimulatedCommands, atLeastOnce()).sendTerminalResponse(
+ eq(mTerminalResponseForNetworkUnableToProcess), any());
+ }
+
+ @Test
+ public void testSendTerminalResponseForDeliveryFailure() {
+ setBroadcastReceiverPendingResult(mCatService.mSmsBroadcastReceiver,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ Intent intent = new Intent(SMS_DELIVERY_ACTION).putExtra("cmdDetails", mCommandDetails);
+ mContext.sendOrderedBroadcast(intent, null, mCatService.mSmsBroadcastReceiver, null,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE, null, null);
+ processAllMessages();
+ //Verify if the command is encoded with correct Result byte as per TS 101.267
+ verify(mSimulatedCommands, atLeastOnce()).sendTerminalResponse(
+ eq(mTerminalResponseForTerminalUnableToProcess), any());
+ }
+
+ @Test
+ public void testSendTerminalResponseForDeliverySuccess() {
+ setBroadcastReceiverPendingResult(mCatService.mSmsBroadcastReceiver,
+ Activity.RESULT_OK);
+ Intent intent = new Intent(SMS_DELIVERY_ACTION).putExtra("cmdDetails", mCommandDetails);
+ mContext.sendOrderedBroadcast(intent, null, mCatService.mSmsBroadcastReceiver, null,
+ Activity.RESULT_OK, null, null);
+ processAllMessages();
+ //Verify if the command is encoded with correct Result byte as per TS 101.267
+ verify(mSimulatedCommands, atLeastOnce()).sendTerminalResponse(
+ eq(mTerminalResponseForDeliverySuccess), any());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index f86dd4b..3445939 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -64,6 +64,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -111,7 +113,9 @@
try {
doReturn(new int[]{UserHandle.USER_SYSTEM}).when(mIActivityManager).getRunningUserIds();
} catch (RemoteException re) {
- fail("Unexpected RemoteException: " + re.getStackTrace());
+ StringWriter reString = new StringWriter();
+ re.printStackTrace(new PrintWriter(reString));
+ fail("Unexpected RemoteException: " + reString);
}
mSmsMessage.mWrappedSmsMessage = mCdmaSmsMessage;
@@ -153,7 +157,7 @@
Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider);
mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(mContext,
- mSmsStorageMonitor, mPhone, null);
+ mSmsStorageMonitor, mPhone, null, mTestableLooper.getLooper());
monitorTestableLooper(new TestableLooper(mCdmaInboundSmsHandler.getHandler().getLooper()));
processAllMessages();
}
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 19234ff..4d116a8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -27,13 +26,17 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.NetworkService;
@@ -61,10 +64,13 @@
private IQualifiedNetworksService mMockedQns;
private IBinder mMockedIBinder;
private AccessNetworksManagerCallback mMockedCallback;
+ private DataConfigManager mMockedDataConfigManager;
// The real callback passed created by AccessNetworksManager.
private IQualifiedNetworksServiceCallback.Stub mQnsCallback;
+ private PersistableBundle mBundle;
+
private void addQnsService() throws Exception {
ServiceInfo QnsInfo = new ServiceInfo();
QnsInfo.packageName = "fake.qns";
@@ -95,6 +101,9 @@
mMockedQns = mock(IQualifiedNetworksService.class);
mMockedIBinder = mock(IBinder.class);
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+
addQnsService();
mContextFixture.putResource(
com.android.internal.R.string.config_qualified_networks_service_package,
@@ -106,15 +115,20 @@
return null;
}).when(mMockedCallback).invokeFromExecutor(any(Runnable.class));
+ mMockedDataConfigManager = Mockito.mock(DataConfigManager.class);
mAccessNetworksManager = new AccessNetworksManager(mPhone, Looper.myLooper());
+
processAllMessages();
- assumeFalse(mAccessNetworksManager.isInLegacyMode());
+ replaceInstance(AccessNetworksManager.class, "mDataConfigManager",
+ mAccessNetworksManager, mMockedDataConfigManager);
+
logd("-setUp");
}
@After
public void tearDown() throws Exception {
mAccessNetworksManager = null;
+ mBundle = null;
super.tearDown();
}
@@ -145,6 +159,32 @@
}
@Test
+ public void testGuideTransportTypeForEmergencyDataNetwork() throws Exception {
+ doAnswer(invocation -> {
+ int accessNetwork = AccessNetworkType.UNKNOWN;
+ if (invocation.getArguments()[1].equals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) {
+ accessNetwork = AccessNetworkType.IWLAN;
+ } else if (invocation.getArguments()[1]
+ .equals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
+ accessNetwork = AccessNetworkType.EUTRAN;
+ }
+ mQnsCallback.onQualifiedNetworkTypesChanged(ApnSetting.TYPE_EMERGENCY,
+ new int[]{accessNetwork});
+ return null;
+ }).when(mMockedQns).reportEmergencyDataNetworkPreferredTransportChanged(anyInt(), anyInt());
+
+ AsyncResult asyncResult =
+ new AsyncResult(null, AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
+ Message msg = this.mAccessNetworksManager
+ .obtainMessage(1 /* EVENT_GUIDE_TRANSPORT_TYPE_FOR_EMERGENCY */, asyncResult);
+ mAccessNetworksManager.sendMessage(msg);
+ processAllMessages();
+
+ assertThat(mAccessNetworksManager.getPreferredTransport(ApnSetting.TYPE_EMERGENCY))
+ .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }
+
+ @Test
public void testEmptyNetworkTypes() throws Exception {
testQualifiedNetworkTypesChanged();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
new file mode 100644
index 0000000..b6d77e9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.InetAddress;
+import java.util.List;
+
+public class ApnSettingTest extends TelephonyTest {
+
+ private PersistableBundle mBundle;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mBundle = null;
+ super.tearDown();
+ }
+
+ static ApnSetting createApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, true);
+ }
+
+ private static ApnSetting createDisabledApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, false);
+ }
+
+ private static ApnSetting createApnSettingInternal(int apnTypeBitmask, boolean carrierEnabled) {
+ return new ApnSetting.Builder()
+ .setId(2163)
+ .setOperatorNumeric("44010")
+ .setEntryName("sp-mode")
+ .setApnName("fake_apn")
+ .setApnTypeBitmask(apnTypeBitmask)
+ .setProtocol(ApnSetting.PROTOCOL_IP)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .setCarrierEnabled(carrierEnabled)
+ .build();
+ }
+
+ private static void assertApnSettingsEqual(List<ApnSetting> a1, List<ApnSetting> a2) {
+ assertEquals(a1.size(), a2.size());
+ for (int i = 0; i < a1.size(); ++i) {
+ assertApnSettingEqual(a1.get(i), a2.get(i));
+ }
+ }
+
+ private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
+ assertEquals(a1.getEntryName(), a2.getEntryName());
+ assertEquals(a1.getApnName(), a2.getApnName());
+ assertEquals(a1.getProxyAddressAsString(), a2.getProxyAddressAsString());
+ assertEquals(a1.getProxyPort(), a2.getProxyPort());
+ assertEquals(a1.getMmsc(), a2.getMmsc());
+ assertEquals(a1.getMmsProxyAddressAsString(), a2.getMmsProxyAddressAsString());
+ assertEquals(a1.getMmsProxyPort(), a2.getMmsProxyPort());
+ assertEquals(a1.getUser(), a2.getUser());
+ assertEquals(a1.getPassword(), a2.getPassword());
+ assertEquals(a1.getAuthType(), a2.getAuthType());
+ assertEquals(a1.getId(), a2.getId());
+ assertEquals(a1.getOperatorNumeric(), a2.getOperatorNumeric());
+ assertEquals(a1.getProtocol(), a2.getProtocol());
+ assertEquals(a1.getRoamingProtocol(), a2.getRoamingProtocol());
+ assertEquals(a1.getApnTypeBitmask(), a2.getApnTypeBitmask());
+ assertEquals(a1.isEnabled(), a2.isEnabled());
+ assertEquals(a1.getProfileId(), a2.getProfileId());
+ assertEquals(a1.isPersistent(), a2.isPersistent());
+ assertEquals(a1.getMaxConns(), a2.getMaxConns());
+ assertEquals(a1.getWaitTime(), a2.getWaitTime());
+ assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
+ assertEquals(a1.getMtuV4(), a2.getMtuV4());
+ assertEquals(a1.getMvnoType(), a2.getMvnoType());
+ assertEquals(a1.getMvnoMatchData(), a2.getMvnoMatchData());
+ assertEquals(a1.getNetworkTypeBitmask(), a2.getNetworkTypeBitmask());
+ assertEquals(a1.getApnSetId(), a2.getApnSetId());
+ assertEquals(a1.getSkip464Xlat(), a2.getSkip464Xlat());
+ }
+
+ @Test
+ public void testCanHandleType() {
+ assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MMS));
+
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_MMS));
+
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+
+ // Hipri is asymmetric
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_HIPRI));
+ assertFalse(createApnSetting(ApnSetting.TYPE_HIPRI)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+
+
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_MMS));
+
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_SUPL));
+
+ // special IA case - doesn't match wildcards
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_IA));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+ .canHandleType(ApnSetting.TYPE_IA));
+
+ // same for emergency, mcx, xcap, and enterprise
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_EMERGENCY));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_EMERGENCY)
+ .canHandleType(ApnSetting.TYPE_EMERGENCY));
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MCX));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_MCX)
+ .canHandleType(ApnSetting.TYPE_MCX));
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_XCAP));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
+ .canHandleType(ApnSetting.TYPE_XCAP));
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
+
+ // check carrier disabled
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MMS));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_HIPRI));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_MMS));
+ assertFalse(createDisabledApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+ .canHandleType(ApnSetting.TYPE_IA));
+ assertFalse(createDisabledApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
+ .canHandleType(ApnSetting.TYPE_XCAP));
+ assertFalse(createDisabledApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ final int dummyInt = 1;
+ final int dummyLong = 1;
+ final String dummyString = "dummy";
+ final String[] dummyStringArr = new String[] {"dummy"};
+ final InetAddress dummyProxyAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
+ final Uri dummyUri = Uri.parse("www.google.com");
+ // base apn
+ ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
+ Field[] fields = ApnSetting.class.getDeclaredFields();
+ for (Field f : fields) {
+ int modifiers = f.getModifiers();
+ if (Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)) {
+ continue;
+ }
+ f.setAccessible(true);
+ ApnSetting testApn = null;
+ if (int.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.setInt(testApn, dummyInt + f.getInt(testApn));
+ } else if (long.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.setLong(testApn, dummyLong + f.getLong(testApn));
+ } else if (boolean.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.setBoolean(testApn, !f.getBoolean(testApn));
+ } else if (String.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyString);
+ } else if (String[].class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyStringArr);
+ } else if (InetAddress.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyProxyAddress);
+ } else if (Uri.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyUri);
+ } else {
+ fail("Unsupported field:" + f.getName());
+ }
+ if (testApn != null) {
+ assertFalse(f.getName() + " is NOT checked", testApn.equals(baseApn));
+ }
+ }
+ }
+
+ @Test
+ public void testEqualsRoamingProtocol() {
+ ApnSetting apn1 = new ApnSetting.Builder()
+ .setId(1234)
+ .setOperatorNumeric("310260")
+ .setEntryName("ims")
+ .setApnName("ims")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask(
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131071))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .build();
+
+ ApnSetting apn2 = new ApnSetting.Builder()
+ .setId(1235)
+ .setOperatorNumeric("310260")
+ .setEntryName("ims")
+ .setApnName("ims")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask(
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131072))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .build();
+
+ assertTrue(apn1.equals(apn2, false));
+ assertFalse(apn1.equals(apn2, true));
+ }
+
+ @Test
+ public void testCanHandleNetwork() {
+ ApnSetting apn1 = new ApnSetting.Builder()
+ .setId(1234)
+ .setOperatorNumeric("310260")
+ .setEntryName("ims")
+ .setApnName("ims")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .build();
+
+ ApnSetting apn2 = new ApnSetting.Builder()
+ .setId(1235)
+ .setOperatorNumeric("310260")
+ .setEntryName("ims")
+ .setApnName("ims")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .build();
+
+ assertFalse(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_1xRTT));
+ assertTrue(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_LTE));
+ assertTrue(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_UMTS));
+
+ assertFalse(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_1xRTT));
+ assertFalse(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_LTE));
+ assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_GPRS));
+ assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_EDGE));
+
+ assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_GSM));
+ }
+
+ @Test
+ public void testParcel() {
+ ApnSetting apn = createApnSetting(ApnSetting.TYPE_DEFAULT);
+
+ Parcel parcel = Parcel.obtain();
+ apn.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ ApnSetting fromParcel = ApnSetting.CREATOR.createFromParcel(parcel);
+
+ assertEquals(apn, fromParcel);
+
+ parcel.recycle();
+ }
+
+ @Test
+ public void testBuild_mmsProxyAddrStartsWithHttp() {
+ ApnSetting apn1 = new ApnSetting.Builder()
+ .setId(1234)
+ .setOperatorNumeric("310260")
+ .setEntryName("mms")
+ .setApnName("mms")
+ .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .setMmsProxyAddress("http://proxy.mobile.att.net")
+ .build();
+ assertEquals("proxy.mobile.att.net", apn1.getMmsProxyAddressAsString());
+
+ ApnSetting apn2 = new ApnSetting.Builder()
+ .setId(1235)
+ .setOperatorNumeric("310260")
+ .setEntryName("mms")
+ .setApnName("mms")
+ .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .setMmsProxyAddress("https://proxy.mobile.att.net")
+ .build();
+ assertEquals("proxy.mobile.att.net", apn2.getMmsProxyAddressAsString());
+
+ ApnSetting apn3 = new ApnSetting.Builder()
+ .setId(1236)
+ .setOperatorNumeric("310260")
+ .setEntryName("mms")
+ .setApnName("mms")
+ .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS))
+ .setMtuV4(1440)
+ .setCarrierEnabled(true)
+ .setMmsProxyAddress("proxy.mobile.att.net")
+ .build();
+ assertEquals("proxy.mobile.att.net", apn3.getMmsProxyAddressAsString());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
index 511b2df..428699f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
@@ -48,6 +48,7 @@
import android.testing.TestableLooper;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Before;
@@ -76,7 +77,8 @@
doReturn(CAPABILITY_WITH_VALIDATION_SUPPORTED).when(mPhoneConfigurationManager)
.getCurrentPhoneCapability();
mValidatorUT = new CellularNetworkValidator(mContext);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
+ doReturn(new SubscriptionInfoInternal.Builder().setSimSlotIndex(0).setId(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
processAllMessages();
setCacheTtlInCarrierConfig(5000);
}
@@ -490,6 +492,7 @@
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle bundle = carrierConfigManager.getConfigForSubId(anyInt());
- bundle.putLong(CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, ttl);
+ bundle.putLong(CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG,
+ ttl);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
index 46d1f0a..4b5189a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
@@ -16,12 +16,6 @@
package com.android.internal.telephony.data;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.os.Parcel;
@@ -37,15 +31,21 @@
import java.util.Arrays;
public class DataCallResponseTest extends AndroidTestCase {
- public static final String FAKE_DNN = "FAKE_DNN";
+ private static final String FAKE_ADDRESS = "99.88.77.66";
+ private static final String FAKE_DNS = "55.66.77.88";
+ private static final String FAKE_DNN = "FAKE_DNN";
+ private static final String FAKE_DNN_2 = "FAKE_DNN_2";
+ private static final String FAKE_GATEWAY = "11.22.33.44";
+ private static final String FAKE_IFNAME = "FAKE IFNAME";
+ private static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
// 97a498e3fc925c9489860333d06e4e470a454e5445525052495345.
// [OsAppId.ANDROID_OS_ID, "ENTERPRISE", 1]
- public static final byte[] FAKE_OS_APP_ID = {-105, -92, -104, -29, -4, -110, 92,
+ private static final byte[] FAKE_OS_APP_ID = {-105, -92, -104, -29, -4, -110, 92,
-108, -119, -122, 3, 51, -48, 110, 78, 71, 10, 69, 78, 84, 69,
82, 80, 82, 73, 83, 69};
// 97a498e3fc925c9489860333d06e4e470a454e544552505249534532.
// [OsAppId.ANDROID_OS_ID, "ENTERPRISE", 2]
- public static final byte[] FAKE_OS_APP_ID_2 = {-105, -92, -104, -29, -4, -110, 92,
+ private static final byte[] FAKE_OS_APP_ID_2 = {-105, -92, -104, -29, -4, -110, 92,
-108, -119, -122, 3, 51, -48, 110, 78, 71, 10, 69, 78, 84, 69,
82, 80, 82, 73, 83, 69, 50};
@@ -82,7 +82,7 @@
}
@SmallTest
- public void testEquals() {
+ public void testEqualsAndHashCode() {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1L)
@@ -123,6 +123,7 @@
assertEquals(response, response);
assertEquals(response, response1);
+ assertEquals(response.hashCode(), response1.hashCode());
DataCallResponse response2 = new DataCallResponse.Builder()
.setCause(1)
@@ -141,12 +142,39 @@
InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1441)
.setMtuV6(1440)
- .setTrafficDescriptors(
- Arrays.asList(new TrafficDescriptor("FAKE_DNN_2", FAKE_OS_APP_ID_2)))
+ .setTrafficDescriptors(Arrays.asList(
+ new TrafficDescriptor(FAKE_DNN, FAKE_OS_APP_ID),
+ new TrafficDescriptor(FAKE_DNN_2, FAKE_OS_APP_ID_2)))
.build();
assertNotSame(response1, response2);
assertNotSame(response1, null);
assertNotSame(response1, new String[1]);
+ assertNotSame(response1.hashCode(), response2.hashCode());
+
+ DataCallResponse response3 = new DataCallResponse.Builder()
+ .setCause(1)
+ .setRetryDurationMillis(-1L)
+ .setId(1)
+ .setLinkStatus(3)
+ .setProtocolType(ApnSetting.PROTOCOL_IP)
+ .setInterfaceName(FAKE_IFNAME)
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS),
+ InetAddresses.parseNumericAddress(FAKE_DNS)))
+ .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS),
+ InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
+ .setMtuV4(1441)
+ .setMtuV6(1440)
+ .setTrafficDescriptors(Arrays.asList(
+ new TrafficDescriptor(FAKE_DNN_2, FAKE_OS_APP_ID_2),
+ new TrafficDescriptor(FAKE_DNN, FAKE_OS_APP_ID)))
+ .build();
+
+ assertEquals(response2, response3);
+ assertEquals(response2.hashCode(), response3.hashCode());
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
index 7b0443f..f312808 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -18,7 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
import android.os.Looper;
+import android.os.PersistableBundle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,11 +38,14 @@
@TestableLooper.RunWithLooper
public class DataConfigManagerTest extends TelephonyTest {
private DataConfigManager mDataConfigManagerUT;
+ private PersistableBundle mBundle;
@Before
public void setUp() throws Exception {
logd("DataConfigManagerTest +Setup!");
super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
mDataConfigManagerUT = new DataConfigManager(mPhone, Looper.myLooper());
logd("DataConfigManagerTest -Setup!");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java
deleted file mode 100644
index 252274c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class DataEnabledOverrideTest extends TelephonyTest {
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testCreateByRules() throws Exception {
- DataEnabledOverride deo1 = new DataEnabledOverride(
- "mms=nonDefault, default=inVoiceCall&nonDefault");
- DataEnabledOverride deo2 = new DataEnabledOverride(
- "mms=nonDefault, default=inVoiceCall");
- DataEnabledOverride deo3 = new DataEnabledOverride(
- "default=inVoiceCall&nonDefault, mms=nonDefault");
- assertEquals(deo1, deo3);
- assertNotEquals(deo1, deo2);
- }
-
- @Test
- @SmallTest
- public void testOverrideEnabled() throws Exception {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "mms=nonDefault, default=inVoiceCall&nonDefault");
- doReturn(1).when(mPhone).getSubId();
- doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_MMS));
-
- doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
-
- doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
- }
-
- @Test
- @SmallTest
- public void testGetRules() throws Exception {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "mms=nonDefault, default=inVoiceCall&nonDefault");
- String rules = dataEnabledOverride.getRules();
- assertEquals(dataEnabledOverride, new DataEnabledOverride(rules));
- }
-
- @Test
- @SmallTest
- public void testUpdateRules() throws Exception {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "mms=nonDefault, default=inVoiceCall&nonDefault");
- doReturn(1).when(mPhone).getSubId();
- doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_MMS));
-
- doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
-
- doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
-
- dataEnabledOverride.updateRules("");
-
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_MMS));
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
- }
-
- @Test
- @SmallTest
- public void testAlwaysEnabled() throws Exception {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "mms =unconditionally, default= unconditionally , ");
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_MMS));
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
-
- dataEnabledOverride.updateRules("");
-
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_MMS));
- assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
- }
-
- @Test
- @SmallTest
- public void testAllApnTypesInRule() throws Exception {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride("*=inVoiceCall");
- doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
- assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_FOTA));
- }
-
- @Test
- @SmallTest
- public void testInvalidRules() throws Exception {
- try {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "default=xyz");
- fail("Invalid conditions but not threw IllegalArgumentException.");
- } catch (IllegalArgumentException ex) {
-
- }
-
- try {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "mms=");
- fail("Invalid conditions but not threw IllegalArgumentException.");
- } catch (IllegalArgumentException ex) {
-
- }
-
- try {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "abc=nonDefault");
- fail("Invalid APN type but not threw IllegalArgumentException.");
- } catch (IllegalArgumentException ex) {
-
- }
-
- try {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- " =nonDefault");
- fail("Invalid APN type but not threw IllegalArgumentException.");
- } catch (IllegalArgumentException ex) {
-
- }
-
- try {
- DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
- "Invalid rule");
- fail("Invalid rule but not threw IllegalArgumentException.");
- } catch (IllegalArgumentException ex) {
-
- }
- }
-
- @Test
- @SmallTest
- public void testSetAlwaysAllowMms() throws Exception {
- DataEnabledOverride deo = new DataEnabledOverride("");
- deo.setAlwaysAllowMms(true);
- assertTrue(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
- deo.setAlwaysAllowMms(false);
- assertFalse(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
- }
-
- @Test
- @SmallTest
- public void testSetDataAllowedInVoiceCall() throws Exception {
- DataEnabledOverride deo = new DataEnabledOverride("");
- deo.setDataAllowedInVoiceCall(true);
- assertFalse(deo.getRules(), deo.shouldOverrideDataEnabledSettings(mPhone,
- ApnSetting.TYPE_DEFAULT));
- assertTrue(deo.isDataAllowedInVoiceCall());
- doReturn(1).when(mPhone).getSubId();
- doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
-
- doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
- deo.setDataAllowedInVoiceCall(false);
- assertFalse(deo.getRules(), deo.shouldOverrideDataEnabledSettings(
- mPhone, ApnSetting.TYPE_DEFAULT));
- assertFalse(deo.isDataAllowedInVoiceCall());
- }
-}
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 0078277..62a797c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.data;
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
import static com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import static com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
@@ -24,6 +26,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -40,7 +43,6 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.LinkAddress;
@@ -72,10 +74,10 @@
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
-import android.telephony.TelephonyProtoEnums;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;
@@ -103,6 +105,7 @@
import com.android.internal.telephony.MultiSimSettingController;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
@@ -111,6 +114,7 @@
import com.android.internal.telephony.data.DataRetryManager.DataRetryManagerCallback;
import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
import com.android.internal.telephony.ims.ImsResolver;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Before;
@@ -130,7 +134,6 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -141,6 +144,12 @@
private static final String FAKE_MMTEL_PACKAGE = "fake.mmtel.package";
private static final String FAKE_RCS_PACKAGE = "fake.rcs.package";
+ // Events
+ private static final int EVENT_SIM_STATE_CHANGED = 9;
+ private static final int EVENT_REEVALUATE_EXISTING_DATA_NETWORKS = 16;
+ private static final int EVENT_VOICE_CALL_ENDED = 18;
+ private static final int EVENT_SUBSCRIPTION_OVERRIDE = 23;
+
// Mocked classes
private PhoneSwitcher mMockedPhoneSwitcher;
protected ISub mMockedIsub;
@@ -163,6 +172,7 @@
private final SparseArray<RegistrantList> mDataCallListChangedRegistrants = new SparseArray<>();
private DataNetworkController mDataNetworkControllerUT;
private PersistableBundle mCarrierConfig;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private AccessNetworksManagerCallback mAccessNetworksManagerCallback;
private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
@@ -181,6 +191,37 @@
.setRoamingProtocol(ApnSetting.PROTOCOL_IP)
.setCarrierEnabled(true)
.setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR
+ | TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT))
+ .setLingeringNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT
+ | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setProfileId(1234)
+ .setMaxConns(321)
+ .setWaitTime(456)
+ .setMaxConnsTime(789)
+ .build())
+ .setPreferred(false)
+ .build();
+
+ // The same data profile but with different auto generated ID, should be considered the same as
+ // mGeneralPurposeDataProfile
+ private final DataProfile mDuplicatedGeneralPurposeDataProfile = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setId(3612)
+ .setOperatorNumeric("12345")
+ .setEntryName("internet_supl_mms_apn")
+ .setApnName("internet_supl_mms_apn")
+ .setUser("user")
+ .setPassword("passwd")
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL
+ | ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .setCarrierEnabled(true)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN
| TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT))
.setLingeringNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
@@ -195,6 +236,36 @@
.setPreferred(false)
.build();
+ // Created to test preferred data profiles that apply to different network types
+ private final DataProfile mGeneralPurposeDataProfileAlternative = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setId(2161)
+ .setOperatorNumeric("12345")
+ .setEntryName("internet_supl_mms_apn")
+ .setApnName("internet_supl_mms_apn")
+ .setUser("user")
+ .setPassword("passwd")
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL
+ | ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .setCarrierEnabled(true)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR
+ | TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT))
+ .setLingeringNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT
+ | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setProfileId(4321)
+ .setMaxConns(321)
+ .setWaitTime(456)
+ .setMaxConnsTime(789)
+ .build())
+ .setPreferred(false)
+ .build();
+
private final DataProfile mImsCellularDataProfile = new DataProfile.Builder()
.setApnSetting(new ApnSetting.Builder()
.setId(2164)
@@ -210,6 +281,7 @@
.setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT))
.setLingeringNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT
| TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN
| TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
| TelephonyManager.NETWORK_TYPE_BITMASK_NR))
@@ -285,7 +357,7 @@
.setApnName("dun_apn")
.setUser("user")
.setPassword("passwd")
- .setApnTypeBitmask(ApnSetting.TYPE_DUN)
+ .setApnTypeBitmask(ApnSetting.TYPE_DUN | ApnSetting.TYPE_DEFAULT)
.setProtocol(ApnSetting.PROTOCOL_IPV6)
.setRoamingProtocol(ApnSetting.PROTOCOL_IP)
.setCarrierEnabled(true)
@@ -305,6 +377,12 @@
"ENTERPRISE", 1).getBytes()))
.build();
+ private final DataProfile mLowLatencyDataProfile = new DataProfile.Builder()
+ .setTrafficDescriptor(new TrafficDescriptor(null,
+ new TrafficDescriptor.OsAppId(TrafficDescriptor.OsAppId.ANDROID_OS_ID,
+ "PRIORITIZE_LATENCY", 1).getBytes()))
+ .build();
+
/** Data call response map. The first key is the transport type, the second key is the cid. */
private final Map<Integer, Map<Integer, DataCallResponse>> mDataCallResponses = new HashMap<>();
@@ -437,9 +515,10 @@
private void carrierConfigChanged() {
// Trigger carrier config reloading
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
- mContext.sendBroadcast(intent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* logicalSlotIndex */,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
processAllMessages();
}
@@ -475,9 +554,13 @@
private void serviceStateChanged(@NetworkType int networkType,
@RegistrationState int regState) {
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
serviceStateChanged(networkType, regState, regState,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -492,17 +575,35 @@
private void serviceStateChanged(@NetworkType int networkType,
@RegistrationState int dataRegState, @RegistrationState int voiceRegState,
@RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri) {
+ ServiceState ss = createSS(networkType, networkType, dataRegState, voiceRegState,
+ iwlanRegState, dsri);
+
+ doReturn(ss).when(mSST).getServiceState();
+ doReturn(ss).when(mPhone).getServiceState();
+
+ mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
+ processAllMessages();
+ }
+
+ private ServiceState createSS(@NetworkType int dataNetworkType,
+ @NetworkType int voiceNetworkType,
+ @RegistrationState int dataRegState, @RegistrationState int voiceRegState,
+ @RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri) {
if (dsri == null) {
- dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
}
ServiceState ss = new ServiceState();
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setAccessNetworkTechnology(networkType)
+ .setAccessNetworkTechnology(dataNetworkType)
.setRegistrationState(dataRegState)
.setDomain(NetworkRegistrationInfo.DOMAIN_PS)
.setDataSpecificInfo(dsri)
@@ -517,19 +618,15 @@
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setAccessNetworkTechnology(networkType)
+ .setAccessNetworkTechnology(voiceNetworkType)
.setRegistrationState(voiceRegState)
.setDomain(NetworkRegistrationInfo.DOMAIN_CS)
.build());
+
ss.setDataRoamingFromRegistration(dataRegState
== NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
processServiceStateRegStateForTest(ss);
-
- doReturn(ss).when(mSST).getServiceState();
- doReturn(ss).when(mPhone).getServiceState();
-
- mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
- processAllMessages();
+ return ss;
}
// set SS reg state base on SST impl, where WLAN overrides WWAN's data reg.
@@ -580,6 +677,7 @@
private void initializeConfig() {
mCarrierConfig = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mCarrierConfig);
mCarrierConfig.putStringArray(
CarrierConfigManager.KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY,
new String[]{
@@ -598,8 +696,8 @@
CarrierConfigManager.KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY,
new String[]{
"capabilities=eims, retry_interval=1000, maximum_retries=20",
- "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
- + "2254, maximum_retries=0", // No retry for those causes
+ "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",
"capabilities=mms|supl|cbs, retry_interval=2000",
"capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+ "5000|10000|15000|20000|40000|60000|120000|240000|"
@@ -614,12 +712,18 @@
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL,
true);
+ mCarrierConfig.putStringArray(CarrierConfigManager.KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY,
+ new String[] {"NR_NSA", "NR_NSA_MMWAVE", "NR_SA", "NR_SA_MMWAVE"});
mCarrierConfig.putIntArray(CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[]{TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
TelephonyManager.NETWORK_TYPE_EVDO_B});
+ mCarrierConfig.putIntArray(CarrierConfigManager
+ .KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY,
+ new int[]{NetworkCapabilities.NET_CAPABILITY_IMS});
+
mContextFixture.putResource(com.android.internal.R.string.config_bandwidthEstimateSource,
"bandwidth_estimator");
@@ -667,16 +771,13 @@
doReturn(true).when(mSST).getPowerStateFromCarrier();
doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
- doReturn("").when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
- doReturn(true).when(mSubscriptionController).setDataEnabledOverrideRules(
- anyInt(), anyString());
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
List<SubscriptionInfo> infoList = new ArrayList<>();
infoList.add(mMockSubInfo);
- doReturn(infoList).when(mSubscriptionController).getSubscriptionsInGroup(
- any(), any(), any());
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
- doReturn(0).when(mSubscriptionController).getPhoneId(anyInt());
+ doReturn(0).when(mSubscriptionManagerService).getPhoneId(1);
+ doReturn(1).when(mSubscriptionManagerService).getPhoneId(2);
for (int transport : new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN}) {
@@ -701,10 +802,27 @@
return null;
}).when(mMockedDataServiceManagers.get(transport)).registerForDataCallListChanged(any(
Handler.class), anyInt());
+
+ doAnswer(invocation -> {
+ Message msg = (Message) invocation.getArguments()[1];
+ msg.sendToTarget();
+ return null;
+ }).when(mMockedDataServiceManagers.get(transport)).startHandover(anyInt(),
+ any(Message.class));
+
+ doAnswer(invocation -> {
+ Message msg = (Message) invocation.getArguments()[1];
+ msg.sendToTarget();
+ return null;
+ }).when(mMockedDataServiceManagers.get(transport)).cancelHandover(anyInt(),
+ any(Message.class));
}
doReturn(-1).when(mPhone).getSubId();
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
// Note that creating a "real" data network controller will also result in creating
// real DataRetryManager, DataConfigManager, etc...Normally in unit test we should isolate
// other modules and make them mocked, but only focusing on testing the unit we would like
@@ -712,6 +830,12 @@
// between DataNetworkController and its sub-modules, we intend to make those modules "real"
// as well, except some modules below we replaced with mocks.
mDataNetworkControllerUT = new DataNetworkController(mPhone, Looper.myLooper());
+ // First two come from DataServiceManager and the third comes from DataConfigManager which
+ // is what we want to capture and assign to mCarrierConfigChangeListener
+ verify(mCarrierConfigManager, times(3)).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(2);
+ assertThat(mCarrierConfigChangeListener).isNotNull();
doReturn(mDataNetworkControllerUT).when(mPhone).getDataNetworkController();
doReturn(1).when(mPhone).getSubId();
@@ -745,20 +869,48 @@
mLinkBandwidthEstimatorCallback = linkBandwidthEstimatorCallbackCaptor.getValue();
List<DataProfile> profiles = List.of(mGeneralPurposeDataProfile,
- mImsCellularDataProfile,
+ mGeneralPurposeDataProfileAlternative, mImsCellularDataProfile,
mImsIwlanDataProfile, mEmergencyDataProfile, mFotaDataProfile,
mTetheringDataProfile);
doAnswer(invocation -> {
+ DataProfile dp = (DataProfile) invocation.getArguments()[0];
+
+ if (dp.getApnSetting() == null) return true;
+
+ for (DataProfile dataProfile : profiles) {
+ if (dataProfile.getApnSetting() != null
+ && dataProfile.getApnSetting().equals(dp.getApnSetting(), false)) {
+ return true;
+ }
+ }
+ return null;
+ }).when(mDataProfileManager).isDataProfileCompatible(any(DataProfile.class));
+
+ doAnswer(invocation -> {
+ DataProfile a = (DataProfile) invocation.getArguments()[0];
+ DataProfile b = (DataProfile) invocation.getArguments()[1];
+ return a != null
+ && b != null
+ && a.getApnSetting() != null
+ && a.getApnSetting().equals(b.getApnSetting(),
+ mPhone.getServiceState().getDataRoamingFromRegistration());
+ }).when(mDataProfileManager).areDataProfilesSharingApn(any(DataProfile.class),
+ any(DataProfile.class));
+
+ doAnswer(invocation -> {
TelephonyNetworkRequest networkRequest =
(TelephonyNetworkRequest) invocation.getArguments()[0];
int networkType = (int) invocation.getArguments()[1];
+ boolean ignorePermanentFailure = (boolean) invocation.getArguments()[2];
for (DataProfile dataProfile : profiles) {
if (dataProfile.canSatisfy(networkRequest.getCapabilities())
&& (dataProfile.getApnSetting().getNetworkTypeBitmask() == 0
|| (dataProfile.getApnSetting().getNetworkTypeBitmask()
- & ServiceState.getBitmaskForTech(networkType)) != 0)) {
+ & ServiceState.getBitmaskForTech(networkType)) != 0)
+ && (ignorePermanentFailure || (dataProfile.getApnSetting() != null
+ && !dataProfile.getApnSetting().getPermanentFailed()))) {
return dataProfile;
}
}
@@ -766,32 +918,10 @@
+ TelephonyManager.getNetworkTypeName(networkType));
return null;
}).when(mDataProfileManager).getDataProfileForNetworkRequest(
- any(TelephonyNetworkRequest.class), anyInt());
+ any(TelephonyNetworkRequest.class), anyInt(), anyBoolean());
doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
.getPreferredTransportByNetworkCapability(anyInt());
- doReturn(true).when(mDataProfileManager).isDataProfilePreferred(any(DataProfile.class));
- doAnswer(invocation -> {
- String apnName = (String) invocation.getArguments()[0];
- TrafficDescriptor td = (TrafficDescriptor) invocation.getArguments()[1];
-
- List<DataProfile> dps = profiles;
-
- if (td != null) {
- dps = dps.stream()
- .filter(dp -> td.equals(dp.getTrafficDescriptor()))
- .collect(Collectors.toList());
- }
-
- if (apnName != null) {
- dps = dps.stream()
- .filter(dp -> dp.getApnSetting() != null)
- .filter(dp -> apnName.equals(dp.getApnSetting().getApnName()))
- .collect(Collectors.toList());
- }
-
- return dps.isEmpty() ? null : dps.get(0);
- }).when(mDataProfileManager).getDataProfile(anyString(), any());
doAnswer(invocation -> {
((Runnable) invocation.getArguments()[0]).run();
@@ -805,7 +935,7 @@
mDataNetworkControllerUT.registerDataNetworkControllerCallback(
mMockedDataNetworkControllerCallback);
- mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SIM_STATE_CHANGED,
10/*SIM_STATE_LOADED*/, 0).sendToTarget();
mDataNetworkControllerUT.obtainMessage(8/*EVENT_DATA_SERVICE_BINDING_CHANGED*/,
new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, true, null))
@@ -850,6 +980,7 @@
mRcsRegCallback = regCallbackCaptor.getValue();
processAllMessages();
+ Mockito.clearInvocations(mMockedDataNetworkControllerCallback);
logd("DataNetworkControllerTest -Setup!");
}
@@ -1021,6 +1152,75 @@
}
@Test
+ public void testSetupDataNetworkWithSimilarDataProfile() throws Exception {
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+
+ List<DataNetwork> dataNetworkList = getDataNetworks();
+ assertThat(dataNetworkList).hasSize(1);
+ DataNetwork dataNetwork = dataNetworkList.get(0);
+ assertThat(dataNetworkList.get(0).getLinkProperties().getAddresses()).containsExactly(
+ InetAddresses.parseNumericAddress(IPV4_ADDRESS),
+ InetAddresses.parseNumericAddress(IPV6_ADDRESS));
+
+ verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+
+ // database updated/reloaded, causing data profile id change
+ List<DataProfile> profiles = List.of(mDuplicatedGeneralPurposeDataProfile);
+ doAnswer(invocation -> {
+ DataProfile dp = (DataProfile) invocation.getArguments()[0];
+
+ if (dp.getApnSetting() == null) return true;
+
+ for (DataProfile dataProfile : profiles) {
+ if (dataProfile.getApnSetting() != null
+ && dataProfile.getApnSetting().equals(dp.getApnSetting(), false)) {
+ return true;
+ }
+ }
+ return null;
+ }).when(mDataProfileManager).isDataProfileCompatible(any(DataProfile.class));
+ doAnswer(invocation -> {
+ TelephonyNetworkRequest networkRequest =
+ (TelephonyNetworkRequest) invocation.getArguments()[0];
+ int networkType = (int) invocation.getArguments()[1];
+
+ for (DataProfile dataProfile : profiles) {
+ if (dataProfile.canSatisfy(networkRequest.getCapabilities())
+ && (dataProfile.getApnSetting().getNetworkTypeBitmask() == 0
+ || (dataProfile.getApnSetting().getNetworkTypeBitmask()
+ & ServiceState.getBitmaskForTech(networkType)) != 0)) {
+ return dataProfile;
+ }
+ }
+ logd("Cannot find data profile to satisfy " + networkRequest + ", network type="
+ + TelephonyManager.getNetworkTypeName(networkType));
+ return null;
+ }).when(mDataProfileManager).getDataProfileForNetworkRequest(
+ any(TelephonyNetworkRequest.class), anyInt(), anyBoolean());
+
+ // verify the network still connects
+ verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+
+ // A NOT_VCN_MANAGED request cannot be satisfied by the existing network, but will adopt the
+ // same data profile
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+
+ processAllMessages();
+
+ // verify the network still connects
+ verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+ // verify we don't try to setup a separate network for the not_vcn_managed request
+ dataNetworkList = getDataNetworks();
+ assertThat(dataNetworkList).hasSize(1);
+ }
+
+ @Test
public void testSetupImsDataNetwork() throws Exception {
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
@@ -1044,7 +1244,8 @@
setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager,
createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
- .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt());
+ .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
+ anyBoolean());
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
@@ -1058,28 +1259,46 @@
@Test
public void testDataNetworkControllerCallback() throws Exception {
+ Field field = DataNetworkController.class.getDeclaredField(
+ "mDataNetworkControllerCallbacks");
+ field.setAccessible(true);
+ Set<DataNetworkControllerCallback> dataNetworkControllerCallbacks =
+ (Set<DataNetworkControllerCallback>) field.get(mDataNetworkControllerUT);
+
+ // Verify register callback
mDataNetworkControllerUT.registerDataNetworkControllerCallback(
mMockedDataNetworkControllerCallback);
+ TelephonyNetworkRequest request = createNetworkRequest(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ mDataNetworkControllerUT.addNetworkRequest(request);
processAllMessages();
- testSetupDataNetwork();
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(true));
verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
- mDataNetworkControllerUT.unregisterDataNetworkControllerCallback(
- mMockedDataNetworkControllerCallback);
+ int countOfCallbacks = dataNetworkControllerCallbacks.size();
+
+ // Verify unregister callback
+ mDataNetworkControllerUT.removeNetworkRequest(request);
processAllMessages();
+ getDataNetworks().get(0).tearDown(DataNetwork
+ .TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED);
+ processAllFutureMessages();
+
+ assertEquals(countOfCallbacks - 1, dataNetworkControllerCallbacks.size());
}
@Test
public void testSimRemovalDataTearDown() throws Exception {
testSetupDataNetwork();
- mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SIM_STATE_CHANGED,
TelephonyManager.SIM_STATE_ABSENT, 0).sendToTarget();
processAllMessages();
verifyAllDataDisconnected();
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
+ verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
}
@Test
@@ -1088,7 +1307,7 @@
Mockito.clearInvocations(mMockedDataNetworkControllerCallback);
// Insert the SIM again.
- mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SIM_STATE_CHANGED,
TelephonyManager.SIM_STATE_LOADED, 0).sendToTarget();
processAllMessages();
@@ -1187,6 +1406,41 @@
}
@Test
+ public void testPsRestrictedAllowIwlan() throws Exception {
+ // IMS preferred on IWLAN.
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+ .getPreferredTransportByNetworkCapability(
+ eq(NetworkCapabilities.NET_CAPABILITY_IMS));
+
+ // PS restricted
+ mDataNetworkControllerUT.obtainMessage(6/*EVENT_PS_RESTRICT_ENABLED*/).sendToTarget();
+ processAllMessages();
+
+ // PS restricted, new setup NOT allowed
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+ .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 2);
+ processAllMessages();
+ verifyAllDataDisconnected();
+
+ // Request IMS
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL));
+ setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+ .get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN), 3);
+ processAllMessages();
+
+ // Make sure IMS on IWLAN.
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS);
+ assertThat(getDataNetworks()).hasSize(1);
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }
+
+ @Test
public void testRatChanges() throws Exception {
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
@@ -1200,24 +1454,28 @@
// Now RAT changes from UMTS to GSM
doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
- any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_GSM));
+ any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_GSM),
+ anyBoolean());
serviceStateChanged(TelephonyManager.NETWORK_TYPE_GSM,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
verifyAllDataDisconnected();
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
-
+ verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
Mockito.clearInvocations(mMockedDataNetworkControllerCallback);
// Now RAT changes from GSM to UMTS
doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
- any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_UMTS));
+ any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_UMTS),
+ anyBoolean());
serviceStateChanged(TelephonyManager.NETWORK_TYPE_UMTS,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
doReturn(mGeneralPurposeDataProfile).when(mDataProfileManager)
- .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt());
+ .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
+ anyBoolean());
// Now RAT changes from UMTS to LTE
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
@@ -1259,7 +1517,7 @@
// Call ended.
doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
- mDataNetworkControllerUT.obtainMessage(18/*EVENT_VOICE_CALL_ENDED*/).sendToTarget();
+ mDataNetworkControllerUT.obtainMessage(EVENT_VOICE_CALL_ENDED).sendToTarget();
processAllMessages();
// It should have no internet setup at the beginning.
@@ -1364,9 +1622,7 @@
boolean isDataEnabled = mDataNetworkControllerUT.getDataSettingsManager().isDataEnabled();
doReturn(mDataNetworkControllerUT.getDataSettingsManager())
.when(mPhone).getDataSettingsManager();
- MultiSimSettingController instance = MultiSimSettingController.getInstance();
- MultiSimSettingController controller = Mockito.spy(
- new MultiSimSettingController(mContext, mSubscriptionController));
+ MultiSimSettingController controller = Mockito.spy(new MultiSimSettingController(mContext));
doReturn(true).when(controller).isCarrierConfigLoadedForAllSub();
replaceInstance(MultiSimSettingController.class, "sInstance", null, controller);
@@ -1403,7 +1659,8 @@
mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
// Always allow MMS
- mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(true);
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true);
processAllMessages();
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
@@ -1415,8 +1672,17 @@
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ mDataNetworkControllerUT.obtainMessage(16 /*EVENT_REEVALUATE_EXISTING_DATA_NETWORKS*/,
+ DataEvaluation.DataEvaluationReason.DATA_SERVICE_STATE_CHANGED).sendToTarget();
+
+ processAllFutureMessages();
+
+ // Make sure IMS network is not torn down
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_MMS);
+
// Remove MMS data enabled override
- mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(false);
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, false);
processAllMessages();
// Make sure MMS is torn down when the override is disabled.
@@ -1434,7 +1700,8 @@
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
// Always allow MMS
- mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(true);
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true);
processAllMessages();
mDataNetworkControllerUT.addNetworkRequest(
@@ -1447,6 +1714,100 @@
}
@Test
+ public void testIsDataEnabledOverriddenForApnDataDuringCall() throws Exception {
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
+ // Data disabled
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+
+ // Enable during data call mobile policy
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, true);
+ processAllMessages();
+
+ // No active phone call
+ doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+
+ // Verify no internet connection due to no active phone call
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Phone ringing
+ doReturn(PhoneConstants.State.RINGING).when(mPhone).getState();
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+
+ // Verify internet connection
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Disable during data call mobile policy
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, false);
+ processAllMessages();
+
+ // Verify no internet connection
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ @Test
+ public void testIsDataEnabledOverriddenForApnAutoDataSwitch() throws Exception {
+ // Assume phone2 is the default data phone
+ Phone phone2 = Mockito.mock(Phone.class);
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, phone2});
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
+
+ // Data disabled on nonDDS
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+
+ // Enable auto data switch mobile policy
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
+ processAllMessages();
+
+ // use disabled data on DDS
+ doReturn(false).when(phone2).isUserDataEnabled();
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+
+ // Verify no internet connection
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // use enabled data on DDS
+ doReturn(true).when(phone2).isUserDataEnabled();
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+
+ // Verify internet connection
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Disable auto data switch mobile policy, but enabled data during call
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, false);
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, true);
+ doReturn(PhoneConstants.State.RINGING).when(phone2).getState();
+ processAllMessages();
+
+ // Verify internet connection
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Disable data during call
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, false);
+ processAllMessages();
+
+ // Verify no internet connection
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ @Test
public void testUnmeteredRequestPreferredOnIwlan() throws Exception {
// Preferred on cellular
doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
@@ -1570,6 +1931,16 @@
NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_EIMS);
assertThat(handoverRule.isOnlyForRoaming).isTrue();
+ handoverRule = new HandoverRule("source=EUTRAN|NGRAN|IWLAN|UNKNOWN, "
+ + "target=EUTRAN|NGRAN|IWLAN, type=disallowed, capabilities = IMS|EIMS");
+ assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.EUTRAN,
+ AccessNetworkType.NGRAN, AccessNetworkType.IWLAN, AccessNetworkType.UNKNOWN);
+ assertThat(handoverRule.targetAccessNetworks).containsExactly(AccessNetworkType.EUTRAN,
+ AccessNetworkType.NGRAN, AccessNetworkType.IWLAN);
+ assertThat(handoverRule.type).isEqualTo(HandoverRule.RULE_TYPE_DISALLOWED);
+ assertThat(handoverRule.networkCapabilities).containsExactly(
+ NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_EIMS);
+
assertThrows(IllegalArgumentException.class,
() -> new HandoverRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ=="));
@@ -1580,6 +1951,14 @@
() -> new HandoverRule("source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"));
assertThrows(IllegalArgumentException.class,
+ () -> new HandoverRule("source=GERAN, target=UNKNOWN, type=disallowed, "
+ + "capabilities=IMS"));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HandoverRule("source=UNKNOWN, target=IWLAN, type=allowed, "
+ + "capabilities=IMS"));
+
+ assertThrows(IllegalArgumentException.class,
() -> new HandoverRule("source=GERAN, target=IWLAN, type=wtf"));
assertThrows(IllegalArgumentException.class,
@@ -1612,7 +1991,7 @@
// Set 5G unmetered
congestedNetworkTypes.add(TelephonyManager.NETWORK_TYPE_NR);
- mDataNetworkControllerUT.obtainMessage(23/*EVENT_SUBSCRIPTION_OVERRIDE*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SUBSCRIPTION_OVERRIDE,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED,
new int[]{TelephonyManager.NETWORK_TYPE_NR}).sendToTarget();
@@ -1625,7 +2004,7 @@
// Change data network type to NR
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
processAllMessages();
@@ -1634,7 +2013,7 @@
// Set all network types metered
congestedNetworkTypes.clear();
- mDataNetworkControllerUT.obtainMessage(23/*EVENT_SUBSCRIPTION_OVERRIDE*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SUBSCRIPTION_OVERRIDE,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED, 0,
TelephonyManager.getAllNetworkTypes()).sendToTarget();
dataNetwork.sendMessage(16/*EVENT_SUBSCRIPTION_PLAN_OVERRIDE*/);
@@ -1654,7 +2033,7 @@
// Set 5G unmetered
unmeteredNetworkTypes.add(TelephonyManager.NETWORK_TYPE_NR);
- mDataNetworkControllerUT.obtainMessage(23/*EVENT_SUBSCRIPTION_OVERRIDE*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SUBSCRIPTION_OVERRIDE,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED,
new int[]{TelephonyManager.NETWORK_TYPE_NR}).sendToTarget();
@@ -1668,7 +2047,7 @@
// Change data network type to NR
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
processAllMessages();
@@ -1678,7 +2057,7 @@
// Set all network types metered
unmeteredNetworkTypes.clear();
- mDataNetworkControllerUT.obtainMessage(23/*EVENT_SUBSCRIPTION_OVERRIDE*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SUBSCRIPTION_OVERRIDE,
NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED, 0,
TelephonyManager.getAllNetworkTypes()).sendToTarget();
dataNetwork.sendMessage(16/*EVENT_SUBSCRIPTION_PLAN_OVERRIDE*/);
@@ -1724,7 +2103,7 @@
// Change data network type to NR
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
processAllMessages();
@@ -1761,7 +2140,7 @@
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
NetworkCapabilities.NET_CAPABILITY_MMTEL);
- // Add internet, should be compatible with
+ // Add internet, should be compatible
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
setSuccessfulSetupDataResponse(mMockedDataServiceManagers
@@ -1772,6 +2151,15 @@
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
NetworkCapabilities.NET_CAPABILITY_MMTEL);
+ // Both internet and IMS should be retained after network re-evaluation
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
// Add MMS, whose priority > internet, internet should be town down, IMS left untouched
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
@@ -1783,6 +2171,15 @@
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
NetworkCapabilities.NET_CAPABILITY_MMTEL);
+ // Both internet and IMS should be retained after network re-evaluation
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_MMS);
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
// Temporarily remove IMS
mDataNetworkControllerUT.removeNetworkRequest(ims);
processAllMessages();
@@ -1860,7 +2257,6 @@
updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-
// Before setup data call response, change the preference back to IWLAN.
updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
@@ -1882,6 +2278,46 @@
}
@Test
+ public void testHandoverDataNetworkBackToBackPreferenceChangedHandoverFail() throws Exception {
+ testSetupImsDataNetwork();
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+
+ Mockito.reset(mMockedWlanDataServiceManager);
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+ // Capture the message for setup data call response. We want to delay it.
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
+ anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+ messageCaptor.capture());
+
+ // Before setup data call response, change the preference back to cellular.
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ // Finally handover is completed.
+ Message msg = messageCaptor.getValue();
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(DataFailCause.ERROR_UNSPECIFIED)
+ .setRetryDurationMillis(123)
+ .setHandoverFailureMode(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL)
+ .build();
+ msg.getData().putParcelable("data_call_response", response);
+ msg.arg1 = DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Make sure handover request is only sent once.
+ verify(mMockedWlanDataServiceManager, times(1)).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), messageCaptor.capture());
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS);
+ assertSame(dataNetwork, getDataNetworks().get(0));
+ }
+
+ @Test
public void testHandoverDataNetworkNotAllowedByPolicy() throws Exception {
mCarrierConfig.putStringArray(CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
new String[]{"source=EUTRAN, target=IWLAN, type=disallowed, capabilities=MMS|IMS",
@@ -1900,6 +2336,8 @@
// Verify all data disconnected.
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
+ verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
// A new data network should be connected on IWLAN
List<DataNetwork> dataNetworkList = getDataNetworks();
@@ -1960,6 +2398,8 @@
// Verify all data disconnected.
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
+ verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
// Should setup a new one instead of handover.
verify(mMockedWwanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
@@ -1991,9 +2431,26 @@
assertThat(dataNetwork.getTransport()).isEqualTo(
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ // Set target transport OOS
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, /* data */
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, /* voice */
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* iwlan */
+ null);
setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager, 1);
processAllFutureMessages();
+ // Verify that data network is still on cellular
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ // Set target transport back to service
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, /* data */
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, /* voice */
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, /* iwlan */
+ null);
+ processAllFutureMessages();
dataNetwork = getDataNetworks().get(0);
// Verify that data network is handovered to IWLAN
@@ -2002,9 +2459,59 @@
}
@Test
+ public void testHandoverDataNetworkDuplicateRetry() throws Exception {
+ testSetupImsDataNetwork();
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .when(mAccessNetworksManager).getPreferredTransportByNetworkCapability(anyInt());
+
+ DataRetryManager.DataHandoverRetryEntry retry1 =
+ new DataRetryManager.DataHandoverRetryEntry.Builder<>()
+ .setDataNetwork(dataNetwork)
+ .build();
+ DataRetryManager.DataHandoverRetryEntry retry2 =
+ new DataRetryManager.DataHandoverRetryEntry.Builder<>()
+ .setDataNetwork(dataNetwork)
+ .build();
+ final Message msg1 = new Message();
+ msg1.what = 4 /*EVENT_DATA_HANDOVER_RETRY*/;
+ msg1.obj = retry1;
+
+ final Message msg2 = new Message();
+ msg2.what = 4 /*EVENT_DATA_HANDOVER_RETRY*/;
+ msg2.obj = retry2;
+
+ Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
+ field.setAccessible(true);
+ List<DataRetryManager.DataRetryEntry> dataRetryEntries =
+ (List<DataRetryManager.DataRetryEntry>)
+ field.get(mDataNetworkControllerUT.getDataRetryManager());
+ dataRetryEntries.add(retry1);
+ dataRetryEntries.add(retry2);
+
+ mDataNetworkControllerUT.getDataRetryManager().sendMessageDelayed(msg1, 0);
+ mDataNetworkControllerUT.getDataRetryManager().sendMessageDelayed(msg2, 0);
+
+ processAllFutureMessages();
+
+ setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager, 1);
+ processAllMessages();
+
+ dataNetwork = getDataNetworks().get(0);
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
+ anyBoolean(), anyBoolean(), eq(DataService.REQUEST_REASON_HANDOVER), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+ assertThat(mDataNetworkControllerUT.getDataRetryManager()
+ .isAnyHandoverRetryScheduled(dataNetwork)).isFalse();
+ }
+
+ @Test
public void testHandoverDataNetworkRetryReachedMaximum() throws Exception {
testSetupImsDataNetwork();
+ // 1. Normal case
setFailedSetupDataResponse(mMockedWlanDataServiceManager,
DataFailCause.HANDOVER_FAILED, -1, true);
updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
@@ -2025,6 +2532,30 @@
verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
anyBoolean(), anyBoolean(), eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(),
any(), any(), anyBoolean(), any(Message.class));
+
+ // 2. Active VoPS call, should delay tear down
+ doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL,
+ true);
+ carrierConfigChanged();
+
+ setFailedSetupDataResponse(mMockedWwanDataServiceManager,
+ DataFailCause.HANDOVER_FAILED, -1, true);
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ processAllFutureMessages();
+
+ // Verify the network wasn't torn down
+ verify(mMockedWlanDataServiceManager, never()).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+ // Verify tear down after call ends
+ doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+ mDataNetworkControllerUT.obtainMessage(EVENT_VOICE_CALL_ENDED).sendToTarget();
+ processAllFutureMessages();
+
+ verify(mMockedWlanDataServiceManager).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
}
@Test
@@ -2128,6 +2659,44 @@
verifyConnectedNetworkHasDataProfile(mImsIwlanDataProfile);
}
+ @Test
+ public void testHandoverDataNetworkFailedNullResponse() throws Exception {
+ testSetupImsDataNetwork();
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+
+ // Set failed null response
+ doAnswer(invocation -> {
+ final Message msg = (Message) invocation.getArguments()[10];
+ msg.getData().putParcelable("data_call_response", null);
+ msg.arg1 = DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE;
+ msg.getTarget().sendMessageDelayed(msg, 0);
+ return null;
+ }).when(mMockedWlanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
+ anyBoolean(), anyBoolean(), eq(DataService.REQUEST_REASON_HANDOVER), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+
+ // Attempt handover
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ processAllMessages();
+
+ // Verify that data network is still on cellular and data network was not torn down
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ assertThat(dataNetwork.isConnected()).isTrue();
+
+ // Process all handover retries and failures
+ processAllFutureMessages();
+
+ // Verify that original data network was torn down and new connection set up on cellular
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ assertThat(dataNetwork.isConnected()).isFalse();
+ dataNetwork = getDataNetworks().get(0);
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ assertThat(dataNetwork.isConnected()).isTrue();
+ }
@Test
public void testSetupDataNetworkRetrySuggestedByNetwork() {
@@ -2137,8 +2706,9 @@
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
processAllFutureMessages();
- // Should retried 20 times, which is the maximum based on the retry config rules.
- verify(mMockedWwanDataServiceManager, times(21)).setupDataCall(anyInt(),
+ // The first 8 retries are short timers that scheduled by handler, future retries are
+ // scheduled by intent and require more complex mock, so we only verify the first 8 here.
+ verify(mMockedWwanDataServiceManager, times(9)).setupDataCall(anyInt(),
any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
any(), any(), anyBoolean(), any(Message.class));
}
@@ -2243,6 +2813,64 @@
}
@Test
+ public void testSetupDataNetworkRetryFailedNetworkRequestRemovedAndAdded() throws Exception {
+ mDataNetworkControllerUT.getDataRetryManager()
+ .registerCallback(mMockedDataRetryManagerCallback);
+ setFailedSetupDataResponse(mMockedWwanDataServiceManager, DataFailCause.CONGESTION,
+ 10000, false);
+
+ TelephonyNetworkRequest firstTnr = createNetworkRequest(
+ NetworkCapabilities.NET_CAPABILITY_IMS);
+ TelephonyNetworkRequest secondTnr = createNetworkRequest(
+ NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ mDataNetworkControllerUT.addNetworkRequest(firstTnr);
+ processAllMessages();
+
+ mDataNetworkControllerUT.removeNetworkRequest(firstTnr);
+ mDataNetworkControllerUT.addNetworkRequest(secondTnr);
+ processAllFutureMessages();
+
+ verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+
+ ArgumentCaptor<DataRetryManager.DataSetupRetryEntry> retryEntry =
+ ArgumentCaptor.forClass(DataRetryManager.DataSetupRetryEntry.class);
+ verify(mMockedDataRetryManagerCallback, times(1))
+ .onDataNetworkSetupRetry(retryEntry.capture());
+ assertThat(retryEntry.getValue().getState()).isEqualTo(
+ DataRetryManager.DataRetryEntry.RETRY_STATE_FAILED);
+ assertThat(retryEntry.getValue().networkRequestList.size()).isEqualTo(1);
+ assertThat(retryEntry.getValue().networkRequestList.get(0)).isEqualTo(firstTnr);
+
+ DataRetryManager.DataSetupRetryEntry dataSetupRetryEntry = retryEntry.getValue();
+ logd("DataSetupRetryEntry:" + dataSetupRetryEntry);
+
+ processAllMessages();
+ processAllFutureMessages();
+
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 1);
+ logd("Sending TAC_CHANGED event");
+ mDataNetworkControllerUT.obtainMessage(25/*EVENT_TAC_CHANGED*/).sendToTarget();
+ mDataNetworkControllerUT.getDataRetryManager().obtainMessage(10/*EVENT_TAC_CHANGED*/)
+ .sendToTarget();
+ processAllFutureMessages();
+
+ // TAC changes should clear the already-scheduled retry and throttling.
+ assertThat(mDataNetworkControllerUT.getDataRetryManager().isDataProfileThrottled(
+ mImsCellularDataProfile, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)).isFalse();
+
+ // But DNC should re-evaluate unsatisfied request and setup IMS again.
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
+ verify(mMockedWwanDataServiceManager, times(2)).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+ }
+
+ @Test
public void testSetupDataNetworkPermanentFailure() {
setFailedSetupDataResponse(mMockedWwanDataServiceManager, DataFailCause.PROTOCOL_ERRORS,
DataCallResponse.RETRY_DURATION_UNDEFINED, false);
@@ -2253,8 +2881,36 @@
// There should be only one attempt, and no retry should happen because it's a permanent
// failure.
verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+ eq(mGeneralPurposeDataProfile), anyBoolean(), anyBoolean(), anyInt(), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+
+ Mockito.clearInvocations(mMockedWwanDataServiceManager);
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllFutureMessages();
+
+ // Even receiving a new network request, setup data call should not be sent.
+ verify(mMockedWwanDataServiceManager, never()).setupDataCall(anyInt(),
any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
any(), any(), anyBoolean(), any(Message.class));
+ // APN should be marked as permanent failure.
+ assertThat(mGeneralPurposeDataProfile.getApnSetting().getPermanentFailed()).isTrue();
+ }
+
+ @Test
+ public void testSetupDataNetworkConditionChangesAfterPermanentFailure() throws Exception {
+ testSetupDataNetworkPermanentFailure();
+
+ setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+ .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 1);
+
+ // From LTE to NR
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_NR,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+ // condition change should trigger setup data, even though previously the APN has been
+ // marked as permanent failure.
+ verifyInternetConnected();
}
@Test
@@ -2307,6 +2963,35 @@
}
@Test
+ public void testHandoverDataNetworkNetworkSuggestedRetryTimerDataThrottled() throws Exception {
+ testSetupImsDataNetwork();
+
+ DataNetwork network = getDataNetworks().get(0);
+ setFailedSetupDataResponse(mMockedWlanDataServiceManager,
+ DataFailCause.HANDOVER_FAILED, 10000, true);
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+ // Verify retry scheduled on this network
+ assertThat(mDataNetworkControllerUT.getDataRetryManager()
+ .isAnyHandoverRetryScheduled(network)).isTrue();
+ // Verify the data profile is throttled on WLAN
+ assertThat(mDataNetworkControllerUT.getDataRetryManager().isDataProfileThrottled(
+ network.getDataProfile(), AccessNetworkConstants.TRANSPORT_TYPE_WLAN)).isTrue();
+
+ // Test even if network disconnected, the throttle status should remain
+ network.tearDown(DataNetwork.TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED);
+ processAllFutureMessages();
+
+ // 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
public void testTacChangesClearThrottlingAndRetryHappens() throws Exception {
testSetupDataNetworkNetworkSuggestedRetryTimerDataThrottled();
processAllFutureMessages();
@@ -2319,7 +3004,7 @@
processAllFutureMessages();
// TAC changes should clear the already-scheduled retry and throttling.
- assertThat(mDataNetworkControllerUT.getDataRetryManager().isAnySetupRetryScheduled(
+ assertThat(mDataNetworkControllerUT.getDataRetryManager().isDataProfileThrottled(
mImsCellularDataProfile, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)).isFalse();
// But DNC should re-evaluate unsatisfied request and setup IMS again.
@@ -2424,15 +3109,13 @@
mDataNetworkControllerUT.addNetworkRequest(tnr);
processAllMessages();
- // VCN managed network won't trigger onInternetDataNetworkConnected.
- // DataNetwork.isInternetSupported() is false for VCN managed network.
- verify(mMockedDataNetworkControllerCallback, never())
+ verify(mMockedDataNetworkControllerCallback)
.onInternetDataNetworkConnected(any());
List<DataNetwork> dataNetworks = getDataNetworks();
assertThat(dataNetworks).hasSize(1);
assertThat(dataNetworks.get(0).getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)).isFalse();
- assertThat(dataNetworks.get(0).isInternetSupported()).isFalse();
+ assertThat(dataNetworks.get(0).isInternetSupported()).isTrue();
assertThat(dataNetworks.get(0).getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
}
@@ -2502,10 +3185,139 @@
}
@Test
+ public void testDataDisableTearingDownTetheringNetwork() throws Exception {
+ // User data enabled
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, true, mContext.getOpPackageName());
+ processAllMessages();
+
+ // Request the restricted tethering network.
+ NetworkCapabilities netCaps = new NetworkCapabilities();
+ netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+
+ NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
+ ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
+
+ mDataNetworkControllerUT.addNetworkRequest(
+ new TelephonyNetworkRequest(nativeNetworkRequest, mPhone));
+ processAllMessages();
+
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_DUN);
+
+ // User data disabled
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+ processAllMessages();
+
+ // Everything should be disconnected.
+ verifyAllDataDisconnected();
+ }
+
+ @Test
+ public void testSetPreferredDataProfileMultiInternetDataProfile() throws Exception {
+ // No preferred data profile in the beginning
+ doReturn(false).when(mDataProfileManager).canPreferredDataProfileSatisfy(
+ any(NetworkRequestList.class));
+
+ testSetupDataNetwork();
+
+ // Verify this network still alive after evaluation
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
+ verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+
+ // Network connected, became preferred data profile
+ doAnswer(invocation -> {
+ NetworkRequestList networkRequests =
+ (NetworkRequestList) invocation.getArguments()[0];
+ return networkRequests.stream()
+ .allMatch(request -> request.canBeSatisfiedBy(mGeneralPurposeDataProfile));
+ }).when(mDataProfileManager).canPreferredDataProfileSatisfy(
+ any(NetworkRequestList.class));
+ doReturn(true).when(mDataProfileManager)
+ .isDataProfilePreferred(mGeneralPurposeDataProfile);
+
+ // 1. Test DUN | DEFAULT data profile is compatible with preferred default internet
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_DUN));
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 2);
+ processAllMessages();
+
+ // Verify both DUN and preferred default network are alive
+ verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+ verifyConnectedNetworkHasDataProfile(mTetheringDataProfile);
+
+ // Verify this network still alive after evaluation
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
+ verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+ verifyConnectedNetworkHasDataProfile(mTetheringDataProfile);
+
+ // 2. Test tear down when user changes preferred data profile
+ doAnswer(invocation -> {
+ NetworkRequestList networkRequests =
+ (NetworkRequestList) invocation.getArguments()[0];
+ return networkRequests.stream()
+ .allMatch(request -> request.canBeSatisfiedBy(
+ mGeneralPurposeDataProfileAlternative));
+ }).when(mDataProfileManager).canPreferredDataProfileSatisfy(
+ any(NetworkRequestList.class));
+ doReturn(true).when(mDataProfileManager)
+ .isDataProfilePreferred(mGeneralPurposeDataProfileAlternative);
+ doReturn(false).when(mDataProfileManager)
+ .isDataProfilePreferred(mGeneralPurposeDataProfile);
+
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
+ List<DataNetwork> dataNetworks = getDataNetworks();
+ assertThat(dataNetworks).hasSize(1);
+ verifyConnectedNetworkHasDataProfile(mTetheringDataProfile);
+ }
+
+ @Test
+ public void testDataDisableNotAllowingBringingUpTetheringNetwork() throws Exception {
+ // User data disabled
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+ processAllMessages();
+
+ // Request the restricted tethering network.
+ NetworkCapabilities netCaps = new NetworkCapabilities();
+ netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+
+ NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
+ ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
+
+ mDataNetworkControllerUT.addNetworkRequest(
+ new TelephonyNetworkRequest(nativeNetworkRequest, mPhone));
+ processAllMessages();
+
+ // Everything should be disconnected.
+ verifyAllDataDisconnected();
+
+ // Telephony should not try to setup a data call for DUN.
+ verify(mMockedWwanDataServiceManager, never()).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+ }
+
+ @Test
public void testNonVoPSNoIMSSetup() throws Exception {
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -2520,10 +3332,18 @@
@Test
public void testNonVoPStoVoPSImsSetup() throws Exception {
+ // Even allow lingering when NoVops, should have no effect on NoVops -> Vops
+ mCarrierConfig.putBoolean(CarrierConfigManager.Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, true);
+ carrierConfigChanged();
+
// VOPS not supported
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -2534,9 +3354,13 @@
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
// VoPS supported
- dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -2578,6 +3402,33 @@
}
@Test
+ public void testDelayImsTearDownDuringSrvcc() throws Exception {
+ testSetupImsDataNetwork();
+ // SRVCC in progress, delay tear down
+ mDataNetworkControllerUT.obtainMessage(4 /*EVENT_SRVCC_STATE_CHANGED*/,
+ new AsyncResult(null,
+ new int[]{TelephonyManager.SRVCC_STATE_HANDOVER_STARTED}, null))
+ .sendToTarget();
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_HSPAP,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllMessages();
+
+ // Make sure IMS network is still connected.
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
+ // SRVCC handover ends, tear down as normal
+ mDataNetworkControllerUT.obtainMessage(4 /*EVENT_SRVCC_STATE_CHANGED*/,
+ new AsyncResult(null,
+ new int[]{TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED}, null))
+ .sendToTarget();
+ processAllFutureMessages();
+
+ // Make sure IMS network is torn down
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL);
+ }
+
+ @Test
public void testUnmeteredMmsWhenDataDisabled() throws Exception {
mCarrierConfig.putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
@@ -2689,11 +3540,11 @@
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_DUN);
mDataNetworkControllerUT.addNetworkRequest(
- createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
processAllFutureMessages();
// Lower priority network should not trump the higher priority network.
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_DUN);
- verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
// Now send a higher priority network request
TelephonyNetworkRequest fotaRequest = createNetworkRequest(
@@ -2802,7 +3653,7 @@
mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
processAllMessages();
- doReturn(true).when(mPhone).isInEmergencyCall();
+ doReturn(true).when(mTelecomManager).isInEmergencyCall();
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_SUPL));
processAllMessages();
@@ -2816,7 +3667,7 @@
@Test
public void testEmergencyCallDataDisabled() throws Exception {
- doReturn(true).when(mPhone).isInEmergencyCall();
+ doReturn(true).when(mTelecomManager).isInEmergencyCall();
mDataNetworkControllerUT.addNetworkRequest(
createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
processAllMessages();
@@ -2865,38 +3716,63 @@
@Test
public void testHandoverDataNetworkOos() throws Exception {
- ServiceState ss = new ServiceState();
- ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .build());
+ // Config delay IMS tear down enabled
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL,
+ true);
+ carrierConfigChanged();
- ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
- .setRegistrationState(
- NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .build());
-
- ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
- .build());
- processServiceStateRegStateForTest(ss);
- doReturn(ss).when(mSST).getServiceState();
- doReturn(ss).when(mPhone).getServiceState();
-
- mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
- processAllMessages();
+ // VoPS supported
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME /*data*/,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME /*voice*/,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING /*iwlan*/,
+ dsri);
testSetupImsDataNetwork();
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+
+ // 1. Active VoPS call, mock target IWLAN OOS, should schedule retry
+ doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ // Process DRM event to schedule retry
+ processAllMessages();
+
+ // Verify scheduled new handover retry
+ assertTrue(mDataNetworkControllerUT.getDataRetryManager()
+ .isAnyHandoverRetryScheduled(dataNetwork));
+ // Verify the network wasn't torn down
+ verify(mMockedWwanDataServiceManager, never()).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+ // Get the scheduled retry
+ Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
+ field.setAccessible(true);
+ DataRetryManager.DataHandoverRetryEntry dataRetryEntry =
+ (DataRetryManager.DataHandoverRetryEntry) ((List<DataRetryManager.DataRetryEntry>)
+ field.get(mDataNetworkControllerUT.getDataRetryManager())).get(0);
+
+ // Process the retry
+ moveTimeForward(1000 /*The retry delay of the first attempt*/);
+ processAllMessages();
+
+ // Verify the previous retry is set to FAILED
+ assertEquals(DataRetryManager.DataRetryEntry.RETRY_STATE_FAILED, dataRetryEntry.getState());
+ // Verify a new retry is scheduled
+ assertTrue(mDataNetworkControllerUT.getDataRetryManager()
+ .isAnyHandoverRetryScheduled(dataNetwork));
+
+ // 2. Normal case (call ended), should tear down
+ doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+ mDataNetworkControllerUT.obtainMessage(EVENT_VOICE_CALL_ENDED).sendToTarget();
+ processAllFutureMessages();
// Verify that handover is not performed.
verify(mMockedWlanDataServiceManager, never()).setupDataCall(anyInt(),
@@ -2904,17 +3780,85 @@
eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(), anyBoolean(),
any(Message.class));
- // IMS network should be torn down.
+ // Verify IMS network should be torn down.
verifyAllDataDisconnected();
}
@Test
+ public void testHandoverDataNetworkSourceOos() throws Exception {
+ testSetupImsDataNetwork();
+ // Configured handover is disallowed from OOS to 4G/5G/IWLAN.
+ mCarrierConfig.putStringArray(
+ CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
+ new String[]{
+ "source=EUTRAN|NGRAN|IWLAN|UNKNOWN, target=EUTRAN|NGRAN|IWLAN, "
+ + "type=disallowed, capabilities=IMS|EIMS|MMS|XCAP|CBS"
+ });
+ carrierConfigChanged();
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+ // Verify IMS network was torn down on source first.
+ verify(mMockedWwanDataServiceManager).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+ // Verify that IWLAN is brought up again on IWLAN.
+ verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }
+
+ @Test
+ public void testHandoverDataNetworkSourceOosNoUnknownRule() throws Exception {
+ testSetupImsDataNetwork();
+ // Configured handover is allowed from OOS to 4G/5G/IWLAN.
+ mCarrierConfig.putStringArray(
+ CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
+ new String[]{
+ "source=EUTRAN|NGRAN|IWLAN, target=EUTRAN|NGRAN|IWLAN, "
+ + "type=disallowed, capabilities=IMS|EIMS|MMS|XCAP|CBS"
+ });
+ carrierConfigChanged();
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+ // Verify IMS network was torn down on source first.
+ verify(mMockedWwanDataServiceManager).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+ // Verify that IWLAN is brought up again on IWLAN.
+ verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+ assertThat(dataNetwork.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }
+
+ @Test
public void testHandoverDataNetworkNonVops() throws Exception {
ServiceState ss = new ServiceState();
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
@@ -2968,12 +3912,84 @@
}
@Test
+ public void testHandoverDataNetworkNonVopsAllowLingeringVoPS() throws Exception {
+ // Allow lingering IMS PDN when enter area VoPS -> no VoPS
+ mCarrierConfig.putBoolean(CarrierConfigManager.Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, true);
+ carrierConfigChanged();
+
+ ServiceState ss = new ServiceState();
+
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setDataSpecificInfo(dsri)
+ .build());
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .build());
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .build());
+ processServiceStateRegStateForTest(ss);
+ doReturn(ss).when(mSST).getServiceState();
+ doReturn(ss).when(mPhone).getServiceState();
+
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+ .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL));
+ processAllMessages();
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
+ // Change the preference to cellular
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ // Verify that handover is not performed.
+ verify(mMockedWwanDataServiceManager).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+
+ // IMS network still alive.
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+ }
+
+ @Test
public void testNonMmtelImsHandoverDataNetworkNonVops() throws Exception {
ServiceState ss = new ServiceState();
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
@@ -3032,12 +4048,20 @@
@Test
public void testMmtelImsDataNetworkMovingToNonVops() throws Exception {
+ // Allow lingering IMS PDN when enter area VoPS -> no VoPS
+ mCarrierConfig.putBoolean(CarrierConfigManager.Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, true);
+ carrierConfigChanged();
+
ServiceState ss = new ServiceState();
// VoPS network
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
@@ -3082,9 +4106,13 @@
ss = new ServiceState();
// Non VoPS network
- dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
@@ -3114,6 +4142,16 @@
mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
processAllMessages();
+ // The IMS network is alive due to KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = true
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL);
+
+ mCarrierConfig.putBoolean(CarrierConfigManager.Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
+ carrierConfigChanged();
+ mDataNetworkControllerUT.obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)
+ .sendToTarget();
+ processAllMessages();
+
// The IMS network should be torn down by data network controller.
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL);
@@ -3126,9 +4164,13 @@
carrierConfigChanged();
// VoPS supported
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -3140,9 +4182,13 @@
doReturn(PhoneConstants.State.OFFHOOK).when(mCT).getState();
- dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -3152,7 +4198,7 @@
// Call ends
doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
- mDataNetworkControllerUT.obtainMessage(18/*EVENT_VOICE_CALL_ENDED*/).sendToTarget();
+ mDataNetworkControllerUT.obtainMessage(EVENT_VOICE_CALL_ENDED).sendToTarget();
processAllMessages();
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
@@ -3168,16 +4214,18 @@
}).when(mMockedWwanDataServiceManager).deactivateDataCall(
anyInt(), anyInt(), any(Message.class));
// Simulate old devices
- doReturn(RIL.RADIO_HAL_VERSION_1_6).when(mPhone).getHalVersion();
+ doReturn(RIL.RADIO_HAL_VERSION_1_6).when(mPhone).getHalVersion(HAL_SERVICE_DATA);
testSetupDataNetwork();
- mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+ mDataNetworkControllerUT.obtainMessage(EVENT_SIM_STATE_CHANGED,
TelephonyManager.SIM_STATE_ABSENT, 0).sendToTarget();
processAllMessages();
verifyAllDataDisconnected();
verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
+ verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
}
@Test
@@ -3250,7 +4298,7 @@
mDataNetworkControllerUT.addNetworkRequest(request);
processAllMessages();
- // Now DDS temporarily switched to phone 1
+ // this slot is 0, modem preferred on slot 1
doReturn(1).when(mMockedPhoneSwitcher).getPreferredDataPhoneId();
// Simulate telephony network factory remove request due to switch.
@@ -3263,13 +4311,13 @@
@Test
public void testSetupDataOnNonDds() throws Exception {
- // Now DDS switched to phone 1
+ // this slot is 0, modem preferred on slot 1
doReturn(1).when(mMockedPhoneSwitcher).getPreferredDataPhoneId();
TelephonyNetworkRequest request = createNetworkRequest(
NetworkCapabilities.NET_CAPABILITY_MMS);
// Test Don't allow setup if both data and voice OOS
- serviceStateChanged(TelephonyProtoEnums.NETWORK_TYPE_1XRTT,
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_1xRTT,
// data, voice, Iwlan reg state
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
@@ -3280,19 +4328,93 @@
verifyAllDataDisconnected();
// Test Don't allow setup if CS is in service, but current RAT is already PS(e.g. LTE)
- serviceStateChanged(TelephonyProtoEnums.NETWORK_TYPE_LTE,
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, null);
verifyAllDataDisconnected();
- // Test Allow if voice is in service if RAT is 2g/3g
- serviceStateChanged(TelephonyProtoEnums.NETWORK_TYPE_1XRTT,
- NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ // Test Allow if voice is in service if RAT is 2g/3g, use voice RAT to select data profile
+ ServiceState ss = createSS(TelephonyManager.NETWORK_TYPE_UNKNOWN /* data RAT */,
+ TelephonyManager.NETWORK_TYPE_1xRTT /* voice RAT */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING ,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, null);
+ doReturn(ss).when(mSST).getServiceState();
+ mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
+ mDataNetworkControllerUT.removeNetworkRequest(request);
+ mDataNetworkControllerUT.addNetworkRequest(request);
+ processAllMessages();
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_MMS);
}
+
+ @Test
+ public void testTrafficDescriptionChangedDataRetry() throws Exception {
+ List<TrafficDescriptor> tdList = List.of(
+ new TrafficDescriptor.Builder()
+ .setOsAppId(new OsAppId(OsAppId.ANDROID_OS_ID, "PRIORITIZE_LATENCY", 1)
+ .getBytes()).build(),
+ new TrafficDescriptor.Builder()
+ .setOsAppId(new OsAppId(OsAppId.ANDROID_OS_ID, "ENTERPRISE", 1).getBytes())
+ .build()
+ );
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager,
+ createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
+ doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
+ .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
+ anyBoolean());
+ mDataNetworkControllerUT.addNetworkRequest(new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .build(), mPhone));
+ processAllMessages();
+ verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+ NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY);
+
+ mDataNetworkControllerUT.addNetworkRequest(new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .build(), mPhone));
+ processAllMessages();
+ List<DataNetwork> dataNetworkList = getDataNetworks();
+ assertThat(dataNetworkList).hasSize(1);
+
+ // Now remove low latency TD from the data call response.
+ logd("Now remove PRIORITIZE_LATENCY");
+ tdList = List.of(new TrafficDescriptor.Builder()
+ .setOsAppId(new OsAppId(OsAppId.ANDROID_OS_ID, "ENTERPRISE", 1).getBytes())
+ .build());
+ mDataCallResponses.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).put(1,
+ createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
+ mDataCallListChangedRegistrants.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .notifyRegistrants(new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ List.of(createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE,
+ tdList)), null));
+
+ tdList = List.of(new TrafficDescriptor.Builder()
+ .setOsAppId(new OsAppId(OsAppId.ANDROID_OS_ID, "PRIORITIZE_LATENCY", 1).getBytes())
+ .build());
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager,
+ createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
+ doReturn(mLowLatencyDataProfile).when(mDataProfileManager)
+ .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
+ anyBoolean());
+ processAllFutureMessages();
+
+ dataNetworkList = getDataNetworks();
+ assertThat(dataNetworkList).hasSize(2);
+
+ assertThat(dataNetworkList.get(0).getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
+ assertThat(dataNetworkList.get(0).getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)).isFalse();
+ assertThat(dataNetworkList.get(1).getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isFalse();
+ assertThat(dataNetworkList.get(1).getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)).isTrue();
+ }
}
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 f53298d..b85081f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -56,6 +56,7 @@
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
+import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
@@ -71,6 +72,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
import com.android.internal.telephony.data.DataNetwork.DataNetworkCallback;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
@@ -200,30 +202,8 @@
doAnswer(invocation -> {
final Message msg = (Message) invocation.getArguments()[10];
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1L)
- .setId(cid)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
- .setInterfaceName("ifname")
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS), 32),
- new LinkAddress(IPV6_ADDRESS + "/64")))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
- InetAddresses.parseNumericAddress("fd00:976a::9")))
- .setGatewayAddresses(Arrays.asList(
- InetAddresses.parseNumericAddress("10.0.2.15"),
- InetAddresses.parseNumericAddress("fe80::2")))
- .setPcscfAddresses(Arrays.asList(
- InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
- InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
- InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
- .setMtuV4(1234)
- .setPduSessionId(1)
- .setQosBearerSessions(new ArrayList<>())
- .setTrafficDescriptors(tds)
- .build();
+ DataCallResponse response = createDataCallResponse(
+ cid, DataCallResponse.LINK_STATUS_ACTIVE, tds);
msg.getData().putParcelable("data_call_response", response);
msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
msg.sendToTarget();
@@ -233,6 +213,34 @@
any(Message.class));
}
+ private DataCallResponse createDataCallResponse(int cid, int linkStatus,
+ List<TrafficDescriptor> tds) {
+ return new DataCallResponse.Builder()
+ .setCause(0)
+ .setRetryDurationMillis(-1L)
+ .setId(cid)
+ .setLinkStatus(linkStatus)
+ .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+ .setInterfaceName("ifname")
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS), 32),
+ new LinkAddress(IPV6_ADDRESS + "/64")))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+ InetAddresses.parseNumericAddress("fd00:976a::9")))
+ .setGatewayAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("10.0.2.15"),
+ InetAddresses.parseNumericAddress("fe80::2")))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
+ InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
+ .setMtuV4(1234)
+ .setPduSessionId(1)
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(tds)
+ .build();
+ }
+
private void setFailedSetupDataResponse(DataServiceManager dsm,
@DataServiceCallback.ResultCode int resultCode) {
doAnswer(invocation -> {
@@ -312,13 +320,32 @@
mMockedWwanDataServiceManager);
mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
mMockedWlanDataServiceManager);
+
+ for (int transport : new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN}) {
+ doAnswer(invocation -> {
+ Message msg = (Message) invocation.getArguments()[1];
+ msg.sendToTarget();
+ return null;
+ }).when(mDataServiceManagers.get(transport)).startHandover(anyInt(),
+ any(Message.class));
+
+ doAnswer(invocation -> {
+ Message msg = (Message) invocation.getArguments()[1];
+ msg.sendToTarget();
+ return null;
+ }).when(mDataServiceManagers.get(transport)).cancelHandover(anyInt(),
+ any(Message.class));
+ }
+
doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
.getPreferredTransportByNetworkCapability(anyInt());
doReturn(DataNetwork.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR)
.when(mDataConfigManager).getBandwidthEstimateSource();
doReturn(true).when(mDataConfigManager).isTempNotMeteredSupportedByCarrier();
- doReturn(true).when(mDataConfigManager).isImsDelayTearDownEnabled();
+ doReturn(true).when(mDataConfigManager).isNetworkTypeUnmetered(
+ any(TelephonyDisplayInfo.class), any(ServiceState.class));
doReturn(DEFAULT_MTU).when(mDataConfigManager).getDefaultMtu();
doReturn(FAKE_IMSI).when(mPhone).getSubscriberId();
doReturn(true).when(mDataNetworkController)
@@ -346,8 +373,7 @@
.setQosBearerSessions(new ArrayList<>())
.setTrafficDescriptors(new ArrayList<>())
.build();
- mDataNetworkUT.sendMessage(7/*EVENT_TEAR_DOWN_NETWORK*/,
- 1/*TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED*/);
+ mDataNetworkUT.tearDown(1/*TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED*/);
mDataNetworkUT.sendMessage(8/*EVENT_DATA_STATE_CHANGED*/,
new AsyncResult(transport, new ArrayList<>(Arrays.asList(response)), null));
processAllMessages();
@@ -384,6 +410,7 @@
// updated later.
assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
+ verify(mDataConfigManager).getTcpConfigString(any(TelephonyDisplayInfo.class));
verify(mSimulatedCommandsVerifier, never()).allocatePduSessionId(any(Message.class));
verify(mMockedWwanDataServiceManager).setupDataCall(eq(AccessNetworkType.EUTRAN),
eq(mInternetDataProfile), eq(false), eq(false),
@@ -451,6 +478,139 @@
}
@Test
+ public void testCreateDataNetworkWhenOos() throws Exception {
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
+ // Out of service
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, dsri);
+
+ NetworkRequestList networkRequestList = new NetworkRequestList();
+ networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), mPhone));
+
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+
+ mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
+ mInternetDataProfile, networkRequestList,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ DataAllowedReason.NORMAL, mDataNetworkCallback);
+ replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+ mDataNetworkUT, mDataCallSessionStats);
+ processAllMessages();
+
+ ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+
+ verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
+ any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
+ anyInt());
+
+ // Make sure the initial network capability has NOT_SUSPENDED
+ assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isTrue();
+
+ // The final network should not have NOT_SUSPENDED because the device is OOS.
+ assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isFalse();
+ }
+
+ @Test
+ public void testRecreateAgentWhenOos() throws Exception {
+ testCreateDataNetwork();
+
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
+ // Out of service
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, dsri);
+
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(0)
+ .setRetryDurationMillis(-1L)
+ .setId(123)
+ .setLinkStatus(2)
+ .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+ .setInterfaceName("ifname")
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS1), 32),
+ new LinkAddress(IPV6_ADDRESS + "/64")))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+ InetAddresses.parseNumericAddress("fd00:976a::9")))
+ .setGatewayAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("10.0.2.15"),
+ InetAddresses.parseNumericAddress("fe80::2")))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
+ InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
+ .setMtuV4(1234)
+ .setMtuV6(5678)
+ .setPduSessionId(1)
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(Collections.emptyList())
+ .build();
+
+ // IP changes
+ mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
+ new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ List.of(response), null)).sendToTarget();
+ processAllMessages();
+
+ ArgumentCaptor<NetworkCapabilities> networkCapabilitiesCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+
+ // Agent re-created, so register should be called twice.
+ verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
+ any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
+ anyInt());
+
+ // Make sure the 2nd network agent was created with NOT_SUSPENDED.
+ assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isTrue();
+
+ // The final network should not have NOT_SUSPENDED because the device is OOS.
+ assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isFalse();
+ }
+
+ @Test
+ public void testRilCrash() throws Exception {
+ NetworkRequestList networkRequestList = new NetworkRequestList();
+ networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), mPhone));
+ setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+
+ mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
+ mInternetDataProfile, networkRequestList,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ DataAllowedReason.NORMAL, mDataNetworkCallback);
+ processAllMessages();
+
+ // verify connected
+ verify(mDataNetworkCallback).onConnected(eq(mDataNetworkUT));
+
+ // RIL crash
+ mDataNetworkUT.sendMessage(4/*EVENT_RADIO_NOT_AVAILABLE*/);
+ processAllMessages();
+
+ verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT),
+ eq(DataFailCause.RADIO_NOT_AVAILABLE), eq(DataNetwork.TEAR_DOWN_REASON_NONE));
+ }
+
+ @Test
public void testCreateImsDataNetwork() throws Exception {
NetworkRequestList networkRequestList = new NetworkRequestList();
networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
@@ -705,8 +865,9 @@
anyInt());
verify(mMockedWwanDataServiceManager).deactivateDataCall(eq(123),
eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
- verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
- DataFailCause.EMM_DETACHED));
+ verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT),
+ eq(DataFailCause.EMM_DETACHED),
+ eq(DataNetwork.TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED));
ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
ArgumentCaptor.forClass(PreciseDataConnectionState.class);
@@ -819,7 +980,7 @@
verify(mMockedWlanDataServiceManager).deactivateDataCall(eq(123),
eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
- DataFailCause.EMM_DETACHED));
+ DataFailCause.EMM_DETACHED), anyInt() /* tear down reason */);
ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
ArgumentCaptor.forClass(PreciseDataConnectionState.class);
@@ -850,6 +1011,7 @@
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())
@@ -873,8 +1035,9 @@
Mockito.clearInvocations(mDataNetworkCallback);
Mockito.clearInvocations(mLinkBandwidthEstimator);
mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, null);
- processAllMessages();
+ processAllFutureMessages();
+ verify(mMockedWlanDataServiceManager).startHandover(eq(456), any(Message.class));
verify(mLinkBandwidthEstimator).registerCallback(
any(LinkBandwidthEstimatorCallback.class));
assertThat(mDataNetworkUT.getTransport())
@@ -893,11 +1056,11 @@
mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
processAllMessages();
+ verify(mMockedWwanDataServiceManager).startHandover(eq(123), any(Message.class));
+ verify(mMockedWwanDataServiceManager).cancelHandover(eq(123), any(Message.class));
verify(mDataNetworkCallback).onHandoverFailed(eq(mDataNetworkUT),
eq(DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE), eq(-1L),
- eq(DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY));
- verify(mLinkBandwidthEstimator, never()).unregisterForBandwidthChanged(
- eq(mDataNetworkUT.getHandler()));
+ eq(DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN));
assertThat(mDataNetworkUT.getTransport())
.isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertThat(mDataNetworkUT.getId()).isEqualTo(123);
@@ -915,6 +1078,17 @@
assertThat(pdcsList.get(3).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
assertThat(pdcsList.get(3).getLastCauseCode())
.isEqualTo(DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE);
+
+ // Test source PDN lost during the HO, expect tear down after HO
+ setFailedSetupDataResponse(mMockedWlanDataServiceManager,
+ DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
+ mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
+ mDataNetworkUT.sendMessage(8/*EVENT_DATA_STATE_CHANGED*/,
+ new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ Collections.emptyList(), null)); // the source transport report PDN lost
+ processAllMessages();
+
+ assertThat(mDataNetworkUT.isConnected()).isFalse();
}
@Test
@@ -954,40 +1128,42 @@
testTearDown();
// Verify register all events.
- verify(mDataConfigManager).registerForConfigUpdate(any(Handler.class), anyInt());
+ verify(mDataConfigManager).registerCallback(any(DataConfigManagerCallback.class));
verify(mDisplayInfoController).registerForTelephonyDisplayInfoChanged(
any(Handler.class), anyInt(), eq(null));
verify(mMockedWwanDataServiceManager).registerForDataCallListChanged(
any(Handler.class), anyInt());
verify(mMockedWlanDataServiceManager).registerForDataCallListChanged(
any(Handler.class), anyInt());
- verify(mCarrierPrivilegesTracker).registerCarrierPrivilegesListener(any(Handler.class),
- anyInt(), eq(null));
+ verify(mTelephonyManager).registerCarrierPrivilegesCallback(anyInt(), any(), any());
verify(mLinkBandwidthEstimator).registerCallback(
any(LinkBandwidthEstimatorCallback.class));
verify(mSimulatedCommandsVerifier).registerForNattKeepaliveStatus(any(Handler.class),
anyInt(), eq(null));
verify(mSimulatedCommandsVerifier).registerForPcoData(any(Handler.class), anyInt(),
eq(null));
+ verify(mSimulatedCommandsVerifier).registerForNotAvailable(any(Handler.class), anyInt(),
+ eq(null));
verify(mVcnManager).addVcnNetworkPolicyChangeListener(any(Executor.class),
any(VcnNetworkPolicyChangeListener.class));
verify(mSST).registerForCssIndicatorChanged(any(Handler.class), anyInt(), eq(null));
- verify(mSST).registerForServiceStateChanged(any(Handler.class), anyInt());
+ verify(mSST).registerForServiceStateChanged(any(Handler.class), anyInt(), any());
verify(mCT).registerForVoiceCallStarted(any(Handler.class), anyInt(), eq(null));
verify(mCT).registerForVoiceCallEnded(any(Handler.class), anyInt(), eq(null));
verify(mImsCT).registerForVoiceCallStarted(any(Handler.class), anyInt(), eq(null));
verify(mImsCT).registerForVoiceCallEnded(any(Handler.class), anyInt(), eq(null));
// Verify unregister all events.
- verify(mDataConfigManager).unregisterForConfigUpdate(any(Handler.class));
+ verify(mDataConfigManager).unregisterCallback(any(DataConfigManagerCallback.class));
verify(mDisplayInfoController).unregisterForTelephonyDisplayInfoChanged(any(Handler.class));
verify(mMockedWwanDataServiceManager).unregisterForDataCallListChanged(any(Handler.class));
verify(mMockedWlanDataServiceManager).unregisterForDataCallListChanged(any(Handler.class));
- verify(mCarrierPrivilegesTracker).unregisterCarrierPrivilegesListener(any(Handler.class));
+ verify(mTelephonyManager).unregisterCarrierPrivilegesCallback(any());
verify(mLinkBandwidthEstimator).unregisterCallback(
any(LinkBandwidthEstimatorCallback.class));
verify(mSimulatedCommandsVerifier).unregisterForNattKeepaliveStatus(any(Handler.class));
verify(mSimulatedCommandsVerifier).unregisterForPcoData(any(Handler.class));
+ verify(mSimulatedCommandsVerifier).unregisterForNotAvailable(any(Handler.class));
verify(mVcnManager).removeVcnNetworkPolicyChangeListener(
any(VcnNetworkPolicyChangeListener.class));
verify(mSST).unregisterForCssIndicatorChanged(any(Handler.class));
@@ -1077,7 +1253,7 @@
processAllMessages();
verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
- DataFailCause.RADIO_NOT_AVAILABLE));
+ DataFailCause.RADIO_NOT_AVAILABLE), eq(DataNetwork.TEAR_DOWN_REASON_NONE));
assertThat(mDataNetworkUT.isConnected()).isFalse();
}
@@ -1203,9 +1379,13 @@
@Test
public void testMovingToNonVops() throws Exception {
- DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_SUPPORTED));
+ DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED))
+ .build();
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
testCreateImsDataNetwork();
@@ -1213,9 +1393,13 @@
assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_MMTEL)).isTrue();
- dsri = new DataSpecificRegistrationInfo(8, false, true, true,
- new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
- LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED));
+ dsri = new DataSpecificRegistrationInfo.Builder(8)
+ .setNrAvailable(true)
+ .setEnDcAvailable(true)
+ .setVopsSupportInfo(new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+ LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED))
+ .build();
logd("Trigger non VoPS");
serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, dsri);
@@ -1284,7 +1468,7 @@
assertThat(mDataNetworkUT.getApnTypeNetworkCapability())
.isEqualTo(NetworkCapabilities.NET_CAPABILITY_SUPL);
- mDataNetworkUT.detachNetworkRequest(networkRequest);
+ mDataNetworkUT.detachNetworkRequest(networkRequest, false);
processAllMessages();
assertThat(mDataNetworkUT.getApnTypeNetworkCapability())
@@ -1307,7 +1491,7 @@
// SUPL priority is 80
assertThat(mDataNetworkUT.getPriority()).isEqualTo(80);
- mDataNetworkUT.detachNetworkRequest(networkRequest);
+ mDataNetworkUT.detachNetworkRequest(networkRequest, false);
processAllMessages();
// Internet priority is 20
@@ -1591,4 +1775,36 @@
assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
InetAddresses.parseNumericAddress(IPV4_ADDRESS));
}
+
+ @Test
+ public void testLinkStatusUpdate() throws Exception {
+ setupDataNetwork();
+
+ // verify link status sent on connected
+ verify(mDataNetworkCallback).onConnected(eq(mDataNetworkUT));
+ verify(mDataNetworkCallback).onLinkStatusChanged(eq(mDataNetworkUT),
+ eq(DataCallResponse.LINK_STATUS_ACTIVE));
+
+ // data state updated
+ DataCallResponse response = createDataCallResponse(123,
+ DataCallResponse.LINK_STATUS_DORMANT, Collections.emptyList());
+ mDataNetworkUT.sendMessage(8 /*EVENT_DATA_STATE_CHANGED*/, new AsyncResult(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN, List.of(response), null));
+ processAllMessages();
+
+ // verify link status sent on data state updated
+ assertThat(mDataNetworkUT.isConnected()).isTrue();
+ verify(mDataNetworkCallback).onLinkStatusChanged(eq(mDataNetworkUT),
+ eq(DataCallResponse.LINK_STATUS_DORMANT));
+
+ // RIL crash
+ mDataNetworkUT.sendMessage(4 /*EVENT_RADIO_NOT_AVAILABLE*/);
+ processAllMessages();
+
+ // verify link status sent on disconnected
+ verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT),
+ eq(DataFailCause.RADIO_NOT_AVAILABLE), eq(DataNetwork.TEAR_DOWN_REASON_NONE));
+ verify(mDataNetworkCallback).onLinkStatusChanged(eq(mDataNetworkUT),
+ eq(DataCallResponse.LINK_STATUS_INACTIVE));
+ }
}
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 783df82..43f65e2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -42,6 +42,7 @@
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataProfile;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.data.TrafficDescriptor.OsAppId;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -49,6 +50,7 @@
import android.testing.TestableLooper;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback;
@@ -63,18 +65,27 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class DataProfileManagerTest extends TelephonyTest {
private static final String GENERAL_PURPOSE_APN = "GP_APN";
+ private static final String GENERAL_PURPOSE_APN_LEGACY_RAT = "GP_APN_RAT";
private static final String GENERAL_PURPOSE_APN1 = "GP_APN1";
private static final String IMS_APN = "IMS_APN";
private static final String TETHERING_APN = "DUN_APN";
+ private static final String APN_SET_ID_1_APN = "APN_SET_ID_1_APN";
+ private static final String APN_SET_ID_1_TETHERING_APN = "APN_SET_ID_1_TETHERING_APN";
+ private static final String MATCH_ALL_APN_SET_ID_IMS_APN = "MATCH_ALL_APN_SET_ID_IMS_APN";
private static final String PLMN = "330123";
+ private static final int DEFAULT_APN_SET_ID = Telephony.Carriers.NO_APN_SET_ID;
+ private static final int APN_SET_ID_1 = 1;
+ private static final int MATCH_ALL_APN_SET_ID = Telephony.Carriers.MATCH_ALL_APN_SET_ID;
// Mocked classes
private DataProfileManagerCallback mDataProfileManagerCallback;
@@ -88,6 +99,8 @@
private DataNetworkControllerCallback mDataNetworkControllerCallback;
+ private DataConfigManagerCallback mDataConfigManagerCallback;
+
private boolean mSimInserted = true;
private class ApnSettingContentProvider extends MockContentProvider {
@@ -153,7 +166,42 @@
TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
0, // lingering_network_type_bitmask
- 0, // apn_set_id
+ DEFAULT_APN_SET_ID, // apn_set_id
+ -1, // carrier_id
+ -1, // skip_464xlat
+ 0 // always_on
+ },
+ // default internet data profile for RAT CDMA, to test update preferred data profile
+ new Object[]{
+ 9, // id
+ PLMN, // numeric
+ GENERAL_PURPOSE_APN_LEGACY_RAT, // name
+ GENERAL_PURPOSE_APN_LEGACY_RAT, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default,supl,mms,ia", // types
+ "IPV4V6", // protocol
+ "IPV4V6", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ 1280, // mtu_v4
+ 1280, // mtu_v6
+ "", // mvno_type
+ "", // mnvo_match_data
+ TelephonyManager.NETWORK_TYPE_BITMASK_CDMA, // network_type_bitmask
+ 0, // lingering_network_type_bitmask
+ DEFAULT_APN_SET_ID, // apn_set_id
-1, // carrier_id
-1, // skip_464xlat
0 // always_on
@@ -187,7 +235,7 @@
"", // mnvo_match_data
TelephonyManager.NETWORK_TYPE_BITMASK_LTE, // network_type_bitmask
0, // lingering_network_type_bitmask
- 0, // apn_set_id
+ DEFAULT_APN_SET_ID, // apn_set_id
-1, // carrier_id
-1, // skip_464xlat
0 // always_on
@@ -221,7 +269,7 @@
"", // mnvo_match_data
TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
0, // lingering_network_type_bitmask
- 0, // apn_set_id
+ DEFAULT_APN_SET_ID, // apn_set_id
-1, // carrier_id
-1, // skip_464xlat
0 // alwys_on
@@ -256,7 +304,7 @@
TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
0, // lingering_network_type_bitmask
- 0, // apn_set_id
+ DEFAULT_APN_SET_ID, // apn_set_id
-1, // carrier_id
-1, // skip_464xlat
0 // always_on
@@ -292,13 +340,120 @@
TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
0, // lingering_network_type_bitmask
- 0, // apn_set_id
+ DEFAULT_APN_SET_ID, // apn_set_id
+ -1, // carrier_id
+ -1, // skip_464xlat
+ 0 // always_on
+ },
+ new Object[]{
+ 6, // id
+ PLMN, // numeric
+ APN_SET_ID_1_APN, // name
+ APN_SET_ID_1_APN, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default,supl,mms,ia", // types
+ "IPV4V6", // protocol
+ "IPV4V6", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ 1280, // mtu_v4
+ 1280, // mtu_v6
+ "", // mvno_type
+ "", // mnvo_match_data
+ TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+ 0, // lingering_network_type_bitmask
+ APN_SET_ID_1, // apn_set_id
+ -1, // carrier_id
+ -1, // skip_464xlat
+ 0 // always_on
+ },
+ new Object[]{
+ 7, // id
+ PLMN, // numeric
+ APN_SET_ID_1_TETHERING_APN, // name
+ APN_SET_ID_1_TETHERING_APN, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "dun", // types
+ "IPV4V6", // protocol
+ "IPV4V6", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ 1280, // mtu_v4
+ 1280, // mtu_v6
+ "", // mvno_type
+ "", // mnvo_match_data
+ TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+ 0, // lingering_network_type_bitmask
+ APN_SET_ID_1, // apn_set_id
+ -1, // carrier_id
+ -1, // skip_464xlat
+ 0 // always_on
+ },
+ new Object[]{
+ 8, // id
+ PLMN, // numeric
+ MATCH_ALL_APN_SET_ID_IMS_APN, // name
+ MATCH_ALL_APN_SET_ID_IMS_APN, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "ims", // types
+ "IPV4V6", // protocol
+ "IPV4V6", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ 1280, // mtu_v4
+ 1280, // mtu_v6
+ "", // mvno_type
+ "", // mnvo_match_data
+ TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+ 0, // lingering_network_type_bitmask
+ MATCH_ALL_APN_SET_ID, // apn_set_id
-1, // carrier_id
-1, // skip_464xlat
0 // always_on
}
);
+ private Set<Object> mDeletedApns = new HashSet<>();
+
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
@@ -320,7 +475,9 @@
MatrixCursor mc = new MatrixCursor(APN_COLUMNS);
if (mSimInserted) {
for (Object apnSetting : mAllApnSettings) {
- mc.addRow((Object[]) apnSetting);
+ if (!mDeletedApns.contains(apnSetting)) {
+ mc.addRow((Object[]) apnSetting);
+ }
}
}
return mc;
@@ -335,11 +492,13 @@
return mc;
} else if (uri.isPathPrefixMatch(Telephony.Carriers.PREFERRED_APN_URI)) {
for (Object apnSetting : mAllApnSettings) {
- int id = (int) ((Object[]) apnSetting)[0];
- if (id == mPreferredApnId) {
- MatrixCursor mc = new MatrixCursor(APN_COLUMNS);
- mc.addRow((Object[]) apnSetting);
- return mc;
+ if (!mDeletedApns.contains(apnSetting)) {
+ int id = (int) ((Object[]) apnSetting)[0];
+ if (id == mPreferredApnId) {
+ MatrixCursor mc = new MatrixCursor(APN_COLUMNS);
+ mc.addRow((Object[]) apnSetting);
+ return mc;
+ }
}
}
}
@@ -377,6 +536,44 @@
}
return null;
}
+
+ public boolean removeApnByApnId(int apnId) {
+ for (Object apnSetting : mAllApnSettings) {
+ int id = (int) ((Object[]) apnSetting)[0];
+ if (apnId == id) {
+ mDeletedApns.add(apnSetting);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void restoreApnSettings() {
+ mDeletedApns.clear();
+ }
+
+ public void setPreferredApn(String apnName) {
+ for (Object apnSetting : mAllApnSettings) {
+ if (apnName == ((Object[]) apnSetting)[3]) {
+ mPreferredApnId = (int) ((Object[]) apnSetting)[0];
+ mPreferredApnSet = (int) ((Object[]) apnSetting)[28];
+ logd("mPreferredApnId=" + mPreferredApnId + " ,mPreferredApnSet="
+ + mPreferredApnSet);
+ break;
+ }
+ }
+ }
+
+ public int getCountApnSpecifyType(String type) {
+ int count = 0;
+ for (Object apnSetting : mAllApnSettings) {
+ String apnTypes = (String) ((Object[]) apnSetting)[12];
+ if (apnTypes.contains(type)) {
+ count++;
+ }
+ }
+ return count;
+ }
}
/**
@@ -440,7 +637,11 @@
verify(mDataNetworkController).registerDataNetworkControllerCallback(
dataNetworkControllerCallbackCaptor.capture());
mDataNetworkControllerCallback = dataNetworkControllerCallbackCaptor.getValue();
- mDataProfileManagerUT.obtainMessage(1 /*EVENT_DATA_CONFIG_UPDATED*/).sendToTarget();
+ ArgumentCaptor<DataConfigManagerCallback> dataConfigManagerCallbackCaptor =
+ ArgumentCaptor.forClass(DataConfigManagerCallback.class);
+ verify(mDataConfigManager).registerCallback(dataConfigManagerCallbackCaptor.capture());
+ mDataConfigManagerCallback = dataConfigManagerCallbackCaptor.getValue();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
logd("DataProfileManagerTest -Setup!");
@@ -450,6 +651,7 @@
public void tearDown() throws Exception {
mDataProfileManagerUT = null;
mDataNetworkControllerCallback = null;
+ mDataConfigManagerCallback = null;
super.tearDown();
}
@@ -460,7 +662,7 @@
.build();
TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -471,7 +673,7 @@
.build();
tnr = new TelephonyNetworkRequest(request, mPhone);
dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -481,7 +683,7 @@
.build();
tnr = new TelephonyNetworkRequest(request, mPhone);
dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
assertThat(dp.getApnSetting().getApnName()).isEqualTo(IMS_APN);
@@ -490,7 +692,7 @@
.build();
tnr = new TelephonyNetworkRequest(request, mPhone);
dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dp).isNull();
doReturn(new NetworkRegistrationInfo.Builder()
@@ -503,7 +705,7 @@
.build();
tnr = new TelephonyNetworkRequest(request, mPhone);
dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_NR);
+ TelephonyManager.NETWORK_TYPE_NR, false);
assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
assertThat(dp.getApnSetting().getApnName()).isEqualTo(TETHERING_APN);
}
@@ -515,7 +717,7 @@
.build();
TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_GSM);
+ TelephonyManager.NETWORK_TYPE_GSM, false);
// Should not find data profile due to RAT incompatible.
assertThat(dp).isNull();
}
@@ -527,14 +729,14 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
logd("Set setLastSetupTimestamp on " + dataProfile);
dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
// See if another one can be returned.
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
}
@@ -545,7 +747,7 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting()).isNull();
OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
@@ -558,7 +760,7 @@
.addEnterpriseId(2), ConnectivityManager.TYPE_NONE,
0, NetworkRequest.Type.REQUEST), mPhone);
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting()).isNull();
osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
@@ -574,7 +776,7 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting()).isNull();
OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
@@ -590,7 +792,7 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting()).isNull();
OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
@@ -607,17 +809,55 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
dataProfile.setPreferred(true);
- mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(dataProfile));
+ DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
+ doReturn(dataProfile).when(internetNetwork).getDataProfile();
+ doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
+ .when(internetNetwork).getAttachedNetworkRequestList();
+ mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
processAllMessages();
- // See if the same one can be returned.
+ // Test See if the same one can be returned.
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
+
+ // Test Another default internet network connected due to RAT changed. Verify the preferred
+ // data profile is updated.
+ DataProfile legacyRatDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+ tnr, TelephonyManager.NETWORK_TYPE_CDMA, false);
+ DataNetwork legacyRatInternetNetwork = Mockito.mock(DataNetwork.class);
+ doReturn(legacyRatDataProfile).when(legacyRatInternetNetwork).getDataProfile();
+ doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
+ .when(legacyRatInternetNetwork).getAttachedNetworkRequestList();
+ mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(
+ // Because internetNetwork is torn down due to network type mismatch
+ legacyRatInternetNetwork));
+ processAllMessages();
+
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
+
+ // Test Another supl default internet network temporarily connected. Verify preferred
+ // doesn't change.
+ TelephonyNetworkRequest suplTnr = new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
+ .build(), mPhone);
+ DataProfile suplDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+ suplTnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+ DataNetwork suplInternetNetwork = Mockito.mock(DataNetwork.class);
+ doReturn(suplDataProfile).when(suplInternetNetwork).getDataProfile();
+ doReturn(new DataNetworkController.NetworkRequestList(List.of(suplTnr)))
+ .when(suplInternetNetwork).getAttachedNetworkRequestList();
+ mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(
+ legacyRatInternetNetwork, suplInternetNetwork));
+ processAllMessages();
+
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
}
@Test
@@ -632,6 +872,44 @@
}
@Test
+ public void testSetInitialAttachDataProfileMultipleRequests() throws Exception {
+ // Test: Modem Cleared IA, should always send IA to modem
+ // TODO(b/237444788): this case should be removed from U
+ mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+ processAllMessages();
+
+ Field IADPField = DataProfileManager.class.getDeclaredField("mInitialAttachDataProfile");
+ IADPField.setAccessible(true);
+ DataProfile dp = (DataProfile) IADPField.get(mDataProfileManagerUT);
+
+ Mockito.clearInvocations(mMockedWwanDataServiceManager);
+ mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+ processAllMessages();
+ DataProfile dp2 = (DataProfile) IADPField.get(mDataProfileManagerUT);
+
+ assertThat(Objects.equals(dp, dp2)).isTrue();
+ verify(mMockedWwanDataServiceManager)
+ .setInitialAttachApn(any(DataProfile.class), eq(false), eq(null));
+
+ // Test: Modem did NOT clear IA, should not send IA to modem even if IA stays the same
+ mDataProfileManagerUT.obtainMessage(2 /* EVENT_APN_DATABASE_CHANGED */).sendToTarget();
+ processAllMessages();
+
+ IADPField = DataProfileManager.class.getDeclaredField("mInitialAttachDataProfile");
+ IADPField.setAccessible(true);
+ dp = (DataProfile) IADPField.get(mDataProfileManagerUT);
+ Mockito.clearInvocations(mMockedWwanDataServiceManager);
+
+ mDataProfileManagerUT.obtainMessage(2 /* EVENT_APN_DATABASE_CHANGED */).sendToTarget();
+ processAllMessages();
+ dp2 = (DataProfile) IADPField.get(mDataProfileManagerUT);
+
+ assertThat(Objects.equals(dp, dp2)).isTrue();
+ verify(mMockedWwanDataServiceManager, Mockito.never())
+ .setInitialAttachApn(any(DataProfile.class), eq(false), eq(null));
+ }
+
+ @Test
public void testSimRemoval() {
Mockito.clearInvocations(mDataProfileManagerCallback);
mSimInserted = false;
@@ -645,24 +923,26 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile).isNull();
+ // expect default EIMS when SIM absent
tnr = new TelephonyNetworkRequest(
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
.build(), mPhone);
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
+ // expect no default IMS when SIM absent
tnr = new TelephonyNetworkRequest(
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
.build(), mPhone);
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
- assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("ims");
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+ assertThat(dataProfile).isEqualTo(null);
}
@Test
@@ -692,7 +972,7 @@
new TelephonyNetworkRequest(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
.build(), mPhone),
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(IMS_APN);
}
@@ -704,7 +984,7 @@
TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
// This should get the merged data profile after deduping.
DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
- TelephonyManager.NETWORK_TYPE_LTE);
+ TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
}
@@ -725,6 +1005,8 @@
.setProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setCarrierEnabled(true)
+ .setMvnoMatchData("1")
+ .setMvnoType(1)
.build())
.build();
@@ -739,6 +1021,8 @@
.setProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setCarrierEnabled(true)
+ .setMvnoMatchData("2")
+ .setMvnoType(2)
.build())
.build();
@@ -835,7 +1119,7 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApn()).isEqualTo("sos");
assertThat(dataProfile.getTrafficDescriptor().getDataNetworkName()).isEqualTo("sos");
@@ -852,15 +1136,15 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), mPhone);
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
- dataProfile.setPreferred(true);
- mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(dataProfile));
+ DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
+ doReturn(dataProfile).when(internetNetwork).getDataProfile();
+ mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
processAllMessages();
// After internet connected, preferred APN should be set
- assertThat(mDataProfileManagerUT.isAnyPreferredDataProfileExisting()).isTrue();
assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
// APN reset
@@ -868,9 +1152,40 @@
mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
processAllMessages();
- // There should be no preferred APN after APN reset
- assertThat(mDataProfileManagerUT.isAnyPreferredDataProfileExisting()).isFalse();
+ // preferred APN should set to be the last data profile that succeeded for internet setup
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
+
+ // Test user selected a bad data profile, expects to adopt the last data profile that
+ // succeeded for internet setup after APN reset
+ // some bad profile that cannot be used for internet
+ mApnSettingContentProvider.setPreferredApn(MATCH_ALL_APN_SET_ID_IMS_APN);
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isFalse();
+
+ // APN reset, preferred APN should set to be the last data profile that succeeded for
+ // internet setup
+ mPreferredApnId = -1;
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
+
+ // Test removed data profile(user created after reset) shouldn't show up
+ mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
+ processAllMessages();
+ //APN reset and removed GENERAL_PURPOSE_APN from APN DB
+ mPreferredApnId = -1;
+ mApnSettingContentProvider.removeApnByApnId(1);
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ // There should be no preferred APN after APN reset because last working profile is removed
+ assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isFalse();
+
+ // restore mApnSettingContentProvider
+ mApnSettingContentProvider.restoreApnSettings();
}
@Test
@@ -887,7 +1202,7 @@
// The carrier configured data profile should be the preferred APN after APN reset
DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
@@ -900,7 +1215,7 @@
// The carrier configured data profile should be the preferred APN after APN reset
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
- tnr, TelephonyManager.NETWORK_TYPE_LTE);
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
}
@@ -940,11 +1255,277 @@
@Test
public void testNoDefaultIms() throws Exception {
List<DataProfile> dataProfiles = getAllDataProfiles();
+ int countIms = mApnSettingContentProvider.getCountApnSpecifyType("ims");
// Since the database already had IMS, there should not be default IMS created in the
// database.
assertThat(dataProfiles.stream()
.filter(dp -> dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_IMS))
- .collect(Collectors.toList())).hasSize(1);
+ .collect(Collectors.toList())).hasSize(countIms);
+ }
+
+ @Test
+ public void testDataProfileCompatibility() {
+ DataProfile enterpriseDataProfile = new DataProfile.Builder()
+ .setTrafficDescriptor(new TrafficDescriptor(null,
+ new TrafficDescriptor.OsAppId(TrafficDescriptor.OsAppId.ANDROID_OS_ID,
+ "ENTERPRISE", 1).getBytes()))
+ .build();
+
+ // Make sure the TD only profile is always compatible.
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(enterpriseDataProfile)).isTrue();
+
+ // Make sure the profile which is slightly modified is also compatible.
+ DataProfile dataProfile1 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(GENERAL_PURPOSE_APN)
+ .setId(1)
+ .setApnName(GENERAL_PURPOSE_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS
+ | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA | ApnSetting.TYPE_FOTA)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_NONE)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setPersistent(true)
+ .setMtuV4(1280)
+ .setMtuV6(1280)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(DEFAULT_APN_SET_ID)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile1)).isTrue();
+ }
+
+ @Test
+ public void testDataProfileCompatibility_FilteringWithPreferredApnSetIdAsDefault() {
+ mApnSettingContentProvider.setPreferredApn(GENERAL_PURPOSE_APN);
+ mSimInserted = true;
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ // Test DataProfile's apn_set_id is default so it match with current preferred apn_set_id.
+ DataProfile dataProfile1 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(TETHERING_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(TETHERING_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_DUN)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setProfileId(2)
+ .setPersistent(true)
+ .setMtuV4(0)
+ .setMtuV6(0)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(DEFAULT_APN_SET_ID)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile1)).isTrue();
+
+ // Test DataProfile's apn_set_id is specified(1) so it not match with current
+ // preferred apn_set_id.
+ DataProfile dataProfile2 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(APN_SET_ID_1_TETHERING_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(APN_SET_ID_1_TETHERING_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_DUN)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setPersistent(true)
+ .setMtuV4(1280)
+ .setMtuV6(1280)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(APN_SET_ID_1)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile2)).isFalse();
+
+ // Test DataProfile has Telephony.Carriers.MATCH_ALL_APN_SET_ID so it matches any preferred
+ // apn_set_id.
+ DataProfile dataProfile3 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(MATCH_ALL_APN_SET_ID_IMS_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(MATCH_ALL_APN_SET_ID_IMS_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setPersistent(true)
+ .setMtuV4(1280)
+ .setMtuV6(1280)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(MATCH_ALL_APN_SET_ID)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile3)).isTrue();
+ }
+
+ @Test
+ public void testDataProfileCompatibility_FilteringWithPreferredApnSetIdAs1() {
+ mApnSettingContentProvider.setPreferredApn(APN_SET_ID_1_APN);
+ mSimInserted = true;
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ // Test DataProfile's apn_set_id is same as preferred apn_set_id.
+ DataProfile dataProfile1 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(APN_SET_ID_1_TETHERING_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(APN_SET_ID_1_TETHERING_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_DUN)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setPersistent(true)
+ .setMtuV4(1280)
+ .setMtuV6(1280)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(APN_SET_ID_1)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile1)).isTrue();
+
+ // Test DataProfile's apn_set_id is default. so it not match with current preferred
+ // apn_set_id(1).
+ DataProfile dataProfile2 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(TETHERING_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(TETHERING_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_DUN)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setProfileId(2)
+ .setPersistent(true)
+ .setMtuV4(0)
+ .setMtuV6(0)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(DEFAULT_APN_SET_ID)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile2)).isFalse();
+
+ // Test DataProfile has Telephony.Carriers.MATCH_ALL_APN_SET_ID so it matches any preferred
+ // apn_set_id.
+ DataProfile dataProfile3 = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName(MATCH_ALL_APN_SET_ID_IMS_APN)
+ .setOperatorNumeric(PLMN)
+ .setApnName(MATCH_ALL_APN_SET_ID_IMS_APN)
+ .setProxyAddress("")
+ .setMmsProxyAddress("")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setUser("")
+ .setPassword("")
+ .setAuthType(ApnSetting.AUTH_TYPE_UNKNOWN)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setCarrierEnabled(true)
+ .setPersistent(true)
+ .setMtuV4(1280)
+ .setMtuV6(1280)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setMvnoMatchData("")
+ .setApnSetId(MATCH_ALL_APN_SET_ID)
+ .build())
+ .build();
+
+ assertThat(mDataProfileManagerUT.isDataProfileCompatible(dataProfile3)).isTrue();
+ }
+
+ @Test
+ public void testPermanentFailureWithNoPreferredDataProfile() {
+ // No preferred APN is set
+
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+ DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
+ TelephonyManager.NETWORK_TYPE_LTE, false);
+
+ // Mark the APN as permanent failed.
+ dp.getApnSetting().setPermanentFailed(true);
+
+ // Data profile manager should return a different data profile for setup as the previous
+ // data profile has been marked as permanent failed.
+ assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
+ TelephonyManager.NETWORK_TYPE_LTE, false)).isNotEqualTo(dp);
+ }
+
+ @Test
+ public void testPermanentFailureWithPreferredDataProfile() {
+ // Set the preferred APN
+ mApnSettingContentProvider.setPreferredApn(GENERAL_PURPOSE_APN);
+ mDataProfileManagerUT.obtainMessage(2 /* EVENT_APN_DATABASE_CHANGED */).sendToTarget();
+ processAllMessages();
+
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+ DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
+ TelephonyManager.NETWORK_TYPE_LTE, false);
+
+ // Mark the APN as permanent failed.
+ dp.getApnSetting().setPermanentFailed(true);
+
+ // Since preferred APN is already set, and that data profile was marked as permanent failed,
+ // so this should result in getting nothing.
+ assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
+ TelephonyManager.NETWORK_TYPE_LTE, false)).isNull();
}
}
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 bdb1929..62e434e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -16,18 +16,25 @@
package com.android.internal.telephony.data;
+import static com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry;
+import static com.android.internal.telephony.data.DataRetryManager.DataRetryEntry;
import static com.android.internal.telephony.data.DataRetryManager.DataSetupRetryEntry;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.AsyncResult;
@@ -43,6 +50,8 @@
import android.util.SparseArray;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
+import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryRule;
import com.android.internal.telephony.data.DataRetryManager.DataRetryManagerCallback;
import com.android.internal.telephony.data.DataRetryManager.DataSetupRetryRule;
@@ -54,9 +63,9 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -125,14 +134,16 @@
.setPreferred(false)
.build();
- private final List<DataProfile> mAllDataProfileList = List.of(mDataProfile1, mDataProfile2,
- mDataProfile3);
-
// Mocked classes
+ private AlarmManager mAlarmManager;
private DataRetryManagerCallback mDataRetryManagerCallbackMock;
private DataRetryManager mDataRetryManagerUT;
+ private DataConfigManagerCallback mDataConfigManagerCallback;
+
+ private DataNetworkControllerCallback mDataNetworkControllerCallback;
+
@Before
public void setUp() throws Exception {
logd("DataRetryManagerTest +Setup!");
@@ -142,6 +153,7 @@
((Runnable) invocation.getArguments()[0]).run();
return null;
}).when(mDataRetryManagerCallbackMock).invokeFromExecutor(any(Runnable.class));
+ mAlarmManager = Mockito.mock(AlarmManager.class);
SparseArray<DataServiceManager> mockedDataServiceManagers = new SparseArray<>();
mockedDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
mMockedWwanDataServiceManager);
@@ -150,28 +162,19 @@
mDataRetryManagerUT = new DataRetryManager(mPhone, mDataNetworkController,
mockedDataServiceManagers, Looper.myLooper(), mDataRetryManagerCallbackMock);
- doAnswer(invocation -> {
- String apnName = (String) invocation.getArguments()[0];
- TrafficDescriptor td = (TrafficDescriptor) invocation.getArguments()[1];
+ ArgumentCaptor<DataConfigManagerCallback> dataConfigManagerCallbackCaptor =
+ ArgumentCaptor.forClass(DataConfigManagerCallback.class);
+ verify(mDataConfigManager).registerCallback(dataConfigManagerCallbackCaptor.capture());
+ mDataConfigManagerCallback = dataConfigManagerCallbackCaptor.getValue();
- if (apnName == null && td == null) return null;
+ ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+ ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+ verify(mDataNetworkController).registerDataNetworkControllerCallback(
+ dataNetworkControllerCallbackCaptor.capture());
+ mDataNetworkControllerCallback = dataNetworkControllerCallbackCaptor.getValue();
- List<DataProfile> dataProfiles = mAllDataProfileList;
- if (apnName != null) {
- dataProfiles = dataProfiles.stream()
- .filter(dp -> dp.getApnSetting() != null
- && apnName.equals(dp.getApnSetting().getApnName()))
- .collect(Collectors.toList());
- }
- if (td != null) {
- dataProfiles = dataProfiles.stream()
- .filter(dp -> dp.getTrafficDescriptor() != null
- && td.equals(dp.getTrafficDescriptor()))
- .collect(Collectors.toList());
- }
- return dataProfiles.isEmpty() ? null : dataProfiles.get(0);
- }).when(mDataProfileManager).getDataProfile(anyString(), any());
-
+ replaceInstance(DataRetryManager.class, "mAlarmManager",
+ mDataRetryManagerUT, mAlarmManager);
logd("DataRetryManagerTest -Setup!");
}
@@ -199,7 +202,6 @@
assertThat(rule.getMaxRetries()).isEqualTo(0);
assertThat(rule.getFailCauses()).containsExactly(8, 27, 28, 29, 30, 32, 33, 35, 50,
51, 111, -5, -6, 65537, 65538, -3, 2253, 2254);
- assertThat(rule.getRetryIntervalsMillis()).isEmpty();
ruleString = "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500| 3000|"
+ " 5000| 10000 | 15000| 20000|40000|60000| 120000|240000 |"
@@ -339,15 +341,52 @@
}
@Test
- public void testDataSetupUnthrottling() {
+ public void testDataSetupUnthrottling() throws Exception {
testDataSetupRetryNetworkSuggestedNeverRetry();
Mockito.clearInvocations(mDataRetryManagerCallbackMock);
+ DataNetworkController.NetworkRequestList mockNrl = Mockito.mock(
+ DataNetworkController.NetworkRequestList.class);
+ Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
+ field.setAccessible(true);
+ List<DataRetryEntry> mDataRetryEntries =
+ (List<DataRetryEntry>) field.get(mDataRetryManagerUT);
+ // schedule 2 setup retries
+ DataSetupRetryEntry scheduledRetry1 = new DataSetupRetryEntry.Builder<>()
+ .setDataProfile(mDataProfile3)
+ .setNetworkRequestList(mockNrl)
+ .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setSetupRetryType(1)
+ .build();
+ DataSetupRetryEntry scheduledRetry2 = new DataSetupRetryEntry.Builder<>()
+ .setNetworkRequestList(mockNrl)
+ .setDataProfile(mDataProfile3)
+ .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setSetupRetryType(1)
+ .build();
+ mDataRetryEntries.addAll(List.of(scheduledRetry1, scheduledRetry2));
+ // Suppose we set the data profile as permanently failed.
+ mDataProfile3.getApnSetting().setPermanentFailed(true);
+
+ DataProfile dataProfile3ReconstructedFromModem = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setEntryName("some_fake_ims")
+ .setApnName("fake_ims")
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .build())
+ .build();
+
+ // unthrottle the data profile, expect previous retries of the same transport is cancelled
mDataRetryManagerUT.obtainMessage(6/*EVENT_DATA_PROFILE_UNTHROTTLED*/,
- new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, mDataProfile3, null))
+ new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ dataProfile3ReconstructedFromModem, null))
.sendToTarget();
processAllMessages();
+ // check unthrottle
+ assertThat(mDataProfile3.getApnSetting().getPermanentFailed()).isFalse();
ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
ArgumentCaptor.forClass(List.class);
verify(mDataRetryManagerCallbackMock).onThrottleStatusChanged(
@@ -369,19 +408,117 @@
assertThat(entry.dataProfile).isEqualTo(mDataProfile3);
assertThat(entry.retryDelayMillis).isEqualTo(0);
assertThat(entry.transport).isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ // check mDataProfile3-WWAN retry is cancelled, but not the WLAN
+ assertThat(scheduledRetry1.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
+ assertThat(scheduledRetry2.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+ }
+
+ @Test
+ public void testEnterpriseUnthrottling() throws Exception {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
+ .build();
+ TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+ DataNetworkController.NetworkRequestList
+ networkRequestList = new DataNetworkController.NetworkRequestList(tnr);
+
+ DataProfile enterpriseDataProfile = new DataProfile.Builder()
+ .setTrafficDescriptor(new TrafficDescriptor(null,
+ new TrafficDescriptor.OsAppId(TrafficDescriptor.OsAppId.ANDROID_OS_ID,
+ "ENTERPRISE", 1).getBytes()))
+ .build();
+
+ mDataRetryManagerUT.evaluateDataSetupRetry(enterpriseDataProfile,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN, networkRequestList, 123,
+ Long.MAX_VALUE);
+ processAllFutureMessages();
+
+ mDataRetryManagerUT.obtainMessage(6/*EVENT_DATA_PROFILE_UNTHROTTLED*/,
+ new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, enterpriseDataProfile,
+ null)).sendToTarget();
+ processAllMessages();
+
+ ArgumentCaptor<DataSetupRetryEntry> dataSetupRetryEntryCaptor =
+ ArgumentCaptor.forClass(DataSetupRetryEntry.class);
+ verify(mDataRetryManagerCallbackMock)
+ .onDataNetworkSetupRetry(dataSetupRetryEntryCaptor.capture());
+ DataSetupRetryEntry entry = dataSetupRetryEntryCaptor.getValue();
+ assertThat(entry.dataProfile).isEqualTo(enterpriseDataProfile);
+ assertThat(entry.retryDelayMillis).isEqualTo(0);
+ assertThat(entry.transport).isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ @Test
+ public void testCancellingRetries() throws Exception {
+ DataNetworkController.NetworkRequestList mockNrl = Mockito.mock(
+ DataNetworkController.NetworkRequestList.class);
+
+ // Test: setup retry
+ DataRetryEntry retry = new DataSetupRetryEntry.Builder<>()
+ .setSetupRetryType(1)
+ .setNetworkRequestList(mockNrl)
+ .setTransport(1)
+ .build();
+ retry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
+
+ mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, retry).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, never()).onDataNetworkSetupRetry(any());
+
+ mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, null).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, never()).onDataNetworkSetupRetry(any());
+
+ retry.setState(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+ mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, retry).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, times(1)).onDataNetworkSetupRetry(any());
+
+ // Test: handover retry
+ retry = new DataHandoverRetryEntry.Builder<>().build();
+ retry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
+ mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, retry).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, never()).onDataNetworkHandoverRetry(any());
+
+ mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, null).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, never()).onDataNetworkHandoverRetry(any());
+
+ retry.setState(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+ mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, retry).sendToTarget();
+ processAllMessages();
+ verify(mDataRetryManagerCallbackMock, times(1))
+ .onDataNetworkHandoverRetry(any());
+
+ // Test: cancelPendingHandoverRetry
+ DataNetwork mockDn = Mockito.mock(DataNetwork.class);
+ 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);
+ processAllMessages();
+
+ assertThat(mDataRetryManagerUT.isAnyHandoverRetryScheduled(mockDn)).isFalse();
+ assertThat(retry.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
}
@Test
public void testDataSetupRetryPermanentFailure() {
DataSetupRetryRule retryRule = new DataSetupRetryRule(
- "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
- + "2254, maximum_retries=0");
+ "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");
doReturn(Collections.singletonList(retryRule)).when(mDataConfigManager)
.getDataSetupRetryRules();
- mDataRetryManagerUT.obtainMessage(1/*EVENT_DATA_CONFIG_UPDATED*/).sendToTarget();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
-
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
@@ -393,8 +530,17 @@
DataCallResponse.RETRY_DURATION_UNDEFINED);
processAllFutureMessages();
- verify(mDataRetryManagerCallbackMock, never())
+ // Permanent failure is only applicable to the failed APN. Retry should still happen if
+ // there are other APNs.
+ verify(mDataRetryManagerCallbackMock)
.onDataNetworkSetupRetry(any(DataSetupRetryEntry.class));
+ assertThat(mDataProfile1.getApnSetting().getPermanentFailed()).isTrue();
+
+ // When condition changes, data will still be retried. As soon as data network is connected,
+ // we should clear the previous permanent failure.
+ mDataNetworkControllerCallback.onDataNetworkConnected(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN, mDataProfile1);
+ assertThat(mDataProfile1.getApnSetting().getPermanentFailed()).isFalse();
}
@Test
@@ -403,7 +549,7 @@
"capabilities=internet, retry_interval=2000, maximum_retries=2");
doReturn(Collections.singletonList(retryRule)).when(mDataConfigManager)
.getDataSetupRetryRules();
- mDataRetryManagerUT.obtainMessage(1/*EVENT_DATA_CONFIG_UPDATED*/).sendToTarget();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
NetworkRequest request = new NetworkRequest.Builder()
@@ -463,6 +609,16 @@
// Verify there is no retry.
verify(mDataRetryManagerCallbackMock, never())
.onDataNetworkSetupRetry(any(DataSetupRetryEntry.class));
+
+ // 4th failed on a different transport and retry.
+ mDataRetryManagerUT.evaluateDataSetupRetry(mDataProfile1,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN, networkRequestList, 123,
+ DataCallResponse.RETRY_DURATION_UNDEFINED);
+ processAllFutureMessages();
+
+ // Verify retry occurs
+ verify(mDataRetryManagerCallbackMock)
+ .onDataNetworkSetupRetry(any(DataSetupRetryEntry.class));
}
@Test
@@ -472,7 +628,7 @@
DataSetupRetryRule retryRule2 = new DataSetupRetryRule(
"capabilities=ims|mms|fota, retry_interval=3000, maximum_retries=1");
doReturn(List.of(retryRule1, retryRule2)).when(mDataConfigManager).getDataSetupRetryRules();
- mDataRetryManagerUT.obtainMessage(1/*EVENT_DATA_CONFIG_UPDATED*/).sendToTarget();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
NetworkRequest request = new NetworkRequest.Builder()
@@ -534,7 +690,7 @@
doReturn(List.of(retryRule1, retryRule2, retryRule3)).when(mDataConfigManager)
.getDataSetupRetryRules();
- mDataRetryManagerUT.obtainMessage(1/*EVET_DATA_CONFIG_UPDATED*/).sendToTarget();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
NetworkRequest request = new NetworkRequest.Builder()
@@ -565,7 +721,7 @@
assertThat(entry.networkRequestList).isEqualTo(networkRequestList);
assertThat(entry.appliedDataRetryRule).isEqualTo(retryRule3);
- entry.setState(DataRetryManager.DataRetryEntry.RETRY_STATE_FAILED);
+ entry.setState(DataRetryEntry.RETRY_STATE_FAILED);
}
// The last fail should not trigger any retry.
@@ -588,7 +744,6 @@
assertThat(rule.getMaxRetries()).isEqualTo(0);
assertThat(rule.getFailCauses()).containsExactly(8, 27, 28, 29, 30, 32, 33, 35, 50,
51, 111, -5, -6, 65537, 65538, -3, 2253, 2254);
- assertThat(rule.getRetryIntervalsMillis()).isEmpty();
ruleString = "retry_interval=1000|2000|4000|8000|16000, maximum_retries=5";
rule = new DataHandoverRetryRule(ruleString);
@@ -617,6 +772,43 @@
}
@Test
+ public void testDataRetryLongTimer() {
+ // Rule requires a long timer
+ DataSetupRetryRule retryRule = new DataSetupRetryRule(
+ "capabilities=internet, retry_interval=120000, maximum_retries=2");
+ doReturn(Collections.singletonList(retryRule)).when(mDataConfigManager)
+ .getDataSetupRetryRules();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
+ processAllMessages();
+
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+ DataNetworkController.NetworkRequestList
+ networkRequestList = new DataNetworkController.NetworkRequestList(tnr);
+ mDataRetryManagerUT.evaluateDataSetupRetry(mDataProfile1,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN, networkRequestList, 2253,
+ DataCallResponse.RETRY_DURATION_UNDEFINED);
+ processAllFutureMessages();
+
+ // Verify scheduled via Alarm Manager
+ ArgumentCaptor<PendingIntent> pendingIntentArgumentCaptor =
+ ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAlarmManager).setAndAllowWhileIdle(anyInt(), anyLong(),
+ pendingIntentArgumentCaptor.capture());
+
+ // Verify starts retry attempt after receiving intent
+ PendingIntent pendingIntent = pendingIntentArgumentCaptor.getValue();
+ Intent intent = pendingIntent.getIntent();
+ mContext.sendBroadcast(intent);
+ processAllFutureMessages();
+
+ verify(mDataRetryManagerCallbackMock)
+ .onDataNetworkSetupRetry(any(DataSetupRetryEntry.class));
+ }
+
+ @Test
public void testDataHandoverRetryInvalidRulesFromString() {
assertThrows(IllegalArgumentException.class,
() -> new DataHandoverRetryRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ=="));
@@ -628,7 +820,7 @@
"capabilities=internet, retry_interval=2000, maximum_retries=2");
doReturn(Collections.singletonList(retryRule)).when(mDataConfigManager)
.getDataSetupRetryRules();
- mDataRetryManagerUT.obtainMessage(1/*EVENT_DATA_CONFIG_UPDATED*/).sendToTarget();
+ mDataConfigManagerCallback.onCarrierConfigChanged();
processAllMessages();
NetworkRequest request = new NetworkRequest.Builder()
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java
new file mode 100644
index 0000000..88f642b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
+import android.telephony.data.DataServiceCallback;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DataServiceManagerTest extends TelephonyTest {
+ private final DataProfile mGeneralPurposeDataProfile = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setId(2163)
+ .setOperatorNumeric("12345")
+ .setEntryName("internet_supl_mms_apn")
+ .setApnName("internet_supl_mms_apn")
+ .setUser("user")
+ .setPassword("passwd")
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL
+ | ApnSetting.TYPE_MMS)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .setCarrierEnabled(true)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT))
+ .setLingeringNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT
+ | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setProfileId(1234)
+ .setMaxConns(321)
+ .setWaitTime(456)
+ .setMaxConnsTime(789)
+ .build())
+ .setPreferred(false)
+ .build();
+
+ private DataServiceManager mDataServiceManagerUT;
+ private CellularDataService mCellularDataService;
+
+ private Handler mHandler;
+ private Handler mDataServiceHandler;
+ private PersistableBundle mBundle;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDataServiceManagerUT = null;
+ mBundle = null;
+ super.tearDown();
+ }
+
+ private void createDataServiceManager(boolean validDataServiceExisting) throws Exception {
+ if (validDataServiceExisting) {
+ mContextFixture.putResource(R.string.config_wwan_data_service_package,
+ "com.android.phone");
+ }
+
+ mHandler = mock(Handler.class);
+
+ mCellularDataService = new CellularDataService();
+
+ Field field = DataService.class.getDeclaredField("mHandler");
+ field.setAccessible(true);
+ mDataServiceHandler = (Handler) field.get(mCellularDataService);
+
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = "com.android.phone";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
+ IntentFilter filter = new IntentFilter();
+ mContextFixture.addService(
+ DataService.SERVICE_INTERFACE,
+ null,
+ "com.android.phone",
+ mCellularDataService.mBinder,
+ serviceInfo,
+ filter);
+
+ mDataServiceManagerUT = new DataServiceManager(mPhone, Looper.myLooper(),
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ private void waitAndVerifyResult(Message message, int resultCode) {
+ waitForLastHandlerAction(mDataServiceHandler);
+ processAllMessages();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(message.getTarget()).sendMessageAtTime(messageCaptor.capture(), anyLong());
+ assertThat(messageCaptor.getValue().arg1).isEqualTo(resultCode);
+ }
+
+ @Test
+ public void testSetupDataCall() throws Exception {
+ createDataServiceManager(true);
+ Message message = Message.obtain(mHandler, 1234);
+ mDataServiceManagerUT.setupDataCall(AccessNetworkType.NGRAN, mGeneralPurposeDataProfile,
+ false, true, DataService.REQUEST_REASON_NORMAL, null, 1, null, null, false,
+ message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).setupDataCall(anyInt(), any(DataProfile.class),
+ anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testSetupDataCallServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = Message.obtain(mHandler, 1234);
+ mDataServiceManagerUT.setupDataCall(AccessNetworkType.NGRAN, mGeneralPurposeDataProfile,
+ false, true, DataService.REQUEST_REASON_NORMAL, null, 1, null, null, false,
+ message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).setupDataCall(anyInt(), any(DataProfile.class),
+ anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testDeactivateDataCall() throws Exception {
+ createDataServiceManager(true);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.deactivateDataCall(123, DataService.REQUEST_REASON_NORMAL, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).deactivateDataCall(anyInt(), anyInt(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testDeactivateDataCallServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.deactivateDataCall(123, DataService.REQUEST_REASON_NORMAL, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).deactivateDataCall(anyInt(), anyInt(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testSetInitialAttachApn() throws Exception {
+ createDataServiceManager(true);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.setInitialAttachApn(mGeneralPurposeDataProfile, false, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).setInitialAttachApn(any(DataProfile.class),
+ anyBoolean(), any(Message.class));
+ }
+
+ @Test
+ public void testSetInitialAttachApnServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.setInitialAttachApn(mGeneralPurposeDataProfile, false, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).setInitialAttachApn(any(DataProfile.class),
+ anyBoolean(), any(Message.class));
+ }
+
+ @Test
+ public void testSetDataProfile() throws Exception {
+ createDataServiceManager(true);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.setDataProfile(List.of(mGeneralPurposeDataProfile), false, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).setDataProfile(any(DataProfile[].class), anyBoolean(),
+ any(Message.class));
+ }
+
+ @Test
+ public void testSetDataProfileServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.setDataProfile(List.of(mGeneralPurposeDataProfile), false, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).setDataProfile(any(DataProfile[].class),
+ anyBoolean(), any(Message.class));
+ }
+
+ @Test
+ public void testStartHandover() throws Exception {
+ createDataServiceManager(true);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.startHandover(123, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).startHandover(any(Message.class), anyInt());
+ }
+
+ @Test
+ public void testStartHandoverServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.startHandover(123, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).startHandover(any(Message.class), anyInt());
+ }
+
+ @Test
+ public void testCancelHandover() throws Exception {
+ createDataServiceManager(true);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.cancelHandover(123, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
+ verify(mSimulatedCommandsVerifier).cancelHandover(any(Message.class), anyInt());
+ }
+
+ @Test
+ public void testCancelHandoverServiceNotBound() throws Exception {
+ createDataServiceManager(false);
+ Message message = mHandler.obtainMessage(1234);
+ mDataServiceManagerUT.cancelHandover(123, message);
+ waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ verify(mSimulatedCommandsVerifier, never()).cancelHandover(any(Message.class), anyInt());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
new file mode 100644
index 0000000..f7525c1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DataSettingsManagerTest extends TelephonyTest {
+ private static final String DATA_ROAMING_IS_USER_SETTING = "data_roaming_is_user_setting_key0";
+
+ // Mocked
+ DataSettingsManagerCallback mMockedDataSettingsManagerCallback;
+
+ DataSettingsManager mDataSettingsManagerUT;
+ PersistableBundle mBundle;
+
+ @Before
+ public void setUp() throws Exception {
+ logd("DataSettingsManagerTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+ mMockedDataSettingsManagerCallback = Mockito.mock(DataSettingsManagerCallback.class);
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ doReturn(true).when(mDataConfigManager).isConfigCarrierSpecific();
+
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+ mDataSettingsManagerUT = new DataSettingsManager(mPhone, mDataNetworkController,
+ Looper.myLooper(), mMockedDataSettingsManagerCallback);
+ logd("DataSettingsManagerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd("tearDown");
+ super.tearDown();
+ }
+
+ @Test
+ public void testMobileDataPolicyParsing() {
+ //Valid new data policy
+ Set<Integer> policies = mDataSettingsManagerUT.getMobileDataPolicyEnabled("1, 2");
+ assertThat(policies.size()).isEqualTo(2);
+ Set<Integer> policies2 = mDataSettingsManagerUT.getMobileDataPolicyEnabled(",2");
+ assertThat(policies2.size()).isEqualTo(1);
+ Set<Integer> policies3 = mDataSettingsManagerUT.getMobileDataPolicyEnabled("");
+ assertThat(policies3.size()).isEqualTo(0);
+
+ // Invalid
+ Set<Integer> invalid = mDataSettingsManagerUT.getMobileDataPolicyEnabled(
+ "nonExistent, 1, 2");
+ assertThat(invalid.size()).isEqualTo(2);
+
+ Set<Integer> invalid2 = mDataSettingsManagerUT.getMobileDataPolicyEnabled(
+ "nonExistent ,,");
+ assertThat(invalid2.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetPolicies() {
+ mDataSettingsManagerUT.setMobileDataPolicy(1, true);
+ mDataSettingsManagerUT.setMobileDataPolicy(2, true);
+ processAllMessages();
+
+ ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSubscriptionManagerService, times(2))
+ .setEnabledMobileDataPolicies(anyInt(), stringArgumentCaptor.capture());
+ assertEquals("1,2", stringArgumentCaptor.getValue());
+ }
+
+ @Test
+ public void testDefaultDataRoamingEnabled() {
+ doReturn(true).when(mDataConfigManager).isDataRoamingEnabledByDefault();
+ mDataSettingsManagerUT.setDefaultDataRoamingEnabled();
+ assertTrue(mDataSettingsManagerUT.isDataRoamingEnabled());
+
+ mDataSettingsManagerUT.setDataRoamingEnabled(false);
+ processAllMessages();
+ assertFalse(mDataSettingsManagerUT.isDataRoamingEnabled());
+
+ mDataSettingsManagerUT.setDefaultDataRoamingEnabled();
+ assertFalse(mDataSettingsManagerUT.isDataRoamingEnabled());
+ }
+
+ @Test
+ public void testDefaultDataRoamingEnabledFromUpgrade() {
+ doReturn(true).when(mDataConfigManager).isDataRoamingEnabledByDefault();
+ mContext.getSharedPreferences("", 0).edit()
+ .putBoolean(DATA_ROAMING_IS_USER_SETTING, true).commit();
+ mDataSettingsManagerUT.setDefaultDataRoamingEnabled();
+ assertFalse(mDataSettingsManagerUT.isDataRoamingEnabled());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index de0998d..35d3b92 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -29,7 +28,6 @@
import android.net.NetworkAgent;
import android.telephony.Annotation.ValidationStatus;
import android.telephony.CarrierConfigManager;
-import android.telephony.data.DataProfile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -71,14 +69,10 @@
.getDataStallRecoveryShouldSkipArray();
doReturn(true).when(mDataNetworkController).isInternetDataAllowed();
- doAnswer(
- invocation -> {
- ((Runnable) invocation.getArguments()[0]).run();
- return null;
- })
- .when(mDataStallRecoveryManagerCallback)
- .invokeFromExecutor(any(Runnable.class));
- doReturn("").when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArguments()[0]).run();
+ return null;
+ }).when(mDataStallRecoveryManagerCallback).invokeFromExecutor(any(Runnable.class));
mDataStallRecoveryManager =
new DataStallRecoveryManager(
@@ -117,7 +111,7 @@
dataNetworkControllerCallbackCaptor.getValue();
if (isConnected) {
- List<DataProfile> dataprofile = new ArrayList<DataProfile>();
+ List<DataNetwork> dataprofile = new ArrayList<>();
dataNetworkControllerCallback.onInternetDataNetworkConnected(dataprofile);
} else {
dataNetworkControllerCallback.onInternetDataNetworkDisconnected();
@@ -350,4 +344,23 @@
assertThat(mDataStallRecoveryManager.getRecoveryAction()).isEqualTo(0);
}
}
+
+ @Test
+ public void testStartTimeNotZero() throws Exception {
+ sendOnInternetDataNetworkCallback(false);
+ doReturn(mSignalStrength).when(mPhone).getSignalStrength();
+ doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
+
+ logd("Sending validation failed callback");
+ sendValidationStatusCallback(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ processAllFutureMessages();
+
+ for (int i = 0; i < 2; i++) {
+ sendValidationStatusCallback(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ logd("Sending validation failed callback");
+ processAllMessages();
+ moveTimeForward(101);
+ }
+ assertThat(mDataStallRecoveryManager.mDataStallStartMs != 0).isTrue();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataUtilsTest.java
index 0be85c5..ea8a7d7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataUtilsTest.java
@@ -53,26 +53,40 @@
NetworkCapabilities.NET_CAPABILITY_INTERNET,
NetworkCapabilities.NET_CAPABILITY_MMS,
NetworkCapabilities.NET_CAPABILITY_MMS,
- NetworkCapabilities.NET_CAPABILITY_EIMS
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
};
int requestId = 0;
+ int enterpriseId = 1;
TelephonyNetworkRequest networkRequest;
for (int netCap : netCaps) {
- networkRequest = new TelephonyNetworkRequest(new NetworkRequest(
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(netCap).build(), -1, requestId++,
- NetworkRequest.Type.REQUEST), mPhone);
+ if (netCap == NetworkCapabilities.NET_CAPABILITY_ENTERPRISE) {
+ networkRequest = new TelephonyNetworkRequest(new NetworkRequest(
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
+ .addEnterpriseId(enterpriseId).build(), -1, requestId++,
+ NetworkRequest.Type.REQUEST), mPhone);
+ if (enterpriseId == 1) enterpriseId++;
+ } else {
+ networkRequest = new TelephonyNetworkRequest(new NetworkRequest(
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(netCap).build(), -1, requestId++,
+ NetworkRequest.Type.REQUEST), mPhone);
+ }
requestList.add(networkRequest);
}
- assertThat(requestList).hasSize(5);
+ assertThat(requestList).hasSize(8);
List<NetworkRequestList> requestListList =
DataUtils.getGroupedNetworkRequestList(requestList);
- assertThat(requestListList).hasSize(3);
+ assertThat(requestListList).hasSize(5);
requestList = requestListList.get(0);
assertThat(requestList).hasSize(1);
assertThat(requestList.get(0).hasCapability(
@@ -91,6 +105,21 @@
NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
assertThat(requestList.get(1).hasCapability(
NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
+
+ requestList = requestListList.get(3);
+ assertThat(requestList).hasSize(1);
+ assertThat(requestList.get(0).hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
+ assertThat(requestList.get(0).getCapabilityDifferentiator() == 1).isTrue();
+
+ requestList = requestListList.get(4);
+ assertThat(requestList).hasSize(2);
+ assertThat(requestList.get(0).hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
+ assertThat(requestList.get(0).getCapabilityDifferentiator() == 2).isTrue();
+ assertThat(requestList.get(1).hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
+ assertThat(requestList.get(1).getCapabilityDifferentiator() == 2).isTrue();
}
@Test
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 6cd7542..d41be7d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
@@ -32,7 +32,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
@@ -43,9 +42,7 @@
import static org.mockito.Mockito.when;
import android.net.NetworkCapabilities;
-import android.os.AsyncResult;
import android.os.Handler;
-import android.os.Message;
import android.telephony.CellIdentityLte;
import android.telephony.ModemActivityInfo;
import android.telephony.NetworkRegistrationInfo;
@@ -63,7 +60,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@@ -80,7 +76,6 @@
new ModemActivityInfo(100L, 0, 0, TX_TIME_2_MS, RX_TIME_2_MS);
private static final ModemActivityInfo MAI_RX_TIME_HIGH =
new ModemActivityInfo(100L, 0, 0, TX_TIME_1_MS, RX_TIME_2_MS);
- private static final int EVENT_BANDWIDTH_ESTIMATOR_UPDATE = 1;
private NetworkCapabilities mNetworkCapabilities;
private CellIdentityLte mCellIdentity;
private long mElapsedTimeMs = 0;
@@ -89,14 +84,20 @@
private NetworkRegistrationInfo mNri;
// Mocked classes
- TelephonyFacade mTelephonyFacade;
+ private TelephonyFacade mTelephonyFacade;
private Handler mTestHandler;
+ private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mTelephonyFacade = mock(TelephonyFacade.class);
mTestHandler = mock(Handler.class);
+ mLinkBandwidthEstimatorCallback = Mockito.mock(LinkBandwidthEstimatorCallback.class);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArguments()[0]).run();
+ return null;
+ }).when(mLinkBandwidthEstimatorCallback).invokeFromExecutor(any(Runnable.class));
mNetworkCapabilities = new NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.build();
@@ -116,10 +117,10 @@
when(mSignalStrength.getDbm()).thenReturn(-100);
when(mSignalStrength.getLevel()).thenReturn(1);
mLBE = new LinkBandwidthEstimator(mPhone, mTelephonyFacade);
- mLBE.registerForBandwidthChanged(mTestHandler, EVENT_BANDWIDTH_ESTIMATOR_UPDATE, null);
mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, false).sendToTarget();
mLBE.obtainMessage(MSG_ACTIVE_PHONE_CHANGED, 1).sendToTarget();
+ mLBE.registerCallback(mLinkBandwidthEstimatorCallback);
processAllMessages();
}
@@ -184,12 +185,8 @@
}
private void verifyUpdateBandwidth(int txKbps, int rxKbps) {
- ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mTestHandler, atLeast(1))
- .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
- assertEquals(EVENT_BANDWIDTH_ESTIMATOR_UPDATE, messageArgumentCaptor.getValue().what);
- assertEquals(new Pair<Integer, Integer>(txKbps, rxKbps),
- ((AsyncResult) messageArgumentCaptor.getValue().obj).result);
+ verify(mLinkBandwidthEstimatorCallback, atLeast(1))
+ .onBandwidthChanged(eq(txKbps), eq(rxKbps));
}
@Test
@@ -468,6 +465,32 @@
}
@Test
+ public void testNoOverflowAfterLargeTimeGap() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ if (i == BW_STATS_COUNT_THRESHOLD) {
+ addElapsedTime(26 * 24 * 3_600_000L);
+ moveTimeForward(26 * 24 * 3_600_000L);
+ } else {
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ }
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verify(mTelephonyManager, times(BW_STATS_COUNT_THRESHOLD + 2))
+ .requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, 19_531);
+ }
+
+ @Test
public void testAbnormalTrafficCountTriggerLessBwUpdate() throws Exception {
mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
processAllMessages();
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 d50cb72..bc691f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -28,10 +28,6 @@
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_IMS_RADIO_TECH_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_MULTI_SIM_CONFIG_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -41,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -62,10 +59,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneCapability;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -75,11 +74,13 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.GsmCdmaCall;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISub;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Before;
@@ -87,6 +88,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.lang.reflect.Field;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
@@ -96,15 +99,19 @@
private static final int ACTIVE_PHONE_SWITCH = 1;
private static final int EVENT_RADIO_ON = 108;
private static final int EVENT_MODEM_COMMAND_DONE = 112;
+ private static final int EVENT_EVALUATE_AUTO_SWITCH = 111;
+ private static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117;
+ private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 114;
// Mocked classes
CompletableFuture<Boolean> mFuturePhone;
private CommandsInterface mCommandsInterface0;
private CommandsInterface mCommandsInterface1;
- private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+ private ServiceStateTracker mSST2;
private Phone mImsPhone;
- // TODO: Add logic for DataSettingsManager
- private DataEnabledSettings mDataEnabledSettings2;
+ private DataSettingsManager mDataSettingsManager2;
private Handler mActivePhoneSwitchHandler;
private GsmCdmaCall mActiveCall;
private GsmCdmaCall mHoldingCall;
@@ -115,12 +122,15 @@
private ISetOpportunisticDataCallback mSetOpptDataCallback2;
PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
private SubscriptionInfo mSubscriptionInfo;
+ private ISub mMockedIsub;
- private PhoneSwitcher mPhoneSwitcher;
+ private PhoneSwitcher mPhoneSwitcherUT;
private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
private ConnectivityManager mConnectivityManager;
// The messenger of PhoneSwitcher used to receive network requests.
private Messenger mNetworkProviderMessenger = null;
+ private Map<Integer, DataSettingsManager.DataSettingsManagerCallback>
+ mDataSettingsManagerCallbacks;
private int mDefaultDataSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private int[][] mSlotIndexToSubId;
private boolean[] mDataAllowed;
@@ -134,9 +144,9 @@
mFuturePhone = mock(CompletableFuture.class);
mCommandsInterface0 = mock(CommandsInterface.class);
mCommandsInterface1 = mock(CommandsInterface.class);
- mPhone2 = mock(Phone.class); // mPhone as phone 1 is already defined in TelephonyTest.
+ mSST2 = mock(ServiceStateTracker.class);
mImsPhone = mock(Phone.class);
- mDataEnabledSettings2 = mock(DataEnabledSettings.class);
+ mDataSettingsManager2 = mock(DataSettingsManager.class);
mActivePhoneSwitchHandler = mock(Handler.class);
mActiveCall = mock(GsmCdmaCall.class);
mHoldingCall = mock(GsmCdmaCall.class);
@@ -147,6 +157,7 @@
mSetOpptDataCallback2 = mock(ISetOpportunisticDataCallback.class);
mMockImsRegTechProvider = mock(PhoneSwitcher.ImsRegTechProvider.class);
mSubscriptionInfo = mock(SubscriptionInfo.class);
+ mMockedIsub = mock(ISub.class);
PhoneCapability phoneCapability = new PhoneCapability(1, 1, null, false, new int[0]);
doReturn(phoneCapability).when(mPhoneConfigurationManager).getCurrentPhoneCapability();
@@ -163,11 +174,16 @@
replaceInstance(Phone.class, "mCi", mPhone, mCommandsInterface0);
replaceInstance(Phone.class, "mCi", mPhone2, mCommandsInterface1);
+
+ doReturn(1).when(mMockedIsub).getDefaultDataSubId();
+ doReturn(mMockedIsub).when(mIBinder).queryLocalInterface(anyString());
+ doReturn(mPhone).when(mPhone).getImsPhone();
+ mServiceManagerMockedServices.put("isub", mIBinder);
}
@After
public void tearDown() throws Exception {
- mPhoneSwitcher = null;
+ mPhoneSwitcherUT = null;
mSubChangedListener = null;
mConnectivityManager = null;
mNetworkProviderMessenger = null;
@@ -183,19 +199,19 @@
initialize();
// verify nothing has been done while there are no inputs
- assertFalse("data allowed initially", mDataAllowed[0]);
+ assertTrue("data should be always allowed for emergency", mDataAllowed[0]);
assertFalse("data allowed initially", mDataAllowed[1]);
NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
- assertFalse("phone active after request", mPhoneSwitcher
+ assertFalse("phone active after request", mPhoneSwitcherUT
.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetNetworkRequest, mPhone), 0));
// not registered yet - shouldn't inc
verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
- mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+ mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
@@ -210,7 +226,7 @@
processAllMessages();
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -231,7 +247,7 @@
// 1 lose default via default sub change
setDefaultDataSubId(1);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -241,7 +257,7 @@
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -251,7 +267,7 @@
// 2 gain default via default sub change
setDefaultDataSubId(0);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -262,7 +278,7 @@
setSlotIndexToSubId(0, 2);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
@@ -275,7 +291,7 @@
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -285,17 +301,17 @@
// 5 lose default network request
releaseNetworkRequest(internetNetworkRequest);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
- assertFalse("data allowed", mDataAllowed[0]);
+ assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 6 gain subscription-specific request
NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -307,7 +323,7 @@
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -319,7 +335,7 @@
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -329,11 +345,11 @@
// 9 lose subscription-specific request
releaseNetworkRequest(specificInternetRequest);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
- assertFalse("data allowed", mDataAllowed[0]);
+ assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 10 don't switch phones when in emergency mode
@@ -357,6 +373,290 @@
// if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
}
+ /** Test Data Auto Switch **/
+
+ /**
+ * Trigger conditions
+ * 1. service state changes
+ * 2. data setting changes
+ * - user toggle data
+ * - user toggle auto switch feature
+ * 3. default network changes
+ * - current network lost
+ * - network become active on non-cellular network
+ * 4. subscription changes
+ * - slot/sub mapping changes
+ */
+ @Test
+ @SmallTest
+ public void testAutoDataSwitchCancelScenario_onPrimary() throws Exception {
+ initialize();
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ // 0. When all conditions met
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+
+ // Verify attempting to switch
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ doReturn(true).when(mCellularNetworkValidator).isValidating();
+
+ // 1. Service state becomes not ideal - primary is available again
+ clearInvocations(mCellularNetworkValidator);
+ serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).stopValidation();
+
+ // 2.1 User data disabled on primary SIM
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).stopValidation();
+
+ // 2.2 Auto switch feature is disabled
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(false, 123 , "");
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).stopValidation();
+
+ // 3.1 No default network
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
+ .when(mConnectivityManager).getNetworkCapabilities(any());
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).stopValidation();
+ }
+
+ public void testAutoSwitchToSecondarySucceed() {
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
+ processAllMessages();
+ // Confirm auto switched to secondary sub Id 1/phone 0
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+ }
+
+ @Test
+ @SmallTest
+ public void testAutoDataSwitch_switchBackToPrimary() throws Exception {
+ initialize();
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ testAutoSwitchToSecondarySucceed();
+ // 1.1 service state changes - primary becomes available, need validation pass to switch
+ serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+ verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+ processAllMessages();
+
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId()); // since validation failed
+
+ serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
+ processAllMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since validation passed
+
+ testAutoSwitchToSecondarySucceed();
+ // 1.2 service state changes - secondary becomes unavailable, NO need validation
+ // The later validation requirement overrides the previous
+ serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
+ serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
+ processAllFutureMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+ processAllMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+
+ testAutoSwitchToSecondarySucceed();
+ // 2.1 User data disabled on primary SIM
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+ processAllFutureMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+ verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+
+ testAutoSwitchToSecondarySucceed();
+ // 2.2 Auto switch feature is disabled
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+ processAllFutureMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+ verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+
+ testAutoSwitchToSecondarySucceed();
+ // 3.1 Default network is active on non-cellular transport
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
+ .when(mConnectivityManager).getNetworkCapabilities(any());
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ processAllFutureMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+ processAllMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+ }
+
+ @Test
+ @SmallTest
+ public void testAutoDataSwitchCancel_onSecondary() throws Exception {
+ initialize();
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ testAutoSwitchToSecondarySucceed();
+ clearInvocations(mCellularNetworkValidator);
+ // attempts the switch back due to secondary becomes ROAMING
+ serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ doReturn(true).when(mCellularNetworkValidator).isValidating();
+
+ // cancel the switch back attempt due to secondary back to HOME
+ serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
+ processAllMessages();
+
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+ verify(mCellularNetworkValidator).stopValidation();
+ }
+
+ @Test
+ @SmallTest
+ public void testAutoDataSwitch_retry() throws Exception {
+ initialize();
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+
+ // Verify attempting to switch
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
+ processAllMessages();
+
+ assertTrue(mPhoneSwitcherUT.hasMessages(EVENT_EVALUATE_AUTO_SWITCH));
+
+ processAllFutureMessages();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
+ processAllFutureMessages();
+
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+ }
+
+ @Test
+ @SmallTest
+ public void testAutoDataSwitch_setNotification() throws Exception {
+ SubscriptionInfo mockedInfo = mock(SubscriptionInfo.class);
+ doReturn(false).when(mockedInfo).isOpportunistic();
+ doReturn(mockedInfo).when(mSubscriptionManagerService).getSubscriptionInfo(anyInt());
+ initialize();
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ testAutoSwitchToSecondarySucceed();
+ clearInvocations(mSubscriptionManagerService);
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null, null))
+ .sendToTarget();
+ processAllMessages();
+ verify(mSubscriptionManagerService).getSubscriptionInfo(2);
+ // switch back to primary
+ clearInvocations(mSubscriptionManagerService);
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(0, null, null))
+ .sendToTarget();
+ processAllMessages();
+ verify(mSubscriptionManagerService, never()).getSubscriptionInfo(1);
+
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null, null))
+ .sendToTarget();
+ processAllMessages();
+ verify(mSubscriptionManagerService, never()).getSubscriptionInfo(2);
+ }
+
+ @Test
+ @SmallTest
+ public void testAutoDataSwitch_exemptPingTest() throws Exception {
+ initialize();
+ // Change resource overlay
+ doReturn(false).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
+ mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
+ processAllMessages();
+
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ //1. Attempting to switch to nDDS, switch even if validation failed
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
+ processAllMessages();
+
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId()); // switch succeeds
+
+ //2. Attempting to switch back to DDS, switch even if validation failed
+ serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+ verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+ processAllMessages();
+
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // switch succeeds
+ }
+
/**
* Test a multi-sim case with limited active phones:
* - lose default via default sub change
@@ -382,7 +682,7 @@
setSlotIndexToSubId(0, 0);
setSlotIndexToSubId(1, 1);
setDefaultDataSubId(0);
- mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+ mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
processAllMessages();
// verify initial conditions
@@ -395,7 +695,7 @@
addMmsNetworkRequest(1);
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
// After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
@@ -453,7 +753,9 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
// Notify phoneSwitcher about default data sub and default network request.
addInternetNetworkRequest(null, 50);
@@ -461,31 +763,115 @@
assertTrue(mDataAllowed[0]);
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
assertFalse(mDataAllowed[0]);
assertTrue(mDataAllowed[1]);
// Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
assertTrue(mDataAllowed[0]);
assertFalse(mDataAllowed[1]);
}
+ /**
+ * TestSetPreferredData in the event of different priorities.
+ * The following events can set preferred data subId with priority in the order of
+ * 1. Emergency call
+ * 2. Voice call (when data during call feature is enabled).
+ * 3. CBRS requests OR Auto switch requests - only one case applies at a time
+ */
+ @Test
+ @SmallTest
+ public void testSetPreferredDataCasePriority_CbrsWaitsForVoiceCall() 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);
+ // single visible sub, as the other one is CBRS
+ doReturn(new int[1]).when(mSubscriptionManagerService).getActiveSubIdList(true);
+ setDefaultDataSubId(1);
+
+ // Notify phoneSwitcher about default data sub and default network request.
+ NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
+ // Phone 0 (sub 1) should be activated as it has default data sub.
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+
+ // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
+ processAllMessages();
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
+ // A higher priority event occurring E.g. Phone1 has active IMS call on LTE.
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(true).when(mPhone).isUserDataEnabled();
+ doReturn(true).when(mPhone).isDataAllowed();
+ mockImsRegTech(0, REGISTRATION_TECH_LTE);
+ notifyPhoneAsInCall(mPhone);
+
+ // switch shouldn't occur due to the higher priority event
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+ assertEquals(2, mPhoneSwitcherUT.getAutoSelectedDataSubId());
+
+ // The higher priority event ends, time to switch to auto selected subId.
+ notifyPhoneAsInactive(mPhone);
+
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+ assertEquals(2, mPhoneSwitcherUT.getAutoSelectedDataSubId());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ }
+
+ @Test
+ @SmallTest
+ public void testSetPreferredData_NoAutoSwitchWhenCbrs() 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);
+
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(new int[1]).when(mSubscriptionManagerService).getActiveSubIdList(true);
+ prepareIdealAutoSwitchCondition();
+ processAllFutureMessages();
+
+ verify(mCellularNetworkValidator, never()).validate(eq(2), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+ }
+
@Test
@SmallTest
public void testSetPreferredDataModemCommand() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
- mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+ mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
- mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+ mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
clearInvocations(mMockRadioConfig);
@@ -500,7 +886,7 @@
// Phone 0 (sub 1) should be preferred data phone as it has default data sub.
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
clearInvocations(mMockRadioConfig);
@@ -512,55 +898,58 @@
NetworkRequest mmsRequest = addMmsNetworkRequest(2);
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
// Set sub 2 as preferred sub should make phone 1 preferredDataModem
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
clearInvocations(mMockRadioConfig);
clearInvocations(mActivePhoneSwitchHandler);
// Unset preferred sub should make phone0 preferredDataModem again.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
// SetDataAllowed should never be triggered.
@@ -569,7 +958,7 @@
// Set preferred data modem should be triggered after radio on or available.
clearInvocations(mMockRadioConfig);
- Message.obtain(mPhoneSwitcher, EVENT_RADIO_ON, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_RADIO_ON, res).sendToTarget();
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
}
@@ -580,8 +969,6 @@
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
- // Mark sub 2 as opportunistic.
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
// 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.
@@ -589,43 +976,47 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
// Phone 0 (sub 1) should be activated as it has default data sub.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, null);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
+ eq(mPhoneSwitcherUT.mValidationCallback));
// Validation failed. Preferred data sub should remain 1, data phone should remain 0.
- mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
processAllMessages();
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Validation succeeds. Preferred data sub changes to 2, data phone changes to 1.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
- assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Switching data back to primary (subId 1) with customized validation timeout.
long timeout = 1234;
mContextFixture.getCarrierConfigBundle().putLong(
KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, timeout);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, null);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(1), eq(timeout), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
- mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
+ eq(mPhoneSwitcherUT.mValidationCallback));
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
processAllMessages();
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
private void mockImsRegTech(int phoneId, int regTech) {
doReturn(regTech).when(mMockImsRegTechProvider).get(any(), eq(phoneId));
- mPhoneSwitcher.mImsRegTechProvider = mMockImsRegTechProvider;
+ mPhoneSwitcherUT.mImsRegTechProvider = mMockImsRegTechProvider;
}
@Test
@@ -643,17 +1034,17 @@
processAllMessages();
// Phone 0 should be the default data phoneId.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
// trigger data switch.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
- doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_LTE);
notifyPhoneAsInCall(mImsPhone);
- // Phone 1 should become the default data phone.
- assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ // Phone 1 should become the preferred data phone.
+ assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@Test
@@ -672,17 +1063,17 @@
processAllMessages();
// Phone 0 should be the default data phoneId.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
// trigger data switch.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
- doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_LTE);
notifyPhoneAsInDial(mImsPhone);
- // Phone 1 should become the default data phone.
- assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ // Phone2 should be preferred data phone
+ assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@Test
@SmallTest
@@ -700,17 +1091,17 @@
processAllMessages();
// Phone 0 should be the default data phoneId.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
// trigger data switch.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
- doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_LTE);
notifyPhoneAsInIncomingCall(mImsPhone);
- // Phone 1 should become the default data phone.
- assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ // Phone 1 should become the preferred data phone.
+ assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@Test
@@ -728,16 +1119,16 @@
processAllMessages();
// Phone 0 should be the default data phoneId.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone2 has active call, but data is turned off. So no data switching should happen.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
- doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
notifyPhoneAsInCall(mImsPhone);
// Phone 0 should remain the default data phone.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@Test
@@ -755,17 +1146,18 @@
processAllMessages();
// Phone 0 should be the default data phoneId.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone 1 has active IMS call on CROSS_SIM. And data of DEFAULT apn is enabled. This should
// not trigger data switch.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
- doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ doReturn(true).when(mPhone).isUserDataEnabled();
+ doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_CROSS_SIM);
notifyPhoneAsInCall(mImsPhone);
// Phone 0 should remain the default data phone.
- assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
// Phone 1 has has handed over the call to LTE. And data of DEFAULT apn is enabled.
// This should trigger data switch.
@@ -773,7 +1165,7 @@
notifyImsRegistrationTechChange(mPhone2);
// Phone 1 should become the default data phone.
- assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@Test
@@ -788,9 +1180,9 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
@@ -800,38 +1192,99 @@
notifyDataEnabled(false);
notifyPhoneAsInCall(mPhone2);
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- // Phone2 has active call. So data switch to it.
+ // Phone2 has active call, and data is on. So data switch to it.
+ doReturn(true).when(mPhone).isUserDataEnabled();
notifyDataEnabled(true);
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
clearInvocations(mMockRadioConfig);
- // Phone2 call ended. So data switch back to default data sub.
+ // Phone2(nDDS) call ended. But Phone1 having cross-SIM call. Don't switch.
+ mockImsRegTech(0, REGISTRATION_TECH_CROSS_SIM);
+ notifyPhoneAsInIncomingCall(mPhone);
notifyPhoneAsInactive(mPhone2);
- verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
- new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- clearInvocations(mMockRadioConfig);
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- // Phone2 has holding call, but data is turned off. So no data switching should happen.
- notifyPhoneAsInHoldingCall(mPhone2);
- verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
- new TelephonyNetworkRequest(internetRequest, mPhone), 1));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ // Phone(DDS) call ended.
+ // Honor auto data switch's suggestion: if DDS is OOS, auto switch to Phone2(nDDS).
+ serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ serviceStateChanged(0, NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+ doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
+ notifyPhoneAsInactive(mPhone);
+
+ // verify immediately switch back to DDS upon call ends
+ verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+
+ // verify the attempt to do auto data switch to Phone2(nDDS)
+ processAllFutureMessages();
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+ eq(mPhoneSwitcherUT.mValidationCallback));
+
+ // Phone2 has holding call on VoWifi, no need to switch data
+ clearInvocations(mMockRadioConfig);
+ mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
+ notifyPhoneAsInHoldingCall(mPhone2);
+ verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
}
+ @Test
+ @SmallTest
+ public void testDataEnabledChangedDuringVoiceCall() throws Exception {
+ doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
+ initialize();
+ // 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);
+ NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+ clearInvocations(mMockRadioConfig);
+ setAllPhonesInactive();
+ // Initialization done.
+
+ // Phone2 has active call and data is on. So switch to nDDS Phone2
+ notifyDataEnabled(true);
+ notifyPhoneAsInCall(mPhone2);
+ verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+
+ // During the active call, user turns off data, should immediately switch back to DDS
+ notifyDataEnabled(false);
+ verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+ new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+ }
@Test
@SmallTest
@@ -845,16 +1298,16 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
NetworkRequest internetRequest = addInternetNetworkRequest(2, 50);
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
// Restricted network request will should be applied.
internetRequest = addInternetNetworkRequest(2, 50, true);
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
}
@@ -870,7 +1323,7 @@
clearInvocations(mMockRadioConfig);
// override the phone ID in prep for emergency call
- mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
@@ -891,7 +1344,7 @@
clearInvocations(mMockRadioConfig);
// override the phone ID in prep for emergency call
- mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
processAllMessages();
// The radio command should never be called because the DDS hasn't changed.
verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
@@ -915,7 +1368,7 @@
// override the phone ID in prep for emergency call
- mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
@@ -930,7 +1383,7 @@
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
// Make sure the correct broadcast is sent out for the phone ID
@@ -952,7 +1405,7 @@
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
- mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
@@ -981,7 +1434,7 @@
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
@@ -1002,7 +1455,7 @@
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
- mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
@@ -1012,7 +1465,7 @@
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
// Make sure the correct broadcast is sent out for the phone ID
@@ -1037,7 +1490,7 @@
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
futurePhone.whenComplete((r, error) -> queue.offer(r));
- mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, futurePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
Boolean result = queue.poll();
@@ -1047,7 +1500,7 @@
// try override the phone ID again while there is an existing override for a different phone
futurePhone = new CompletableFuture<>();
futurePhone.whenComplete((r, error) -> queue.offer(r));
- mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, futurePhone);
+ mPhoneSwitcherUT.overrideDefaultDataForEmergency(0, 1, futurePhone);
processAllMessages();
result = queue.poll();
assertNotNull(result);
@@ -1063,7 +1516,7 @@
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
AsyncResult res = new AsyncResult(1, null, null);
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
processAllMessages();
// Make sure the correct broadcast is sent out for the phone ID
@@ -1076,57 +1529,74 @@
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
- // Mark sub 2 as opportunistic.
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
// 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);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
+ // Switch to primary before a primary is selected/inactive.
+ setDefaultDataSubId(-1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
+ processAllMessages();
+
+ assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ mPhoneSwitcherUT.getAutoSelectedDataSubId());
+ verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+
+ // once the primary is selected, it becomes the active sub.
+ setDefaultDataSubId(2);
+ assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+
+ setDefaultDataSubId(1);
// Validating on sub 10 which is inactive.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
+ clearInvocations(mSetOpptDataCallback1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
// Switch to active subId without validating. Should always succeed.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
// Validating on sub 1 and fails.
clearInvocations(mSetOpptDataCallback1);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
// Validating on sub 2 and succeeds.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
// Switching data back to primary and validation fails.
clearInvocations(mSetOpptDataCallback2);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
// Switching data back to primary and succeeds.
clearInvocations(mSetOpptDataCallback2);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
processAllMessages();
verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
@@ -1134,36 +1604,36 @@
clearInvocations(mSetOpptDataCallback1);
clearInvocations(mSetOpptDataCallback2);
clearInvocations(mCellularNetworkValidator);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
+ eq(mPhoneSwitcherUT.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
verify(mSetOpptDataCallback2, never()).onComplete(anyInt());
// Validation succeeds.
doReturn(false).when(mCellularNetworkValidator).isValidating();
- mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
clearInvocations(mSetOpptDataCallback1);
clearInvocations(mSetOpptDataCallback2);
clearInvocations(mCellularNetworkValidator);
// Back to back call, call 1 to switch to subId 2, call 2 to switch back.
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
+ eq(mPhoneSwitcherUT.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
- mPhoneSwitcher.trySetOpportunisticDataSubscription(
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
processAllMessages();
// Call 1 should be cancelled and failed. Call 2 return success immediately as there's no
@@ -1188,12 +1658,11 @@
setNumPhones(2, 2);
AsyncResult result = new AsyncResult(null, 2, null);
- Message.obtain(mPhoneSwitcher, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
processAllMessages();
verify(mPhone2).registerForEmergencyCallToggle(any(), anyInt(), any());
verify(mPhone2).registerForPreciseCallStateChanged(any(), anyInt(), any());
- verify(mDataEnabledSettings2).registerForDataEnabledChanged(any(), anyInt(), any());
clearInvocations(mMockRadioConfig);
setSlotIndexToSubId(1, 2);
@@ -1213,28 +1682,31 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
// Initialization done.
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
+ eq(mPhoneSwitcherUT.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
// Network available on different sub. Should do nothing.
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
// Network available on corresponding sub. Should confirm switch.
- mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+ mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
@@ -1251,28 +1723,31 @@
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 0));
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
// Initialization done.
- doReturn(true).when(mSubscriptionController).isOpportunistic(2);
- mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+ doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+ .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcher.mValidationCallback));
+ eq(mPhoneSwitcherUT.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
// Validation failed on different sub. Should do nothing.
- mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
// Network available on corresponding sub. Should confirm switch.
- mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
@@ -1290,16 +1765,15 @@
// modem retry not invoked.
AsyncResult res1 = new AsyncResult(0, null,
new CommandException(CommandException.Error.INVALID_SIM_STATE));
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res1).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res1).sendToTarget();
processAllMessages();
moveTimeForward(5000);
processAllMessages();
verify(mMockRadioConfig, times(0)).setPreferredDataModem(eq(0), any());
- doReturn(0).when(mSubscriptionController).getPhoneId(anyInt());
AsyncResult res2 = new AsyncResult(0, null,
new CommandException(CommandException.Error.NETWORK_NOT_READY));
- Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res2).sendToTarget();
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res2).sendToTarget();
processAllMessages();
moveTimeForward(5000);
processAllMessages();
@@ -1307,8 +1781,8 @@
verify(mMockRadioConfig, times(1)).setPreferredDataModem(eq(0), any());
clearInvocations(mMockRadioConfig);
- doReturn(mSubscriptionInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfoForSimSlotIndex(eq(0), any(), any());
+ doReturn(mSubscriptionInfo).when(mSubscriptionManagerService)
+ .getActiveSubscriptionInfoForSimSlotIndex(eq(0), any(), any());
doReturn(true).when(mSubscriptionInfo).areUiccApplicationsEnabled();
doReturn(mIccCard).when(mPhone).getIccCard();
doReturn(true).when(mIccCard).isEmptyProfile();
@@ -1330,8 +1804,77 @@
verify(mMockRadioConfig, times(1)).setPreferredDataModem(eq(0), any());
}
+ @Test
+ public void testScheduledRetryWhileMultiSimConfigChange() throws Exception {
+ doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
+ initialize();
+
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+
+ // for EVENT_MODEM_COMMAND_RETRY
+ AsyncResult res = new AsyncResult(
+ 1, null, new CommandException(CommandException.Error.GENERIC_FAILURE));
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
+
+ // reduce count of phone
+ setNumPhones(1, 1);
+ AsyncResult result = new AsyncResult(null, 1, null);
+ Message.obtain(mPhoneSwitcherUT, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
+ processAllMessages();
+
+ // fire retries
+ moveTimeForward(5000);
+ processAllMessages();
+
+ verify(mCommandsInterface0, never()).setDataAllowed(anyBoolean(), any());
+ verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
+ }
+
/* Private utility methods start here */
+ private void prepareIdealAutoSwitchCondition() {
+ // 1. service state changes
+ serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ serviceStateChanged(0, NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+ // 2.1 User data enabled on primary SIM
+ doReturn(true).when(mPhone).isUserDataEnabled();
+
+ // 2.2 Auto switch feature is enabled
+ doReturn(true).when(mPhone2).isDataAllowed();
+
+ // 3.1 No default network
+ doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
+
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ }
+
+ private void serviceStateChanged(int phoneId,
+ @NetworkRegistrationInfo.RegistrationState int dataRegState) {
+
+ ServiceState ss = new ServiceState();
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(dataRegState)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .build());
+
+ ss.setDataRoamingFromRegistration(dataRegState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+
+ doReturn(ss).when(mPhones[phoneId]).getServiceState();
+
+ Message msg = mPhoneSwitcherUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
+ msg.obj = new AsyncResult(phoneId, null, null);
+ mPhoneSwitcherUT.sendMessage(msg);
+ }
+
private void setAllPhonesInactive() {
doReturn(mInactiveCall).when(mPhone).getForegroundCall();
doReturn(mInactiveCall).when(mPhone).getBackgroundCall();
@@ -1346,43 +1889,47 @@
private void notifyPhoneAsInCall(Phone phone) {
doReturn(mActiveCall).when(phone).getForegroundCall();
- mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInDial(Phone phone) {
doReturn(mDialCall).when(phone).getForegroundCall();
- mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInIncomingCall(Phone phone) {
doReturn(mIncomingCall).when(phone).getForegroundCall();
- mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInHoldingCall(Phone phone) {
doReturn(mHoldingCall).when(phone).getBackgroundCall();
- mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInactive(Phone phone) {
doReturn(mInactiveCall).when(phone).getForegroundCall();
- mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyDataEnabled(boolean dataEnabled) {
- doReturn(dataEnabled).when(mDataEnabledSettings).isDataEnabled(anyInt());
- doReturn(dataEnabled).when(mDataEnabledSettings2).isDataEnabled(anyInt());
- mPhoneSwitcher.sendEmptyMessage(EVENT_DATA_ENABLED_CHANGED);
+ doReturn(true).when(mPhone).isUserDataEnabled();
+ doReturn(dataEnabled).when(mDataSettingsManager).isDataEnabled();
+ doReturn(dataEnabled).when(mPhone2).isDataAllowed();
+ mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(dataEnabled, 123 , "");
+ if (mDataSettingsManagerCallbacks.size() > 1) {
+ mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(dataEnabled, 123, "");
+ }
processAllMessages();
}
private void notifyImsRegistrationTechChange(Phone phone) {
- mPhoneSwitcher.sendEmptyMessage(EVENT_IMS_RADIO_TECH_CHANGED);
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_IMS_RADIO_TECH_CHANGED);
processAllMessages();
}
@@ -1432,10 +1979,10 @@
for (int i = 0; i < mActiveModemCount; i++) {
if (defaultDataSub == (i + 1)) {
// sub id is always phoneId+1 for testing
- assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), i));
} else {
- assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+ assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
new TelephonyNetworkRequest(internetRequest, mPhone), i));
}
}
@@ -1454,8 +2001,16 @@
initializeCommandInterfacesMock();
initializeTelRegistryMock();
initializeConnManagerMock();
+ initializeConfigMock();
- mPhoneSwitcher = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
+ mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
+
+ Field field = PhoneSwitcher.class.getDeclaredField("mDataSettingsManagerCallbacks");
+ field.setAccessible(true);
+ mDataSettingsManagerCallbacks =
+ (Map<Integer, DataSettingsManager.DataSettingsManagerCallback>)
+ field.get(mPhoneSwitcherUT);
+
processAllMessages();
verify(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
@@ -1470,7 +2025,8 @@
doReturn(0).when(mPhone).getPhoneId();
doReturn(1).when(mPhone2).getPhoneId();
doReturn(true).when(mPhone2).isUserDataEnabled();
- doReturn(mDataEnabledSettings2).when(mPhone2).getDataEnabledSettings();
+ doReturn(mDataSettingsManager2).when(mPhone2).getDataSettingsManager();
+ doReturn(mSST2).when(mPhone2).getServiceStateTracker();
for (int i = 0; i < supportedModemCount; i++) {
mSlotIndexToSubId[i] = new int[1];
mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1546,8 +2102,14 @@
* Capture mNetworkProviderMessenger so that testing can request or release
* network requests on PhoneSwitcher.
*/
- private void initializeSubControllerMock() {
- doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
+ private void initializeSubControllerMock() throws Exception {
+ doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
+ doReturn(mDefaultDataSub).when(mMockedIsub).getDefaultDataSubId();
+ doReturn(0).when(mSubscriptionManagerService).getPhoneId(1);
+ doReturn(0).when(mMockedIsub).getPhoneId(1);
+ doReturn(1).when(mSubscriptionManagerService).getPhoneId(2);
+ doReturn(1).when(mMockedIsub).getPhoneId(2);
+
doAnswer(invocation -> {
int phoneId = (int) invocation.getArguments()[0];
if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
@@ -1557,28 +2119,62 @@
} else {
return mSlotIndexToSubId[phoneId][0];
}
- }).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+ }).when(mMockedIsub).getSubId(anyInt());
+
+ doAnswer(invocation -> {
+ int phoneId = (int) invocation.getArguments()[0];
+ if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
+ return mSlotIndexToSubId[0][0];
+ } else {
+ return mSlotIndexToSubId[phoneId][0];
+ }
+ }).when(mSubscriptionManagerService).getSubId(anyInt());
doAnswer(invocation -> {
int subId = (int) invocation.getArguments()[0];
- if (!SubscriptionManager.isUsableSubIdValue(subId)) return false;
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) return null;
+ int slotIndex = -1;
for (int i = 0; i < mSlotIndexToSubId.length; i++) {
- if (mSlotIndexToSubId[i][0] == subId) return true;
+ if (mSlotIndexToSubId[i][0] == subId) slotIndex = i;
}
- return false;
- }).when(mSubscriptionController).isActiveSubId(anyInt());
+ return new SubscriptionInfoInternal.Builder()
+ .setSimSlotIndex(slotIndex).setId(subId).build();
+ }).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+ doReturn(new int[mSlotIndexToSubId.length]).when(mSubscriptionManagerService)
+ .getActiveSubIdList(true);
}
- private void setDefaultDataSubId(int defaultDataSub) {
+ private void initializeConfigMock() {
+ doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
+ doReturn(mDataConfigManager).when(mDataNetworkController).getDataConfigManager();
+ doReturn(1000L).when(mDataConfigManager)
+ .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ doReturn(7).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry();
+ doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
+ }
+
+ private void setDefaultDataSubId(int defaultDataSub) throws Exception {
mDefaultDataSub = defaultDataSub;
- doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
+ doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
+ if (defaultDataSub == 1) {
+ doReturn(true).when(mPhone).isUserDataEnabled();
+ doReturn(false).when(mPhone2).isUserDataEnabled();
+ } else {
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ doReturn(true).when(mPhone2).isUserDataEnabled();
+ }
sendDefaultDataSubChanged();
}
private void setSlotIndexToSubId(int slotId, int subId) {
mSlotIndexToSubId[slotId][0] = subId;
+ Phone phone = slotId == 0 ? mPhone : mPhone2;
+ doReturn(subId).when(phone).getSubId();
}
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
index 00bca4d..d528d85 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
@@ -27,15 +27,17 @@
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
+import android.net.INetworkAgentRegistry;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.QosSession;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.EpsQos;
import android.telephony.data.Qos;
import android.telephony.data.QosBearerFilter;
import android.telephony.data.QosBearerSession;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -51,7 +53,6 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
-import java.util.Arrays;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -60,6 +61,7 @@
class Filter implements QosCallbackTracker.IFilter {
InetSocketAddress mLocalAddress;
InetSocketAddress mRemoteAddress;
+ int mProtocol = QosBearerFilter.QOS_PROTOCOL_TCP;
Filter(@NonNull final InetSocketAddress localAddress) {
this.mLocalAddress = localAddress;
@@ -76,7 +78,7 @@
final int startPort, final int endPort) {
return startPort <= mLocalAddress.getPort()
&& endPort >= mLocalAddress.getPort()
- && mLocalAddress.getAddress().equals(address);
+ && (address.isAnyLocalAddress() || mLocalAddress.getAddress().equals(address));
}
public boolean matchesRemoteAddress(final @NonNull InetAddress address,
@@ -84,13 +86,18 @@
return mRemoteAddress != null
&& startPort <= mRemoteAddress.getPort()
&& endPort >= mRemoteAddress.getPort()
- && mRemoteAddress.getAddress().equals(address);
+ && (address.isAnyLocalAddress() || mRemoteAddress.getAddress().equals(address));
+ }
+
+ public boolean matchesProtocol(int protocol) {
+ return mProtocol == protocol;
}
}
// Mocked classes
private Phone mPhone;
private TelephonyNetworkAgent mNetworkAgent;
+ private INetworkAgentRegistry mINetworkAgentRegistry;
private Network mNetwork;
private RcsStats mRcsStats;
@@ -101,6 +108,9 @@
super.setUp(getClass().getSimpleName());
mPhone = mock(Phone.class);
mNetworkAgent = mock(TelephonyNetworkAgent.class);
+ mINetworkAgentRegistry = mock(INetworkAgentRegistry.class);
+ replaceInstance(NetworkAgent.class, "mRegistry", mNetworkAgent, mINetworkAgentRegistry);
+ replaceInstance(NetworkAgent.class, "mPreConnectedQueue", mNetworkAgent, new ArrayList());
mNetwork = mock(Network.class);
mRcsStats = mock(RcsStats.class);
doReturn(mNetwork).when(mNetworkAgent).getNetwork();
@@ -132,28 +142,44 @@
public static QosBearerFilter createIpv4QosFilter(String localAddress,
QosBearerFilter.PortRange localPort, int precedence) {
+ ArrayList<LinkAddress> localAddresses = new ArrayList<>();
+ if (localAddress != null) {
+ localAddresses.add(
+ new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32));
+ }
return new QosBearerFilter(
- Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32)),
- new ArrayList<>(), localPort, null, QosBearerFilter.QOS_PROTOCOL_TCP,
- 7, 987, 678, QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
+ localAddresses, new ArrayList<>(), localPort, null,
+ QosBearerFilter.QOS_PROTOCOL_TCP, 7, 987, 678,
+ QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
}
private static QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort,
int precedence) {
+ return createIpv4QosFilter(localAddress, remoteAddress, localPort, remotePort,
+ QosBearerFilter.QOS_PROTOCOL_TCP, precedence);
+ }
+
+ private static QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
+ QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort, int protocol,
+ int precedence) {
+ ArrayList<LinkAddress> localAddresses = new ArrayList<>();
+ if (localAddress != null) {
+ localAddresses.add(
+ new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32));
+ }
+ ArrayList<LinkAddress> remoteAddresses = new ArrayList<>();
+ if (localAddress != null) {
+ remoteAddresses.add(
+ new LinkAddress(InetAddresses.parseNumericAddress(remoteAddress), 32));
+ }
return new QosBearerFilter(
- Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32)),
- Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(remoteAddress), 32)),
- localPort, remotePort,
- QosBearerFilter.QOS_PROTOCOL_TCP, 7, 987, 678,
+ localAddresses, remoteAddresses, localPort, remotePort,
+ protocol, 7, 987, 678,
QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
}
@Test
- @SmallTest
public void testAddFilterBeforeUpdateSessions() throws Exception {
Filter filter = new Filter(new InetSocketAddress(
InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
@@ -170,8 +196,8 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
- eq(1234), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
// Matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
@@ -182,14 +208,12 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
public void testAddFilterAfterUpdateSessions() throws Exception {
// Non-matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -213,13 +237,11 @@
mQosCallbackTracker.addFilter(1, filter);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
public void testRemoveFilter() throws Exception {
// Add filter
Filter filter = new Filter(new InetSocketAddress(
@@ -237,8 +259,8 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
- eq(1234), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
// Remove the filter
mQosCallbackTracker.removeFilter(1);
@@ -253,13 +275,11 @@
processAllMessages();
// Verify that notifyQosSessionAvailable is not invoked as the filter is already removed
- verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+ verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
public void testSessionLost() throws Exception {
// Non-matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -287,19 +307,18 @@
mQosCallbackTracker.addFilter(1, filter);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
// Remove the matching QosBearerFilter
qosSessions.remove(1);
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1235), eq(1));
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
}
@Test
- @SmallTest
public void testModifiedQos() throws Exception {
// Non-matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -326,10 +345,10 @@
mQosCallbackTracker.addFilter(1, filter);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
- reset(mNetworkAgent);
+ reset(mINetworkAgentRegistry);
// Update the QOS
qosSessions.remove(1);
@@ -337,12 +356,11 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
public void testUnmodifiedQos() throws Exception {
// Non-matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -369,8 +387,8 @@
mQosCallbackTracker.addFilter(1, filter);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
reset(mNetworkAgent);
@@ -380,12 +398,11 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
public void testEmptyQosSessions() throws Exception {
// Add filter
Filter filter = new Filter(new InetSocketAddress(
@@ -418,21 +435,21 @@
mQosCallbackTracker.addFilter(2, filter2);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1234), any(EpsBearerQosSessionAttributes.class));
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(2),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
// Update empty QOS sessions list
mQosCallbackTracker.updateSessions(new ArrayList<>());
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
- verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(2), any(QosSession.class));
}
@Test
- @SmallTest
public void testMultipleQosSessions() throws Exception {
// Add filter 1
Filter filter1 = new Filter(new InetSocketAddress(
@@ -465,21 +482,20 @@
mQosCallbackTracker.updateSessions(qosSessions);
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
- eq(1234), any(EpsBearerQosSessionAttributes.class));
- verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(2),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
// Update empty QOS sessions list
mQosCallbackTracker.updateSessions(new ArrayList<>());
processAllMessages();
- verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
- verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(2), any(QosSession.class));
}
@Test
- @SmallTest
public void testQosSessionWithInvalidPortRange() throws Exception {
// Non-matching QosBearerFilter
ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -503,14 +519,110 @@
mQosCallbackTracker.addFilter(1, filter);
processAllMessages();
- verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
- eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+ verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
}
@Test
- @SmallTest
- public void testQosMetrics() throws Exception {
+ public void testQosSessionFilterPortRangeWithoutAddress() throws Exception {
+ // QosBearerFilter including remote port range without remote address
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter(null, null, null,
+ new QosBearerFilter.PortRange(3200, 3220), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 1357),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 3202));
+ mQosCallbackTracker.addFilter(1, filter);
+ processAllMessages();
+
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
+ // Remove the matching QosBearerFilter
+ qosSessions.remove(0);
+ mQosCallbackTracker.updateSessions(qosSessions);
+ processAllMessages();
+
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+ }
+
+ @Test
+ public void testQosSessionFilterProtocol() throws Exception {
+ // QosBearerFilter including protocol
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter(null, null, null,
+ new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_UDP, 45));
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 1357),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 3207));
+ mQosCallbackTracker.addFilter(1, filter);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
+ // Remove the matching QosBearerFilter
+ qosSessions.remove(0);
+ mQosCallbackTracker.updateSessions(qosSessions);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, never()).sendQosSessionLost(eq(1), any(QosSession.class));
+
+ qosFilters1.clear();
+ qosFilters1.add(createIpv4QosFilter(null, null, null,
+ new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_TCP, 45));
+ qosSessions.clear();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+ mQosCallbackTracker.updateSessions(qosSessions);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+ qosSessions.remove(0);
+ mQosCallbackTracker.updateSessions(qosSessions);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+ }
+
+ @Test
+ public void testQosSessionFilterProtocolEsp() throws Exception {
+ // QosBearerFilter including protocol
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter(null, null, null,
+ new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_ESP, 45));
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 1357),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 3211));
+ mQosCallbackTracker.addFilter(1, filter);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+ any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
+ // Remove the matching QosBearerFilter
+ qosSessions.remove(0);
+ mQosCallbackTracker.updateSessions(qosSessions);
+ processAllMessages();
+ verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+ }
+
+ @Test
+ public void testQosMetrics() {
final int callbackId = 1;
final int slotId = mPhone.getPhoneId();
// Add filter before update session
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
index d109ee9..5941f06 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
@@ -27,19 +27,14 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
-import android.os.AsyncResult;
-import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -47,11 +42,9 @@
import androidx.test.filters.FlakyTest;
+import com.android.internal.telephony.ISub;
import com.android.internal.telephony.RadioConfig;
import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams.HandoverCallback;
import com.android.telephony.Rlog;
import org.junit.After;
@@ -59,9 +52,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -74,13 +65,13 @@
// Mocked classes
PhoneSwitcher mPhoneSwitcher;
private RadioConfig mMockRadioConfig;
- private DataConnection mDataConnection;
+ private ISub mMockedIsub;
private String mTestName = "";
// List of all requests filed by a test
private final ArraySet<TelephonyNetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
- // List of requests active in DcTracker
+ // List of requests active
private final ArrayList<TelephonyNetworkRequest> mNetworkRequestList = new ArrayList<>();
// List of complete messages associated with the network requests
private final Map<TelephonyNetworkRequest, Message> mNetworkRequestMessageMap = new HashMap<>();
@@ -156,9 +147,13 @@
super.setUp(getClass().getSimpleName());
mPhoneSwitcher = mock(PhoneSwitcher.class);
mMockRadioConfig = mock(RadioConfig.class);
- mDataConnection = mock(DataConnection.class);
+ mMockedIsub = mock(ISub.class);
replaceInstance(RadioConfig.class, "sRadioConfig", null, mMockRadioConfig);
+ doReturn(mMockedIsub).when(mIBinder).queryLocalInterface(anyString());
+ doReturn(mPhone).when(mPhone).getImsPhone();
+ mServiceManagerMockedServices.put("isub", mIBinder);
+
mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
new String[]{"wifi,1,1,1,-1,true", "mobile,0,0,0,-1,true",
"mobile_mms,2,0,2,60000,true", "mobile_supl,3,0,2,60000,true",
@@ -217,8 +212,7 @@
// test at this time.
doAnswer(invocation -> {
final NetworkCapabilities capabilitiesFilter =
- mTelephonyNetworkFactoryUT.makeNetworkFilter(
- mSubscriptionController.getSubIdUsingPhoneId(0));
+ mTelephonyNetworkFactoryUT.makeNetworkFilter(mMockedIsub.getSubId(0));
for (final TelephonyNetworkRequest request : mAllNetworkRequestSet) {
final int message = request.canBeSatisfiedBy(capabilitiesFilter)
? CMD_REQUEST_NETWORK : CMD_CANCEL_REQUEST;
@@ -230,7 +224,7 @@
}
/**
- * Test that phone active changes cause the DcTracker to get poked.
+ * Test that phone active changes
*/
@FlakyTest
@Test
@@ -243,7 +237,7 @@
createMockedTelephonyComponents();
doReturn(false).when(mPhoneSwitcher).shouldApplyNetworkRequest(any(), anyInt());
- doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
+ doReturn(subId).when(mMockedIsub).getSubId(phoneId);
// fake onSubscriptionChangedListener being triggered.
mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
@@ -301,7 +295,7 @@
}
/**
- * Test that network request changes cause the DcTracker to get poked.
+ * Test that network request changes
*/
@Test
@SmallTest
@@ -316,7 +310,7 @@
createMockedTelephonyComponents();
- doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
+ doReturn(subId).when(mMockedIsub).getSubId(phoneId);
mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
processAllMessages();
@@ -330,7 +324,7 @@
processAllMessages();
assertEquals(1, mNetworkRequestList.size());
- doReturn(altSubId).when(mSubscriptionController).getSubIdUsingPhoneId(altPhoneId);
+ doReturn(altSubId).when(mMockedIsub).getSubId(altPhoneId);
processAllMessages();
assertEquals(1, mNetworkRequestList.size());
@@ -345,7 +339,7 @@
processAllMessages();
assertEquals(1, mNetworkRequestList.size());
- doReturn(unusedSubId).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
+ doReturn(unusedSubId).when(mMockedIsub).getSubId(phoneId);
mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
processAllMessages();
@@ -355,7 +349,7 @@
processAllMessages();
assertEquals(0, mNetworkRequestList.size());
- doReturn(subId).when(mSubscriptionController).getSubIdUsingPhoneId(phoneId);
+ doReturn(subId).when(mMockedIsub).getSubId(phoneId);
mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
processAllMessages();
@@ -364,79 +358,4 @@
processAllMessages();
assertEquals(3, mNetworkRequestList.size());
}
-
- /**
- * Test handover when there is no live data connection
- */
- @Test
- @SmallTest
- public void testHandoverNoLiveData() throws Exception {
- createMockedTelephonyComponents();
- doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
- mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
- TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
-
- activatePhoneInPhoneSwitcher(0, true);
- makeDefaultInternetRequest();
-
- makeSubSpecificMmsRequest(0);
- processAllMessages();
-
- Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
- f.setAccessible(true);
- Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);
-
- HandoverCallback handoverCallback = mock(HandoverCallback.class);
-
- HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
- AsyncResult ar = new AsyncResult(null, hp, null);
- h.sendMessage(h.obtainMessage(5, ar));
- processAllMessages();
-
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
- .getCurrentTransport(anyInt());
-
- hp = new HandoverParams(ApnSetting.TYPE_MMS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- handoverCallback);
- ar = new AsyncResult(null, hp, null);
- h.sendMessage(h.obtainMessage(5, ar));
- processAllMessages();
- }
-
- /**
- * Test handover when the data connection is being connected.
- */
- @Test
- @SmallTest
- public void testHandoverActivatingData() throws Exception {
- createMockedTelephonyComponents();
- doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
- mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
- TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
-
- activatePhoneInPhoneSwitcher(0, true);
- makeDefaultInternetRequest();
-
- makeSubSpecificMmsRequest(0);
- processAllMessages();
-
- Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
- f.setAccessible(true);
- Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);
-
- HandoverCallback handoverCallback = mock(HandoverCallback.class);
- Mockito.reset(mDataNetworkController);
- doReturn(mDataConnection).when(mDcTracker).getDataConnectionByApnType(anyString());
- doReturn(false).when(mDataConnection).isActive();
-
- HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
- AsyncResult ar = new AsyncResult(null, hp, null);
- h.sendMessage(h.obtainMessage(5, ar));
- processAllMessages();
-
- verify(mDataNetworkController, times(1)).removeNetworkRequest(any());
- verify(mDataNetworkController, times(1)).addNetworkRequest(any());
- }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
deleted file mode 100644
index f2d694f..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ApnConfigTypeRepositoryTest {
-
- PersistableBundle mCarrierConfig;
-
- @Before
- public void setUp() throws Exception {
- mCarrierConfig = new PersistableBundle();
- }
-
- @After
- public void tearDown() {
- mCarrierConfig = null;
- }
-
- @Test
- public void testReturnsDefaultsWhenCarrierConfigNull() {
- ApnConfigTypeRepository repository = new ApnConfigTypeRepository(null);
- checkDefaults(repository);
- }
-
- @Test
- public void testReturnsDefaultsWhenCarrierConfigApnContextKeyReturnsNull() {
- mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
- null);
-
- ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
- checkDefaults(repository);
- }
-
- @Test
- public void testReturnsDefaultsWhenCarrierConfigHasInvalidTypes() {
-
- List<String> apnConfigStringArray = new ArrayList<>();
- apnConfigStringArray.add("xcap,cbs:3");
- apnConfigStringArray.add("default:0a");
-
- mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
- apnConfigStringArray.toArray(new String[0]));
-
- ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
- checkDefaults(repository);
- }
-
- @Test
- public void testReturnsCarrierConfigOverride() {
- List<String> apnConfigStringArray = new ArrayList<>();
- //Shouldn't match or override any keys
- apnConfigStringArray.add("xcap,cbs:3");
-
- //Priorities must be integers
- apnConfigStringArray.add("default:10a");
-
- //Key isn't case sensitive, which means that this priority should be taken
- apnConfigStringArray.add("fotA:10");
-
- mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
- apnConfigStringArray.toArray(new String[0]));
-
- ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
- assertEquals(10, repository.getByType(ApnSetting.TYPE_FOTA).getPriority());
- checkDefaults(repository);
- }
-
- private void checkDefaults(ApnConfigTypeRepository repository) {
- assertEquals(0, repository.getByType(ApnSetting.TYPE_ENTERPRISE).getPriority());
- assertEquals(1, repository.getByType(ApnSetting.TYPE_DEFAULT).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_MMS).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_SUPL).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_DUN).getPriority());
- assertEquals(3, repository.getByType(ApnSetting.TYPE_HIPRI).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_IMS).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_CBS).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_IA).getPriority());
- assertEquals(2, repository.getByType(ApnSetting.TYPE_EMERGENCY).getPriority());
- assertEquals(3, repository.getByType(ApnSetting.TYPE_MCX).getPriority());
- assertEquals(3, repository.getByType(ApnSetting.TYPE_XCAP).getPriority());
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
deleted file mode 100644
index b270548..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ApnContextTest extends TelephonyTest {
- // Mocked classes
- ApnSetting mApnSetting;
-
- private ApnContext mApnContext;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mApnSetting = mock(ApnSetting.class);
- mApnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
- }
-
- @After
- public void tearDown() throws Exception {
- mApnContext = null;
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testSetGetApnSetting() throws Exception {
- mApnContext.setApnSetting(mApnSetting);
- assertEquals(mApnSetting, mApnContext.getApnSetting());
- }
-
- @Test
- @SmallTest
- public void testGetApnType() {
- assertEquals(ApnSetting.TYPE_DEFAULT_STRING, mApnContext.getApnType());
- }
-
- @Test
- @SmallTest
- public void testConnectionGeneration() throws Exception {
- for (int i = 0; i < 100; i++) {
- mApnContext.incAndGetConnectionGeneration();
- assertEquals(i + 1, mApnContext.getConnectionGeneration());
- }
- }
-
- @Test
- @SmallTest
- public void testReason() throws Exception {
- mApnContext.setReason("dataEnabled");
- assertEquals("dataEnabled", mApnContext.getReason());
- mApnContext.setReason("simLoaded");
- assertEquals("simLoaded", mApnContext.getReason());
- }
-
- @Test
- @SmallTest
- public void testState() throws Exception {
- mApnContext.setState(DctConstants.State.DISCONNECTING);
- assertEquals(DctConstants.State.DISCONNECTING, mApnContext.getState());
- mApnContext.setEnabled(true);
- assertFalse(mApnContext.isConnectable());
-
- mApnContext.setState(DctConstants.State.RETRYING);
- assertTrue(mApnContext.isConnectable());
- assertTrue(mApnContext.isConnectedOrConnecting());
-
- mApnContext.setState(DctConstants.State.FAILED);
- assertTrue(mApnContext.isDisconnected());
- mApnContext.setState(DctConstants.State.IDLE);
- assertTrue(mApnContext.isDisconnected());
- }
-
- @Test
- @SmallTest
- public void testNetworkRequestNormal() throws Exception {
- NetworkRequest nr1 = new NetworkRequest.Builder().build();
- mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
- NetworkRequest nr2 = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
-
- mApnContext.requestNetwork(nr2, DcTracker.REQUEST_TYPE_NORMAL, null);
- verify(mDcTracker, times(2)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
- mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_NORMAL);
- verify(mDcTracker, never()).disableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.RELEASE_TYPE_NORMAL));
-
- mApnContext.releaseNetwork(nr2, DcTracker.RELEASE_TYPE_NORMAL);
- verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.RELEASE_TYPE_NORMAL));
- }
-
- @Test
- @SmallTest
- public void testNetworkRequestDetach() throws Exception {
- NetworkRequest nr1 = new NetworkRequest.Builder().build();
- mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_NORMAL, null);
- verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
- NetworkRequest nr2 = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
-
- mApnContext.requestNetwork(nr2, DcTracker.REQUEST_TYPE_NORMAL, null);
- verify(mDcTracker, times(2)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
- mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_DETACH);
- verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.RELEASE_TYPE_DETACH));
-
- mApnContext.releaseNetwork(nr2, DcTracker.RELEASE_TYPE_NORMAL);
- verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.RELEASE_TYPE_NORMAL));
- }
-
- @Test
- @SmallTest
- public void testNetworkRequestHandover() throws Exception {
- NetworkRequest nr1 = new NetworkRequest.Builder().build();
- mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_HANDOVER, null);
- verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.REQUEST_TYPE_HANDOVER), eq(null));
-
- mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_HANDOVER);
- verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
- eq(DcTracker.RELEASE_TYPE_HANDOVER));
- }
-
- @Test
- @SmallTest
- public void testConcurrentVoiceAndDataAllowed() throws Exception {
- mApnContext.setConcurrentVoiceAndDataAllowed(true);
- assertTrue(mApnContext.isConcurrentVoiceAndDataAllowed());
- mApnContext.setConcurrentVoiceAndDataAllowed(false);
- assertFalse(mApnContext.isConcurrentVoiceAndDataAllowed());
- }
-
- @Test
- @SmallTest
- public void testEnableDisable() throws Exception {
- mApnContext.setEnabled(true);
- assertTrue(mApnContext.isEnabled());
- mApnContext.setEnabled(false);
- assertFalse(mApnContext.isEnabled());
- }
-
- @Test
- @SmallTest
- public void testProvisionApn() throws Exception {
- mContextFixture.putResource(R.string.mobile_provisioning_apn, "fake_apn");
-
- ApnSetting myApn = new ApnSetting.Builder()
- .setId(2163)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("fake_apn")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- mApnContext.setApnSetting(myApn);
- assertTrue(mApnContext.isProvisioningApn());
- mApnContext.setApnSetting(mApnSetting);
- assertFalse(mApnContext.isProvisioningApn());
- }
-
- @Test
- @SmallTest
- public void testIsReady() throws Exception {
- mApnContext.setEnabled(true);
- assertTrue(mApnContext.isReady());
-
- mApnContext.setEnabled(false);
- assertFalse(mApnContext.isReady());
- }
-
- @Test
- @SmallTest
- public void testErrorCodeRetry() throws Exception {
- mContextFixture.putStringArrayResource(
- com.android.internal.R.array.config_cell_retries_per_error_code,
- new String[]{"36,2"});
- mApnContext.resetErrorCodeRetries();
-
- assertFalse(mApnContext.restartOnError(36));
- assertTrue(mApnContext.restartOnError(36));
- assertFalse(mApnContext.restartOnError(37));
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
deleted file mode 100644
index ddf2fb8..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.InetAddress;
-import java.util.List;
-
-public class ApnSettingTest extends TelephonyTest {
-
- private PersistableBundle mBundle;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mBundle = mContextFixture.getCarrierConfigBundle();
- }
-
- @After
- public void tearDown() throws Exception {
- mBundle = null;
- super.tearDown();
- }
-
- static ApnSetting createApnSetting(int apnTypesBitmask) {
- return createApnSettingInternal(apnTypesBitmask, true);
- }
-
- private static ApnSetting createDisabledApnSetting(int apnTypesBitmask) {
- return createApnSettingInternal(apnTypesBitmask, false);
- }
-
- private static ApnSetting createApnSettingInternal(int apnTypeBitmask, boolean carrierEnabled) {
- return new ApnSetting.Builder()
- .setId(2163)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("fake_apn")
- .setApnTypeBitmask(apnTypeBitmask)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(carrierEnabled)
- .build();
- }
-
- private static void assertApnSettingsEqual(List<ApnSetting> a1, List<ApnSetting> a2) {
- assertEquals(a1.size(), a2.size());
- for (int i = 0; i < a1.size(); ++i) {
- assertApnSettingEqual(a1.get(i), a2.get(i));
- }
- }
-
- private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
- assertEquals(a1.getEntryName(), a2.getEntryName());
- assertEquals(a1.getApnName(), a2.getApnName());
- assertEquals(a1.getProxyAddressAsString(), a2.getProxyAddressAsString());
- assertEquals(a1.getProxyPort(), a2.getProxyPort());
- assertEquals(a1.getMmsc(), a2.getMmsc());
- assertEquals(a1.getMmsProxyAddressAsString(), a2.getMmsProxyAddressAsString());
- assertEquals(a1.getMmsProxyPort(), a2.getMmsProxyPort());
- assertEquals(a1.getUser(), a2.getUser());
- assertEquals(a1.getPassword(), a2.getPassword());
- assertEquals(a1.getAuthType(), a2.getAuthType());
- assertEquals(a1.getId(), a2.getId());
- assertEquals(a1.getOperatorNumeric(), a2.getOperatorNumeric());
- assertEquals(a1.getProtocol(), a2.getProtocol());
- assertEquals(a1.getRoamingProtocol(), a2.getRoamingProtocol());
- assertEquals(a1.getApnTypeBitmask(), a2.getApnTypeBitmask());
- assertEquals(a1.isEnabled(), a2.isEnabled());
- assertEquals(a1.getProfileId(), a2.getProfileId());
- assertEquals(a1.isPersistent(), a2.isPersistent());
- assertEquals(a1.getMaxConns(), a2.getMaxConns());
- assertEquals(a1.getWaitTime(), a2.getWaitTime());
- assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
- assertEquals(a1.getMtuV4(), a2.getMtuV4());
- assertEquals(a1.getMvnoType(), a2.getMvnoType());
- assertEquals(a1.getMvnoMatchData(), a2.getMvnoMatchData());
- assertEquals(a1.getNetworkTypeBitmask(), a2.getNetworkTypeBitmask());
- assertEquals(a1.getApnSetId(), a2.getApnSetId());
- assertEquals(a1.getSkip464Xlat(), a2.getSkip464Xlat());
- }
-
- @Test
- @SmallTest
- public void testIsMetered() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
- doReturn(false).when(mServiceState).getDataRoaming();
- doReturn(1).when(mPhone).getSubId();
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_SUPL, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_CBS, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DUN, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
-
- // Carrier config settings changes.
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
- }
-
- @Test
- @SmallTest
- public void testIsRoamingMetered() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- doReturn(true).when(mServiceState).getDataRoaming();
- doReturn(1).when(mPhone).getSubId();
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
-
- // Carrier config settings changes.
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_FOTA_STRING});
-
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
- }
-
- @Test
- @SmallTest
- public void testIsMeteredAnother() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
-
- doReturn(false).when(mServiceState).getDataRoaming();
- doReturn(1).when(mPhone).getSubId();
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_XCAP), mPhone));
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_ENTERPRISE), mPhone));
- }
-
- @Test
- @SmallTest
- public void testIsRoamingMeteredAnother() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
- doReturn(true).when(mServiceState).getDataRoaming();
- doReturn(2).when(mPhone).getSubId();
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
-
- assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_SUPL, mPhone));
- assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_CBS, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DUN, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
- assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
- }
-
- @Test
- @SmallTest
- public void testIsMeteredNothingCharged() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{});
-
- doReturn(false).when(mServiceState).getDataRoaming();
- doReturn(3).when(mPhone).getSubId();
-
- assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- }
-
- @Test
- @SmallTest
- public void testIsRoamingMeteredNothingCharged() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{});
- doReturn(true).when(mServiceState).getDataRoaming();
- doReturn(3).when(mPhone).getSubId();
-
- assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
-
- assertFalse(ApnSettingUtils.isMetered(
- createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- }
-
- @Test
- @SmallTest
- public void testCanHandleType() {
- String types[] = {"mms"};
-
- assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_MMS));
-
- assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT)
- .canHandleType(ApnSetting.TYPE_MMS));
-
- assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
- .canHandleType(ApnSetting.TYPE_DEFAULT));
-
- // Hipri is asymmetric
- assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
- .canHandleType(ApnSetting.TYPE_HIPRI));
- assertFalse(createApnSetting(ApnSetting.TYPE_HIPRI)
- .canHandleType(ApnSetting.TYPE_DEFAULT));
-
-
- assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
- .canHandleType(ApnSetting.TYPE_DEFAULT));
-
- assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
- .canHandleType(ApnSetting.TYPE_MMS));
-
- assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
- .canHandleType(ApnSetting.TYPE_SUPL));
-
- // special IA case - doesn't match wildcards
- assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_IA));
- assertTrue(createApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
- .canHandleType(ApnSetting.TYPE_IA));
-
- // same for emergency, mcx, xcap, and enterprise
- assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_EMERGENCY));
- assertTrue(createApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_EMERGENCY)
- .canHandleType(ApnSetting.TYPE_EMERGENCY));
- assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_MCX));
- assertTrue(createApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_MCX)
- .canHandleType(ApnSetting.TYPE_MCX));
- assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_XCAP));
- assertTrue(createApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
- .canHandleType(ApnSetting.TYPE_XCAP));
- assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_ENTERPRISE));
- assertTrue(createApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
- .canHandleType(ApnSetting.TYPE_ENTERPRISE));
-
- // check carrier disabled
- assertFalse(createDisabledApnSetting(ApnSetting.TYPE_ALL)
- .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
- .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
- .canHandleType(ApnSetting.TYPE_HIPRI));
- assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
- .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
- .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createDisabledApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
- .canHandleType(ApnSetting.TYPE_IA));
- assertFalse(createDisabledApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
- .canHandleType(ApnSetting.TYPE_XCAP));
- assertFalse(createDisabledApnSetting(
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
- .canHandleType(ApnSetting.TYPE_ENTERPRISE));
- }
-
- @Test
- @SmallTest
- public void testEquals() throws Exception {
- final int dummyInt = 1;
- final int dummyLong = 1;
- final String dummyString = "dummy";
- final String[] dummyStringArr = new String[] {"dummy"};
- final InetAddress dummyProxyAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
- final Uri dummyUri = Uri.parse("www.google.com");
- // base apn
- ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
- Field[] fields = ApnSetting.class.getDeclaredFields();
- for (Field f : fields) {
- int modifiers = f.getModifiers();
- if (Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)) {
- continue;
- }
- f.setAccessible(true);
- ApnSetting testApn = null;
- if (int.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.setInt(testApn, dummyInt + f.getInt(testApn));
- } else if (long.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.setLong(testApn, dummyLong + f.getLong(testApn));
- } else if (boolean.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.setBoolean(testApn, !f.getBoolean(testApn));
- } else if (String.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.set(testApn, dummyString);
- } else if (String[].class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.set(testApn, dummyStringArr);
- } else if (InetAddress.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.set(testApn, dummyProxyAddress);
- } else if (Uri.class.equals(f.getType())) {
- testApn = ApnSetting.makeApnSetting(baseApn);
- f.set(testApn, dummyUri);
- } else {
- fail("Unsupported field:" + f.getName());
- }
- if (testApn != null) {
- assertFalse(f.getName() + " is NOT checked", testApn.equals(baseApn));
- }
- }
- }
-
- @Test
- @SmallTest
- public void testEqualsRoamingProtocol() {
- ApnSetting apn1 = new ApnSetting.Builder()
- .setId(1234)
- .setOperatorNumeric("310260")
- .setEntryName("ims")
- .setApnName("ims")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setNetworkTypeBitmask(
- ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131071))
- .setMtuV4(1440)
- .setCarrierEnabled(true)
- .build();
-
- ApnSetting apn2 = new ApnSetting.Builder()
- .setId(1235)
- .setOperatorNumeric("310260")
- .setEntryName("ims")
- .setApnName("ims")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
- .setNetworkTypeBitmask(
- ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131072))
- .setMtuV4(1440)
- .setCarrierEnabled(true)
- .build();
-
- assertTrue(apn1.equals(apn2, false));
- assertFalse(apn1.equals(apn2, true));
- }
-
- @Test
- @SmallTest
- public void testCanHandleNetwork() {
- ApnSetting apn1 = new ApnSetting.Builder()
- .setId(1234)
- .setOperatorNumeric("310260")
- .setEntryName("ims")
- .setApnName("ims")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
- | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS))
- .setMtuV4(1440)
- .setCarrierEnabled(true)
- .build();
-
- ApnSetting apn2 = new ApnSetting.Builder()
- .setId(1235)
- .setOperatorNumeric("310260")
- .setEntryName("ims")
- .setApnName("ims")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IPV6)
- .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
- | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS))
- .setMtuV4(1440)
- .setCarrierEnabled(true)
- .build();
-
- assertFalse(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_1xRTT));
- assertTrue(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_LTE));
- assertTrue(apn1.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_UMTS));
-
- assertFalse(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_1xRTT));
- assertFalse(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_LTE));
- assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_GPRS));
- assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_EDGE));
-
- assertTrue(apn2.canSupportNetworkType(TelephonyManager.NETWORK_TYPE_GSM));
- }
-
- @Test
- public void testParcel() {
- ApnSetting apn = createApnSetting(ApnSetting.TYPE_DEFAULT);
-
- Parcel parcel = Parcel.obtain();
- apn.writeToParcel(parcel, 0 /* flags */);
- parcel.setDataPosition(0);
-
- ApnSetting fromParcel = ApnSetting.CREATOR.createFromParcel(parcel);
-
- assertEquals(apn, fromParcel);
-
- parcel.recycle();
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
deleted file mode 100644
index dcaa2a0..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ /dev/null
@@ -1,1479 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.net.InetAddresses;
-import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketData;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.vcn.VcnNetworkPolicyResult;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.UserManager;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
-import android.telephony.ServiceState.RegState;
-import android.telephony.ServiceState.RilRadioTechnology;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.TrafficDescriptor;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
-
-import com.android.internal.R;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
-import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
-import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
-import com.android.internal.telephony.metrics.DataCallSessionStats;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.function.Consumer;
-
-public class DataConnectionTest extends TelephonyTest {
- private static final int DEFAULT_DC_CID = 10;
- private static final ArrayList<TrafficDescriptor> DEFAULT_TD_LIST = new ArrayList<>();
-
- // Mocked classes
- DcTesterFailBringUpAll mDcTesterFailBringUpAll;
- ConnectionParams mCp;
- DisconnectParams mDcp;
- ApnContext mApnContext;
- ApnContext mEnterpriseApnContext;
- DcFailBringUp mDcFailBringUp;
- DataCallSessionStats mDataCallSessionStats;
- DataConnection mDefaultDc;
- DataServiceManager mDataServiceManager;
-
- private DataConnection mDc;
- private DataConnectionTestHandler mDataConnectionTestHandler;
- private DcController mDcc;
-
- private final ApnSetting mApn1 = new ApnSetting.Builder()
- .setId(2163)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private final ApnSetting mApn2 = new ApnSetting.Builder()
- .setId(2164)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private final ApnSetting mApn3 = new ApnSetting.Builder()
- .setId(2165)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setNetworkTypeBitmask(0)
- .setCarrierEnabled(true)
- .setCarrierId(1)
- .setSkip464Xlat(1)
- .build();
-
- private final ApnSetting mApn4 = new ApnSetting.Builder()
- .setId(2166)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private final ApnSetting mApn5 = new ApnSetting.Builder()
- .setId(2167)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_IMS)
- .setProtocol(ApnSetting.PROTOCOL_IPV6)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .setSkip464Xlat(Telephony.Carriers.SKIP_464XLAT_DISABLE)
- .build();
-
- private final ApnSetting mApn6 = new ApnSetting.Builder()
- .setId(2168)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private class DataConnectionTestHandler extends HandlerThread {
-
- private DataConnectionTestHandler(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- Handler h = new Handler();
- mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, h.getLooper(), "");
- mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
- mDcTesterFailBringUpAll, mDcc);
- }
- }
-
- private void setSuccessfulSetupDataResponse(int cid, ArrayList<TrafficDescriptor> tds) {
- doAnswer(invocation -> {
- final Message msg = (Message) invocation.getArguments()[10];
-
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1L)
- .setId(cid)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
- .setInterfaceName("ifname")
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
- new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
- InetAddresses.parseNumericAddress("fd00:976a::9")))
- .setGatewayAddresses(Arrays.asList(
- InetAddresses.parseNumericAddress("10.0.2.15"),
- InetAddresses.parseNumericAddress("fe80::2")))
- .setPcscfAddresses(Arrays.asList(
- InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
- InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
- InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
- .setMtu(1500)
- .setMtuV4(1500)
- .setMtuV6(1500)
- .setPduSessionId(1)
- .setQosBearerSessions(new ArrayList<>())
- .setTrafficDescriptors(tds)
- .build();
- msg.getData().putParcelable("data_call_response", response);
- msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
- msg.sendToTarget();
- return null;
- }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
- anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
- any(Message.class));
- }
-
- private void setFailedSetupDataResponse(@DataServiceCallback.ResultCode int resultCode) {
- doAnswer(invocation -> {
- final Message msg = (Message) invocation.getArguments()[10];
- msg.arg1 = resultCode;
- msg.sendToTarget();
- return null;
- }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
- anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
- any(Message.class));
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mDcTesterFailBringUpAll = mock(DcTesterFailBringUpAll.class);
- mCp = mock(ConnectionParams.class);
- mDcp = mock(DisconnectParams.class);
- mApnContext = mock(ApnContext.class);
- mEnterpriseApnContext = mock(ApnContext.class);
- mDcFailBringUp = mock(DcFailBringUp.class);
- mDataCallSessionStats = mock(DataCallSessionStats.class);
- mDefaultDc = mock(DataConnection.class);
- mDataServiceManager = mock(DataServiceManager.class);
- logd("+Setup!");
- doReturn("fake.action_detached").when(mPhone).getActionDetached();
- doReturn(false).when(mPhone).isUsingNewDataStack();
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
- replaceInstance(ConnectionParams.class, "mRilRat", mCp,
- ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
- doReturn(mApn1).when(mApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_DEFAULT_STRING).when(mApnContext).getApnType();
- doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
-
- mDcFailBringUp.saveParameters(0, 0, -2);
- doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
-
- mContextFixture.putStringArrayResource(com.android.internal.R.array
- .config_mobile_tcp_buffers, new String[]{
- "umts:131072,262144,1452032,4096,16384,399360",
- "hspa:131072,262144,2441216,4096,16384,399360",
- "hsupa:131072,262144,2441216,4096,16384,399360",
- "hsdpa:131072,262144,2441216,4096,16384,399360",
- "hspap:131072,262144,2441216,4096,16384,399360",
- "edge:16384,32768,131072,4096,16384,65536",
- "gprs:4096,8192,24576,4096,8192,24576",
- "1xrtt:16384,32768,131070,4096,16384,102400",
- "evdo:131072,262144,1048576,4096,16384,524288",
- "lte:524288,1048576,8388608,262144,524288,4194304"});
-
- mContextFixture.putResource(R.string.config_wwan_data_service_package,
- "com.android.phone");
-
- mDcp.mApnContext = mApnContext;
-
- setSuccessfulSetupDataResponse(DEFAULT_DC_CID, DEFAULT_TD_LIST);
-
- doAnswer(invocation -> {
- final Message msg = (Message) invocation.getArguments()[2];
- msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
- msg.sendToTarget();
- return null;
- }).when(mDataServiceManager).deactivateDataCall(anyInt(), anyInt(), any(Message.class));
-
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mDataServiceManager)
- .getTransportType();
-
- mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
- mDataConnectionTestHandler.start();
-
- waitForMs(200);
- mDc.setDataCallSessionStats(mDataCallSessionStats);
-
- logd("-Setup!");
- }
-
- @After
- public void tearDown() throws Exception {
- logd("tearDown");
- mDc.quitNow();
- mDc = null;
- mDataConnectionTestHandler.quit();
- mDataConnectionTestHandler.join();
- mDataConnectionTestHandler = null;
- mDcc.removeCallbacksAndMessages(null);
- mDcc = null;
- DEFAULT_TD_LIST.clear();
- waitForMs(100);
- super.tearDown();
- }
-
- private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
- Class[] cArgs = new Class[1];
- cArgs[0] = DataCallResponse.class;
- Method method = DataConnection.class.getDeclaredMethod("getSuggestedRetryDelay", cArgs);
- method.setAccessible(true);
- return (long) method.invoke(mDc, response);
- }
-
- private boolean isUnmeteredUseOnly() throws Exception {
- Method method = DataConnection.class.getDeclaredMethod("isUnmeteredUseOnly");
- method.setAccessible(true);
- return (boolean) method.invoke(mDc);
- }
-
- private boolean isEnterpriseUse() throws Exception {
- Method method = DataConnection.class.getDeclaredMethod("isEnterpriseUse");
- method.setAccessible(true);
- return (boolean) method.invoke(mDc);
- }
-
- private boolean isSuspended() throws Exception {
- Field field = DataConnection.class.getDeclaredField("mIsSuspended");
- field.setAccessible(true);
- return field.getBoolean(mDc);
- }
-
- private SetupResult setLinkProperties(DataCallResponse response, LinkProperties linkProperties)
- throws Exception {
- Class[] cArgs = new Class[2];
- cArgs[0] = DataCallResponse.class;
- cArgs[1] = LinkProperties.class;
- Method method = DataConnection.class.getDeclaredMethod("setLinkProperties", cArgs);
- method.setAccessible(true);
- return (SetupResult) method.invoke(mDc, response, linkProperties);
- }
-
- @Test
- @SmallTest
- public void testConnectEvent() {
- assertTrue(mDc.isInactive());
- connectEvent(true);
-
- verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
- eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
- verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
- eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
- verify(mSimulatedCommandsVerifier, times(1))
- .registerForNattKeepaliveStatus(any(Handler.class),
- eq(DataConnection.EVENT_KEEPALIVE_STATUS), eq(null));
- verify(mSimulatedCommandsVerifier, times(1))
- .registerForLceInfo(any(Handler.class),
- eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
- verify(mVcnManager, atLeastOnce())
- .applyVcnNetworkPolicy(
- argThat(caps ->
- caps.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
- any());
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mDataServiceManager, times(1)).setupDataCall(
- eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
- eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
-
- verify(mSimulatedCommandsVerifier, times(0))
- .allocatePduSessionId(any());
-
- assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
- if (tdCaptor.getValue() != null) {
- if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
- } else {
- assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
- assertEquals(null, tdCaptor.getValue().getOsAppId());
- }
- }
- assertTrue(mDc.isActive());
-
- assertEquals(1, mDc.getPduSessionId());
- assertEquals(3, mDc.getPcscfAddresses().length);
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::5"::equals));
- }
-
- @Test
- @SmallTest
- public void testConnectOnIwlan() throws Exception {
- assertTrue(mDc.isInactive());
- Field field = DataConnection.class.getDeclaredField("mTransportType");
- field.setAccessible(true);
- field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- connectEvent(true);
-
- verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
- eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
- verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
- eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
- verify(mSimulatedCommandsVerifier, times(0))
- .registerForNattKeepaliveStatus(any(Handler.class),
- eq(DataConnection.EVENT_KEEPALIVE_STATUS), eq(null));
- verify(mSimulatedCommandsVerifier, times(0))
- .registerForLceInfo(any(Handler.class),
- eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
- verify(mVcnManager, atLeastOnce())
- .applyVcnNetworkPolicy(
- argThat(caps ->
- caps.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
- any());
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mDataServiceManager, times(1)).setupDataCall(
- eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
- eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
-
- verify(mSimulatedCommandsVerifier, times(1))
- .allocatePduSessionId(any());
-
- assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
- if (tdCaptor.getValue() != null) {
- if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
- } else {
- assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
- assertEquals(null, tdCaptor.getValue().getOsAppId());
- }
- }
- assertTrue(mDc.isActive());
-
- assertEquals(1, mDc.getPduSessionId());
- assertEquals(3, mDc.getPcscfAddresses().length);
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
- assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::5"::equals));
- }
-
- @Test
- public void testConnectEventDuplicateContextIds() throws Exception {
- setUpDefaultData(DEFAULT_DC_CID);
-
- // Try to connect ENTERPRISE with the same CID as default
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
- doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-
- // Verify that ENTERPRISE wasn't set up
- connectEvent(false);
- assertTrue(mDc.isInactive());
-
- // Change the CID
- setSuccessfulSetupDataResponse(DEFAULT_DC_CID + 1, DEFAULT_TD_LIST);
-
- // Verify that ENTERPRISE was set up
- connectEvent(true);
- assertTrue(mDc.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
- }
-
- @Test
- public void testConnectEventDuplicateContextIdsDifferentTDs() throws Exception {
- setUpDefaultData(DEFAULT_DC_CID);
-
- // Try to connect ENTERPRISE with the same CID as default but different TrafficDescriptors
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
- doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
- ArrayList<TrafficDescriptor> tdList = new ArrayList<>();
- tdList.add(new TrafficDescriptor("dnn", DataConnection.getEnterpriseOsAppId()));
- setSuccessfulSetupDataResponse(DEFAULT_DC_CID, tdList);
-
- // Verify that ENTERPRISE wasn't set up but the TD list was updated
- connectEvent(false);
- assertTrue(mDc.isInactive());
- ArgumentCaptor<DataCallResponse> captor = ArgumentCaptor.forClass(DataCallResponse.class);
- verify(mDefaultDc).updateTrafficDescriptors(captor.capture());
- assertEquals(tdList, captor.getValue().getTrafficDescriptors());
- }
-
- @Test
- public void testConnectEventNoDefaultData() throws Exception {
- assertFalse(mDefaultDc.isActive());
-
- // Try to connect ENTERPRISE when default data doesn't exist
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
- doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-
- // Verify that ENTERPRISE wasn't set up
- connectEvent(false);
- assertTrue(mDc.isInactive());
-
- // Set up default data
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
- setUpDefaultData(1);
-
- // Verify that ENTERPRISE was set up
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- connectEvent(true);
- assertTrue(mDc.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
- }
-
- private void setUpDefaultData(int cid) throws Exception {
- replaceInstance(DataConnection.class, "mCid", mDefaultDc, cid);
- doReturn(true).when(mDefaultDc).isActive();
- doReturn(Arrays.asList(mApnContext)).when(mDefaultDc).getApnContexts();
- mDcc.addActiveDcByCid(mDefaultDc);
- assertTrue(mDefaultDc.getApnContexts().stream()
- .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
- }
-
- @Test
- @SmallTest
- public void testDisconnectEvent() {
- testConnectEvent();
-
- mDc.setPduSessionId(5);
- disconnectEvent();
-
- verify(mSimulatedCommandsVerifier, times(1)).unregisterForLceInfo(any(Handler.class));
- verify(mSimulatedCommandsVerifier, times(1))
- .unregisterForNattKeepaliveStatus(any(Handler.class));
- verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
- eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
- verify(mSimulatedCommandsVerifier, times(0))
- .releasePduSessionId(any(), eq(5));
-
- assertTrue(mDc.isInactive());
- }
-
- @Test
- @SmallTest
- public void testDisconnectOnIwlan() throws Exception {
- testConnectEvent();
-
- Field field = DataConnection.class.getDeclaredField("mTransportType");
- field.setAccessible(true);
- field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- mDc.setPduSessionId(5);
- disconnectEvent();
-
- verify(mSimulatedCommandsVerifier, times(0)).unregisterForLceInfo(any(Handler.class));
- verify(mSimulatedCommandsVerifier, times(0))
- .unregisterForNattKeepaliveStatus(any(Handler.class));
- verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
- eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
- verify(mSimulatedCommandsVerifier, times(1))
- .releasePduSessionId(any(), eq(5));
-
- assertTrue(mDc.isInactive());
- }
-
- @Test
- @SmallTest
- public void testModemSuggestRetry() throws Exception {
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(0)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
-
- response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(1000)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
-
- response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(9999)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
- }
-
- @Test
- @SmallTest
- public void testModemNotSuggestRetry() throws Exception {
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
-
- response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-5)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
-
- response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(Long.MIN_VALUE)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
- }
-
- @Test
- @SmallTest
- public void testModemSuggestNoRetry() throws Exception {
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(Long.MAX_VALUE)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
- }
-
- private NetworkCapabilities getNetworkCapabilities() throws Exception {
- Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
- method.setAccessible(true);
- return (NetworkCapabilities) method.invoke(mDc);
- }
-
- private int getDisallowedApnTypes() throws Exception {
- Method method = DataConnection.class.getDeclaredMethod("getDisallowedApnTypes");
- method.setAccessible(true);
- return (int) method.invoke(mDc);
- }
-
- @Test
- @SmallTest
- public void testNetworkCapability() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- doReturn(mApn2).when(mApnContext).getApnSetting();
- testConnectEvent();
-
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[] {"supl"});
-
- disconnectEvent();
- doReturn(mApn1).when(mApnContext).getApnSetting();
- connectEvent(true);
-
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
- }
-
- @Test
- @SmallTest
- public void testVcnNetworkCapability() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- doReturn(mApn2).when(mApnContext).getApnSetting();
-
- doAnswer(invocation -> {
- NetworkCapabilities nc = invocation.getArgument(0);
- NetworkCapabilities policyNc = new NetworkCapabilities.Builder(nc)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
-
- return new VcnNetworkPolicyResult(
- false /* isTearDownRequested */, policyNc);
- }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
- connectEvent(true);
-
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
-
- disconnectEvent();
-
- doAnswer(invocation -> {
- NetworkCapabilities nc = invocation.getArgument(0);
- NetworkCapabilities policyNc = new NetworkCapabilities.Builder(nc)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .build();
-
- return new VcnNetworkPolicyResult(
- false /* isTearDownRequested */, policyNc);
- }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
- connectEvent(true);
-
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
- }
-
- @Test
- @SmallTest
- public void testEnterpriseNetworkCapability() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- doReturn(mApn2).when(mApnContext).getApnSetting();
- testConnectEvent();
-
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
- disconnectEvent();
- setUpDefaultData(1);
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
- doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
- connectEvent(true);
-
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
- assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
- assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
- }
-
- @Test
- @SmallTest
- public void testMeteredCapability() throws Exception {
-
- mContextFixture.getCarrierConfigBundle().
- putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] {"default"});
-
- testConnectEvent();
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- }
-
- @Test
- @SmallTest
- public void testNonMeteredCapability() throws Exception {
-
- doReturn(2819).when(mPhone).getSubId();
- mContextFixture.getCarrierConfigBundle().
- putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] {"mms"});
-
- testConnectEvent();
-
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- }
-
- @Test
- public void testOverrideUnmetered() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- testConnectEvent();
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
- mDc.onMeterednessChanged(true);
- waitForMs(100);
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
- mDc.onMeterednessChanged(false);
- waitForMs(100);
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- }
-
- @Test
- public void testOverrideCongested() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- testConnectEvent();
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
- mDc.onCongestednessChanged(true);
- waitForMs(100);
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
- mDc.onCongestednessChanged(false);
- waitForMs(100);
-
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- }
-
- @Test
- public void testOwnerUid() throws Exception {
- Context mockContext = mContextFixture.getTestDouble();
- doReturn(mockContext).when(mPhone).getContext();
-
- String testPkg = "com.android.telephony.test";
- TelephonyManager telMgr = mockContext.getSystemService(TelephonyManager.class);
- doReturn(testPkg).when(telMgr).getCarrierServicePackageNameForLogicalSlot(anyInt());
-
- UserInfo info = new UserInfo(0 /* id */, "TEST_USER", 0 /* flags */);
- UserManager userMgr = mockContext.getSystemService(UserManager.class);
- doReturn(Collections.singletonList(info)).when(userMgr).getUsers();
-
- int carrierConfigPkgUid = 12345;
- PackageManager pkgMgr = mockContext.getPackageManager();
- doReturn(carrierConfigPkgUid).when(pkgMgr).getPackageUidAsUser(eq(testPkg), anyInt());
-
- mContextFixture
- .getCarrierConfigBundle()
- .putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] {"default"});
- testConnectEvent();
- AsyncResult adminUidsResult = new AsyncResult(null, new int[] {carrierConfigPkgUid}, null);
- mDc.sendMessage(DataConnection.EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, adminUidsResult);
- // Wait for carirer privilege UIDs to be updated
- waitForMs(100);
-
- assertEquals(carrierConfigPkgUid, getNetworkCapabilities().getOwnerUid());
- assertEquals(
- Collections.singleton(carrierConfigPkgUid),
- getNetworkCapabilities().getAllowedUids());
- }
-
- @Test
- public void testSubscriptionIds() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
- testConnectEvent();
-
- assertEquals(Collections.singleton(0), getNetworkCapabilities().getSubscriptionIds());
- }
-
- @Test
- public void testShouldSkip464Xlat() throws Exception {
- assertFalse(testShouldSkip464XlatEvent(mApn1));
- disconnectEvent();
-
- assertTrue(testShouldSkip464XlatEvent(mApn3));
- disconnectEvent();
-
- assertTrue(testShouldSkip464XlatEvent(mApn4));
- disconnectEvent();
-
- assertFalse(testShouldSkip464XlatEvent(mApn5));
- disconnectEvent();
- }
-
- private boolean testShouldSkip464XlatEvent(ApnSetting apn) throws Exception {
- Method method = DataConnection.class.getDeclaredMethod("shouldSkip464Xlat");
- method.setAccessible(true);
-
- doReturn(apn).when(mApnContext).getApnSetting();
- doReturn(apn.getApnTypeBitmask()).when(mApnContext).getApnTypeBitmask();
- connectEvent(true);
- logd(getNetworkCapabilities().toString());
-
- return (Boolean) method.invoke(mDc);
- }
-
- private void connectEvent(boolean validate) {
- mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
- waitForMs(200);
- if (validate) {
- assertTrue(mDc.isActive());
- }
- }
-
- private void disconnectEvent() {
- mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
- waitForMs(100);
- assertTrue(mDc.isInactive());
- }
-
- private void serviceStateChangedEvent(@RegState int dataRegState, @RilRadioTechnology int rat) {
- mDc.obtainMessage(DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED,
- new AsyncResult(null, new Pair<>(dataRegState, rat), null)).sendToTarget();
- waitForMs(100);
- }
-
- @Test
- @SmallTest
- public void testIsIpAddress() {
- // IPv4
- assertTrue(DataConnection.isIpAddress("1.2.3.4"));
- assertTrue(DataConnection.isIpAddress("127.0.0.1"));
-
- // IPv6
- assertTrue(DataConnection.isIpAddress("::1"));
- assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
- }
-
- @Test
- @SmallTest
- public void testSetLinkProperties() throws Exception {
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
-
- LinkProperties linkProperties = new LinkProperties();
- assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
- logd(linkProperties.toString());
- assertEquals(response.getInterfaceName(), linkProperties.getInterfaceName());
- assertEquals(response.getAddresses().size(), linkProperties.getAddresses().size());
- for (int i = 0; i < response.getAddresses().size(); ++i) {
- assertEquals(response.getAddresses().get(i).getAddress(),
- InetAddresses.parseNumericAddress(linkProperties.getLinkAddresses().get(i)
- .getAddress().getHostAddress()));
- }
-
- assertEquals(response.getDnsAddresses().size(), linkProperties.getDnsServers().size());
- for (int i = 0; i < response.getDnsAddresses().size(); ++i) {
- assertEquals("i = " + i, response.getDnsAddresses().get(i),
- InetAddresses.parseNumericAddress(
- linkProperties.getDnsServers().get(i).getHostAddress()));
- }
-
- assertEquals(response.getGatewayAddresses().size(), linkProperties.getRoutes().size());
- for (int i = 0; i < response.getGatewayAddresses().size(); ++i) {
- assertEquals("i = " + i, response.getGatewayAddresses().get(i),
- InetAddresses.parseNumericAddress(linkProperties.getRoutes().get(i)
- .getGateway().getHostAddress()));
- }
-
- assertEquals(response.getPcscfAddresses().size(), linkProperties.getPcscfServers().size());
- for (int i = 0; i < response.getPcscfAddresses().size(); ++i) {
- assertEquals("i = " + i, response.getPcscfAddresses().get(i),
- InetAddresses.parseNumericAddress(linkProperties.getPcscfServers().get(i)
- .getHostAddress()));
- }
-
- assertEquals(response.getMtu(), linkProperties.getMtu());
- }
-
- @Test
- @SmallTest
- public void testSetLinkPropertiesEmptyAddress() throws Exception {
- // 224.224.224.224 is an invalid address.
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
-
- LinkProperties linkProperties = new LinkProperties();
- assertEquals(SetupResult.ERROR_INVALID_ARG, setLinkProperties(response, linkProperties));
- }
-
- @Test
- @SmallTest
- public void testSetLinkPropertiesEmptyDns() throws Exception {
- // Empty dns entry.
- DataCallResponse response = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(2)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
-
- // Make sure no exception was thrown
- LinkProperties linkProperties = new LinkProperties();
- assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
- }
-
- @Test
- @SmallTest
- public void testStartKeepaliveWLAN() throws Exception {
- testConnectEvent();
- waitForMs(200);
-
- Field field = DataConnection.class.getDeclaredField("mTransportType");
- field.setAccessible(true);
- field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-
- final int sessionHandle = 0xF00;
- final int slotId = 3;
- final int interval = 10; // seconds
- // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
- // and check that the packet is sent to the RIL.
- KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
- InetAddresses.parseNumericAddress("1.2.3.4"),
- 1234,
- InetAddresses.parseNumericAddress("8.8.8.8"),
- 4500);
- mDc.obtainMessage(
- DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
- waitForMs(100);
- // testStartStopNattKeepalive() verifies that this request is passed with WWAN.
- // Thus, even though we can't see the response in NetworkAgent, we can verify that the
- // CommandsInterface never receives a request and infer that it was dropped due to WLAN.
- verify(mSimulatedCommandsVerifier, times(0))
- .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
- }
-
- public void checkStartStopNattKeepalive(boolean useCondensedFlow) throws Exception {
- testConnectEvent();
- waitForMs(200);
-
- final int sessionHandle = 0xF00;
- final int slotId = 3;
- final int interval = 10; // seconds
- // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
- // and check that the packet is sent to the RIL.
- KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
- InetAddresses.parseNumericAddress("1.2.3.4"),
- 1234,
- InetAddresses.parseNumericAddress("8.8.8.8"),
- 4500);
- mDc.obtainMessage(
- DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
- waitForMs(100);
- verify(mSimulatedCommandsVerifier, times(1))
- .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
-
- Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
- if (useCondensedFlow) {
- // Send a singled condensed response that a keepalive have been requested and the
- // activation is completed. This flow should be used if the keepalive offload request
- // is handled by a high-priority signalling path.
- AsyncResult.forMessage(
- kaStarted, new KeepaliveStatus(
- sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
- kaStarted.sendToTarget();
- } else {
- // Send the sequential responses indicating first that the request was received and
- // then that the keepalive is running. This should create an active record of the
- // keepalive in DataConnection while permitting the status from a low priority or other
- // high-latency handler to activate the keepalive without blocking a request.
- AsyncResult.forMessage(
- kaStarted, new KeepaliveStatus(
- sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
- kaStarted.sendToTarget();
- Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
- AsyncResult.forMessage(
- kaRunning, new KeepaliveStatus(
- sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
- kaRunning.sendToTarget();
- }
- waitForMs(100);
-
- // Verify that we can stop the connection, which checks that the record in DataConnection
- // has a valid mapping between slotId (from network agent) to sessionHandle (from Radio).
- mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
- waitForMs(100);
- verify(mSimulatedCommandsVerifier, times(1))
- .stopNattKeepalive(eq(sessionHandle), any(Message.class));
-
- Message kaStopped = mDc.obtainMessage(
- DataConnection.EVENT_KEEPALIVE_STOPPED, sessionHandle, slotId);
- AsyncResult.forMessage(kaStopped);
- kaStopped.sendToTarget();
- // Verify that after the connection is stopped, the mapping for a Keepalive Session is
- // removed. Thus, subsequent calls to stop the same keepalive are ignored.
- mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
- waitForMs(100);
- // Check that the mock has not been called subsequent to the previous invocation
- // while avoiding the use of reset()
- verify(mSimulatedCommandsVerifier, times(1))
- .stopNattKeepalive(anyInt(), any(Message.class));
- }
-
- @Test
- @MediumTest
- public void testStartStopNattKeepalive() throws Exception {
- checkStartStopNattKeepalive(false);
- }
-
- @Test
- @MediumTest
- public void testStartStopNattKeepaliveCondensed() throws Exception {
- checkStartStopNattKeepalive(true);
- }
-
- public void checkStartNattKeepaliveFail(boolean useCondensedFlow) throws Exception {
- testConnectEvent();
- waitForMs(200);
-
- final int sessionHandle = 0xF00;
- final int slotId = 3;
- final int interval = 10; // seconds
- // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
- // and check that the packet is sent to the RIL.
- KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
- InetAddresses.parseNumericAddress("1.2.3.4"),
- 1234,
- InetAddresses.parseNumericAddress("8.8.8.8"),
- 4500);
- mDc.obtainMessage(
- DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
- waitForMs(100);
- verify(mSimulatedCommandsVerifier, times(1))
- .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
-
- Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
- if (useCondensedFlow) {
- // Indicate in the response that the keepalive has failed.
- AsyncResult.forMessage(
- kaStarted, new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED),
- null);
- kaStarted.sendToTarget();
- } else {
- // Indicate that the keepalive is queued, and then signal a failure from the modem
- // such that a pending keepalive fails to activate.
- AsyncResult.forMessage(
- kaStarted, new KeepaliveStatus(
- sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
- kaStarted.sendToTarget();
- Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
- AsyncResult.forMessage(
- kaRunning, new KeepaliveStatus(
- sessionHandle, KeepaliveStatus.STATUS_INACTIVE), null);
- kaRunning.sendToTarget();
- }
- waitForMs(100);
- // Verify that a failed connection request cannot be stopped due to no record in
- // the DataConnection.
- mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
- waitForMs(100);
- verify(mSimulatedCommandsVerifier, times(0))
- .stopNattKeepalive(anyInt(), any(Message.class));
- }
-
- @Test
- @SmallTest
- public void testStartNattKeepaliveFail() throws Exception {
- checkStartNattKeepaliveFail(false);
- }
-
- @Test
- @SmallTest
- public void testStartNattKeepaliveFailCondensed() throws Exception {
- checkStartNattKeepaliveFail(true);
- }
-
- @Test
- @SmallTest
- public void testIsUnmeteredUseOnly() throws Exception {
- Field field = DataConnection.class.getDeclaredField("mTransportType");
- field.setAccessible(true);
- field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-
- assertFalse(isUnmeteredUseOnly());
-
- field = DataConnection.class.getDeclaredField("mTransportType");
- field.setAccessible(true);
- field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mServiceState).getDataRoaming();
- doReturn(ApnSetting.TYPE_MMS).when(mApnContext).getApnTypeBitmask();
-
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] { "default" });
-
- assertTrue(isUnmeteredUseOnly());
- }
-
- @Test
- public void testIsEnterpriseUse() throws Exception {
- assertFalse(isEnterpriseUse());
- assertFalse(mDc.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
- setUpDefaultData(1);
- replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
- doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
- doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
- connectEvent(true);
-
- assertTrue(isEnterpriseUse());
- assertTrue(mDc.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
- }
-
- @Test
- @SmallTest
- public void testGetDisallowedApnTypes() throws Exception {
- mContextFixture.getCarrierConfigBundle().putStringArray(
- CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[] { "mms", "supl", "fota" });
- testConnectEvent();
-
- assertEquals(ApnSetting.TYPE_MMS | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA,
- getDisallowedApnTypes());
- }
-
- @Test
- public void testIsSuspended() throws Exception {
- // Return false if not active state
- assertTrue(mDc.isInactive());
- assertFalse(isSuspended());
-
- // Return false for emergency APN
- doReturn(mApn6).when(mApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_EMERGENCY).when(mApnContext).getApnTypeBitmask();
- connectEvent(true);
- assertFalse(isSuspended());
-
- // Back to DEFAULT APN
- disconnectEvent();
- assertTrue(mDc.isInactive());
- doReturn(mApn1).when(mApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
- doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
- connectEvent(true);
-
- // Before getting any service state event, the connection should not be suspended.
- assertFalse(isSuspended());
-
- // Return true if combined reg state is not in service
- serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
- ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
- assertTrue(isSuspended());
-
- // Return false if in service and concurrent voice and data is allowed
- serviceStateChangedEvent(ServiceState.STATE_IN_SERVICE,
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
- assertFalse(isSuspended());
-
- // Return false if in service and concurrent voice/data not allowed but call state is idle
- doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed();
- doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
- mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
- waitForMs(100);
- assertFalse(isSuspended());
-
- // Return true if in service, concurrent voice/data not allowed, and call state not idle
- doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
- mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
- waitForMs(100);
- assertTrue(isSuspended());
- }
-
- @Test
- public void testDataCreatedWhenOutOfService() throws Exception {
- serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
- ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
- ArgumentCaptor<NetworkCapabilities> ncCaptor =
- ArgumentCaptor.forClass(NetworkCapabilities.class);
- doReturn(mock(Network.class)).when(mConnectivityManager).registerNetworkAgent(
- any(), any(), any(), ncCaptor.capture(), any(), any(), anyInt());
-
- doReturn(mApn1).when(mApnContext).getApnSetting();
- doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
- doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
- connectEvent(true);
- waitForMs(100);
-
- NetworkCapabilities nc = ncCaptor.getValue();
- // The network must be created with NOT_SUSPENDED capability.
- assertTrue(nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED));
-
- // But it's final state must be suspended.
- assertTrue(isSuspended());
- }
-
- @Test
- public void testDataServiceTempUnavailable() throws Exception {
- setFailedSetupDataResponse(DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
- replaceInstance(ConnectionParams.class, "mRequestType", mCp,
- DcTracker.REQUEST_TYPE_NORMAL);
- // Verify that no data was setup
- connectEvent(false);
- assertTrue(mDc.isInactive());
-
- // Verify that data service did not suggest any retry (i.e. Frameworks uses configured
- // retry timer).
- verify(mDataThrottler).setRetryTime(eq(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL),
- eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), eq(DcTracker.REQUEST_TYPE_NORMAL));
- }
-
- @Test
- public void testDataHandoverFailed() throws Exception {
- doReturn(mDefaultDc).when(mDcTracker).getDataConnectionByApnType(anyString());
-
- doAnswer(invocation -> {
- final Consumer<Integer> consumer = (Consumer<Integer>) invocation.getArguments()[0];
- consumer.accept(DataServiceCallback.RESULT_SUCCESS);
- return null;
- }).when(mDefaultDc).startHandover(any(Consumer.class));
-
- replaceInstance(ConnectionParams.class, "mRequestType", mCp,
- DcTracker.REQUEST_TYPE_HANDOVER);
- assertTrue(mDc.isInactive());
- connectEvent(false);
-
- // Make sure the data connection is still in inactive state
- assertTrue(mDc.isInactive());
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
deleted file mode 100644
index ffe4542..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.os.HandlerThread;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.util.Objects;
-
-public class DataEnabledSettingsTest extends TelephonyTest {
-
- private DataEnabledSettings mDataEnabledSettingsUT;
-
- private DataEnabledSettingsTestHandler mDataEnabledSettingsTestHandler;
-
- private String mRules = "";
-
- private class DataEnabledSettingsTestHandler extends HandlerThread {
-
- private DataEnabledSettingsTestHandler(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- mDataEnabledSettingsUT = new DataEnabledSettings(mPhone);
- setReady(true);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- doReturn(false).when(mPhone).isUsingNewDataStack();
- doReturn(mRules).when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
-
- doAnswer(invocation -> {
- String rules = (String) invocation.getArguments()[1];
- boolean changed = !Objects.equals(mRules, rules);
- mRules = rules;
- return changed;
- }).when(mSubscriptionController).setDataEnabledOverrideRules(anyInt(), anyString());
-
- mDataEnabledSettingsTestHandler = new DataEnabledSettingsTestHandler(
- getClass().getSimpleName());
- mDataEnabledSettingsTestHandler.start();
- waitUntilReady();
- }
-
- @After
- public void tearDown() throws Exception {
- mDataEnabledSettingsTestHandler.quit();
- mDataEnabledSettingsTestHandler.join();
- mDataEnabledSettingsTestHandler = null;
- mDataEnabledSettingsUT = null;
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testSetDataAllowedInVoiceCall() throws Exception {
- mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(true);
- ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
- verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
- stringCaptor.capture());
- assertEquals("*=nonDefault&inVoiceCall&DefaultDataOn&dsdsEnabled", stringCaptor.getValue());
-
- clearInvocations(mSubscriptionController);
-
- mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(false);
- verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
- stringCaptor.capture());
- assertEquals("", stringCaptor.getValue());
- }
-
- @Test
- @SmallTest
- public void testSetAlwaysAllowMmsData() throws Exception {
- mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, false);
- assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(true));
- ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
- verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
- stringCaptor.capture());
- assertEquals("mms=unconditionally", stringCaptor.getValue());
- assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
-
- clearInvocations(mSubscriptionController);
-
- assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(false));
- verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
- stringCaptor.capture());
- assertEquals("", stringCaptor.getValue());
- assertFalse(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
-
- mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, true);
- assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
- }
-
- @Test
- @SmallTest
- public void testSetThermalDataEnabled() throws Exception {
- mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
- false);
- assertFalse(mDataEnabledSettingsUT.isDataEnabledForReason(
- TelephonyManager.DATA_ENABLED_REASON_THERMAL));
- assertFalse(mDataEnabledSettingsUT.isDataEnabled());
-
- mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
- true);
- assertTrue(mDataEnabledSettingsUT.isDataEnabledForReason(
- TelephonyManager.DATA_ENABLED_REASON_THERMAL));
- assertTrue(mDataEnabledSettingsUT.isDataEnabled());
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
deleted file mode 100644
index d03bc8c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_NORMAL;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.ThrottleStatus;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Data throttler tests
- */
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DataThrottlerTest extends TelephonyTest {
-
- private static final boolean DBG = true;
- private DataThrottler mDataThrottler;
-
- // Mocked classes
- private DataThrottler.Callback mMockChangedCallback1;
- private DataThrottler.Callback mMockChangedCallback2;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mMockChangedCallback1 = mock(DataThrottler.Callback.class);
- mMockChangedCallback2 = mock(DataThrottler.Callback.class);
- mDataThrottler = new DataThrottler(mPhone, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback1);
- doReturn(false).when(mPhone).isUsingNewDataStack();
- }
-
- @After
- public void tearDown() throws Exception {
- mDataThrottler = null;
- super.tearDown();
- }
-
- /**
- * Test the behavior of a retry manager with no waiting APNs set.
- */
- @Test
- @SmallTest
- public void testSetRetryTime() throws Exception {
- final ArgumentCaptor<List<ThrottleStatus>> statusCaptor =
- ArgumentCaptor.forClass((Class) List.class);
-
- List<List<ThrottleStatus>> expectedStatuses = new ArrayList<>();
- processAllMessages();
- expectedStatuses.add(List.of());
-
- mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
- REQUEST_TYPE_NORMAL);
- processAllMessages();
- assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
- mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
-
- processAllMessages();
- expectedStatuses.add(List.of(
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setApnType(ApnSetting.TYPE_DEFAULT)
- .setThrottleExpiryTimeMillis(1234567890L)
- .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
- .build())
- );
-
-
- mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, 13579L,
- REQUEST_TYPE_HANDOVER);
- processAllMessages();
- assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
- assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DUN));
-
- processAllMessages();
- expectedStatuses.add(List.of(
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setApnType(ApnSetting.TYPE_DUN)
- .setThrottleExpiryTimeMillis(13579L)
- .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
- .build(),
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setApnType(ApnSetting.TYPE_DEFAULT)
- .setThrottleExpiryTimeMillis(13579L)
- .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
- .build())
- );
-
-
- mDataThrottler.setRetryTime(ApnSetting.TYPE_MMS, -10,
- REQUEST_TYPE_NORMAL);
- processAllMessages();
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
- mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
- processAllMessages();
- expectedStatuses.add(List.of(
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setNoThrottle()
- .setApnType(ApnSetting.TYPE_MMS)
- .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
- .build()
- ));
-
- mDataThrottler.setRetryTime(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_EMERGENCY,
- RetryManager.NO_RETRY, REQUEST_TYPE_HANDOVER);
-
- processAllMessages();
- expectedStatuses.add(List.of(
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setApnType(ApnSetting.TYPE_EMERGENCY)
- .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
- .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
- .build(),
- new ThrottleStatus.Builder()
- .setSlotIndex(0)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setApnType(ApnSetting.TYPE_FOTA)
- .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
- .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
- .build()
- ));
-
- assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_FOTA));
- assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_EMERGENCY));
-
-
- // Loop through statuses and test everything
- verify(mMockChangedCallback1, times(expectedStatuses.size()))
- .onThrottleStatusChanged(statusCaptor.capture());
-
- // Check actual statuses
- List<List<ThrottleStatus>> actualStatuses =
- (List<List<ThrottleStatus>>) statusCaptor.getAllValues();
- assertEquals(expectedStatuses.size(), actualStatuses.size());
-
- if (DBG) {
- logd("expectedStatuses.size() = " + expectedStatuses.size());
- logd("actualStatuses.size() = " + actualStatuses.size());
- }
-
- Comparator<ThrottleStatus> comparator = (o1, o2) ->
- Integer.compare(o1.getApnType(), o2.getApnType());
-
- for (int i = 0; i < expectedStatuses.size(); i++) {
- List<ThrottleStatus> atsExpected = new ArrayList<>(expectedStatuses.get(i));
- List<ThrottleStatus> atsActual = new ArrayList<>(actualStatuses.get(i));
-
- atsExpected.sort(comparator);
- atsActual.sort(comparator);
- assertEquals("Lists at index " + i + " don't match",
- atsExpected, atsActual);
- }
-
- this.mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback2);
- }
-
- /**
- * Test the behavior of a retry manager with no waiting APNs set.
- */
- @Test
- @SmallTest
- public void testUnthrottle() throws Exception {
- mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
- REQUEST_TYPE_NORMAL);
- processAllMessages();
- assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
-
- mDataThrottler.reset();
- processAllMessages();
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
- mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
deleted file mode 100644
index bddfdb4..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.net.InetAddresses;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DcControllerTest extends TelephonyTest {
-
- private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1;
- private static final int DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE = 2;
-
- private static final int EVENT_DATA_STATE_CHANGED = 0x00040007;
- private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 1;
-
- // Mocked classes
- private List<ApnContext> mApnContexts;
- private DataConnection mDc;
- private DataServiceManager mDataServiceManager;
- private Handler mTestHandler;
-
- UpdateLinkPropertyResult mResult;
-
- private DcController mDcc;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mApnContexts = mock(List.class);
- mDc = mock(DataConnection.class);
- mDataServiceManager = mock(DataServiceManager.class);
- mTestHandler = mock(Handler.class);
-
- doReturn("fake.action_detached").when(mPhone).getActionDetached();
- doReturn(1).when(mApnContexts).size();
- doReturn(mApnContexts).when(mDc).getApnContexts();
- doReturn(false).when(mPhone).isUsingNewDataStack();
-
- LinkProperties lp = new LinkProperties();
- mResult = new UpdateLinkPropertyResult(lp);
- doReturn(mResult).when(mDc).updateLinkProperty(any(DataCallResponse.class));
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .when(mDataServiceManager).getTransportType();
-
- mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, Looper.myLooper(),
- "");
- processAllMessages();
- }
-
- @After
- public void tearDown() throws Exception {
- mDcc.removeCallbacksAndMessages(null);
- mDcc = null;
- mResult = null;
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testDataDormant() throws Exception {
- ArrayList<DataCallResponse> l = new ArrayList<>();
- DataCallResponse dcResponse = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- l.add(dcResponse);
-
- mDc.mCid = 1;
- mDcc.addActiveDcByCid(mDc);
-
- mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
- new AsyncResult(null, l, null)));
- processAllMessages();
-
- verify(mDcTracker, times(1)).sendStopNetStatPoll(eq(DctConstants.Activity.DORMANT));
- }
-
- @Test
- @SmallTest
- public void testPhysicalLinkStatusChanged_defaultApnTypeAndDormant_registrantNotifyResult()
- throws Exception {
- ArrayList<DataCallResponse> l = new ArrayList<>();
- DataCallResponse dcResponse = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- l.add(dcResponse);
- mDc.mCid = 1;
- mDcc.addActiveDcByCid(mDc);
- ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
- List<ApnContext> apnContextList = new ArrayList<>();
- apnContextList.add(apnContext);
- doReturn(apnContextList).when(mDc).getApnContexts();
- doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
- mDcc.registerForPhysicalLinkStatusChanged(mTestHandler, EVENT_PHYSICAL_LINK_STATUS_CHANGED);
-
- mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
- new AsyncResult(null, l, null)));
- processAllMessages();
-
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong());
- Message message = messageCaptor.getValue();
- assertEquals(EVENT_PHYSICAL_LINK_STATUS_CHANGED, message.what);
- AsyncResult ar = (AsyncResult) message.obj;
- assertEquals(DataCallResponse.LINK_STATUS_DORMANT, (int) ar.result);
- }
-
- @Test
- @SmallTest
- public void testPhysicalLinkStatusChanged_imsApnTypeAndDormant_NoNotifyResult()
- throws Exception {
- testPhysicalLinkStatusChanged_defaultApnTypeAndDormant_registrantNotifyResult();
-
- ArrayList<DataCallResponse> l = new ArrayList<>();
- DataCallResponse dcResponse = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- l.add(dcResponse);
- mDc.mCid = 1;
- mDcc.addActiveDcByCid(mDc);
- ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_IMS, TAG, mDcTracker, 1);
- List<ApnContext> apnContextList = new ArrayList<>();
- apnContextList.add(apnContext);
- doReturn(apnContextList).when(mDc).getApnContexts();
- doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
-
- mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
- new AsyncResult(null, l, null)));
- processAllMessages();
-
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong());
- }
-
- @Test
- @SmallTest
- public void testPhysicalLinkStatusChanged_defaultApnTypeAndStateChanged_registrantNotifyResult()
- throws Exception {
- testPhysicalLinkStatusChanged_imsApnTypeAndDormant_NoNotifyResult();
-
- ArrayList<DataCallResponse> l = new ArrayList<>();
- DataCallResponse dcResponse = new DataCallResponse.Builder()
- .setCause(0)
- .setRetryDurationMillis(-1)
- .setId(1)
- .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE)
- .setProtocolType(ApnSetting.PROTOCOL_IP)
- .setInterfaceName(FAKE_IFNAME)
- .setAddresses(Arrays.asList(
- new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
- .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
- .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
- .setPcscfAddresses(
- Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
- .setMtuV4(1440)
- .setMtuV6(1440)
- .build();
- l.add(dcResponse);
- mDc.mCid = 1;
- mDcc.addActiveDcByCid(mDc);
- ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
- List<ApnContext> apnContextList = new ArrayList<>();
- apnContextList.add(apnContext);
- doReturn(apnContextList).when(mDc).getApnContexts();
- doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
-
- mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
- new AsyncResult(null, l, null)));
- processAllMessages();
-
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mTestHandler, times(2)).sendMessageDelayed(messageCaptor.capture(), anyLong());
- Message message = messageCaptor.getValue();
- assertEquals(EVENT_PHYSICAL_LINK_STATUS_CHANGED, message.what);
- AsyncResult ar = (AsyncResult) message.obj;
- assertEquals(DataCallResponse.LINK_STATUS_ACTIVE, (int) ar.result);
- }
-}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java
deleted file mode 100644
index 78438e4..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Mockito.doReturn;
-
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkInfo;
-import android.net.NetworkProvider;
-import android.os.Looper;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.lang.reflect.Field;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DcNetworkAgentTest extends TelephonyTest {
-
- private DcNetworkAgent mDcNetworkAgent;
- private DataConnection mDc;
- private DcController mDcc;
- private DcFailBringUp mDcFailBringUp;
-
- private DataServiceManager mDataServiceManager;
- private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
- private NetworkProvider mNetworkProvider;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- logd("+Setup!");
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
- doReturn(false).when(mPhone).isUsingNewDataStack();
- mDataServiceManager = Mockito.mock(DataServiceManager.class);
- mDcTesterFailBringUpAll = Mockito.mock(DcTesterFailBringUpAll.class);
- mNetworkProvider = Mockito.mock(NetworkProvider.class);
-
- final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
- configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
- configBuilder.setLegacyTypeName("MOBILE");
- configBuilder.setLegacySubType(TelephonyManager.NETWORK_TYPE_LTE);
- configBuilder.setLegacySubTypeName("LTE");
- configBuilder.setLegacyExtraInfo("apn");
-
- doReturn("fake.action_detached").when(mPhone).getActionDetached();
- mDcFailBringUp = new DcFailBringUp();
- mDcFailBringUp.saveParameters(0, 0, -2);
- doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
-
- mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, Looper.myLooper(),
- "");
- mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
- mDcTesterFailBringUpAll, mDcc);
-
- LinkProperties linkProperties = new LinkProperties();
- linkProperties.setInterfaceName("fake_iface");
- Field field = DataConnection.class.getDeclaredField("mLinkProperties");
- field.setAccessible(true);
- field.set(mDc, linkProperties);
-
- mDcNetworkAgent = new DcNetworkAgent(mDc, mPhone, 45, configBuilder.build(),
- mNetworkProvider, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- logd("-Setup!");
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- private void verifyDisconnected() throws Exception {
- Field field = NetworkAgent.class.getDeclaredField("mNetworkInfo");
- field.setAccessible(true);
- NetworkInfo networkInfo = (NetworkInfo) field.get(mDcNetworkAgent);
- assertEquals(NetworkInfo.DetailedState.DISCONNECTED, networkInfo.getDetailedState());
- }
-
- @Test
- public void testUnwantedTimeout() throws Exception {
- mDcNetworkAgent.markConnected();
- mDcNetworkAgent.onNetworkUnwanted();
- processAllFutureMessages();
- verifyDisconnected();
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java
deleted file mode 100644
index b08a680..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.TelephonyNetworkSpecifier;
-import android.telephony.data.ApnSetting;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class DcRequestTest extends TelephonyTest {
- NetworkRequest mNetworkRequest;
-
- // Mocked classes
- ApnConfigTypeRepository mApnConfigTypeRepo;
-
- @Before
- public void setUp() throws Exception {
- mApnConfigTypeRepo = mock(ApnConfigTypeRepository.class);
- super.setUp(getClass().getSimpleName());
- doReturn(false).when(mPhone).isUsingNewDataStack();
- }
-
- @After
- public void tearDown() throws Exception {
- mNetworkRequest = null;
- super.tearDown();
- }
-
- @Test
- public void whenNetworkRequestInternetThenPriorityZero() {
- mNetworkRequest = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
-
- when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_DEFAULT))
- .thenReturn(new ApnConfigType(ApnSetting.TYPE_DEFAULT, 0));
- DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-
- assertEquals(0, dcRequest.priority);
- }
-
- @Test
- public void whenNetworkRequestMcxThenApnConfigTypeMcxPriorityReturned() {
- mNetworkRequest = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- //Testing out multiple transport types here
- .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
- .build();
-
- when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_MCX))
- .thenReturn(new ApnConfigType(ApnSetting.TYPE_MCX, 21));
- DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
- assertEquals(21, dcRequest.priority);
- }
-
- @Test
- public void whenNetworkRequestNotCellularThenDcRequestIsNull() {
- mNetworkRequest = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
- .build();
- when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_NONE)).thenReturn(null);
- DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
- assertNull(dcRequest);
- }
-
- @Test
- public void whenNetworkRequestHasNoTransportThenPriorityStays() {
- //This seems like an invalid case that should be handled differently.
- mNetworkRequest = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
- .build();
-
- when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_MCX))
- .thenReturn(new ApnConfigType(ApnSetting.TYPE_MCX, 11));
- DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
- assertEquals(11, dcRequest.priority);
- }
-
- @Test
- public void whenNetworkRequestNotCellularWithTelephonySpecifierThenDcRequestIsNull() {
- TelephonyNetworkSpecifier telephonyNetworkSpecifier =
- new TelephonyNetworkSpecifier.Builder().setSubscriptionId(5).build();
-
- //This seems like an invalid case that should be handled differently.
- mNetworkRequest = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
- .setNetworkSpecifier(telephonyNetworkSpecifier)
- .build();
-
- when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_NONE)).thenReturn(null);
-
- DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-
- assertNull(dcRequest);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
deleted file mode 100644
index 549c587..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ /dev/null
@@ -1,2983 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ServiceInfo;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.hardware.radio.V1_0.SetupDataCallResult;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkCapabilities;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkRequest;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.Annotation;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyDisplayInfo;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.TrafficDescriptor;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import androidx.test.filters.FlakyTest;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.ISub;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.data.CellularDataService;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.time.Period;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-public class DcTrackerTest extends TelephonyTest {
- public static final String FAKE_APN1 = "FAKE APN 1";
- public static final String FAKE_APN2 = "FAKE APN 2";
- public static final String FAKE_APN3 = "FAKE APN 3";
- public static final String FAKE_APN4 = "FAKE APN 4";
- public static final String FAKE_APN5 = "FAKE APN 5";
- public static final String FAKE_APN6 = "FAKE APN 6";
- public static final String FAKE_APN7 = "FAKE APN 7";
- public static final String FAKE_APN8 = "FAKE APN 8";
- public static final String FAKE_APN9 = "FAKE APN 9";
- public static final String FAKE_IFNAME = "FAKE IFNAME";
- public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
- public static final String FAKE_GATEWAY = "11.22.33.44";
- public static final String FAKE_DNS = "55.66.77.88";
- public static final String FAKE_ADDRESS = "99.88.77.66";
- private static final int NETWORK_TYPE_NR_BITMASK =
- 1 << (TelephonyManager.NETWORK_TYPE_NR - 1);
- private static final int NETWORK_TYPE_LTE_BITMASK =
- 1 << (TelephonyManager.NETWORK_TYPE_LTE - 1);
- private static final int NETWORK_TYPE_EHRPD_BITMASK =
- 1 << (TelephonyManager.NETWORK_TYPE_EHRPD - 1);
- private static final Uri PREFERAPN_URI = Uri.parse(
- Telephony.Carriers.CONTENT_URI + "/preferapn");
- private static final int DATA_ENABLED_CHANGED = 0;
- private static final String FAKE_PLMN = "44010";
- private static final long TEST_TIMEOUT = 1000;
-
- // Mocked classes
- ISub mIsub;
- IBinder mBinder;
- SubscriptionInfo mSubscriptionInfo;
- ApnContext mApnContext;
- DataConnection mDataConnection;
- Handler mHandler;
- NetworkPolicyManager mNetworkPolicyManager;
-
- private DcTracker mDct;
- private DcTrackerTestHandler mDcTrackerTestHandler;
-
- private AlarmManager mAlarmManager;
-
- private PersistableBundle mBundle;
-
- private SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
-
- private final ApnSettingContentProvider mApnSettingContentProvider =
- new ApnSettingContentProvider();
-
- private Message mMessage;
-
- private CellularDataService mCellularDataService;
-
- private void addDataService() {
- mCellularDataService = new CellularDataService();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
- IntentFilter filter = new IntentFilter();
- mContextFixture.addService(
- DataService.SERVICE_INTERFACE,
- null,
- "com.android.phone",
- mCellularDataService.mBinder,
- serviceInfo,
- filter);
- }
-
- private class DcTrackerTestHandler extends HandlerThread {
-
- private DcTrackerTestHandler(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- mDct = new DcTracker(mPhone, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- setReady(true);
- }
- }
-
- private class ApnSettingContentProvider extends MockContentProvider {
- public final String[] FAKE_APN_COLUMNS = new String[]{
- Telephony.Carriers._ID, Telephony.Carriers.NUMERIC,
- Telephony.Carriers.NAME, Telephony.Carriers.APN,
- Telephony.Carriers.PROXY, Telephony.Carriers.PORT,
- Telephony.Carriers.MMSC, Telephony.Carriers.MMSPROXY,
- Telephony.Carriers.MMSPORT, Telephony.Carriers.USER,
- Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
- Telephony.Carriers.TYPE,
- Telephony.Carriers.PROTOCOL,
- Telephony.Carriers.ROAMING_PROTOCOL,
- Telephony.Carriers.CARRIER_ENABLED, Telephony.Carriers.BEARER,
- Telephony.Carriers.BEARER_BITMASK,
- Telephony.Carriers.PROFILE_ID,
- Telephony.Carriers.MODEM_PERSIST,
- Telephony.Carriers.MAX_CONNECTIONS,
- Telephony.Carriers.WAIT_TIME_RETRY,
- Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS,
- Telephony.Carriers.MTU,
- Telephony.Carriers.MTU_V4,
- Telephony.Carriers.MTU_V6,
- Telephony.Carriers.MVNO_TYPE,
- Telephony.Carriers.MVNO_MATCH_DATA,
- Telephony.Carriers.NETWORK_TYPE_BITMASK,
- Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK,
- Telephony.Carriers.APN_SET_ID,
- Telephony.Carriers.CARRIER_ID,
- Telephony.Carriers.SKIP_464XLAT,
- Telephony.Carriers.ALWAYS_ON
- };
-
- private int mPreferredApnSet = 0;
-
- private Object[] mPreferredApn = null;
-
- private String mFakeApn1Types = "default,supl";
-
- private String mFakeApn5Types = "dun";
-
- private int mFakeApn1Bitmask = NETWORK_TYPE_LTE_BITMASK;
-
- private int mRowIdOffset = 0;
-
- public void setFakeApn1Types(String apnTypes) {
- mFakeApn1Types = apnTypes;
- }
-
- public void setFakeApn5Types(String apnTypes) {
- mFakeApn5Types = apnTypes;
- }
-
- public void setFakeApn1NetworkTypeBitmask(int bitmask) {
- mFakeApn1Bitmask = bitmask;
- }
-
- public void setRowIdOffset(int rowIdOffset) {
- mRowIdOffset = rowIdOffset;
- }
-
- public void setFakePreferredApn(Object[] fakeApn) {
- mPreferredApn = fakeApn;
- }
-
- public Object[] getFakeApn1() {
- return new Object[]{
- 2163 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "sp-mode", // name
- FAKE_APN1, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- mFakeApn1Types, // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- mFakeApn1Bitmask, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn2() {
- return new Object[]{
- 2164 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "mopera U", // name
- FAKE_APN2, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,supl", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn3() {
- return new Object[]{
- 2165 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "b-mobile for Nexus", // name
- FAKE_APN3, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "ims", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
- 2, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- 0, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn4() {
- return new Object[]{
- 2166 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "sp-mode ehrpd", // name
- FAKE_APN4, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,supl", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn5() {
- return new Object[]{
- 2167 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "b-mobile for Nexus", // name
- FAKE_APN5, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- mFakeApn5Types, // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- 0, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn6() {
- return new Object[]{
- 2168 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "sp-mode", // name
- FAKE_APN6, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "mms,xcap", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn7() {
- return new Object[]{
- 2169 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "sp-mode", // name
- FAKE_APN7, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 1, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn8() {
- return new Object[]{
- 2170 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "IMS", // name
- FAKE_APN8, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "ims", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 2, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- -1, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- public Object[] getFakeApn9() {
- return new Object[]{
- 2171 + mRowIdOffset, // id
- FAKE_PLMN, // numeric
- "sp-mode nr", // name
- FAKE_APN9, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,enterprise", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- 0, // mtu_v4
- 0, // mtu_v6
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_NR_BITMASK, // network_type_bitmask
- 0, // lingering_network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1, // skip_464xlat
- 0 // always_on
- };
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- logd("ApnSettingContentProvider: query");
- logd(" uri = " + uri);
- logd(" projection = " + Arrays.toString(projection));
- logd(" selection = " + selection);
- logd(" selectionArgs = " + Arrays.toString(selectionArgs));
- logd(" sortOrder = " + sortOrder);
-
- if (uri.compareTo(Telephony.Carriers.CONTENT_URI) == 0
- || uri.toString().startsWith(Uri.withAppendedPath(
- Telephony.Carriers.CONTENT_URI, "filtered").toString())
- || uri.toString().startsWith(Uri.withAppendedPath(
- Telephony.Carriers.SIM_APN_URI, "filtered").toString())) {
- if (projection == null) {
-
- logd("Query '" + FAKE_PLMN + "' APN settings");
- MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
- mc.addRow(getFakeApn1());
- mc.addRow(getFakeApn2());
- mc.addRow(getFakeApn3());
- mc.addRow(getFakeApn4());
- mc.addRow(getFakeApn5());
- mc.addRow(getFakeApn6());
- mc.addRow(getFakeApn7());
- mc.addRow(getFakeApn8());
- mc.addRow(getFakeApn9());
-
- return mc;
- }
- } else if (isPathPrefixMatch(uri,
- Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapnset"))) {
- MatrixCursor mc = new MatrixCursor(
- new String[]{Telephony.Carriers.APN_SET_ID});
- // apn_set_id is the only field used with this URL
- mc.addRow(new Object[]{ mPreferredApnSet });
- mc.addRow(new Object[]{ 0 });
- return mc;
- } else if (isPathPrefixMatch(uri,
- Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn_no_update"))) {
- if (mPreferredApn == null) {
- return null;
- } else {
- MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
- mc.addRow(mPreferredApn);
- return mc;
- }
- }
-
- return null;
- }
-
- @Override
- public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
- mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
- return 1;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
- }
-
- @Before
- public void setUp() throws Exception {
- logd("DcTrackerTest +Setup!");
- super.setUp(getClass().getSimpleName());
- mIsub = Mockito.mock(ISub.class);
- mBinder = Mockito.mock(IBinder.class);
- mSubscriptionInfo = Mockito.mock(SubscriptionInfo.class);
- mApnContext = Mockito.mock(ApnContext.class);
- mDataConnection = Mockito.mock(DataConnection.class);
- mHandler = Mockito.mock(Handler.class);
- mNetworkPolicyManager = Mockito.mock(NetworkPolicyManager.class);
-
- doReturn("fake.action_detached").when(mPhone).getActionDetached();
- doReturn("fake.action_attached").when(mPhone).getActionAttached();
- doReturn(false).when(mPhone).isUsingNewDataStack();
- doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
- .getRilDataRadioTechnology();
-
- mContextFixture.putStringArrayResource(com.android.internal.R.array
- .config_mobile_tcp_buffers, new String[]{
- "umts:131072,262144,1452032,4096,16384,399360",
- "hspa:131072,262144,2441216,4096,16384,399360",
- "hsupa:131072,262144,2441216,4096,16384,399360",
- "hsdpa:131072,262144,2441216,4096,16384,399360",
- "hspap:131072,262144,2441216,4096,16384,399360",
- "edge:16384,32768,131072,4096,16384,65536",
- "gprs:4096,8192,24576,4096,8192,24576",
- "1xrtt:16384,32768,131070,4096,16384,102400",
- "evdo:131072,262144,1048576,4096,16384,524288",
- "lte:524288,1048576,8388608,262144,524288,4194304"});
-
- mContextFixture.putResource(R.string.config_wwan_data_service_package,
- "com.android.phone");
-
- ((MockContentResolver) mContext.getContentResolver()).addProvider(
- Telephony.Carriers.CONTENT_URI.getAuthority(), mApnSettingContentProvider);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
-
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
- .getPreferredTransport(anyInt());
- doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
- doReturn(true).when(mSST).getDesiredPowerState();
- doReturn(true).when(mSST).getPowerStateFromCarrier();
- doAnswer(
- (Answer<Void>) invocation -> {
- mOnSubscriptionsChangedListener =
- (SubscriptionManager.OnSubscriptionsChangedListener)
- invocation.getArguments()[0];
- return null;
- }
- ).when(mSubscriptionManager).addOnSubscriptionsChangedListener(any());
- doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
- doReturn(mNetworkPolicyManager).when(mContext)
- .getSystemService(Context.NETWORK_POLICY_SERVICE);
- doReturn(1).when(mIsub).getDefaultDataSubId();
- doReturn(mIsub).when(mBinder).queryLocalInterface(anyString());
- mServiceManagerMockedServices.put("isub", mBinder);
-
- mContextFixture.putStringArrayResource(
- com.android.internal.R.array.config_cell_retries_per_error_code,
- new String[]{"36,2"});
-
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- mBundle = mContextFixture.getCarrierConfigBundle();
-
- mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
-
- mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
- addDataService();
-
- mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
- mDcTrackerTestHandler.start();
- waitUntilReady();
-
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
-
- waitForMs(600);
- logd("DcTrackerTest -Setup!");
- }
-
- @After
- public void tearDown() throws Exception {
- logd("DcTrackerTest -tearDown");
- mDct.removeCallbacksAndMessages(null);
- mDct.stopHandlerThread();
- mDct = null;
- mDcTrackerTestHandler.quit();
- mDcTrackerTestHandler.join();
- mDcTrackerTestHandler = null;
- mCellularDataService.onDestroy();
- mCellularDataService = null;
- mAlarmManager = null;
- mBundle = null;
- mCellularDataService = null;
- waitForMs(100);
- super.tearDown();
- }
-
- // Create a successful data response
- private static SetupDataCallResult createSetupDataCallResult() {
- SetupDataCallResult result = new SetupDataCallResult();
- result.status = 0;
- result.suggestedRetryTime = -1;
- result.cid = 1;
- result.active = 2;
- result.type = "IP";
- result.ifname = FAKE_IFNAME;
- result.addresses = FAKE_ADDRESS;
- result.dnses = FAKE_DNS;
- result.gateways = FAKE_GATEWAY;
- result.pcscf = FAKE_PCSCF_ADDRESS;
- result.mtu = 1440;
- return result;
- }
-
- private void verifyDataProfile(DataProfile dp, String apn, int profileId,
- int supportedApnTypesBitmap, int type, int bearerBitmask) {
- assertEquals(profileId, dp.getProfileId());
- assertEquals(apn, dp.getApn());
- assertEquals(ApnSetting.PROTOCOL_IP, dp.getProtocolType());
- assertEquals(0, dp.getAuthType());
- assertEquals("", dp.getUserName());
- assertEquals("", dp.getPassword());
- assertEquals(type, dp.getType());
- assertEquals(0, dp.getWaitTime());
- assertTrue(dp.isEnabled());
- assertEquals(supportedApnTypesBitmap, dp.getSupportedApnTypesBitmask());
- assertEquals(ApnSetting.PROTOCOL_IP, dp.getRoamingProtocolType());
- assertEquals(bearerBitmask, dp.getBearerBitmask());
- assertEquals(0, dp.getMtu());
- assertTrue(dp.isPersistent());
- assertFalse(dp.isPreferred());
- }
-
- private void verifyDataConnected(final String apnSetting) {
- verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
- any(PendingIntent.class));
-
- assertEquals(apnSetting, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
- assertArrayEquals(new String[]{ApnSetting.TYPE_DEFAULT_STRING}, mDct.getActiveApnTypes());
-
- assertTrue(mDct.isAnyDataConnected());
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-
- LinkProperties linkProperties = mDct.getLinkProperties(ApnSetting.TYPE_DEFAULT_STRING);
- assertEquals(FAKE_IFNAME, linkProperties.getInterfaceName());
- assertEquals(1, linkProperties.getAddresses().size());
- assertEquals(FAKE_ADDRESS, linkProperties.getAddresses().get(0).getHostAddress());
- assertEquals(1, linkProperties.getDnsServers().size());
- assertEquals(FAKE_DNS, linkProperties.getDnsServers().get(0).getHostAddress());
- assertEquals(FAKE_GATEWAY, linkProperties.getRoutes().get(0).getGateway().getHostAddress());
- }
-
- private boolean isHandoverPending(int apnType) {
- try {
- Method method = DcTracker.class.getDeclaredMethod("isHandoverPending",
- int.class);
- method.setAccessible(true);
- return (boolean) method.invoke(mDct, apnType);
- } catch (Exception e) {
- fail(e.toString());
- return false;
- }
- }
-
- private void addHandoverCompleteMsg(Message onCompleteMsg,
- @Annotation.ApnType int apnType) {
- try {
- Method method = DcTracker.class.getDeclaredMethod("addHandoverCompleteMsg",
- Message.class, int.class);
- method.setAccessible(true);
- method.invoke(mDct, onCompleteMsg, apnType);
- } catch (Exception e) {
- fail(e.toString());
- }
- }
-
- private void sendInitializationEvents() {
- sendCarrierConfigChanged("");
-
- sendSimStateUpdated("");
-
- sendEventDataConnectionAttached("");
-
- waitForMs(200);
- }
-
- private void sendCarrierConfigChanged(String messagePrefix) {
- logd(messagePrefix + "Sending EVENT_CARRIER_CONFIG_CHANGED");
- mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- private void sendSimStateUpdated(String messagePrefix) {
- logd(messagePrefix + "Sending EVENT_SIM_STATE_UPDATED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED,
- TelephonyManager.SIM_STATE_LOADED, 0));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- private void sendEventDataConnectionAttached(String messagePrefix) {
- logd(messagePrefix + "Sending EVENT_DATA_CONNECTION_ATTACHED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- // Test the unmetered APN setup when data is disabled.
- @Test
- @SmallTest
- public void testTrySetupDataUnmeteredDefaultNotSelected() throws Exception {
- initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mIsub).getDefaultDataSubId();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- sendInitializationEvents();
-
- mDct.enableApn(ApnSetting.TYPE_XCAP, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the normal data call setup scenario.
- @Test
- @MediumTest
- public void testDataSetup() throws Exception {
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertFalse(dataConnectionReasons.toString(), allowed);
-
- logd("Sending EVENT_ENABLE_APN");
- // APN id 0 is APN_TYPE_DEFAULT
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- sendInitializationEvents();
-
- dataConnectionReasons = new DataConnectionReasons();
- allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertTrue(dataConnectionReasons.toString(), allowed);
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- verifyDataConnected(FAKE_APN1);
- }
-
- // Test the scenario where the first data call setup is failed, and then retry the setup later.
- @Test
- @MediumTest
- public void testDataRetry() throws Exception {
- AsyncResult ar = new AsyncResult(null,
- new Pair<>(true, DataEnabledSettings.REASON_USER_DATA_ENABLED), null);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // LOST_CONNECTION(0x10004) is a non-permanent failure, so we'll retry data setup later.
- SetupDataCallResult result = createSetupDataCallResult();
- result.status = 0x10004;
-
- // Simulate RIL fails the data call setup
- mSimulatedCommands.setDataCallResult(true, result);
-
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertFalse(dataConnectionReasons.toString(), allowed);
-
- logd("Sending EVENT_ENABLE_APN");
- // APN id 0 is APN_TYPE_DEFAULT
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- sendInitializationEvents();
-
- dataConnectionReasons = new DataConnectionReasons();
- allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertTrue(dataConnectionReasons.toString(), allowed);
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- // This time we'll let RIL command succeed.
- mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
-
- //Send event for reconnecting data
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getPhoneId(), DcTracker.REQUEST_TYPE_NORMAL, mApnContext));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- // Verify connected with APN2 setting.
- verifyDataConnected(FAKE_APN2);
- }
-
- @Test
- @MediumTest
- @Ignore
- @FlakyTest
- public void testUserDisableData() {
- //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
- //set Default and MMS to be metered in the CarrierConfigManager
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending DATA_DISABLED_CMD");
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
- AsyncResult ar = new AsyncResult(null,
- new Pair<>(false, DataEnabledSettings.REASON_USER_DATA_ENABLED), null);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- assertTrue(mDct.isAnyDataConnected());
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
- }
-
- @Test
- @MediumTest
- public void testTrySetupDataMmsAllowedDataDisabled() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
-
- List<DataProfile> dataProfiles = dpCaptor.getAllValues();
- assertEquals(2, dataProfiles.size());
-
- //Verify FAKE_APN1
- Optional<DataProfile> fakeApn1 = dataProfiles.stream()
- .filter(dp -> dp.getApn().equals(FAKE_APN1))
- .findFirst();
- assertTrue(fakeApn1.isPresent());
- verifyDataProfile(fakeApn1.get(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- //Verify FAKE_APN6
- Optional<DataProfile> fakeApn6 = dataProfiles.stream()
- .filter(dp -> dp.getApn().equals(FAKE_APN6))
- .findFirst();
- assertTrue(fakeApn6.isPresent());
- verifyDataProfile(fakeApn6.get(), FAKE_APN6, 0, ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP,
- 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending DATA_DISABLED_CMD for default data");
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
- mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier, times(2)).deactivateDataCall(
- anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
- clearInvocations(mSimulatedCommandsVerifier);
- doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
- mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
- }
-
- @Test
- @MediumTest
- public void testTrySetupDataMmsAlwaysAllowedDataDisabled() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mApnSettingContentProvider.setFakeApn1Types("mms,xcap,default");
- mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
-
- // Verify MMS was set up and is connected
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0,
- ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP | ApnSetting.TYPE_DEFAULT,
- 1, NETWORK_TYPE_LTE_BITMASK);
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
- // Verify DC has all capabilities specified in fakeApn1Types
- Map<Integer, ApnContext> apnContexts = mDct.getApnContexts().stream().collect(
- Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
- assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
- assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET));
-
- // Disable mobile data
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
- doReturn(false).when(mDataEnabledSettings).isMmsAlwaysAllowed();
- mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Expected tear down all metered DataConnections
- waitForMs(200);
- verify(mSimulatedCommandsVerifier).deactivateDataCall(
- anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
- // Allow MMS unconditionally
- clearInvocations(mSimulatedCommandsVerifier);
- doReturn(true).when(mDataEnabledSettings).isMmsAlwaysAllowed();
- doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
- mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Verify MMS was set up and is connected
- waitForMs(200);
- verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
- // Ensure MMS data connection has the MMS capability only.
- apnContexts = mDct.getApnContexts().stream().collect(
- Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
- assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
- assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
- .getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET));
- }
-
- @Test
- @MediumTest
- public void testUserDisableRoaming() {
- //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
- //step 2: set roaming disabled, data is enabled
- //step 3: under roaming service
- //step 4: only tear down metered data connections.
-
- //set Default and MMS to be metered in the CarrierConfigManager
- boolean roamingEnabled = mDct.getDataRoamingEnabled();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForHandlerAction(mDct, 1000);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForHandlerAction(mDct, 1000);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- //user is in roaming
- doReturn(true).when(mServiceState).getDataRoaming();
- logd("Sending DISABLE_ROAMING_CMD");
- mDct.setDataRoamingEnabledByUser(false);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_ROAMING_ON));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- assertTrue(mDct.isAnyDataConnected());
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
-
- // reset roaming settings / data enabled settings at end of this test
- mDct.setDataRoamingEnabledByUser(roamingEnabled);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- @Test
- @MediumTest
- public void testDataCallOnUserDisableRoaming() {
- //step 1: mock under roaming service and user disabled roaming from settings.
- //step 2: user toggled data settings on
- //step 3: only non-metered data call is established
-
- boolean roamingEnabled = mDct.getDataRoamingEnabled();
- doReturn(true).when(mServiceState).getDataRoaming();
-
- //set Default and MMS to be metered in the CarrierConfigManager
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- logd("Sending DISABLE_ROAMING_CMD");
- mDct.setDataRoamingEnabledByUser(false);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
-
- assertTrue(mDct.isAnyDataConnected());
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
-
- // reset roaming settings / data enabled settings at end of this test
- mDct.setDataRoamingEnabledByUser(roamingEnabled);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- // Test the default data switch scenario.
- @FlakyTest /* flakes 1.57% of the time */
- @Test
- @MediumTest
- public void testDDSResetAutoAttach() throws Exception {
- mContextFixture.putBooleanResource(
- com.android.internal.R.bool.config_auto_attach_data_on_creation, true);
- testDataSetup();
- assertTrue(mDct.shouldAutoAttach());
- mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- // The auto attach flag should be reset after update
- assertFalse(mDct.shouldAutoAttach());
- }
-
- // Test for API carrierActionSetMeteredApnsEnabled.
- @FlakyTest
- @Ignore
- @Test
- @MediumTest
- public void testCarrierActionSetMeteredApnsEnabled() {
- //step 1: setup two DataCalls one for Internet and IMS
- //step 2: set data is enabled
- //step 3: cold sim is detected
- //step 4: all data connection is torn down
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
- assertTrue(mDct.isAnyDataConnected());
-
- AsyncResult ar = new AsyncResult(null,
- new Pair<>(false, DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER), null);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // Validate all metered data connections have been torn down
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- assertTrue(mDct.isAnyDataConnected());
- assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- }
-
- private void initApns(String targetApn, String[] canHandleTypes) {
- doReturn(targetApn).when(mApnContext).getApnType();
- doReturn(ApnSetting.getApnTypesBitmaskFromString(mApnContext.getApnType()))
- .when(mApnContext).getApnTypeBitmask();
- doReturn(true).when(mApnContext).isConnectable();
- ApnSetting apnSetting = createApnSetting(ApnSetting.getApnTypesBitmaskFromString(
- TextUtils.join(",", canHandleTypes)));
- doReturn(apnSetting).when(mApnContext).getNextApnSetting();
- doReturn(apnSetting).when(mApnContext).getApnSetting();
- doReturn(mDataConnection).when(mApnContext).getDataConnection();
- doReturn(true).when(mApnContext).isEnabled();
- doReturn(true).when(mApnContext).isDependencyMet();
- doReturn(true).when(mApnContext).isReady();
- doReturn(false).when(mApnContext).hasRestrictedRequests(eq(true));
- }
-
- // Test the emergency APN setup.
- @Test
- @SmallTest
- public void testTrySetupDataEmergencyApn() {
- initApns(ApnSetting.TYPE_EMERGENCY_STRING,
- new String[]{ApnSetting.TYPE_EMERGENCY_STRING});
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
- anyBoolean(), any(Message.class));
- }
-
- // Test the XCAP APN setup.
- @Test
- @SmallTest
- public void testTrySetupDataXcapApn() {
- initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
- mDct.enableApn(ApnSetting.TYPE_XCAP, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
- anyBoolean(), any(Message.class));
- }
-
- // Test the ENTERPRISE APN setup.
- @Test
- public void testTrySetupDataEnterpriseApn() {
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
-
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
- assertEquals(null, tdCaptor.getValue().getOsAppId());
-
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- SetupDataCallResult result = createSetupDataCallResult();
- result.cid = 10;
- mSimulatedCommands.setDataCallResult(true, result);
- mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
- }
-
- // Test the ENTERPRISE APN setup when default data is not set up yet.
- @Test
- public void testTrySetupDataEnterpriseApnNoDefaultData() {
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
-
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
-
- // Check APN contexts with no DEFAULT set up
- Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertEquals(DctConstants.State.IDLE, apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
- assertEquals(DctConstants.State.FAILED,
- apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
- assertEquals(null, tdCaptor.getValue().getOsAppId());
-
- // Check APN contexts after DEFAULT is set up (and ENTERPRISE failure)
- apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertEquals(DctConstants.State.CONNECTED,
- apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
- assertEquals(DctConstants.State.FAILED,
- apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- SetupDataCallResult result = createSetupDataCallResult();
- result.cid = 10;
- mSimulatedCommands.setDataCallResult(true, result);
- mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
-
- // Check APN contexts after DEFAULT is set up (and ENTERPRISE reenabled)
- apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertEquals(DctConstants.State.CONNECTED,
- apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
- assertEquals(DctConstants.State.CONNECTED,
- apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
- }
-
- // Test the ENTERPRISE APN setup when the same CID is returned.
- @Test
- public void testTrySetupDataEnterpriseApnDuplicateCid() {
- mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
- NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- // mSimulatedCommandsVerifier will return the same CID in SetupDataCallResult
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
- waitForMs(200);
-
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- List<TrafficDescriptor> tds = tdCaptor.getAllValues();
- // [0] is default and [1] is enterprise, since default should be set up first
- assertEquals(FAKE_APN1, tds.get(0).getDataNetworkName());
- assertEquals(null, tds.get(0).getOsAppId());
- assertEquals(null, tds.get(1).getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(), tds.get(1).getOsAppId()));
-
- // Check APN contexts after DEFAULT and ENTERPRISE set up
- Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertEquals(DctConstants.State.CONNECTED,
- apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
- assertEquals(DctConstants.State.FAILED,
- apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
- }
-
- @Test
- @SmallTest
- public void testGetDataConnectionState() {
- initApns(ApnSetting.TYPE_SUPL_STRING,
- new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_DEFAULT_STRING});
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- // Assert that both APN_TYPE_SUPL & APN_TYPE_DEFAULT are connected even we only setup data
- // for APN_TYPE_SUPL
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_SUPL_STRING));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- }
-
- // Test the unmetered APN setup when data is disabled.
- @Test
- @SmallTest
- public void testTrySetupDataUnmeteredDataDisabled() {
- initApns(ApnSetting.TYPE_SUPL_STRING, new String[]{ApnSetting.TYPE_SUPL_STRING});
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_FOTA_STRING});
-
- mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the unmetered default APN setup when data is disabled. Default APN should always honor
- // the users's setting.
- @Test
- @SmallTest
- public void testTrySetupDataUnmeteredDefaultDataDisabled() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_MMS_STRING});
-
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, never()).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
-
- // Test the metered APN setup when data is disabled.
- @Test
- @SmallTest
- public void testTrySetupMeteredDataDisabled() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the restricted data request when data is disabled.
- @Test
- @SmallTest
- public void testTrySetupRestrictedDataDisabled() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- doReturn(false).when(mDataEnabledSettings).isDataEnabled();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- sendInitializationEvents();
-
- NetworkRequest nr = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
- mDct.requestNetwork(nr, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the restricted data request when roaming is disabled.
- @Test
- @SmallTest
- public void testTrySetupRestrictedRoamingDisabled() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- mDct.setDataRoamingEnabledByUser(false);
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- //user is in roaming
- doReturn(true).when(mServiceState).getDataRoaming();
-
- sendInitializationEvents();
-
- NetworkRequest nr = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .build();
- mDct.requestNetwork(nr, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the default data when data is not connectable.
- @Test
- @SmallTest
- public void testTrySetupNotConnectable() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- doReturn(false).when(mApnContext).isConnectable();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the default data on IWLAN.
- @Test
- @SmallTest
- public void testTrySetupDefaultOnIWLAN() {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test the default data when the phone is in ECBM.
- @Test
- @SmallTest
- public void testTrySetupDefaultInECBM() {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- doReturn(true).when(mPhone).isInEcm();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
- sendInitializationEvents();
-
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- // Test update waiting apn list when on data rat change
- @FlakyTest /* flakes 0.86% of the time */
- @Ignore
- @Test
- @SmallTest
- public void testUpdateWaitingApnListOnDataRatChange() {
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
- assertTrue(mDct.isAnyDataConnected());
-
- //data rat change from ehrpd to lte
- logd("Sending EVENT_DATA_RAT_CHANGED");
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // Verify the disconnected data call due to rat change and retry manger schedule another
- // data call setup
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- verify(mAlarmManager, times(1)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
- anyLong(), any(PendingIntent.class));
-
- //Send event for reconnecting data
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getPhoneId(), DcTracker.RELEASE_TYPE_NORMAL, mApnContext));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
- assertTrue(mDct.isAnyDataConnected());
- }
-
- /**
- * Test that fetchDunApns() returns list that prioritize the preferred APN when the preferred
- * APN including DUN type.
- */
- @Test
- public void testFetchDunApnWithPreferredApn() {
- // Set support APN types of FAKE_APN1 and FAKE_APN5
- mApnSettingContentProvider.setFakeApn1Types("default,dun");
- mApnSettingContentProvider.setFakeApn5Types("default,dun");
-
- // Set prefer apn set id.
- ContentResolver cr = mContext.getContentResolver();
- ContentValues values = new ContentValues();
- values.put(Telephony.Carriers.APN_SET_ID, 0);
- cr.update(PREFERAPN_URI, values, null, null);
- // Set FAKE_APN5 as the preferred APN.
- mApnSettingContentProvider.setFakePreferredApn(mApnSettingContentProvider.getFakeApn5());
-
- sendInitializationEvents();
-
- // Return the APN list that set the preferred APN at the top.
- ArrayList<ApnSetting> dunApns = mDct.fetchDunApns();
- assertEquals(2, dunApns.size());
- assertEquals(FAKE_APN5, dunApns.get(0).getApnName());
- assertEquals(FAKE_APN1, dunApns.get(1).getApnName());
- }
-
- // This tests simulates the race case where the sim status change event is triggered, the
- // default data connection is attached, and then the carrier config gets changed which bumps
- // the database id which we want to ignore when cleaning up connections and matching against
- // the dun APN. Tests b/158908392.
- @Test
- @SmallTest
- public void testCheckForCompatibleDataConnectionWithDunWhenIdsChange() {
- //Set dun as a support apn type of FAKE_APN1
- mApnSettingContentProvider.setFakeApn1Types("default,supl,dun");
-
- // Enable the default apn
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- //Load the sim and attach the data connection without firing the carrier changed event
- final String logMsgPrefix = "testCheckForCompatibleDataConnectionWithDunWhenIdsChange: ";
- sendSimStateUpdated(logMsgPrefix);
- sendEventDataConnectionAttached(logMsgPrefix);
- waitForMs(200);
-
- // Confirm that FAKE_APN1 comes up as a dun candidate
- ApnSetting dunApn = mDct.fetchDunApns().get(0);
- assertEquals(dunApn.getApnName(), FAKE_APN1);
- Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
- //Double check that the default apn content is connected while the dun apn context is not
- assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
- DctConstants.State.CONNECTED);
- assertNotEquals(apnContexts.get(ApnSetting.TYPE_DUN).getState(),
- DctConstants.State.CONNECTED);
-
-
- //Change the row ids the same way as what happens when we have old apn values in the
- //carrier table
- mApnSettingContentProvider.setRowIdOffset(100);
- sendCarrierConfigChanged(logMsgPrefix);
- waitForMs(200);
-
- mDct.enableApn(ApnSetting.TYPE_DUN, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
- //Make sure that the data connection used earlier wasn't cleaned up and still in use.
- assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
- apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
-
- //Check that the DUN is using the same active data connection
- assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
- apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DUN).getDataConnection());
- }
-
- @Test
- @SmallTest
- public void testCheckForCompatibleDataConnectionWithEnterprise() {
- // Allow both DEFAULT and ENTERPRISE to use APN 1
- mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
- NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
-
- // Enable the DEFAULT APN
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- sendInitializationEvents();
-
- ArgumentCaptor<TrafficDescriptor> tdCaptor =
- ArgumentCaptor.forClass(TrafficDescriptor.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
- assertEquals(null, tdCaptor.getValue().getOsAppId());
-
- // Check APN contexts after DEFAULT is set up
- Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
- assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
- DctConstants.State.CONNECTED);
- assertNotEquals(apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState(),
- DctConstants.State.CONNECTED);
-
- // Enable the ENTERPRISE APN
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- SetupDataCallResult result = createSetupDataCallResult();
- result.cid = 10;
- mSimulatedCommands.setDataCallResult(true, result);
- mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
- anyBoolean(), any(Message.class));
- assertEquals(null, tdCaptor.getValue().getDataNetworkName());
- assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
- tdCaptor.getValue().getOsAppId()));
-
- // Check APN contexts after ENTERPRISE is set up
- Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
- .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
- // Make sure that the data connection used earlier wasn't cleaned up and still in use.
- assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
- apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
-
- // Check that ENTERPRISE isn't using the same data connection as DEFAULT
- assertNotEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
- apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_ENTERPRISE).getDataConnection());
- }
-
- // Test for Data setup with APN Set ID
- @Test
- @SmallTest
- public void testDataSetupWithApnSetId() throws Exception {
- // Set the prefer apn set id to "1"
- ContentResolver cr = mContext.getContentResolver();
- ContentValues values = new ContentValues();
- values.put(Telephony.Carriers.APN_SET_ID, 1);
- cr.update(PREFERAPN_URI, values, null, null);
-
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
-
- List<DataProfile> dataProfiles = dpCaptor.getAllValues();
- assertEquals(2, dataProfiles.size());
-
- // Verify to use FAKE APN7 which is Default APN with apnSetId=1(Same as the pereferred
- // APN's set id).
- Optional<DataProfile> fakeApn7 = dataProfiles.stream()
- .filter(dp -> dp.getApn().equals(FAKE_APN7)).findFirst();
- assertTrue(fakeApn7.isPresent());
- verifyDataProfile(fakeApn7.get(), FAKE_APN7, 0, 17, 1, NETWORK_TYPE_LTE_BITMASK);
-
- // Verify to use FAKE APN8 which is IMS APN with apnSetId=-1
- // (Telephony.Carriers.MATCH_ALL_APN_SET_ID).
- Optional<DataProfile> fakeApn8 = dataProfiles.stream()
- .filter(dp -> dp.getApn().equals(FAKE_APN8)).findFirst();
- assertTrue(fakeApn8.isPresent());
- verifyDataProfile(fakeApn8.get(), FAKE_APN8, 2, 64, 1, NETWORK_TYPE_LTE_BITMASK);
- }
-
- // Test oos
- @Test
- @SmallTest
- public void testDataRatChangeOOS() {
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING});
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
- assertTrue(mDct.isAnyDataConnected());
-
- // Data rat change from ehrpd to unknown due to OOS
- logd("Sending EVENT_DATA_RAT_CHANGED");
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UNKNOWN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // Verify data connection is on
- verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
-
- // Data rat resume from unknown to ehrpd
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Verify the same data connection
- assertEquals(FAKE_APN4, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
- assertTrue(mDct.isAnyDataConnected());
- }
-
- // Test provisioning
- /*@Test
- @SmallTest
- public void testDataEnableInProvisioning() throws Exception {
- ContentResolver resolver = mContext.getContentResolver();
-
- assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
- assertTrue(mDct.isDataEnabled());
- assertTrue(mDct.isUserDataEnabled());
-
- mDct.setUserDataEnabled(false);
- waitForMs(200);
-
- assertEquals(0, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
- assertFalse(mDct.isDataEnabled());
- assertFalse(mDct.isUserDataEnabled());
-
- // Changing provisioned to 0.
- Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
- waitForMs(200);
-
- assertTrue(mDct.isDataEnabled());
- assertTrue(mDct.isUserDataEnabled());
-
- // Enable user data during provisioning. It should write to
- // Settings.Global.MOBILE_DATA and keep data enabled when provisioned.
- mDct.setUserDataEnabled(true);
- Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
- waitForMs(200);
-
- assertTrue(mDct.isDataEnabled());
- assertTrue(mDct.isUserDataEnabled());
- assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
- }*/
-
- /*
- @Test
- @SmallTest
- public void testNotifyDataEnabledChanged() throws Exception {
- doAnswer(invocation -> {
- mMessage = (Message) invocation.getArguments()[0];
- return true;
- }).when(mHandler).sendMessageDelayed(any(), anyLong());
-
- // Test registration.
- mDct.registerForDataEnabledChanged(mHandler, DATA_ENABLED_CHANGED, null);
- verifyDataEnabledChangedMessage(true, DataEnabledSettings.REASON_REGISTERED);
-
- // Disable user data. Should receive data enabled change to false.
- mDct.setUserDataEnabled(false);
- waitForMs(200);
- verifyDataEnabledChangedMessage(false, DataEnabledSettings.REASON_USER_DATA_ENABLED);
-
- // Changing provisioned to 0. Shouldn't receive any message, as data enabled remains false.
- ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
- Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
- 0);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
- waitForMs(200);
- assertFalse(mDct.isDataEnabled());
- verify(mHandler, never()).sendMessageDelayed(any(), anyLong());
-
- // Changing provisioningDataEnabled to 1. It should trigger data enabled change to true.
- Settings.Global.putInt(resolver,
- Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
- mDct.sendMessage(mDct.obtainMessage(
- DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE, null));
- waitForMs(200);
- verifyDataEnabledChangedMessage(
- true, DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED);
- }*/
-
- @Test
- @SmallTest
- public void testNetworkStatusChangedRecoveryOFF() {
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.VALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.INVALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- waitForMs(200);
-
- // Verify that its no-op when the new data stall detection feature is disabled
- verify(mSimulatedCommandsVerifier, times(0)).getDataCallList(any(Message.class));
- }
-
- @FlakyTest
- @Test
- @SmallTest
- public void testNetworkStatusChangedRecoveryON() {
- ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
- Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 0);
- doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.VALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.INVALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).getDataCallList(any(Message.class));
- }
-
- @FlakyTest
- @Test
- @SmallTest
- public void testRecoveryStepPDPReset() {
- ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
- Settings.Global.putLong(resolver,
- Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
- Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 1);
- doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.INVALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- waitForMs(200);
-
- // expected tear down all DataConnections
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- }
-
-
- @Test
- @SmallTest
- public void testRecoveryStepReRegister() {
- ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
- Settings.Global.putLong(resolver,
- Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
- Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 2);
- doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
- doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.INVALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // expected to get preferred network type
- verify(mSST, times(1)).reRegisterNetwork(eq(null));
- }
-
- @Test
- @SmallTest
- public void testRecoveryStepRestartRadio() {
- ContentResolver resolver = mContext.getContentResolver();
- Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
- Settings.Global.putLong(resolver,
- Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
- Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 3);
- doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
- doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
- NetworkAgent.INVALID_NETWORK, 1, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // expected to get preferred network type
- verify(mSST, times(1)).powerOffRadioSafely();
- }
-
- private void verifyDataEnabledChangedMessage(boolean enabled, int reason) {
- verify(mHandler, times(1)).sendMessageDelayed(any(), anyLong());
- Pair<Boolean, Integer> result = (Pair) ((AsyncResult) mMessage.obj).result;
- assertEquals(DATA_ENABLED_CHANGED, mMessage.what);
- assertEquals(enabled, result.first);
- assertEquals(reason, (int) result.second);
- clearInvocations(mHandler);
- }
-
- private void setUpSubscriptionPlans(boolean isNrUnmetered) throws Exception {
- List<SubscriptionPlan> plans = new ArrayList<>();
- if (isNrUnmetered) {
- plans.add(SubscriptionPlan.Builder
- .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
- Period.ofMonths(1))
- .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
- SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_NR})
- .build());
- }
- plans.add(SubscriptionPlan.Builder
- .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
- Period.ofMonths(1))
- .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
- .setDataUsage(500_000_000, System.currentTimeMillis())
- .build());
- replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, plans);
- doReturn(plans.toArray(new SubscriptionPlan[0])).when(mNetworkPolicyManager)
- .getSubscriptionPlans(anyInt(), any());
- }
-
- private void resetSubscriptionPlans() throws Exception {
- replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, null);
- }
-
- private void setUpSubscriptionOverride(int[] networkTypes, boolean isUnmetered)
- throws Exception {
- List<Integer> networkTypesList = null;
- if (networkTypes != null) {
- networkTypesList = new ArrayList<>();
- for (int networkType : networkTypes) {
- networkTypesList.add(networkType);
- }
- }
- replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, networkTypesList);
- replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, isUnmetered);
- }
-
- private void resetSubscriptionOverride() throws Exception {
- replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, null);
- replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, false);
- }
-
- private boolean isNetworkTypeUnmetered(int networkType) throws Exception {
- Method method = DcTracker.class.getDeclaredMethod(
- "isNetworkTypeUnmetered", int.class);
- method.setAccessible(true);
- return (boolean) method.invoke(mDct, networkType);
- }
-
- private int setUpDataConnection() throws Exception {
- Field dc = DcTracker.class.getDeclaredField("mDataConnections");
- dc.setAccessible(true);
- Field uig = DcTracker.class.getDeclaredField("mUniqueIdGenerator");
- uig.setAccessible(true);
- int id = ((AtomicInteger) uig.get(mDct)).getAndIncrement();
- ((HashMap<Integer, DataConnection>) dc.get(mDct)).put(id, mDataConnection);
- return id;
- }
-
- private void resetDataConnection(int id) throws Exception {
- Field dc = DcTracker.class.getDeclaredField("mDataConnections");
- dc.setAccessible(true);
- ((HashMap<Integer, DataConnection>) dc.get(mDct)).remove(id);
- }
-
- private void setUpWatchdogTimer() {
- // Watchdog active for 10s
- mBundle.putLong(CarrierConfigManager.KEY_5G_WATCHDOG_TIME_MS_LONG, 10000);
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- private boolean getWatchdogStatus() throws Exception {
- Field field = DcTracker.class.getDeclaredField(("mWatchdog"));
- field.setAccessible(true);
- return (boolean) field.get(mDct);
- }
-
- private Map<Integer, List<Message>> getHandoverCompletionMessages() throws Exception {
- Field field = DcTracker.class.getDeclaredField(("mHandoverCompletionMsgs"));
- field.setAccessible(true);
- return (Map<Integer, List<Message>>) field.get(mDct);
- }
-
- private void setUpTempNotMetered() {
- doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
- .when(mPhone).getRadioAccessFamily();
- doReturn(1).when(mPhone).getSubId();
- mBundle.putBoolean(CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- @Test
- public void testIsNetworkTypeUnmetered() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
- // only 5G unmetered
- setUpSubscriptionOverride(new int[]{TelephonyManager.NETWORK_TYPE_NR}, true);
-
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- // all network types metered
- setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), false);
-
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- // all network types unmetered
- setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), true);
-
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- resetSubscriptionOverride();
- }
-
- @Test
- public void testIsNetworkTypeUnmeteredViaSubscriptionPlans() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
- // only 5G unmetered
- setUpSubscriptionPlans(true);
-
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- // all network types metered
- setUpSubscriptionPlans(false);
-
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- // all network types unmetered
- List<SubscriptionPlan> plans = new ArrayList<>();
- plans.add(SubscriptionPlan.Builder
- .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
- Period.ofMonths(1))
- .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
- SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .build());
- replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, plans);
-
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
- assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
- resetSubscriptionPlans();
- }
-
- @Test
- public void testIsNrUnmeteredSubscriptionPlans() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- int id = setUpDataConnection();
- setUpSubscriptionPlans(false);
- setUpWatchdogTimer();
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- setUpTempNotMetered();
- clearInvocations(mDataConnection);
-
- // NetCapability should be metered when connected to 5G with no unmetered plan or frequency
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
- // Set SubscriptionPlans unmetered
- setUpSubscriptionPlans(true);
-
- // NetCapability should switch to unmetered with an unmetered plan
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
- // Set MMWAVE frequency to unmetered
- mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- clearInvocations(mDataConnection);
-
- // NetCapability should switch to metered without fr=MMWAVE
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
- // NetCapability should switch to unmetered with fr=MMWAVE
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
- resetDataConnection(id);
- resetSubscriptionPlans();
- }
-
- @Test
- public void testIsNrUnmeteredCarrierConfigs() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- int id = setUpDataConnection();
- setUpSubscriptionPlans(false);
- setUpWatchdogTimer();
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- setUpTempNotMetered();
- clearInvocations(mDataConnection);
-
- // NetCapability should be metered when connected to 5G with no unmetered plan or frequency
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
- // Set MMWAVE frequency to unmetered
- mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL, true);
- mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- clearInvocations(mDataConnection);
-
- // NetCapability should switch to unmetered when fr=MMWAVE and MMWAVE unmetered
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
- // NetCapability should switch to metered when fr=SUB6 and MMWAVE unmetered
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
- // Set SUB6 frequency to unmetered
- doReturn(2).when(mPhone).getSubId();
- mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
- mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL, true);
- intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- clearInvocations(mDataConnection);
-
- // NetCapability should switch to unmetered when fr=SUB6 and SUB6 unmetered
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
- resetDataConnection(id);
- resetSubscriptionPlans();
- }
-
- @Test
- public void testReevaluateUnmeteredConnectionsOnNetworkChange() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- int id = setUpDataConnection();
- setUpSubscriptionPlans(true);
- setUpWatchdogTimer();
- setUpTempNotMetered();
- clearInvocations(mDataConnection);
-
- // NetCapability should be unmetered when connected to 5G
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
- // NetCapability should be metered when disconnected from 5G
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
- resetDataConnection(id);
- resetSubscriptionPlans();
- }
-
- @Test
- public void testReevaluateUnmeteredConnectionsOnWatchdog() throws Exception {
- initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
- int id = setUpDataConnection();
- setUpSubscriptionPlans(true);
- setUpWatchdogTimer();
-
- // Watchdog inactive when unmetered and not connected to 5G
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NR_TIMER_WATCHDOG));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- assertFalse(getWatchdogStatus());
-
- // Watchdog active when unmetered and connected to 5G
- doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
- .when(mDisplayInfoController).getTelephonyDisplayInfo();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- assertTrue(getWatchdogStatus());
-
- // Watchdog inactive when metered
- setUpSubscriptionPlans(false);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- assertFalse(getWatchdogStatus());
-
- resetDataConnection(id);
- resetSubscriptionPlans();
- }
-
- /**
- * Test if this is a path prefix match against the given Uri. Verifies that
- * scheme, authority, and atomic path segments match.
- *
- * Copied from frameworks/base/core/java/android/net/Uri.java
- */
- private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
- if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
- if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
-
- List<String> segA = uriA.getPathSegments();
- List<String> segB = uriB.getPathSegments();
-
- final int size = segB.size();
- if (segA.size() < size) return false;
-
- for (int i = 0; i < size; i++) {
- if (!Objects.equals(segA.get(i), segB.get(i))) {
- return false;
- }
- }
-
- return true;
- }
-
- @Test
- public void testNoApnContextsWhenDataIsDisabled() throws java.lang.InterruptedException {
- //Check that apn contexts are loaded.
- assertTrue(mDct.getApnContexts().size() > 0);
-
- //Do work normally done in teardown.
- mDct.removeCallbacksAndMessages(null);
- mDcTrackerTestHandler.quit();
- mDcTrackerTestHandler.join();
-
- //Set isDataCapable to false for the new DcTracker being created in DcTrackerTestHandler.
- doReturn(false).when(mTelephonyManager).isDataCapable();
- mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
- setReady(false);
-
- mDcTrackerTestHandler.start();
- waitUntilReady();
- assertEquals(0, mDct.getApnContexts().size());
-
- //No need to clean up handler because that work is done in teardown.
- }
-
- @Test
- public void testRatChanged() throws Exception {
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertFalse(dataConnectionReasons.toString(), allowed);
-
- logd("Sending EVENT_ENABLE_APN");
- // APN id 0 is APN_TYPE_DEFAULT
- mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
- sendInitializationEvents();
-
- dataConnectionReasons = new DataConnectionReasons();
- allowed = mDct.isDataAllowed(dataConnectionReasons);
- assertTrue(dataConnectionReasons.toString(), allowed);
-
- ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- // Verify if RIL command was sent properly.
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
- eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
- verifyDataConnected(FAKE_APN1);
-
- doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS).when(mServiceState)
- .getRilDataRadioTechnology();
-
- logd("Sending EVENT_DATA_RAT_CHANGED");
- mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .build();
- doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
- anyInt(), anyInt());
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Data connection is running on a different thread. Have to wait.
- waitForMs(200);
- // expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- }
-
- @Test
- public void testApnConfigRepositoryUpdatedOnCarrierConfigChange() {
- assertPriority(ApnSetting.TYPE_CBS_STRING, 2);
- assertPriority(ApnSetting.TYPE_MMS_STRING, 2);
-
- mBundle.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
- new String[] {
- ApnSetting.TYPE_CBS_STRING + ":11",
- ApnSetting.TYPE_MMS_STRING + ":19",
- });
-
- sendInitializationEvents();
-
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- mContext.sendBroadcast(intent);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- assertPriority(ApnSetting.TYPE_CBS_STRING, 11);
- assertPriority(ApnSetting.TYPE_MMS_STRING, 19);
-
- //Ensure apns are in sorted order.
- ApnContext lastApnContext = null;
- for (ApnContext apnContext : mDct.getApnContexts()) {
- if (lastApnContext != null) {
- assertTrue(apnContext.getPriority() <= lastApnContext.getPriority());
- }
- lastApnContext = apnContext;
- }
- }
-
- private void assertPriority(String type, int priority) {
- assertEquals(priority, mDct.getApnContexts().stream()
- .filter(x -> x.getApnType().equals(type))
- .findFirst().get().getPriority());
- }
-
- @Test
- public void testProvisionBroadcastReceiver() {
- Intent intent = new Intent("com.android.internal.telephony.PROVISION");
- intent.putExtra("provision.phone.id", mPhone.getPhoneId());
- try {
- mContext.sendBroadcast(intent);
- } catch (SecurityException e) {
- fail();
- }
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- }
-
- @Test
- public void testRetryHandoverWhenDisconnecting() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- setUpDataConnection();
- SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
- ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
- doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
- doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
- doReturn(false).when(mApnContext).isConnectable();
- doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
- doReturn(DctConstants.State.DISCONNECTING).when(mApnContext).getState();
- replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
- replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
-
- sendInitializationEvents();
-
- logd("Sending EVENT_ENABLE_APN");
- // APN id 0 is APN_TYPE_DEFAULT
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
- mDct.obtainMessage(12345));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- assertTrue(isHandoverPending(ApnSetting.TYPE_IMS));
-
- // Verify no handover request was sent
- verify(mDataConnection, never()).bringUp(any(ApnContext.class), anyInt(), anyInt(),
- any(Message.class), anyInt(), anyInt(), anyInt(), anyBoolean());
-
- doReturn(DctConstants.State.RETRYING).when(mApnContext).getState();
- // Data now is disconnected
- doReturn(true).when(mApnContext).isConnectable();
- doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
- new AsyncResult(Pair.create(mApnContext, 0), null, null)));
-
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- verify(mDataConnection).bringUp(any(ApnContext.class), anyInt(), anyInt(),
- any(Message.class), anyInt(), eq(DcTracker.REQUEST_TYPE_HANDOVER), anyInt(),
- anyBoolean());
- }
-
- @Test
- public void testDataUnthrottled() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_UNTHROTTLED,
- new AsyncResult(null, FAKE_APN3, null)));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- verify(mDataThrottler).setRetryTime(
- eq(ApnSetting.TYPE_IMS),
- eq(RetryManager.NO_SUGGESTED_RETRY_DELAY),
- eq(DcTracker.REQUEST_TYPE_NORMAL));
- }
-
- @Test
- public void testDataUnthrottledAfterAPNChanged() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_CHANGED, null));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- // Verify unthrottling
- verify(mDataThrottler, times(2)).reset();
- }
-
- @Test
- public void testDataUnthrottledOnSimStateChanged() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
- sendSimStateUpdated("testDataUnthrottledOnSimStateChanged");
-
- // Verify unthrottling
- verify(mDataThrottler, times(2)).reset();
- }
-
- @Test
- public void testHandlingSecondHandoverRequest() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- setUpDataConnection();
- SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
- ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
- doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
- doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
- doReturn(false).when(mApnContext).isConnectable();
- doReturn(DctConstants.State.CONNECTING).when(mApnContext).getState();
- replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
- replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
-
- sendInitializationEvents();
-
- logd("Sending EVENT_ENABLE_APN");
- // APN id 0 is APN_TYPE_DEFAULT
- Message msg = mDct.obtainMessage(12345);
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, msg);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- Map<Integer, List<Message>> msgs = getHandoverCompletionMessages();
- // Make sure the messages was queued properly instead of fired right away.
- assertTrue(msgs.get(ApnSetting.TYPE_IMS).contains(msg));
- }
-
- @Test
- public void testDataThrottledNotAllowData() throws Exception {
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
- doReturn(SystemClock.elapsedRealtime() + 100000).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_IMS);
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- sendInitializationEvents();
-
- DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = mDct.isDataAllowed(mApnContext, DcTracker.REQUEST_TYPE_NORMAL,
- dataConnectionReasons);
- assertFalse(dataConnectionReasons.toString(), allowed);
- assertTrue(dataConnectionReasons.contains(DataDisallowedReasonType.DATA_THROTTLED));
-
- // Makre sure no data setup request
- verify(mSimulatedCommandsVerifier, never()).setupDataCall(
- anyInt(), any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(),
- anyInt(), any(), any(), anyBoolean(), any(Message.class));
- }
-
- @Test
- public void testNotifyDataDisconnected() {
- // Verify notify data disconnected on DCT constructor, initialized in setUp()
- ArgumentCaptor<PreciseDataConnectionState> captor =
- ArgumentCaptor.forClass(PreciseDataConnectionState.class);
- verify(mPhone, times(13)).notifyDataConnection(captor.capture());
- for (PreciseDataConnectionState state : captor.getAllValues()) {
- assertEquals(TelephonyManager.DATA_DISCONNECTED, state.getState());
- }
- }
-
- /**
- * There is a corresponding test {@link DataConnectionTest#testDataServiceTempUnavailable()} to
- * test DataConnection behavior.
- */
- @Test
- public void testDataServiceTempUnavailable() {
- Handler handler = Mockito.mock(Handler.class);
- Message handoverCompleteMessage = Message.obtain(handler);
- addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_SETUP_COMPLETE,
- DcTracker.REQUEST_TYPE_HANDOVER, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
- new AsyncResult(Pair.create(mApnContext, 0),
- DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE, new Exception())));
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- // Ensure handover is not completed yet
- verify(handler, never()).sendMessageDelayed(any(), anyLong());
- }
-
- @Test
- public void testNormalRequestDoesNotFailHandoverRequest() {
- Handler handler = Mockito.mock(Handler.class);
- Message handoverCompleteMessage = Message.obtain(handler);
- addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- // Ensure handover is not completed yet
- verify(handler, never()).sendMessageDelayed(any(), anyLong());
- }
-
- @Test
- public void testPreferenceChangedFallback() {
- Handler handler = Mockito.mock(Handler.class);
- doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
- .getPreferredTransport(anyInt());
- Message handoverCompleteMessage = Message.obtain(handler);
- addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
- initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
- mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
- handoverCompleteMessage);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
- Bundle bundle = handoverCompleteMessage.getData();
- assertTrue(bundle.getBoolean("extra_handover_failure_fallback"));
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
deleted file mode 100644
index 4acfefb..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ /dev/null
@@ -1,996 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.uicc.UiccController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-
-/**
- * APN retry manager tests
- */
-public class RetryManagerTest extends TelephonyTest {
-
- // This is the real APN data for the Japanese carrier NTT Docomo.
- private final ApnSetting mApn1 = new ApnSetting.Builder()
- .setId(2163)
- .setOperatorNumeric("44010")
- .setEntryName("sp-mode")
- .setApnName("spmode.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private final ApnSetting mApn2 = new ApnSetting.Builder()
- .setId(2164)
- .setOperatorNumeric("44010")
- .setEntryName("mopera U")
- .setApnName("mopera.net")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private final ApnSetting mApn3 = new ApnSetting.Builder()
- .setId(2165)
- .setOperatorNumeric("44010")
- .setEntryName("b-mobile for Nexus")
- .setApnName("bmobile.ne.jp")
- .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
- .setProtocol(ApnSetting.PROTOCOL_IP)
- .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
- .setCarrierEnabled(true)
- .build();
-
- private PersistableBundle mBundle;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mBundle = mContextFixture.getCarrierConfigBundle();
- doReturn(false).when(mPhone).isUsingNewDataStack();
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
- replaceInstance(UiccController.class, "mInstance", null, mUiccController);
- }
-
- @After
- public void tearDown() throws Exception {
- mBundle = null;
- super.tearDown();
- }
-
- /**
- * Test the behavior of a retry manager with no waiting APNs set.
- */
- @Test
- @SmallTest
- public void testRetryManagerEmpty() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:2000"});
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-
- long delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the basic retry scenario where only one APN and no retry configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerOneApnNoRetry() throws Exception {
-
- mBundle.putStringArray(
- CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the basic retry scenario where only one APN with two retries configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerOneApnTwoRetries() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"supl:2000,3000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_SUPL);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(3000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- // No matter how many times we call getNextApnSetting, it should always return the next APN
- // with NO_RETRY because we've already reached the maximum retry count.
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the basic retry scenario where two waiting APNs with one retry configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerTwoApnsOneRetry() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"others:2000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the basic retry scenario where two waiting APNs with two retries configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerTwoApnsTwoRetries() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"dun:2000,5000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(5000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the basic retry scenario where two mms APNs with two retries configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerTwoMmsApnsTwoRetries() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"mms: 3000,6000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(3000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(6000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the permanent fail scenario with one APN configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerApnPermanentFailed() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"fota:1000,4000,7000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
- waitingApns.add(apn);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_FOTA);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
-
- rm.markApnPermanentFailed(apn);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
- }
-
- /**
- * Test the permanent fail scenario with two APNs configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerApnPermanentFailedWithTwoApns() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"mms : 1000,4000,7000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- rm.markApnPermanentFailed(myApn1);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(7000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the permanent fail scenario with three APNs configured.
- */
- @Test
- @SmallTest
- public void testRetryManagerApnPermanentFailedWithThreeApns() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:2000:2000,3000:3000", "ims:1000,4000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
- waitingApns.add(myApn3);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_IMS);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- rm.markApnPermanentFailed(myApn2);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the permanent fail scenario with two APN all failed
- */
- @Test
- @SmallTest
- public void testRetryManagerApnPermanentFailedAll() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:1000,4000,7000,9000", "mms:1234,4123"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- rm.markApnPermanentFailed(myApn1);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(7000, delay);
-
- rm.markApnPermanentFailed(myApn2);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
- }
-
- /**
- * Test the randomized delay scenario.
- */
- @Test
- @SmallTest
- public void testRetryManagerDelayWithRandomization() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:default_randomization=1000,3000:2000,6000:3000,10000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertTrue(delay >= 3000 && delay < 5000); // 3s + 2s rand
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertTrue(delay >= 6000 && delay < 9000); // 6s + 3s rand
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertTrue(delay >= 10000 && delay < 11000); // 10s + 1s default rand
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the retry forever scenario
- */
- @Test
- @SmallTest
- public void testRetryManagerRetryForever() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:max_retries=infinite,1000,2000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
- }
-
- /**
- * Test the explicit max retry scenario.
- */
- @Test
- @SmallTest
- public void testRetryManagerExplicitMaxRetry() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"hipri: max_retries=4,1000,2000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_HIPRI);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the fail fast scenario.
- */
- @Test
- @SmallTest
- public void testRetryManagerFailFast() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:1000,5000"});
-
- mBundle.putLong(CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 2000);
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(true);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(true);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(true);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(true);
- assertEquals(2000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(true);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the permanent fail scenario with two APN all failed and then reset
- */
- @Test
- @SmallTest
- public void testRetryManagerApnPermanentFailedAllAndThenReset() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"dun:1000,4000,7000,9000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- rm.markApnPermanentFailed(myApn1);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(7000, delay);
-
- rm.markApnPermanentFailed(myApn2);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn == null);
-
- // reset the retry manager
-
- ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
- waitingApns.clear();
- waitingApns.add(myApn3);
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"dun:3000,8000"});
-
- rm.setWaitingApns(waitingApns);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(3000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(8000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn3));
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- private void assertRange(long low, long high, long value) {
- if (value >= low && value <= high) return;
- fail("Not in range[" + low + "," + high + "], value=" + value);
- }
-
- /**
- * Test the scenario where modem suggests retry the current APN once
- */
- @Test
- @SmallTest
- public void testRetryManagerModemSuggestedRetryOnce() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"others:1000,4000,7000,9000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- // Network suggests retrying the current APN
- doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_MMS);
- delay = rm.getDelayForNextApn(false);
- assertRange(2450, 2500, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_MMS);
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- // Modem suggests retrying the current APN
- //rm.setModemSuggestedDelay(30000);
- doReturn(30000 + SystemClock.elapsedRealtime()).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_MMS);
- delay = rm.getDelayForNextApn(false);
- assertRange(29950, 30000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_MMS);
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
- }
-
- /**
- * Test the scenario where modem suggests not retrying
- */
- @Test
- @SmallTest
- public void testRetryManagerModemSuggestedNoRetry() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"default:1000,4000,7000,9000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- // Modem suggests retrying the current APN
- doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_DEFAULT);
- delay = rm.getDelayForNextApn(false);
- assertRange(2450, 2500, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- doReturn(RetryManager.NO_RETRY).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_DEFAULT);
- delay = rm.getDelayForNextApn(false);
- assertEquals(RetryManager.NO_RETRY, delay);
- }
-
- /**
- * Test the scenario that network suggests the same retry for too many times
- */
- @Test
- @SmallTest
- public void testRetryNetworkSuggestedRetryTooManyTimes() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
-
- int maxRetryCount = 10;
-
- mBundle.putInt(CarrierConfigManager
- .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
- maxRetryCount);
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
- rm.setWaitingApns(waitingApns);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- delay = rm.getDelayForNextApn(false);
- assertEquals(1000, delay);
-
- for (int i = 0; i < maxRetryCount; i++) {
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_DEFAULT);
- delay = rm.getDelayForNextApn(false);
- assertRange(2450, 2500, delay);
- }
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_DEFAULT);
- delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn2));
- doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
- .getRetryTime(ApnSetting.TYPE_DEFAULT);
- delay = rm.getDelayForNextApn(false);
- assertEquals(4000, delay);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
new file mode 100644
index 0000000..ce59cc6
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelector;
+import android.telephony.EmergencyRegResult;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.telephony.ims.ImsReasonInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.CallFailCause;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DomainSelectionConnectionTest extends TelephonyTest {
+
+ private static final String TELECOM_CALL_ID1 = "TC1";
+
+ private DomainSelectionController mDomainSelectionController;
+ private DomainSelectionConnection.DomainSelectionConnectionCallback mConnectionCallback;
+ private DomainSelectionConnection mDsc;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+
+ mDomainSelectionController = Mockito.mock(DomainSelectionController.class);
+ mConnectionCallback =
+ Mockito.mock(DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDsc = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testTransportSelectorCallback() {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomain() {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+ false, 0, null, null, null, null);
+
+ mDsc.selectDomain(attr);
+
+ verify(mDomainSelectionController).selectDomain(any(), eq(transportCallback));
+ }
+
+ @Test
+ @SmallTest
+ public void testWwanSelectorCallback() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ WwanSelectorCallback wwanCallback = null;
+ wwanCallback = transportCallback.onWwanSelected();
+
+ assertNotNull(wwanCallback);
+ }
+
+ @Test
+ @SmallTest
+ public void testWwanSelectorCallbackAsync() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+ replaceInstance(DomainSelectionConnection.class, "mWwanSelectedExecutor",
+ mDsc, new Executor() {
+ public void execute(Runnable command) {
+ command.run();
+ }
+ });
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ Consumer<WwanSelectorCallback> consumer = Mockito.mock(Consumer.class);
+ transportCallback.onWwanSelected(consumer);
+
+ verify(consumer).accept(any());
+ }
+
+ @Test
+ @SmallTest
+ public void testWwanSelectorCallbackOnRequestEmergencyNetworkScan() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ WwanSelectorCallback wwanCallback = transportCallback.onWwanSelected();
+
+ assertNotNull(wwanCallback);
+
+ replaceInstance(DomainSelectionConnection.class, "mLooper",
+ mDsc, mTestableLooper.getLooper());
+ List<Integer> preferredNetworks = new ArrayList<>();
+ preferredNetworks.add(EUTRAN);
+ preferredNetworks.add(UTRAN);
+ int scanType = SCAN_TYPE_NO_PREFERENCE;
+ Consumer<EmergencyRegResult> consumer = Mockito.mock(Consumer.class);
+
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, null, consumer);
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ verify(mPhone).registerForEmergencyNetworkScan(
+ handlerCaptor.capture(), eventCaptor.capture(), any());
+
+ int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+ verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+ eq(scanType), any());
+
+ Handler handler = handlerCaptor.getValue();
+ int event = eventCaptor.getValue();
+
+ assertNotNull(handler);
+
+ doReturn(new Executor() {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }).when(mDomainSelectionController).getDomainSelectionServiceExecutor();
+ EmergencyRegResult regResult =
+ new EmergencyRegResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
+ processAllMessages();
+
+ verify(consumer).accept(eq(regResult));
+ }
+
+ @Test
+ @SmallTest
+ public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndCancel() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ WwanSelectorCallback wwanCallback = transportCallback.onWwanSelected();
+
+ assertNotNull(wwanCallback);
+
+ replaceInstance(DomainSelectionConnection.class, "mLooper",
+ mDsc, mTestableLooper.getLooper());
+ CancellationSignal signal = new CancellationSignal();
+ wwanCallback.onRequestEmergencyNetworkScan(new ArrayList<>(),
+ SCAN_TYPE_NO_PREFERENCE, signal, Mockito.mock(Consumer.class));
+
+ verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+ signal.cancel();
+
+ verify(mPhone).cancelEmergencyNetworkScan(eq(false), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectorCancelSelection() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ DomainSelector domainSelector = Mockito.mock(DomainSelector.class);
+ transportCallback.onCreated(domainSelector);
+
+ mDsc.cancelSelection();
+
+ verify(domainSelector).cancelSelection();
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectorReselectDomain() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ DomainSelector domainSelector = Mockito.mock(DomainSelector.class);
+ transportCallback.onCreated(domainSelector);
+
+ DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+ false, CallFailCause.ERROR_UNSPECIFIED, null, null, null, null);
+
+ CompletableFuture<Integer> future = mDsc.reselectDomain(attr);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(domainSelector).reselectDomain(any());
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectorFinishSelection() throws Exception {
+ mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+ mDomainSelectionController);
+
+ TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+ assertNotNull(transportCallback);
+
+ DomainSelector domainSelector = Mockito.mock(DomainSelector.class);
+ transportCallback.onCreated(domainSelector);
+
+ mDsc.finishSelection();
+
+ verify(domainSelector).finishSelection();
+ }
+
+ private DomainSelectionService.SelectionAttributes getSelectionAttributes(
+ int slotId, int subId, int selectorType, boolean isEmergency,
+ boolean exited, int callFailCause, String callId, String number,
+ ImsReasonInfo imsReasonInfo, EmergencyRegResult regResult) {
+ DomainSelectionService.SelectionAttributes.Builder builder =
+ new DomainSelectionService.SelectionAttributes.Builder(
+ slotId, subId, selectorType)
+ .setEmergency(isEmergency)
+ .setExitedFromAirplaneMode(exited)
+ .setCsDisconnectCause(callFailCause);
+
+ if (callId != null) builder.setCallId(callId);
+ if (number != null) builder.setNumber(number);
+ if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
+ if (regResult != null) builder.setEmergencyRegResult(regResult);
+
+ return builder.build();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
new file mode 100644
index 0000000..2c65b50
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
+import static com.android.internal.telephony.RIL.RADIO_HAL_VERSION_2_0;
+import static com.android.internal.telephony.RIL.RADIO_HAL_VERSION_2_1;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.DomainSelectionService;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for DomainSelectionResolver.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DomainSelectionResolverTest extends TelephonyTest {
+ // Mock classes
+ private DomainSelectionController mDsController;
+ private DomainSelectionConnection mDsConnection;
+ private DomainSelectionService mDsService;
+
+ private DomainSelectionResolver mDsResolver;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mDsController = Mockito.mock(DomainSelectionController.class);
+ mDsConnection = Mockito.mock(DomainSelectionConnection.class);
+ mDsService = Mockito.mock(DomainSelectionService.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDsResolver = null;
+ mDsService = null;
+ mDsConnection = null;
+ mDsController = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testGetInstance() throws IllegalStateException {
+ assertThrows(IllegalStateException.class, () -> {
+ DomainSelectionResolver.getInstance();
+ });
+
+ DomainSelectionResolver.make(mContext, true);
+ DomainSelectionResolver resolver = DomainSelectionResolver.getInstance();
+
+ assertNotNull(resolver);
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionSupportedWhenDeviceConfigDisabled() {
+ setUpResolver(false, RADIO_HAL_VERSION_2_1);
+
+ assertFalse(mDsResolver.isDomainSelectionSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionSupportedWhenHalVersionLessThan20() {
+ setUpResolver(true, RADIO_HAL_VERSION_2_0);
+
+ assertFalse(mDsResolver.isDomainSelectionSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionSupported() {
+ setUpResolver(true, RADIO_HAL_VERSION_2_1);
+
+ assertTrue(mDsResolver.isDomainSelectionSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDomainSelectionConnectionWhenNotInitialized() throws Exception {
+ setUpResolver(true, RADIO_HAL_VERSION_2_1);
+
+ assertThrows(IllegalStateException.class, () -> {
+ mDsResolver.getDomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true);
+ });
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDomainSelectionConnectionWhenPhoneNull() throws Exception {
+ setUpResolver(true, RADIO_HAL_VERSION_2_1);
+ mDsResolver.initialize(mDsService);
+ assertNull(mDsResolver.getDomainSelectionConnection(null, SELECTOR_TYPE_CALLING, true));
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDomainSelectionConnectionWhenImsNotAvailable() throws Exception {
+ setUpResolver(true, RADIO_HAL_VERSION_2_1);
+ mDsResolver.initialize(mDsService);
+ when(mPhone.isImsAvailable()).thenReturn(false);
+
+ assertNull(mDsResolver.getDomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true));
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDomainSelectionConnection() throws Exception {
+ setUpResolver(true, RADIO_HAL_VERSION_2_1);
+ setUpController();
+ mDsResolver.initialize(mDsService);
+ when(mPhone.isImsAvailable()).thenReturn(true);
+
+ assertNotNull(mDsResolver.getDomainSelectionConnection(
+ mPhone, SELECTOR_TYPE_CALLING, true));
+ }
+
+ private void setUpResolver(boolean deviceConfigEnabled, HalVersion halVersion) {
+ mDsResolver = new DomainSelectionResolver(mContext, deviceConfigEnabled);
+ when(mPhone.getHalVersion(eq(HAL_SERVICE_NETWORK))).thenReturn(halVersion);
+ }
+
+ private void setUpController() {
+ mDsResolver.setDomainSelectionControllerFactory(
+ new DomainSelectionResolver.DomainSelectionControllerFactory() {
+ @Override
+ public DomainSelectionController create(Context context,
+ DomainSelectionService service) {
+ return mDsController;
+ }
+ });
+
+ when(mDsController.getDomainSelectionConnection(any(Phone.class), anyInt(), anyBoolean()))
+ .thenReturn(mDsConnection);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
new file mode 100644
index 0000000..0c64b82
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
+
+import static com.android.internal.telephony.PhoneConstants.DOMAIN_NON_3GPP_PS;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.DomainSelectionService;
+import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CompletableFuture;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EmergencyCallDomainSelectionConnectionTest extends TelephonyTest {
+
+ private static final String TELECOM_CALL_ID1 = "TC1";
+
+ private DomainSelectionController mDomainSelectionController;
+ private DomainSelectionConnection.DomainSelectionConnectionCallback mConnectionCallback;
+ private EmergencyCallDomainSelectionConnection mEcDsc;
+ private AccessNetworksManager mAnm;
+ private TransportSelectorCallback mTransportCallback;
+ private EmergencyStateTracker mEmergencyStateTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+
+ mDomainSelectionController = Mockito.mock(DomainSelectionController.class);
+ mConnectionCallback =
+ Mockito.mock(DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+ mEmergencyStateTracker = Mockito.mock(EmergencyStateTracker.class);
+ mAnm = Mockito.mock(AccessNetworksManager.class);
+ doReturn(mAnm).when(mPhone).getAccessNetworksManager();
+ mEcDsc = new EmergencyCallDomainSelectionConnection(mPhone,
+ mDomainSelectionController, mEmergencyStateTracker);
+ mTransportCallback = mEcDsc.getTransportSelectorCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mEcDsc = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWifi() throws Exception {
+ doReturn(TRANSPORT_TYPE_WLAN).when(mAnm).getPreferredTransport(anyInt());
+ replaceInstance(EmergencyCallDomainSelectionConnection.class,
+ "mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
+
+ EmergencyRegResult regResult = new EmergencyRegResult(
+ EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, false, 0, 0, "", "", "");
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", 0, null, regResult);
+
+ CompletableFuture<Integer> future =
+ mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mDomainSelectionController).selectDomain(any(), any());
+
+ mTransportCallback.onWlanSelected(true);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_NON_3GPP_PS, (long) future.get());
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), eq(MODE_EMERGENCY_WLAN));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainCs() throws Exception {
+ doReturn(TRANSPORT_TYPE_WWAN).when(mAnm).getPreferredTransport(anyInt());
+ replaceInstance(EmergencyCallDomainSelectionConnection.class,
+ "mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
+
+ EmergencyRegResult regResult = new EmergencyRegResult(
+ UTRAN, REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_CS,
+ true, false, 0, 0, "", "", "");
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", 0, null, regResult);
+
+ CompletableFuture<Integer> future =
+ mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mDomainSelectionController).selectDomain(any(), any());
+
+ WwanSelectorCallback wwanCallback = null;
+ wwanCallback = mTransportCallback.onWwanSelected();
+
+ assertFalse(future.isDone());
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), eq(MODE_EMERGENCY_WWAN));
+
+ wwanCallback.onDomainSelected(DOMAIN_CS, false);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_CS, (long) future.get());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainPs() throws Exception {
+ doReturn(TRANSPORT_TYPE_WWAN).when(mAnm).getPreferredTransport(anyInt());
+ replaceInstance(EmergencyCallDomainSelectionConnection.class,
+ "mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
+
+ EmergencyRegResult regResult = new EmergencyRegResult(
+ EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "", "");
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", 0, null, regResult);
+
+ CompletableFuture<Integer> future =
+ mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mDomainSelectionController).selectDomain(any(), any());
+
+ WwanSelectorCallback wwanCallback = null;
+ wwanCallback = mTransportCallback.onWwanSelected();
+
+ assertFalse(future.isDone());
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), eq(MODE_EMERGENCY_WWAN));
+
+ wwanCallback.onDomainSelected(DOMAIN_PS, true);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_PS, (long) future.get());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnSelectionTerminated() throws Exception {
+ EmergencyRegResult regResult = new EmergencyRegResult(
+ EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true, true, 0, 0, "", "", "");
+
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", 0, null, regResult);
+
+ mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
+ mTransportCallback.onSelectionTerminated(ERROR_UNSPECIFIED);
+
+ verify(mConnectionCallback).onSelectionTerminated(eq(ERROR_UNSPECIFIED));
+ }
+
+ @Test
+ @SmallTest
+ public void testCancelSelection() throws Exception {
+ mEcDsc.cancelSelection();
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
new file mode 100644
index 0000000..25ccecb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelector;
+import android.telephony.NetworkRegistrationInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Unit tests for EmergencySmsDomainSelectionConnection.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EmergencySmsDomainSelectionConnectionTest extends TelephonyTest {
+ private DomainSelectionController mDsController;
+ private DomainSelectionConnection.DomainSelectionConnectionCallback mDscCallback;
+ private DomainSelector mDomainSelector;
+ private EmergencyStateTracker mEmergencyStateTracker;
+
+ private Handler mHandler;
+ private AccessNetworksManager mAnm;
+ private DomainSelectionService.SelectionAttributes mDsAttr;
+ private EmergencySmsDomainSelectionConnection mDsConnection;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mHandler = new Handler(Looper.myLooper());
+ mDsController = Mockito.mock(DomainSelectionController.class);
+ mDscCallback = Mockito.mock(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+ mDomainSelector = Mockito.mock(DomainSelector.class);
+ mEmergencyStateTracker = Mockito.mock(EmergencyStateTracker.class);
+ mAnm = Mockito.mock(AccessNetworksManager.class);
+ when(mPhone.getAccessNetworksManager()).thenReturn(mAnm);
+
+ mDsConnection = new EmergencySmsDomainSelectionConnection(
+ mPhone, mDsController, mEmergencyStateTracker);
+ mDsConnection.getTransportSelectorCallback().onCreated(mDomainSelector);
+ mDsAttr = new DomainSelectionService.SelectionAttributes.Builder(
+ mPhone.getPhoneId(), mPhone.getSubId(), DomainSelectionService.SELECTOR_TYPE_SMS)
+ .setEmergency(true)
+ .build();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDomainSelector = null;
+ mDsAttr = null;
+ mDsConnection = null;
+ mDscCallback = null;
+ mDsController = null;
+ mEmergencyStateTracker = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnWlanSelected() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt()))
+ .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onWlanSelected(true);
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_SMS), eq(MODE_EMERGENCY_WLAN));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnWlanSelectedWithDifferentTransportType() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onWlanSelected(true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_SMS), eq(MODE_EMERGENCY_WLAN));
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnWlanSelectedWithDifferentTransportTypeAndImsPdn() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onWlanSelected(false);
+ processAllMessages();
+
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_SMS), eq(MODE_EMERGENCY_WLAN));
+ verify(mAnm, never()).registerForQualifiedNetworksChanged(any(Handler.class), anyInt());
+ verify(mPhone, never()).notifyEmergencyDomainSelected(anyInt());
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnWlanSelectedWithDifferentTransportTypeWhilePreferredTransportChanged()
+ throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onWlanSelected(true);
+ // When onWlanSelected() is called again,
+ // it will be ignored because the change of preferred transport is in progress.
+ // => onEmergencyTransportChanged() is called only once.
+ mDsConnection.onWlanSelected(true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_SMS), eq(MODE_EMERGENCY_WLAN));
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnWwanSelected() throws Exception {
+ mDsConnection.onWwanSelected();
+
+ verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ eq(EmergencyStateTracker.EMERGENCY_TYPE_SMS), eq(MODE_EMERGENCY_WWAN));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPs() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt()))
+ .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPsWithDifferentTransportType() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPsWithDifferentTransportTypeAndImsPdn() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, false);
+ processAllMessages();
+
+ verify(mAnm, never()).registerForQualifiedNetworksChanged(any(Handler.class), anyInt());
+ verify(mPhone, never()).notifyEmergencyDomainSelected(anyInt());
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPsWithDifferentTransportTypeAndNotChanged() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+
+ assertFalse(future.isDone());
+ verify(mAnm, never()).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPsWithDifferentTransportTypeWhilePreferredTransportChanged()
+ throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ // When onDomainSelected() is called again with the different domain,
+ // it will be ignored because the change of preferred transport is in progress.
+ // => The domain selection result is DOMAIN_PS.
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedCs() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt()))
+ .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_CS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testFinishSelection() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+
+ mDsConnection.finishSelection();
+ processAllMessages();
+
+ assertFalse(future.isDone());
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ verify(mDomainSelector).cancelSelection();
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testFinishSelectionAfterDomainSelectionCompleted() throws Exception {
+ when(mAnm.getPreferredTransport(anyInt())).thenReturn(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, true);
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> msgCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAnm).registerForQualifiedNetworksChanged(
+ handlerCaptor.capture(), msgCaptor.capture());
+ verify(mPhone).notifyEmergencyDomainSelected(
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+
+ Handler handler = handlerCaptor.getValue();
+ Integer msg = msgCaptor.getValue();
+ handler.handleMessage(Message.obtain(handler, msg.intValue()));
+ processAllMessages();
+ mDsConnection.finishSelection();
+
+ assertTrue(future.isDone());
+ // This method should be invoked one time.
+ verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
+ verify(mDomainSelector).finishSelection();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
new file mode 100644
index 0000000..0403232
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.DomainSelectionService;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.telephony.ims.ImsReasonInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.AccessNetworksManager;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
+import com.android.internal.telephony.domainselection.DomainSelectionController;
+import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CompletableFuture;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NormalCallDomainSelectionConnectionTest extends TelephonyTest {
+
+ private static final String TELECOM_CALL_ID1 = "TC1";
+
+ @Mock
+ private DomainSelectionController mMockDomainSelectionController;
+ @Mock
+ private DomainSelectionConnection.DomainSelectionConnectionCallback mMockConnectionCallback;
+ @Mock
+ private AccessNetworksManager mMockAccessNetworksManager;
+
+ private TransportSelectorCallback mTransportCallback;
+ private NormalCallDomainSelectionConnection mNormalCallDomainSelectionConnection;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ doReturn(mMockAccessNetworksManager).when(mPhone).getAccessNetworksManager();
+ mNormalCallDomainSelectionConnection =
+ new NormalCallDomainSelectionConnection(mPhone, mMockDomainSelectionController);
+ mTransportCallback = mNormalCallDomainSelectionConnection.getTransportSelectorCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mNormalCallDomainSelectionConnection = null;
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWifi() throws Exception {
+ DomainSelectionService.SelectionAttributes attributes =
+ NormalCallDomainSelectionConnection.getSelectionAttributes(mPhone.getPhoneId(),
+ mPhone.getSubId(), TELECOM_CALL_ID1, "123", false, 0, null);
+
+ CompletableFuture<Integer> future =
+ mNormalCallDomainSelectionConnection
+ .createNormalConnection(attributes, mMockConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mMockDomainSelectionController).selectDomain(any(), any());
+
+ mTransportCallback.onWlanSelected(false);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_PS, (long) future.get());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainCs() throws Exception {
+ DomainSelectionService.SelectionAttributes attributes =
+ NormalCallDomainSelectionConnection.getSelectionAttributes(mPhone.getPhoneId(),
+ mPhone.getSubId(), TELECOM_CALL_ID1, "123", false, 0, null);
+
+ CompletableFuture<Integer> future =
+ mNormalCallDomainSelectionConnection
+ .createNormalConnection(attributes, mMockConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mMockDomainSelectionController).selectDomain(any(), any());
+
+ WwanSelectorCallback wwanCallback = mTransportCallback.onWwanSelected();
+
+ assertFalse(future.isDone());
+ wwanCallback.onDomainSelected(DOMAIN_CS, false);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_CS, (long) future.get());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainPs() throws Exception {
+ DomainSelectionService.SelectionAttributes attributes =
+ NormalCallDomainSelectionConnection.getSelectionAttributes(mPhone.getPhoneId(),
+ mPhone.getSubId(), TELECOM_CALL_ID1, "123", false, 0, null);
+
+ CompletableFuture<Integer> future =
+ mNormalCallDomainSelectionConnection
+ .createNormalConnection(attributes, mMockConnectionCallback);
+
+ assertNotNull(future);
+ assertFalse(future.isDone());
+
+ verify(mMockDomainSelectionController).selectDomain(any(), any());
+
+ WwanSelectorCallback wwanCallback = mTransportCallback.onWwanSelected();
+
+ assertFalse(future.isDone());
+ wwanCallback.onDomainSelected(DOMAIN_PS, false);
+
+ assertTrue(future.isDone());
+ assertEquals((long) DOMAIN_PS, (long) future.get());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnSelectionTerminated() throws Exception {
+ DomainSelectionService.SelectionAttributes attributes =
+ NormalCallDomainSelectionConnection.getSelectionAttributes(mPhone.getPhoneId(),
+ mPhone.getSubId(), TELECOM_CALL_ID1, "123", false, 0, null);
+
+ CompletableFuture<Integer> future = mNormalCallDomainSelectionConnection
+ .createNormalConnection(attributes, mMockConnectionCallback);
+ mTransportCallback.onSelectionTerminated(ERROR_UNSPECIFIED);
+
+ verify(mMockConnectionCallback).onSelectionTerminated(eq(ERROR_UNSPECIFIED));
+ assertNotNull(future);
+ }
+
+ @Test
+ public void testGetSelectionAttributes() throws Exception {
+ ImsReasonInfo imsReasonInfo = new ImsReasonInfo();
+ DomainSelectionService.SelectionAttributes attributes =
+ NormalCallDomainSelectionConnection.getSelectionAttributes(1, 2,
+ TELECOM_CALL_ID1, "123", false, 10, imsReasonInfo);
+
+ assertEquals(1, attributes.getSlotId());
+ assertEquals(2, attributes.getSubId());
+ assertEquals(TELECOM_CALL_ID1, attributes.getCallId());
+ assertEquals("123", attributes.getNumber());
+ assertEquals(false, attributes.isVideoCall());
+ assertEquals(false, attributes.isEmergency());
+ assertEquals(SELECTOR_TYPE_CALLING, attributes.getSelectorType());
+ assertEquals(10, attributes.getCsDisconnectCause());
+ assertEquals(imsReasonInfo, attributes.getPsDisconnectCause());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
new file mode 100644
index 0000000..e4afa79
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.domainselection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelector;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.Phone;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Unit tests for SmsDomainSelectionConnection.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsDomainSelectionConnectionTest {
+ private static final int SLOT_ID = 0;
+ private static final int SUB_ID = 1;
+
+ @Mock private Phone mPhone;
+ @Mock private DomainSelectionController mDsController;
+ @Mock private DomainSelectionConnection.DomainSelectionConnectionCallback mDscCallback;
+ @Mock private DomainSelector mDomainSelector;
+
+ private Handler mHandler;
+ private TestableLooper mTestableLooper;
+ private DomainSelectionService.SelectionAttributes mDsAttr;
+ private SmsDomainSelectionConnection mDsConnection;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ HandlerThread handlerThread = new HandlerThread(
+ SmsDomainSelectionConnectionTest.class.getSimpleName());
+ handlerThread.start();
+
+ mHandler = new Handler(handlerThread.getLooper());
+ mDsConnection = new SmsDomainSelectionConnection(mPhone, mDsController);
+ mDsConnection.getTransportSelectorCallback().onCreated(mDomainSelector);
+ mDsAttr = new DomainSelectionService.SelectionAttributes.Builder(
+ SLOT_ID, SUB_ID, DomainSelectionService.SELECTOR_TYPE_SMS).build();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+
+ if (mHandler != null) {
+ mHandler.getLooper().quit();
+ mHandler = null;
+ }
+
+ mDomainSelector = null;
+ mDsAttr = null;
+ mDsConnection = null;
+ mDscCallback = null;
+ mDsController = null;
+ mPhone = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testRequestDomainSelection() {
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+
+ assertNotNull(future);
+ verify(mDsController).selectDomain(eq(mDsAttr), any(TransportSelectorCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnWlanSelected() throws Exception {
+ setUpTestableLooper();
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onWlanSelected();
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnSelectionTerminated() {
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ mDsConnection.onSelectionTerminated(DisconnectCause.LOCAL);
+
+ assertFalse(future.isDone());
+ verify(mDscCallback).onSelectionTerminated(eq(DisconnectCause.LOCAL));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedPs() throws Exception {
+ setUpTestableLooper();
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testOnDomainSelectedCs() throws Exception {
+ setUpTestableLooper();
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_CS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS);
+ processAllMessages();
+
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testFinishSelection() throws Exception {
+ setUpTestableLooper();
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
+ processAllMessages();
+ mDsConnection.finishSelection();
+
+ verify(mDomainSelector).finishSelection();
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void testCancelSelection() throws Exception {
+ CompletableFuture<Integer> future =
+ mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
+ future.thenAcceptAsync((domain) -> {
+ assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
+ }, mHandler::post);
+
+ mDsConnection.finishSelection();
+
+ verify(mDomainSelector).cancelSelection();
+ }
+
+ private void setUpTestableLooper() throws Exception {
+ mTestableLooper = new TestableLooper(mHandler.getLooper());
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
index 1273148..5653209 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
@@ -261,7 +261,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
@@ -284,7 +284,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testSameEmergencyNumberDifferentMnc() throws Exception {
@@ -306,7 +306,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testSameEmergencyNumberDifferentCategories() throws Exception {
@@ -328,7 +328,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testSameEmergencyNumberDifferentUrns() throws Exception {
@@ -357,7 +357,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testSameEmergencyNumberCallRouting() throws Exception {
@@ -379,7 +379,36 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ /* case 1: Check routing is not checked when comparing the same numbers. As routing will
+ be unknown for all numbers apart from DB. Check merge when both are not from DB then
+ routing value is merged from first number. */
+ assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
+ assertEquals(num1, EmergencyNumber.mergeSameEmergencyNumbers(num1, num2));
+
+ num2 = new EmergencyNumber(
+ "911",
+ "jp",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ /* case 1: Check routing is not checked when comparing the same numbers. Check merge when
+ one of the number is from DB then routing value is merged from DB number. Along with
+ source value is masked with both*/
+ assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
+
+ num2 = new EmergencyNumber(
+ "911",
+ "jp",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ assertEquals(num2, EmergencyNumber.mergeSameEmergencyNumbers(num1, num2));
}
public void testSameEmergencyNumberDifferentSource() throws Exception {
@@ -401,7 +430,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testSameEmergencyNumberDifferentSourceTestOrNot() throws Exception {
@@ -423,7 +452,7 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
- assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+ assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2, false));
}
public void testMergeSameNumbersInEmergencyNumberListWithDifferentSources() throws Exception {
@@ -595,4 +624,246 @@
assertEquals(outputNumberList, inputNumberList);
}
+
+ public void testMergeSameNumbersEmergencyNumberListByDeterminingFields() throws Exception {
+ List<String> urn1 = new ArrayList<>();
+ urn1.add("sos");
+
+ List<String> urn2 = new ArrayList<>();
+ urn2.add("sos:ambulance");
+
+ List<EmergencyNumber> inputNumberList = new ArrayList<>();
+ EmergencyNumber num1 = new EmergencyNumber(
+ "110",
+ "jp",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ EmergencyNumber num2 = new EmergencyNumber(
+ "110",
+ "jp",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ urn1,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num3 = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num4 = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ EmergencyNumber num5 = new EmergencyNumber(
+ "112",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ EmergencyNumber num6 = new EmergencyNumber(
+ "112",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ urn1,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num7 = new EmergencyNumber(
+ "108",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num8 = new EmergencyNumber(
+ "108",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num9 = new EmergencyNumber(
+ "102",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num10 = new EmergencyNumber(
+ "102",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+ urn1,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber num11 = new EmergencyNumber(
+ "100",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ EmergencyNumber num12 = new EmergencyNumber(
+ "100",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+
+ EmergencyNumber num13 = new EmergencyNumber(
+ "200",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ EmergencyNumber num14 = new EmergencyNumber(
+ "200",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ inputNumberList.add(num1);
+ inputNumberList.add(num2);
+ inputNumberList.add(num3);
+ inputNumberList.add(num4);
+ inputNumberList.add(num5);
+ inputNumberList.add(num6);
+ inputNumberList.add(num7);
+ inputNumberList.add(num8);
+ inputNumberList.add(num9);
+ inputNumberList.add(num10);
+ inputNumberList.add(num11);
+ inputNumberList.add(num12);
+ inputNumberList.add(num13);
+ inputNumberList.add(num14);
+
+ EmergencyNumber mergeOfNum1AndNum2 = new EmergencyNumber(
+ "110",
+ "jp",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ urn1,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ EmergencyNumber mergeOfNum3AndNum4 = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ List<String> mergedUrns1And2 = new ArrayList<>();
+ mergedUrns1And2.add("sos");
+ mergedUrns1And2.add("sos:ambulance");
+
+ EmergencyNumber mergeOfNum5AndNum6 = new EmergencyNumber(
+ "112",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ mergedUrns1And2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ EmergencyNumber mergeOfNum7AndNum8 = new EmergencyNumber(
+ "108",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber mergeOfNum11AndNum12 = new EmergencyNumber(
+ "100",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ urn2,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ List<String> mergedUrns2And1 = new ArrayList<>();
+ mergedUrns2And1.add("sos:ambulance");
+ mergedUrns2And1.add("sos");
+
+ EmergencyNumber mergeOfNum9AndNum10 = new EmergencyNumber(
+ "102",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ mergedUrns2And1,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+ EmergencyNumber mergeOfNum13AndNum14 = new EmergencyNumber(
+ "200",
+ "in",
+ "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+ List<EmergencyNumber> outputNumberList = new ArrayList<>();
+ outputNumberList.add(mergeOfNum1AndNum2);
+ outputNumberList.add(mergeOfNum3AndNum4);
+ outputNumberList.add(mergeOfNum5AndNum6);
+ outputNumberList.add(mergeOfNum7AndNum8);
+ outputNumberList.add(mergeOfNum9AndNum10);
+ outputNumberList.add(mergeOfNum11AndNum12);
+ outputNumberList.add(mergeOfNum13AndNum14);
+ Collections.sort(outputNumberList);
+
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(inputNumberList, true);
+ assertEquals(outputNumberList, inputNumberList);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index 39ff133..c47eb3b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -19,27 +19,39 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.IntentFilter;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.InstrumentationRegistry;
-import com.android.internal.telephony.HalVersion;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyTest;
import com.google.i18n.phonenumbers.ShortNumberInfo;
@@ -48,6 +60,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
import java.io.BufferedInputStream;
import java.io.File;
@@ -73,6 +87,7 @@
private static final String EMERGENCY_NUMBER_DB_OTA_FILE = "eccdata_ota";
private static final int CONFIG_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION = 99999;
private static final String CONFIG_EMERGENCY_NUMBER_ADDRESS = "54321";
+ private static final String CONFIG_EMERGENCY_DUPLICATE_NUMBER = "4321";
private static final String CONFIG_EMERGENCY_NUMBER_COUNTRY = "us";
private static final String CONFIG_EMERGENCY_NUMBER_MNC = "";
private static final String NON_3GPP_EMERGENCY_TEST_NUMBER = "9876543";
@@ -96,13 +111,12 @@
private static final String OTA_EMERGENCY_NUMBER_ADDRESS = "98765";
private static final int SUB_ID_PHONE_1 = 1;
private static final int SUB_ID_PHONE_2 = 2;
- private static final int VALID_SLOT_INDEX_VALID_1 = 1;
- private static final int VALID_SLOT_INDEX_VALID_2 = 2;
+ private static final int VALID_SLOT_INDEX_VALID_1 = 0;
+ private static final int VALID_SLOT_INDEX_VALID_2 = 1;
private static final int INVALID_SLOT_INDEX_VALID = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private ParcelFileDescriptor mOtaPracelFileDescriptor = null;
+ private ParcelFileDescriptor mOtaParcelFileDescriptor = null;
// Mocked classes
- private SubscriptionController mSubControllerMock;
- private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+ private CarrierConfigManager mCarrierConfigManagerMock;
// mEmergencyNumberTrackerMock for mPhone
private EmergencyNumberTracker mEmergencyNumberTrackerMock;
@@ -115,15 +129,19 @@
private File mLocalDownloadDirectory;
private ShortNumberInfo mShortNumberInfo;
+ private Context mMockContext;
+ private Resources mResources;
@Before
public void setUp() throws Exception {
logd("EmergencyNumberTrackerTest +Setup!");
super.setUp(getClass().getSimpleName());
mShortNumberInfo = mock(ShortNumberInfo.class);
- mSubControllerMock = mock(SubscriptionController.class);
- mPhone2 = mock(Phone.class);
- mContext = InstrumentationRegistry.getTargetContext();
+ mCarrierConfigManagerMock = mock(CarrierConfigManager.class);
+
+ mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+ mMockContext = mock(Context.class);
+ mResources = mock(Resources.class);
doReturn(mContext).when(mPhone).getContext();
doReturn(0).when(mPhone).getPhoneId();
@@ -142,6 +160,9 @@
// Copy an OTA file to the test directory to similate the OTA mechanism
simulateOtaEmergencyNumberDb(mPhone);
+ AssetManager am = new AssetManager.Builder().build();
+ doReturn(am).when(mMockContext).getAssets();
+
processAllMessages();
logd("EmergencyNumberTrackerTest -Setup!");
}
@@ -158,10 +179,10 @@
mEmergencyNumberTrackerMock2 = null;
mEmergencyNumberListTestSample.clear();
mEmergencyNumberListTestSample = null;
- if (mOtaPracelFileDescriptor != null) {
+ if (mOtaParcelFileDescriptor != null) {
try {
- mOtaPracelFileDescriptor.close();
- mOtaPracelFileDescriptor = null;
+ mOtaParcelFileDescriptor.close();
+ mOtaParcelFileDescriptor = null;
} catch (IOException e) {
logd("Failed to close emergency number db file folder for testing " + e.toString());
}
@@ -182,6 +203,14 @@
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
mEmergencyNumberListTestSample.add(emergencyNumberForTest);
+
+ emergencyNumberForTest = new EmergencyNumber(
+ CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ mEmergencyNumberListTestSample.add(emergencyNumberForTest);
}
private void sendEmergencyNumberListFromRadio() {
@@ -219,11 +248,11 @@
File file = new File(Environment.getExternalStorageDirectory(), LOCAL_DOWNLOAD_DIRECTORY
+ "/" + EMERGENCY_NUMBER_DB_OTA_FILE);
try {
- mOtaPracelFileDescriptor = ParcelFileDescriptor.open(
+ mOtaParcelFileDescriptor = ParcelFileDescriptor.open(
file, ParcelFileDescriptor.MODE_READ_ONLY);
emergencyNumberTrackerMock.obtainMessage(
EmergencyNumberTracker.EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH,
- mOtaPracelFileDescriptor).sendToTarget();
+ mOtaParcelFileDescriptor).sendToTarget();
logd("Changed emergency number db file folder for testing ");
} catch (FileNotFoundException e) {
logd("Failed to open emergency number db file folder for testing " + e.toString());
@@ -268,6 +297,11 @@
}
}
+ private boolean hasDbEmergencyNumbers(List<EmergencyNumber> subList,
+ List<EmergencyNumber> list) {
+ return list.containsAll(subList);
+ }
+
private boolean hasDbEmergencyNumber(EmergencyNumber number, List<EmergencyNumber> list) {
return list.contains(number);
}
@@ -298,27 +332,22 @@
@Test
public void testIsSimAbsent() throws Exception {
setDsdsPhones();
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubControllerMock);
-
- // Both sim slots are active
- doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubControllerMock).getSlotIndex(
+ doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubscriptionManagerService).getSlotIndex(
eq(SUB_ID_PHONE_1));
- doReturn(VALID_SLOT_INDEX_VALID_2).when(mSubControllerMock).getSlotIndex(
+ doReturn(VALID_SLOT_INDEX_VALID_2).when(mSubscriptionManagerService).getSlotIndex(
eq(SUB_ID_PHONE_2));
assertFalse(mEmergencyNumberTrackerMock.isSimAbsent());
// One sim slot is active; the other one is not active
- doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubControllerMock).getSlotIndex(
+ doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubscriptionManagerService).getSlotIndex(
eq(SUB_ID_PHONE_1));
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
+ doReturn(INVALID_SLOT_INDEX_VALID).when(mSubscriptionManagerService).getSlotIndex(
eq(SUB_ID_PHONE_2));
assertFalse(mEmergencyNumberTrackerMock.isSimAbsent());
// Both sim slots are not active
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_1));
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_2));
+ doReturn(INVALID_SLOT_INDEX_VALID).when(mSubscriptionManagerService).getSlotIndex(
+ anyInt());
assertTrue(mEmergencyNumberTrackerMock.isSimAbsent());
}
@@ -330,7 +359,42 @@
}
@Test
- public void testUpdateEmergencyCountryIso() throws Exception {
+ public void testRegistrationForCountryChangeIntent() throws Exception {
+ EmergencyNumberTracker localEmergencyNumberTracker;
+ Context spyContext = spy(mContext);
+ doReturn(spyContext).when(mPhone).getContext();
+ ArgumentCaptor<IntentFilter> intentCaptor = ArgumentCaptor.forClass(IntentFilter.class);
+
+ localEmergencyNumberTracker = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
+ verify(spyContext, times(1)).registerReceiver(any(), intentCaptor.capture());
+ IntentFilter ifilter = intentCaptor.getValue();
+ assertTrue(ifilter.hasAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED));
+ }
+
+ @Test
+ public void testUpdateEmergencyCountryIso_whenStatePowerOff() throws Exception {
+ testUpdateEmergencyCountryIso(ServiceState.STATE_POWER_OFF);
+ }
+
+ @Test
+ public void testUpdateEmergencyCountryIso_whenStateInService() throws Exception {
+ testUpdateEmergencyCountryIso(ServiceState.STATE_IN_SERVICE);
+ }
+
+ @Test
+ public void testUpdateEmergencyCountryIso_whenStateOos() throws Exception {
+ testUpdateEmergencyCountryIso(ServiceState.STATE_OUT_OF_SERVICE);
+ }
+
+ @Test
+ public void testUpdateEmergencyCountryIso_whenStateEmergencyOnly() throws Exception {
+ testUpdateEmergencyCountryIso(ServiceState.STATE_EMERGENCY_ONLY);
+ }
+
+ private void testUpdateEmergencyCountryIso(int ss) throws Exception {
+ doReturn(mLocaleTracker).when(mSST).getLocaleTracker();
+ doReturn("us").when(mLocaleTracker).getLastKnownCountryIso();
+
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange("us");
@@ -338,10 +402,14 @@
assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("us"));
assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("us"));
+ doReturn(ss).when(mServiceState).getState();
mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange("");
processAllMessages();
assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals(""));
assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("us"));
+
+ //make sure we look up cached location whenever current iso is null
+ verify(mLocaleTracker).getLastKnownCountryIso();
}
@Test
@@ -367,18 +435,11 @@
@Test
public void testIsEmergencyNumber_FallbackToShortNumberXml_NoSims() throws Exception {
- // Set up the Hal version as 1.4
- doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
- doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion();
-
setDsdsPhones();
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubControllerMock);
// Both sim slots are not active
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_1));
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_2));
+ doReturn(INVALID_SLOT_INDEX_VALID).when(mSubscriptionManagerService).getSlotIndex(
+ anyInt());
assertTrue(mEmergencyNumberTrackerMock.isSimAbsent());
sendEmptyEmergencyNumberListFromRadio(mEmergencyNumberTrackerMock);
@@ -388,7 +449,7 @@
processAllMessages();
replaceInstance(ShortNumberInfo.class, "INSTANCE", null, mShortNumberInfo);
- mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, true);
+ mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER);
//verify that we fall back to shortnumber xml when there are no SIMs
verify(mShortNumberInfo).isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, "JP");
@@ -405,26 +466,21 @@
}
private void testIsEmergencyNumber_NoFallbackToShortNumberXml(int numSims) throws Exception {
- // Set up the Hal version as 1.4
- doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
- doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion();
-
assertTrue((numSims > 0 && numSims < 3));
setDsdsPhones();
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubControllerMock);
if (numSims == 1) {
// One sim slot is active; the other one is not active
- doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_1));
- doReturn(INVALID_SLOT_INDEX_VALID).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_2));
+ doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubscriptionManagerService).getSlotIndex(
+ eq(SUB_ID_PHONE_1));
+ doReturn(INVALID_SLOT_INDEX_VALID).when(mSubscriptionManagerService).getSlotIndex(
+ eq(SUB_ID_PHONE_2));
} else {
//both slots active
- doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_1));
- doReturn(VALID_SLOT_INDEX_VALID_2).when(mSubControllerMock).getSlotIndex(
- eq(SUB_ID_PHONE_2));
+ doReturn(VALID_SLOT_INDEX_VALID_1).when(mSubscriptionManagerService).getSlotIndex(
+ eq(SUB_ID_PHONE_1));
+ doReturn(VALID_SLOT_INDEX_VALID_2).when(mSubscriptionManagerService).getSlotIndex(
+ eq(SUB_ID_PHONE_2));
}
assertFalse(mEmergencyNumberTrackerMock.isSimAbsent());
@@ -436,7 +492,7 @@
processAllMessages();
replaceInstance(ShortNumberInfo.class, "INSTANCE", null, mShortNumberInfo);
- mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, true);
+ mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER);
//verify we do not use ShortNumber xml
verify(mShortNumberInfo, never()).isEmergencyNumber(anyString(), anyString());
@@ -476,38 +532,236 @@
}
/**
- * In 1.3 or less HAL. we should not use database number.
- */
- @Test
- public void testUsingEmergencyNumberDatabaseWheneverHal_1_3() {
- doReturn(new HalVersion(1, 3)).when(mPhone).getHalVersion();
-
- sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
- mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
- processAllMessages();
-
- boolean hasDatabaseNumber = false;
- for (EmergencyNumber number : mEmergencyNumberTrackerMock.getEmergencyNumberList()) {
- if (number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) {
- hasDatabaseNumber = true;
- break;
- }
- }
- assertFalse(hasDatabaseNumber);
- }
-
- /**
* In 1.4 or above HAL, we should use database number.
*/
@Test
public void testUsingEmergencyNumberDatabaseWheneverHal_1_4() {
- doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+ doReturn(mMockContext).when(mPhone).getContext();
+ doReturn(mContext.getAssets()).when(mMockContext).getAssets();
+ doReturn(mResources).when(mMockContext).getResources();
+ doReturn(true).when(mResources).getBoolean(
+ com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
- sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
- mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+ EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
+ mPhone, mSimulatedCommands);
+ emergencyNumberTrackerMock.sendMessage(
+ emergencyNumberTrackerMock.obtainMessage(
+ 1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
+ new AsyncResult(null, mEmergencyNumberListTestSample, null)));
+ sendEmergencyNumberPrefix(emergencyNumberTrackerMock);
+ emergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
processAllMessages();
+ /* case 1: check DB number exist or not */
assertTrue(hasDbEmergencyNumber(CONFIG_EMERGENCY_NUMBER,
- mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ /* case 2: since ignore_emergency_routing_from_db is true. check for all DB numbers with
+ routing value as unknown by ignoring DB value */
+ List<EmergencyNumber> completeEmergencyNumberList = new ArrayList<>();
+ EmergencyNumber emergencyNumber = new EmergencyNumber(
+ "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ completeEmergencyNumberList.add(emergencyNumber);
+
+ emergencyNumber = new EmergencyNumber(
+ "54321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ completeEmergencyNumberList.add(emergencyNumber);
+
+ emergencyNumber = new EmergencyNumber(
+ "654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ completeEmergencyNumberList.add(emergencyNumber);
+
+ emergencyNumber = new EmergencyNumber(
+ "7654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ completeEmergencyNumberList.add(emergencyNumber);
+
+ assertTrue(hasDbEmergencyNumbers(completeEmergencyNumberList,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ /* case 3: check the routing type of merged duplicate numbers
+ between DB number and radio list. */
+ EmergencyNumber duplicateEmergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ assertTrue(hasDbEmergencyNumber(duplicateEmergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+ }
+
+ @Test
+ public void testUsingEmergencyNumberDatabaseWithRouting() {
+ doReturn(mMockContext).when(mPhone).getContext();
+ doReturn(mContext.getAssets()).when(mMockContext).getAssets();
+ doReturn(mResources).when(mMockContext).getResources();
+ doReturn("05").when(mCellIdentity).getMncString();
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
+
+ EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
+ mPhone, mSimulatedCommands);
+ emergencyNumberTrackerMock.sendMessage(
+ emergencyNumberTrackerMock.obtainMessage(
+ 1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
+ new AsyncResult(null, mEmergencyNumberListTestSample, null)));
+ sendEmergencyNumberPrefix(emergencyNumberTrackerMock);
+ emergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+ processAllMessages();
+
+ // case 1: check DB number with normal routing true and for mnc 05
+ EmergencyNumber emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "05", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // case 2: check DB number with normal routing true in multiple mnc 05, 45, 47
+ emergencyNumber = new EmergencyNumber(
+ "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "05", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ doReturn("47").when(mCellIdentity).getMncString();
+ emergencyNumber = new EmergencyNumber(
+ "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "47", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ /* case 3: check DB number with normal routing false and for mnc 05,
+ but current cell identity is 04 */
+ doReturn("04").when(mCellIdentity).getMncString();
+ emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // case 4: check DB number with normal routing false
+ emergencyNumber = new EmergencyNumber(
+ "654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // case 5: check DB number with normal routing true & empty mnc
+ emergencyNumber = new EmergencyNumber(
+ "7654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ /* case 6: check DB number with normal routing true & empty mnc. But same number exist
+ in radio list. In merge DB routing should be used */
+ emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+ | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+ }
+
+ @Test
+ public void testUsingEmergencyNumberDatabaseWithRoutingInOOS() {
+ doReturn(mMockContext).when(mPhone).getContext();
+ doReturn(mContext.getAssets()).when(mMockContext).getAssets();
+ doReturn(mResources).when(mMockContext).getResources();
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
+
+ EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
+ mPhone, mSimulatedCommands);
+ emergencyNumberTrackerMock.sendMessage(
+ emergencyNumberTrackerMock.obtainMessage(
+ 1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
+ new AsyncResult(null, mEmergencyNumberListTestSample, null)));
+ sendEmergencyNumberPrefix(emergencyNumberTrackerMock);
+ emergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+ processAllMessages();
+
+ // Check routing when cellidentity is null, which is oos
+ doReturn(null).when(mPhone).getCurrentCellIdentity();
+ EmergencyNumber emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // Check routing when cellidentity is 04, which is not part of normal routing mncs
+ doReturn(mCellIdentity).when(mPhone).getCurrentCellIdentity();
+ doReturn("04").when(mCellIdentity).getMncString();
+ emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // Check routing when cellidentity is 05, which is part of normal routing mncs
+ doReturn("05").when(mCellIdentity).getMncString();
+ emergencyNumber = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ "05", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ assertTrue(hasDbEmergencyNumber(emergencyNumber,
+ emergencyNumberTrackerMock.getEmergencyNumberList()));
}
/**
@@ -515,9 +769,6 @@
*/
@Test
public void testOtaEmergencyNumberDatabase() {
- // Set up the Hal version as 1.4 to apply emergency number database
- doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
-
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("");
processAllMessages();
@@ -578,4 +829,42 @@
assertEquals(resultToVerify, resultFromRadio);
}
+
+ @Test
+ public void testOverridingEmergencyNumberPrefixCarrierConfig() throws Exception {
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ doReturn(mMockContext).when(mPhone).getContext();
+ doReturn(Context.CARRIER_CONFIG_SERVICE)
+ .when(mMockContext)
+ .getSystemService(CarrierConfigManager.class);
+ doReturn(mCarrierConfigManagerMock)
+ .when(mMockContext)
+ .getSystemService(eq(Context.CARRIER_CONFIG_SERVICE));
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+ EmergencyNumberTracker localEmergencyNumberTracker =
+ new EmergencyNumberTracker(mPhone, mSimulatedCommands);
+ verify(mCarrierConfigManagerMock)
+ .registerCarrierConfigChangeListener(any(), listenerArgumentCaptor.capture());
+ CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
+ listenerArgumentCaptor.getAllValues().get(0);
+
+ assertFalse(localEmergencyNumberTracker.isEmergencyNumber("*272911"));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putStringArray(
+ CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY,
+ new String[] {"*272"});
+ doReturn(bundle)
+ .when(mCarrierConfigManagerMock)
+ .getConfigForSubId(eq(SUB_ID_PHONE_1), any());
+ carrierConfigChangeListener.onCarrierConfigChanged(
+ mPhone.getPhoneId(),
+ mPhone.getSubId(),
+ TelephonyManager.UNKNOWN_CARRIER_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ processAllMessages();
+
+ assertTrue(localEmergencyNumberTracker.isEmergencyNumber("*272911"));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
new file mode 100644
index 0000000..2a8e4e2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -0,0 +1,1615 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
+import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
+import android.telephony.EmergencyRegResult;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.PhoneSwitcher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for EmergencyStateTracker
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EmergencyStateTrackerTest extends TelephonyTest {
+ private static final String TEST_CALL_ID = "TC@TEST1";
+ private static final String TEST_CALL_ID_2 = "TC@TEST2";
+ private static final String TEST_SMS_ID = "1111";
+ private static final String TEST_SMS_ID_2 = "2222";
+ private static final long TEST_ECM_EXIT_TIMEOUT_MS = 500;
+ private static final EmergencyRegResult E_REG_RESULT = new EmergencyRegResult(
+ EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US");
+
+ @Mock EmergencyStateTracker.PhoneFactoryProxy mPhoneFactoryProxy;
+ @Mock EmergencyStateTracker.PhoneSwitcherProxy mPhoneSwitcherProxy;
+ @Mock EmergencyStateTracker.TelephonyManagerProxy mTelephonyManagerProxy;
+ @Mock PhoneSwitcher mPhoneSwitcher;
+ @Mock RadioOnHelper mRadioOnHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void getInstance_notInitializedTillMake() throws IllegalStateException {
+ assertThrows(IllegalStateException.class, () -> {
+ EmergencyStateTracker.getInstance();
+ });
+
+ EmergencyStateTracker.make(mContext, true);
+
+ assertNotNull(EmergencyStateTracker.getInstance());
+ }
+
+ @Test
+ @SmallTest
+ public void getInstance_returnsSameInstance() {
+ EmergencyStateTracker.make(mContext, true);
+ EmergencyStateTracker instance1 = EmergencyStateTracker.getInstance();
+ EmergencyStateTracker instance2 = EmergencyStateTracker.getInstance();
+
+ assertSame(instance1, instance2);
+ }
+
+ /**
+ * Test that the EmergencyStateTracker turns on radio, performs a DDS switch and sets emergency
+ * mode switch when we are not roaming and the carrier only supports SUPL over the data plane.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_radioOff_turnOnRadioSwitchDdsAndSetEmergencyMode() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones and set radio off
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ false /* isRadioOn */);
+ setConfigForDdsSwitch(testPhone, null,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+ // Spy is used to capture consumer in delayDialForDdsSwitch
+ EmergencyStateTracker spyEst = spy(emergencyStateTracker);
+ CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
+ false);
+
+ // startEmergencyCall should trigger radio on
+ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
+ .forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
+ eq(false), eq(0));
+ // isOkToCall() should return true once radio is on
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertTrue(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ // Once radio on is complete, trigger delay dial
+ callback.getValue().onComplete(null, true);
+ ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
+ .forClass(Consumer.class);
+ verify(spyEst).switchDdsDelayed(eq(testPhone), completeConsumer.capture());
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(testPhone.getPhoneId()),
+ eq(150) /* extensionTime */, any());
+ // After dds switch completes successfully, set emergency mode
+ completeConsumer.getValue().accept(true);
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+ }
+
+ /**
+ * Test that if startEmergencyCall fails to turn on radio, then it's future completes with
+ * DisconnectCause.POWER_OFF.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_radioOnFails_returnsDisconnectCausePowerOff() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones and set radio off
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ false /* isRadioOn */);
+
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // startEmergencyCall should trigger radio on
+ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
+ .forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
+ eq(false), eq(0));
+ // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
+ CompletableFuture<Void> unused = future.thenAccept((result) -> {
+ assertEquals((Integer) result, (Integer) DisconnectCause.POWER_OFF);
+ });
+ callback.getValue().onComplete(null, false /* isRadioReady */);
+ }
+
+ /**
+ * Test that the EmergencyStateTracker does not perform a DDS switch when the carrier supports
+ * control-plane fallback. Radio is set to on so RadioOnHelper not triggered.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_cpFallback_noDdsSwitch() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones and set radio on
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ true /* isRadioOn */);
+ setConfigForDdsSwitch(testPhone, null,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
+
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Radio already on so shouldn't trigger this
+ verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(),
+ anyBoolean(), eq(0));
+ // Carrier supports control-plane fallback, so no DDS switch
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the EmergencyStateTracker does not perform a DDS switch if the non-DDS supports
+ * SUPL.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_supportsSuplOnNonDds_noDdsSwitch() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ false /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ true /* isRadioOn */);
+ setConfigForDdsSwitch(testPhone, null,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "0");
+
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // non-DDS supports SUPL, so no DDS switch
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the EmergencyStateTracker does not perform a DDS switch when the carrier does not
+ * support control-plane fallback while roaming.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_roaming_noDdsSwitch() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(true /* isRoaming */,
+ true /* isRadioOn */);
+ setConfigForDdsSwitch(testPhone, null,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "0");
+
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Is roaming, so no DDS switch
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the EmergencyStateTracker does perform a DDS switch even though the carrier
+ * supports control-plane fallback and the roaming partner is configured to look like a home
+ * network.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_roamingCarrierConfig_switchDds() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ true /* isRadioOn */);
+ // Setup voice roaming scenario
+ String testRoamingOperator = "001001";
+ testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+ String[] roamingPlmns = new String[] { testRoamingOperator };
+ setConfigForDdsSwitch(testPhone, roamingPlmns,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
+
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Verify DDS switch
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /* phoneId */,
+ eq(0) /* extensionTime */, any());
+ }
+
+ /**
+ * Test that the EmergencyStateTracker does perform a DDS switch even though the carrier
+ * supports control-plane fallback if we are roaming and the roaming partner is configured to
+ * use data plane only SUPL.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_roamingCarrierConfigWhileRoaming_switchDds() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(true /* isRoaming */,
+ true /* isRadioOn */);
+ // Setup voice roaming scenario
+ String testRoamingOperator = "001001";
+ testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+ String[] roamingPlmns = new String[] { testRoamingOperator };
+ setConfigForDdsSwitch(testPhone, roamingPlmns,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
+
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Verify DDS switch
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /* phoneId */,
+ eq(0) /* extensionTime */, any());
+ }
+
+ /**
+ * Test that once EmergencyStateTracker handler receives set emergency mode done message it sets
+ * IsInEmergencyCall to true, sets LastEmergencyRegResult and completes future with
+ * DisconnectCause.NOT_DISCONNECTED.
+ */
+ @Test
+ @SmallTest
+ public void setEmergencyModeDone_notifiesListenersAndCompletesFuture() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(true /* isRoaming */,
+ true /* isRadioOn */);
+ setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ // Verify future completes with DisconnectCause.NOT_DISCONNECTED
+ CompletableFuture<Void> unused = future.thenAccept((result) -> {
+ assertEquals((Integer) result, (Integer) DisconnectCause.NOT_DISCONNECTED);
+ });
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ }
+
+ /**
+ * Test that once EmergencyStateTracker handler receives message to exit emergency mode, it sets
+ * IsInEmergencyCall to false.
+ */
+ @Test
+ @SmallTest
+ public void exitEmergencyModeDone_isInEmergencyCallFalse() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(true /* isRoaming */,
+ true /* isRadioOn */);
+ setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(testPhone);
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(testPhone).exitEmergencyMode(any(Message.class));
+ }
+
+ /**
+ * Test that onEmergencyCallDomainUpdated updates the domain correctly so ECBM PS domain is
+ * detected.
+ */
+ @Test
+ @SmallTest
+ public void onEmergencyCallDomainUpdated_PsDomain() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ // set domain
+ emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_IMS,
+ TEST_CALL_ID);
+ // End call to enter ECM
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ // Make sure CS ECBM is true
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInCdmaEcm());
+ assertTrue(emergencyStateTracker.isInImsEcm());
+ }
+
+ /**
+ * Test that onEmergencyCallDomainUpdated updates the domain correctly so ECBM CS domain is
+ * detected.
+ */
+ @Test
+ @SmallTest
+ public void onEmergencyCallDomainUpdated_CsDomain() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ // set domain
+ emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_CDMA,
+ TEST_CALL_ID);
+ // End call to enter ECM
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ // Make sure IMS ECBM is true
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertTrue(emergencyStateTracker.isInCdmaEcm());
+ assertFalse(emergencyStateTracker.isInImsEcm());
+ }
+
+ /**
+ * Ensure that if for some reason we enter ECBM for CS domain and the Phone type is GSM,
+ * isInCdmaEcm returns false.
+ */
+ @Test
+ @SmallTest
+ public void onEmergencyCallDomainUpdated_CsDomain_Gsm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // For some reason the Phone is reporting GSM instead of CDMA.
+ doReturn(PhoneConstants.PHONE_TYPE_GSM).when(testPhone).getPhoneType();
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ // set domain
+ emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_CDMA,
+ TEST_CALL_ID);
+ // End call to enter ECM
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInCdmaEcm());
+ assertFalse(emergencyStateTracker.isInImsEcm());
+ }
+
+ /**
+ * Test that onEmergencyTransportChanged sets the new emergency mode.
+ */
+ @Test
+ @SmallTest
+ public void onEmergencyTransportChanged_setEmergencyMode() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true );
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
+
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+ }
+
+ /**
+ * Test that after endCall() is called, EmergencyStateTracker will enter ECM if the call was
+ * ACTIVE and send related intents.
+ */
+ @Test
+ @SmallTest
+ public void endCall_callWasActive_enterEcm() {
+ // Setup EmergencyStateTracker
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Start emergency call then enter ECM
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ // Set ecm as supported
+ setEcmSupportedConfig(testPhone, true);
+
+ assertFalse(emergencyStateTracker.isInEcm());
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEcm());
+ // Verify intents are sent that ECM is entered
+ ArgumentCaptor<Intent> ecmStateIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendStickyBroadcastAsUser(ecmStateIntent.capture(), eq(UserHandle.ALL));
+ assertTrue(ecmStateIntent.getValue()
+ .getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, true));
+ // Verify emergency callback mode set on modem
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any());
+ }
+
+ /**
+ * Test that after endCall() is called, EmergencyStateTracker will not enter ECM if the call was
+ * not ACTIVE.
+ */
+ @Test
+ @SmallTest
+ public void endCall_callNotActive_noEcm() {
+ // Setup EmergencyStateTracker
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Start emergency call then enter ECM
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ // Call does not reach ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.IDLE, TEST_CALL_ID);
+ // Set ecm as supported
+ setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertFalse(emergencyStateTracker.isInEcm());
+ }
+
+ /**
+ * Test that once endCall() is called and we enter ECM, then we exit ECM after the specified
+ * timeout.
+ */
+ @Test
+ @SmallTest
+ public void endCall_entersEcm_thenExitsEcmAfterTimeout() {
+ // Setup EmergencyStateTracker
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(testPhone);
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ // Set ecm as supported
+ setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+ processAllMessages();
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEcm());
+
+ processAllFutureMessages();
+
+ // Verify exitEmergencyMode() is called after timeout
+ verify(testPhone).exitEmergencyMode(any(Message.class));
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ }
+
+ /**
+ * Test that after exitEmergencyCallbackMode() is called, the correct intents are sent and
+ * emergency mode is exited on the modem.
+ */
+ @Test
+ @SmallTest
+ public void exitEmergencyCallbackMode_sendsCorrectIntentsAndExitsEmergencyMode() {
+ // Setup EmergencyStateTracker
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phone
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(testPhone);
+ // Start emergency call then enter ECM
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ TEST_CALL_ID, false);
+ processAllMessages();
+ // Set call to ACTIVE
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallDomainUpdated(
+ PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ // Set ecm as supported
+ setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+ // End call to enter ECM
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ // verify ecbm states are correct
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertTrue(emergencyStateTracker.isInImsEcm());
+ assertFalse(emergencyStateTracker.isInCdmaEcm());
+
+ emergencyStateTracker.exitEmergencyCallbackMode();
+ processAllFutureMessages();
+
+ // Ensure ECBM states are all correctly false after we exit.
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInImsEcm());
+ assertFalse(emergencyStateTracker.isInCdmaEcm());
+ // Intents sent for ECM: one for entering ECM and another for exiting
+ ArgumentCaptor<Intent> ecmStateIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(2))
+ .sendStickyBroadcastAsUser(ecmStateIntent.capture(), eq(UserHandle.ALL));
+ List<Intent> capturedIntents = ecmStateIntent.getAllValues();
+ assertTrue(capturedIntents.get(0)
+ .getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false));
+ assertFalse(capturedIntents.get(1)
+ .getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false));
+ // Verify exitEmergencyMode() is called only once
+ verify(testPhone).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testOnEmergencyTransportChangedUsingDifferentThread() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(() -> {
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
+ });
+ processAllMessages();
+
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallWithTestEmergencyNumber() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, true);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ verify(phone0, never()).setEmergencyMode(anyInt(), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallDuringActiveCall() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ // First active call
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Second starting call
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID_2, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallInEcm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setEcmSupportedConfig(phone0, true);
+
+ // First active call
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEcm());
+
+ // Second emergency call started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID_2, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallUsingDifferenPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+
+ // First emergency call
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ // Second emergency call
+ Phone phone1 = getPhone(1);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone1,
+ TEST_CALL_ID_2, false);
+
+ // Returns DisconnectCause#ERROR_UNSPECIFIED immediately.
+ assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
+ Integer.valueOf(DisconnectCause.ERROR_UNSPECIFIED));
+ }
+
+ @Test
+ @SmallTest
+ public void testEndCallInEcm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setEcmSupportedConfig(phone0, true);
+
+ // First active call
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Second emergency call started.
+ future = emergencyStateTracker.startEmergencyCall(phone0, TEST_CALL_ID_2, false);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WLAN);
+ emergencyStateTracker.endCall(TEST_CALL_ID_2);
+ processAllMessages();
+
+ // At this time, ECM is still running so still in ECM.
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WLAN), any(Message.class));
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ verify(phone0, never()).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySms() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testEndSms() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsWithTransportChange() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WLAN);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WLAN), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsWithTestEmergencyNumber() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, true);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ verify(phone0, never()).setEmergencyMode(anyInt(), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsWhileSmsBeingSent() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID_2, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsWhileEmergencyModeBeingSet() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID_2, false);
+
+ // Returns DisconnectCause#ERROR_UNSPECIFIED immediately.
+ assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
+ Integer.valueOf(DisconnectCause.ERROR_UNSPECIFIED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsWhileEcmInProgress() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is ended and the emergency callback is entered.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ assertFalse(future.isDone());
+
+ // Completes the emergency mode setting - MODE_EMERGENCY_CALLBACK
+ processAllMessages();
+
+ verify(phone0, times(2)).setEmergencyMode(anyInt(), any(Message.class));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsUsingDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ Phone phone1 = getPhone(1);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone1,
+ TEST_SMS_ID_2, false);
+
+ // Returns DisconnectCause#ERROR_UNSPECIFIED immediately.
+ assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
+ Integer.valueOf(DisconnectCause.ERROR_UNSPECIFIED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallActiveAndSmsOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallInProgressAndSmsOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Emergency call is in progress.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), msgCaptor.capture());
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ assertFalse(future.isDone());
+
+ Message msg = msgCaptor.getValue();
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsActiveAndCallOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is in active.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ // Emergency call is being started.
+ future = emergencyStateTracker.startEmergencyCall(phone0, TEST_CALL_ID, false);
+ processAllMessages();
+
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsInProgressAndCallOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is in progress.
+ CompletableFuture<Integer> smsFuture = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ ArgumentCaptor<Message> smsCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), smsCaptor.capture());
+
+ // Emergency call is being started.
+ CompletableFuture<Integer> callFuture = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+
+ assertFalse(smsFuture.isDone());
+ assertFalse(callFuture.isDone());
+
+ // Response message for setEmergencyMode by SMS.
+ Message msg = smsCaptor.getValue();
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Exit emergency mode and set the emergency mode again by the call when the exit result
+ // is received for obtaining the latest EmergencyRegResult.
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ ArgumentCaptor<Message> callCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), callCaptor.capture());
+
+ // Response message for setEmergencyMode by call.
+ msg = callCaptor.getAllValues().get(1);
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Expect: DisconnectCause#NOT_DISCONNECTED
+ assertEquals(smsFuture.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(callFuture.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallAndSmsOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started using the different phone.
+ Phone phone1 = getPhone(1);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone1,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#ERROR_UNSPECIFIED immediately.
+ assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
+ Integer.valueOf(DisconnectCause.ERROR_UNSPECIFIED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsActiveAndCallOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is in active.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ // Emergency call is being started using the different phone.
+ Phone phone1 = getPhone(1);
+ setUpAsyncResultForSetEmergencyMode(phone1, E_REG_RESULT);
+ future = emergencyStateTracker.startEmergencyCall(phone1, TEST_CALL_ID, false);
+ processAllMessages();
+
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencySmsInProgressAndCallOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is in progress.
+ CompletableFuture<Integer> smsFuture = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ ArgumentCaptor<Message> smsCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), smsCaptor.capture());
+
+ // Emergency call is being started using the different phone.
+ Phone phone1 = getPhone(1);
+ CompletableFuture<Integer> callFuture = emergencyStateTracker.startEmergencyCall(phone1,
+ TEST_CALL_ID, false);
+
+ assertFalse(smsFuture.isDone());
+ assertFalse(callFuture.isDone());
+
+ // Response message for setEmergencyMode by SMS.
+ Message msg = smsCaptor.getValue();
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Exit emergency mode and set the emergency mode again by the call when the exit result
+ // is received for obtaining the latest EmergencyRegResult.
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ ArgumentCaptor<Message> callCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), callCaptor.capture());
+
+ // Response message for setEmergencyMode by call.
+ msg = callCaptor.getValue();
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Expect: DisconnectCause#OUTGOING_EMERGENCY_CALL_PLACED
+ assertEquals(smsFuture.getNow(DisconnectCause.NOT_DISCONNECTED),
+ Integer.valueOf(DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(callFuture.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeCallAndSmsOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, false);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+ processAllMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeSmsAndCallOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, false);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeCallAndSmsOnSamePhoneWhenEcmSupported() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+ processAllMessages();
+
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // ECM timeout.
+ processAllFutureMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeCallAndSmsOnSamePhoneWhenEcmSupportedAndModeChanged() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN);
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+ processAllMessages();
+
+ // Enter emergency callback mode and emergency mode changed by SMS end.
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // ECM timeout.
+ processAllFutureMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeSmsAndCallOnSamePhoneWhenEcmSupported() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ TEST_CALL_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, getTestEmergencyNumber());
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ emergencyStateTracker.endCall(TEST_CALL_ID);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // ECM timeout.
+ processAllFutureMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ private EmergencyStateTracker setupEmergencyStateTracker(
+ boolean isSuplDdsSwitchRequiredForEmergencyCall) {
+ doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+ doNothing().when(mPhoneSwitcher).overrideDefaultDataForEmergency(
+ anyInt(), anyInt(), any());
+ return new EmergencyStateTracker(mContext, mTestableLooper.getLooper(),
+ isSuplDdsSwitchRequiredForEmergencyCall, mPhoneFactoryProxy, mPhoneSwitcherProxy,
+ mTelephonyManagerProxy, mRadioOnHelper, TEST_ECM_EXIT_TIMEOUT_MS);
+ }
+
+ private Phone setupTestPhoneForEmergencyCall(boolean isRoaming, boolean isRadioOn) {
+ Phone testPhone0 = makeTestPhone(0 /* phoneId */, ServiceState.STATE_IN_SERVICE,
+ false /* isEmergencyOnly */);
+ Phone testPhone1 = makeTestPhone(1 /* phoneId */, ServiceState.STATE_OUT_OF_SERVICE,
+ false /* isEmergencyOnly */);
+ List<Phone> phones = new ArrayList<>(2);
+ phones.add(testPhone0);
+ phones.add(testPhone1);
+ doReturn(isRadioOn).when(testPhone0).isRadioOn();
+ doReturn(isRadioOn).when(testPhone1).isRadioOn();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
+ doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+ testPhone0.getServiceState().setRoaming(isRoaming);
+ return testPhone0;
+ }
+
+ private Phone getPhone(int phoneId) {
+ Phone[] phones = mPhoneFactoryProxy.getPhones();
+ return phones[phoneId];
+ }
+
+ private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
+ GsmCdmaPhone phone = mock(GsmCdmaPhone.class);
+ ServiceState testServiceState = new ServiceState();
+ testServiceState.setState(serviceState);
+ testServiceState.setEmergencyOnly(isEmergencyOnly);
+ when(phone.getContext()).thenReturn(mContext);
+ when(phone.getServiceState()).thenReturn(testServiceState);
+ when(phone.getPhoneId()).thenReturn(phoneId);
+ when(phone.getSubId()).thenReturn(0);
+ when(phone.getServiceStateTracker()).thenReturn(mSST);
+ when(phone.getUnitTestMode()).thenReturn(true);
+ // Initialize the phone as a CDMA phone for now for ease of testing ECBM.
+ // Tests can individually override this to GSM if required for the test.
+ doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(phone).getPhoneType();
+ doNothing().when(phone).notifyEmergencyCallRegistrants(anyBoolean());
+ return phone;
+ }
+
+ private void setEcmSupportedConfig(Phone phone, boolean ecmSupported) {
+ CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ cfgManager.getConfigForSubId(phone.getSubId()).putBoolean(
+ CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
+ ecmSupported);
+ }
+
+ private void setConfigForDdsSwitch(Phone phone, String[] roaminPlmns,
+ int suplEmergencyModeType, String esExtensionSec) {
+ CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ cfgManager.getConfigForSubId(phone.getSubId()).putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ roaminPlmns);
+ cfgManager.getConfigForSubId(phone.getSubId()).putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ suplEmergencyModeType);
+ cfgManager.getConfigForSubId(phone.getSubId())
+ .putString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, esExtensionSec);
+ }
+
+ private void setUpAsyncResultForSetEmergencyMode(Phone phone, EmergencyRegResult regResult) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Message msg = (Message) args[1];
+ AsyncResult.forMessage(msg, regResult, null);
+ msg.sendToTarget();
+ return null;
+ }).when(phone).setEmergencyMode(anyInt(), any(Message.class));
+ }
+
+ private void setUpAsyncResultForExitEmergencyMode(Phone phone) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Message msg = (Message) args[0];
+ AsyncResult.forMessage(msg, null, null);
+ msg.sendToTarget();
+ return null;
+ }).when(phone).exitEmergencyMode(any(Message.class));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
new file mode 100644
index 0000000..2c5a873
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.telephony.ServiceState;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests the RadioOnStateListener, which listens to one Phone and waits until its service state
+ * changes to accepting emergency calls or in service. If it can not find a tower to camp onto for
+ * emergency calls, then it will fail after a timeout period.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RadioOnStateListenerTest extends TelephonyTest {
+
+ private static final long TIMEOUT_MS = 1000;
+
+ @Mock Phone mMockPhone;
+ @Mock RadioOnStateListener.Callback mCallback;
+ @Mock CommandsInterface mMockCi;
+ RadioOnStateListener mListener;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ mListener = new RadioOnStateListener();
+ doReturn(mSST).when(mMockPhone).getServiceStateTracker();
+ mMockPhone.mCi = mMockCi;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mListener.setTimeBetweenRetriesMillis(5000);
+ mListener.setMaxNumRetries(5);
+ mListener.getHandler().removeCallbacksAndMessages(null);
+ // Wait for the queue to clear...
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /* ms timeout */);
+ mListener = null;
+ super.tearDown();
+ }
+
+ /**
+ * Ensure that we successfully register for the ServiceState changed messages in Telephony.
+ */
+ @Test
+ public void testRegisterForCallback() {
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
+ verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
+ verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
+ eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
+ verify(mSatelliteController, never()).registerForSatelliteModemStateChanged(
+ anyInt(), any());
+
+ verify(mMockCi).registerForOffOrNotAvailable(any(Handler.class),
+ eq(RadioOnStateListener.MSG_RADIO_OFF_OR_NOT_AVAILABLE), isNull());
+ }
+
+ /**
+ * Ensure that we successfully register for the satellite modem state changed messages.
+ */
+ @Test
+ public void testRegisterForSatelliteCallback() {
+ doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
+ verify(mSatelliteController).registerForSatelliteModemStateChanged(anyInt(), any());
+ }
+
+ /**
+ * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns true after
+ * service state changes, so we are expecting
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
+ * return true.
+ */
+ @Test
+ public void testPhoneChangeState_OkToCallTrue() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_IN_SERVICE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(true);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, state, null)).sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+
+ /**
+ * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns true after
+ * satellite modem state changes, so we are expecting
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
+ * return true.
+ */
+ @Test
+ public void testSatelliteChangeState_OkToCallTrue() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_IN_SERVICE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(true);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SATELLITE_ENABLED_CHANGED)
+ .sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+
+ /**
+ * We never receive a
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} because
+ * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns false.
+ */
+ @Test
+ public void testPhoneChangeState_NoOkToCall_Timeout() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, state, null)).sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback, never()).onComplete(any(RadioOnStateListener.class), anyBoolean());
+ }
+
+ /**
+ * Tests {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returning
+ * false and hitting the max number of retries. This should result in
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} returning
+ * false.
+ */
+ @Test
+ public void testTimeout_RetryFailure() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
+ mListener.setTimeBetweenRetriesMillis(0/* ms */);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
+ waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(false), eq(false), eq(false));
+ verify(mSatelliteController, never()).requestSatelliteEnabled(
+ anyInt(), eq(false), eq(false), any());
+ }
+
+ @Test
+ public void testTimeout_RetryFailure_ForEmergency() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
+ mListener.setTimeBetweenRetriesMillis(0/* ms */);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 0);
+ waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
+ verify(mSatelliteController, never()).requestSatelliteEnabled(
+ anyInt(), eq(false), eq(false), any());
+ }
+
+ @Test
+ public void testTimeout_RetryFailure_WithSatellite() {
+ doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
+ mListener.setTimeBetweenRetriesMillis(0/* ms */);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 0);
+ waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
+ verify(mSatelliteController, times(2)).requestSatelliteEnabled(
+ anyInt(), eq(false), eq(false), any());
+ }
+
+ @Test
+ public void testTimeout_OnTimeoutForEmergency() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean()))
+ .thenReturn(false);
+ when(mCallback.onTimeout(eq(mMockPhone), anyInt(), anyBoolean()))
+ .thenReturn(true);
+ mListener.setTimeBetweenRetriesMillis(0 /* ms */);
+ mListener.setMaxNumRetries(1);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 100);
+ waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);
+
+ verify(mCallback).onTimeout(eq(mMockPhone), anyInt(), anyBoolean());
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
index 93c9142..d4850c8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -354,6 +355,29 @@
assertEquals(mConnector.mAvailableState, mConnector.getCurrentState());
}
+ @Test
+ public void testConnectedState_serviceDisconnected() throws Exception {
+ // Kick off the asynchronous command.
+ prepareEuiccApp(true /* hasPermission */, true /* requiresBindPermission */,
+ true /* hasPriority */);
+ mConnector = new EuiccConnector(mContext, mLooper.getLooper());
+ mConnector.getEid(CARD_ID, new EuiccConnector.GetEidCommandCallback() {
+ @Override public void onGetEidComplete(String eid) {}
+ @Override public void onEuiccServiceUnavailable() {}
+ });
+ mLooper.dispatchAll();
+ assertEquals(mConnector.mConnectedState, mConnector.getCurrentState());
+ // Now, pretend the remote process died.
+ mConnector.onServiceDisconnected(null /* name */);
+ mLooper.dispatchAll();
+ assertEquals(mConnector.mDisconnectedState, mConnector.getCurrentState());
+ // After binder timeout, should now drop back to available state.
+ mLooper.moveTimeForward(EuiccConnector.BIND_TIMEOUT_MILLIS);
+ mLooper.dispatchAll();
+ assertEquals(mConnector.mAvailableState, mConnector.getCurrentState());
+ assertNull(mConnector.getBinder());
+ }
+
private void prepareEuiccApp(
boolean hasPermission, boolean requiresBindPermission, boolean hasPriority) {
when(mPackageManager.checkPermission(
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 491c690..fc8dfbf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -65,6 +65,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback;
import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
@@ -89,6 +91,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
public class EuiccControllerTest extends TelephonyTest {
@@ -128,7 +131,7 @@
private static final int SUBSCRIPTION_ID = 12345;
private static final String ICC_ID = "54321";
private static final int CARD_ID = 25;
- private static final int PORT_INDEX = 0;
+ private static final int REMOVABLE_CARD_ID = 26;
// Mocked classes
private EuiccConnector mMockConnector;
@@ -319,8 +322,8 @@
SUBSCRIPTION, false /* complete */, null /* result */);
verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
0 /* detailedCode */);
- verify(mMockConnector).getDownloadableSubscriptionMetadata(anyInt(), any(), anyBoolean(),
- any());
+ verify(mMockConnector).getDownloadableSubscriptionMetadata(anyInt(), anyInt(), any(),
+ anyBoolean(), anyBoolean(), any());
}
@Test
@@ -787,6 +790,70 @@
}
@Test
+ public void testGetResolvedPortIndexForSubscriptionSwitchWithOutMEP() throws Exception {
+ setUpUiccSlotData();
+ assertEquals(TelephonyManager.DEFAULT_PORT_INDEX,
+ mController.getResolvedPortIndexForSubscriptionSwitch(CARD_ID));
+ }
+
+ @Test
+ public void testGetResolvedPortIndexForSubscriptionSwitchWithMEP() throws Exception {
+ setUpUiccSlotDataWithMEP();
+ when(mUiccSlot.getPortList()).thenReturn(new int[]{0, 1});
+ when(mUiccSlot.isPortActive(TelephonyManager.DEFAULT_PORT_INDEX)).thenReturn(false);
+ when(mUiccSlot.isPortActive(1)).thenReturn(true);
+ when(mTelephonyManager.getActiveModemCount()).thenReturn(2);
+ assertEquals(1, mController.getResolvedPortIndexForSubscriptionSwitch(CARD_ID));
+ }
+
+ @Test
+ public void testGetResolvedPortIndexForSubscriptionSwitchWithUiccSlotNull() throws Exception {
+ assertEquals(TelephonyManager.DEFAULT_PORT_INDEX,
+ mController.getResolvedPortIndexForSubscriptionSwitch(CARD_ID));
+ }
+
+ @Test
+ public void testGetResolvedPortIndexForSubscriptionSwitchWithPsimActiveAndSS()
+ throws Exception {
+ when(mUiccController.getUiccSlot(anyInt())).thenReturn(mUiccSlot);
+ when(mUiccSlot.isRemovable()).thenReturn(true);
+ when(mUiccSlot.isEuicc()).thenReturn(false);
+ when(mUiccSlot.isActive()).thenReturn(true);
+ when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
+ assertEquals(TelephonyManager.DEFAULT_PORT_INDEX,
+ mController.getResolvedPortIndexForSubscriptionSwitch(CARD_ID));
+ }
+
+ @Test
+ public void testGetResolvedPortIndexForSubscriptionSwitchWithPsimInActiveAndSS()
+ throws Exception {
+ setUpUiccSlotDataWithMEP();
+ when(mUiccSlot.getPortList()).thenReturn(new int[]{0});
+ when(mUiccSlot.isPortActive(TelephonyManager.DEFAULT_PORT_INDEX)).thenReturn(true);
+ when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
+ assertEquals(TelephonyManager.DEFAULT_PORT_INDEX,
+ mController.getResolvedPortIndexForSubscriptionSwitch(CARD_ID));
+ }
+
+ @Test
+ public void testgetResolvedPortIndexForDisableSubscriptionForNoActiveSubscription()
+ throws Exception {
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(null);
+ assertEquals(-1,
+ mController.getResolvedPortIndexForDisableSubscription(
+ CARD_ID, PACKAGE_NAME, true));
+ }
+
+ @Test
+ public void testgetResolvedPortIndexForDisableSubscriptionForActiveSubscriptions()
+ throws Exception {
+ setActiveSubscriptionInfoInMEPMode();
+ assertEquals(1,
+ mController.getResolvedPortIndexForDisableSubscription(
+ CARD_ID, PACKAGE_NAME, false));
+ }
+
+ @Test
public void testDeleteSubscription_noPrivileges() throws Exception {
setHasWriteEmbeddedPermission(false);
prepareOperationSubscription(false /* hasPrivileges */);
@@ -1227,12 +1294,171 @@
SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE));
}
+ @Test
+ @EnableCompatChanges({EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK})
+ public void testIsSimPortAvailable_invalidCase() {
+ setUiccCardInfos(false, true, true);
+ // assert non euicc card id
+ assertFalse(mController.isSimPortAvailable(REMOVABLE_CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // assert invalid port index
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 5 /* portIndex */, TEST_PACKAGE_NAME));
+ }
+
+ @Test
+ @EnableCompatChanges({EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK})
+ public void testIsSimPortAvailable_port_active() throws Exception {
+ setUiccCardInfos(false, true, true);
+
+ // port has empty iccid
+ assertTrue(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // Set port is active, has valid iccid(may be boot profile) and UiccProfile is empty
+ setUiccCardInfos(false, true, false);
+ when(mUiccController.getUiccPortForSlot(anyInt(), anyInt())).thenReturn(mUiccPort);
+ when(mUiccPort.getUiccProfile()).thenReturn(mUiccProfile);
+ when(mUiccProfile.isEmptyProfile()).thenReturn(true);
+ assertTrue(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // port is active, valid iccid, not empty profile but Phone object is null
+ when(mUiccPort.getUiccProfile()).thenReturn(mUiccProfile);
+ when(mUiccProfile.isEmptyProfile()).thenReturn(false);
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ // logicalSlotIndex of port#0 is 1, Phone object should be null
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // port is active, valid iccid, not empty profile but no carrier privileges
+ when(mUiccPort.getUiccProfile()).thenReturn(mUiccProfile);
+ when(mUiccProfile.isEmptyProfile()).thenReturn(false);
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone});
+ when(mPhone.getCarrierPrivilegesTracker()).thenReturn(null);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+ when(mPhone.getCarrierPrivilegesTracker()).thenReturn(mCarrierPrivilegesTracker);
+ when(mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // port is active, valid iccid, not empty profile and has carrier privileges
+ when(mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+ }
+
+ @Test
+ @EnableCompatChanges({EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK})
+ public void testIsSimPortAvailable_port_inActive() {
+ setUiccCardInfos(false, false, true);
+ when(mUiccController.getUiccSlots()).thenReturn(new UiccSlot[]{mUiccSlot});
+ when(mUiccSlot.isRemovable()).thenReturn(true);
+
+ // Check getRemovableNonEuiccSlot null case
+ when(mUiccSlot.isEuicc()).thenReturn(true);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // Check getRemovableNonEuiccSlot isActive() false case
+ when(mUiccSlot.isEuicc()).thenReturn(false);
+ when(mUiccSlot.isActive()).thenReturn(false);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // assert false,multisim is not enabled
+ when(mUiccSlot.isEuicc()).thenReturn(false);
+ when(mUiccSlot.isActive()).thenReturn(true);
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(false);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // assert false, caller does not have carrier privileges
+ setHasWriteEmbeddedPermission(false);
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(true);
+ when(mUiccSlot.getPortList()).thenReturn(new int[] {0});
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt())).thenReturn(
+ new SubscriptionInfo.Builder().build());
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // assert true, caller does not have carrier privileges but has write_embedded permission
+ setHasWriteEmbeddedPermission(true);
+ assertTrue(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+
+ // assert true, caller has carrier privileges
+ setHasWriteEmbeddedPermission(false);
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK})
+ public void testIsSimPortAvailable_port_inActive_disable_compactChange() {
+ setUiccCardInfos(false, false, true);
+ when(mUiccController.getUiccSlots()).thenReturn(new UiccSlot[]{mUiccSlot});
+ when(mUiccSlot.isRemovable()).thenReturn(true);
+ when(mUiccSlot.isEuicc()).thenReturn(false);
+ when(mUiccSlot.isActive()).thenReturn(true);
+
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(true);
+ when(mUiccSlot.getPortList()).thenReturn(new int[] {0});
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt())).thenReturn(
+ new SubscriptionInfo.Builder().build());
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE_NAME))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ setHasWriteEmbeddedPermission(true);
+
+ // Even though all conditions are true, isSimPortAvailable should return false as
+ // compact change is disabled
+ assertFalse(mController.isSimPortAvailable(CARD_ID, 0, TEST_PACKAGE_NAME));
+ }
+
+
+ private void setUiccCardInfos(boolean isMepSupported, boolean isPortActive,
+ boolean isEmptyPort) {
+ List<UiccPortInfo> euiccPortInfoList;
+ if (isMepSupported) {
+ euiccPortInfoList = Arrays.asList(
+ new UiccPortInfo(isEmptyPort ? "" : ICC_ID /* iccId */, 0 /* portIdx */,
+ isPortActive ? 1 : -1 /* logicalSlotIdx */,
+ isPortActive /* isActive */),
+ new UiccPortInfo(isEmptyPort ? "" : ICC_ID /* iccId */, 1 /* portIdx */,
+ -1 /* logicalSlotIdx */,
+ isPortActive /* isActive */));
+ } else {
+ euiccPortInfoList = Collections.singletonList(
+ new UiccPortInfo(isEmptyPort ? "" : ICC_ID /* iccId */, 0 /* portIdx */,
+ isPortActive ? 1 : -1 /* logicalSlotIdx */,
+ isPortActive /* isActive */)
+ );
+ }
+
+ UiccCardInfo cardInfo1 = new UiccCardInfo(true, CARD_ID, "", 0,
+ false /* isRemovable */,
+ isMepSupported /* isMultipleEnabledProfileSupported */,
+ euiccPortInfoList);
+ UiccCardInfo cardInfo2 = new UiccCardInfo(false /* isEuicc */,
+ REMOVABLE_CARD_ID /* cardId */,
+ "", 0, true /* isRemovable */,
+ false /* isMultipleEnabledProfileSupported */,
+ Collections.singletonList(
+ new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
+ 0 /* logicalSlotIdx */, true /* isActive */)));
+ ArrayList<UiccCardInfo> cardInfos = new ArrayList<>();
+ cardInfos.add(cardInfo1);
+ cardInfos.add(cardInfo2);
+ when(mTelephonyManager.getUiccCardsInfo()).thenReturn(cardInfos);
+ }
+
private void setUpUiccSlotData() {
when(mUiccController.getUiccSlot(anyInt())).thenReturn(mUiccSlot);
// TODO(b/199559633): Add test cases for isMultipleEnabledProfileSupported true case
when(mUiccSlot.isMultipleEnabledProfileSupported()).thenReturn(false);
}
+ private void setUpUiccSlotDataWithMEP() {
+ when(mUiccController.getUiccSlot(anyInt())).thenReturn(mUiccSlot);
+ when(mUiccSlot.isMultipleEnabledProfileSupported()).thenReturn(true);
+ }
+
private void setGetEidPermissions(
boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges) throws Exception {
doReturn(hasPhoneStatePrivileged
@@ -1258,10 +1484,17 @@
private void setHasCarrierPrivilegesOnActiveSubscription(boolean hasPrivileges)
throws Exception {
- SubscriptionInfo subInfo = new SubscriptionInfo(
- 0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
- hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "", CARD_ID,
- false, null, false, 0, 0, 0, null, null, true, 0);
+ SubscriptionInfo.Builder builder = new SubscriptionInfo.Builder()
+ .setSimSlotIndex(0)
+ .setPortIndex(mTelephonyManager.DEFAULT_PORT_INDEX)
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER_ID)
+ .setEmbedded(true);
+ if (hasPrivileges) {
+ builder.setNativeAccessRules(new UiccAccessRule[] { ACCESS_RULE });
+ }
+ builder.setCardId(CARD_ID);
+ SubscriptionInfo subInfo = builder.build();
+
when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
hasPrivileges);
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(
@@ -1287,14 +1520,18 @@
cardInfos.add(cardInfo2);
when(mTelephonyManager.getUiccCardsInfo()).thenReturn(cardInfos);
- SubscriptionInfo subInfo1 = new SubscriptionInfo(
- 0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
- hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "", CARD_ID,
- false, null, false, 0, 0, 0, null, null, true, 0);
- SubscriptionInfo subInfo2 = new SubscriptionInfo(
- 0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
- hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "",
- 1 /* cardId */, false, null, false, 0, 0, 0, null, null, true, 0);
+ SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+ .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+ .setEmbedded(true)
+ .setCardId(CARD_ID)
+ .setPortIndex(mTelephonyManager.DEFAULT_PORT_INDEX)
+ .build();
+ SubscriptionInfo subInfo2 = new SubscriptionInfo.Builder()
+ .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+ .setEmbedded(true)
+ .setCardId(2)
+ .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+ .build();
when(mSubscriptionManager.canManageSubscription(subInfo1, PACKAGE_NAME)).thenReturn(
hasPrivileges);
when(mSubscriptionManager.canManageSubscription(subInfo2, PACKAGE_NAME)).thenReturn(
@@ -1303,11 +1540,34 @@
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subInfos);
}
+ private void setActiveSubscriptionInfoInMEPMode()
+ throws Exception {
+ SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+ .setEmbedded(true)
+ .setCardId(CARD_ID)
+ .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+ .build();
+ SubscriptionInfo subInfo2 = new SubscriptionInfo.Builder()
+ .setEmbedded(true)
+ .setCardId(CARD_ID)
+ .setPortIndex(1)
+ .build();
+ when(mSubscriptionManager.canManageSubscription(subInfo1, PACKAGE_NAME)).thenReturn(
+ false);
+ when(mSubscriptionManager.canManageSubscription(subInfo2, PACKAGE_NAME)).thenReturn(
+ true);
+ ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1, subInfo2));
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subInfos);
+ }
+
private void prepareOperationSubscription(boolean hasPrivileges) throws Exception {
- SubscriptionInfo subInfo = new SubscriptionInfo(
- SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, "0", "0", "",
- true /* isEmbedded */, hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null,
- null);
+ SubscriptionInfo subInfo = new SubscriptionInfo.Builder()
+ .setId(SUBSCRIPTION_ID)
+ .setIccId(ICC_ID)
+ .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+ .setEmbedded(true)
+ .setCardId(CARD_ID)
+ .build();
when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
hasPrivileges);
when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
@@ -1390,7 +1650,7 @@
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
EuiccConnector.GetMetadataCommandCallback cb = invocation
- .getArgument(3 /* resultCallback */);
+ .getArgument(5 /* resultCallback */);
if (complete) {
cb.onGetMetadataComplete(CARD_ID, result);
} else {
@@ -1398,8 +1658,8 @@
}
return null;
}
- }).when(mMockConnector).getDownloadableSubscriptionMetadata(anyInt(), any(), anyBoolean(),
- any());
+ }).when(mMockConnector).getDownloadableSubscriptionMetadata(anyInt(), anyInt(), any(),
+ anyBoolean(), anyBoolean(), any());
}
private void callGetDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
deleted file mode 100644
index 2e9b88c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
+++ /dev/null
@@ -1,1935 +0,0 @@
-/*
- * Copyright (C) 2007 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 GSMTestHandler.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.gsm;
-
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.ServiceState;
-import android.test.AndroidTestCase;
-import android.test.PerformanceTestCase;
-
-import android.telephony.DisconnectCause;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.MmiCode;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.gsm.CallFailCause;
-import com.android.internal.telephony.gsm.GSMPhone;
-import com.android.internal.telephony.gsm.GSMTestHandler;
-import com.android.internal.telephony.gsm.GsmMmiCode;
-import com.android.internal.telephony.gsm.SuppServiceNotification;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-
-import java.util.List;
-
-
-public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase {
- private SimulatedRadioControl mRadioControl;
- private GSMPhone mGSMPhone;
- private GSMTestHandler mGSMTestHandler;
- private Handler mHandler;
-
- private static final int EVENT_PHONE_STATE_CHANGED = 1;
- private static final int EVENT_DISCONNECT = 2;
- private static final int EVENT_RINGING = 3;
- private static final int EVENT_CHANNEL_OPENED = 4;
- private static final int EVENT_POST_DIAL = 5;
- private static final int EVENT_DONE = 6;
- private static final int EVENT_SSN = 7;
- private static final int EVENT_MMI_INITIATE = 8;
- private static final int EVENT_MMI_COMPLETE = 9;
- private static final int EVENT_IN_SERVICE = 10;
- private static final int SUPP_SERVICE_FAILED = 11;
- private static final int SERVICE_STATE_CHANGED = 12;
- private static final int EVENT_OEM_RIL_MESSAGE = 13;
- public static final int ANY_MESSAGE = -1;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mGSMTestHandler = new GSMTestHandler(mContext);
-
- mGSMTestHandler.start();
- synchronized (mGSMTestHandler) {
- do {
- mGSMTestHandler.wait();
- } while (mGSMTestHandler.getGSMPhone() == null);
- }
-
- mGSMPhone = mGSMTestHandler.getGSMPhone();
- mRadioControl = mGSMTestHandler.getSimulatedCommands();
-
- mHandler = mGSMTestHandler.getHandler();
- mGSMPhone.registerForPreciseCallStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
- mGSMPhone.registerForNewRingingConnection(mHandler, EVENT_RINGING, null);
- mGSMPhone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
-
- mGSMPhone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL, null);
-
- mGSMPhone.registerForSuppServiceNotification(mHandler, EVENT_SSN, null);
- mGSMPhone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
- mGSMPhone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
- mGSMPhone.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED, null);
-
- mGSMPhone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
-
- // wait until we get phone in both voice and data service
- Message msg;
- ServiceState state;
-
- do {
- msg = mGSMTestHandler.waitForMessage(SERVICE_STATE_CHANGED);
- assertNotNull("Message Time Out", msg);
- state = (ServiceState) ((AsyncResult) msg.obj).result;
- } while (state.getState() != ServiceState.STATE_IN_SERVICE);
- }
-
- @Override
- protected void tearDown() throws Exception {
- mRadioControl.shutdown();
-
- mGSMPhone.unregisterForPreciseCallStateChanged(mHandler);
- mGSMPhone.unregisterForNewRingingConnection(mHandler);
- mGSMPhone.unregisterForDisconnect(mHandler);
- mGSMPhone.setOnPostDialCharacter(mHandler, 0, null);
- mGSMPhone.unregisterForSuppServiceNotification(mHandler);
- mGSMPhone.unregisterForMmiInitiate(mHandler);
- mGSMPhone.unregisterForMmiComplete(mHandler);
-
- mGSMPhone = null;
- mRadioControl = null;
- mHandler = null;
- mGSMTestHandler.cleanup();
-
- super.tearDown();
- }
-
- // These test can only be run once.
- public int startPerformance(Intermediates intermediates) {
- return 1;
- }
-
- public boolean isPerformanceOnly() {
- return false;
- }
-
-
- //This test is causing the emulator screen to turn off. I don't understand
- //why, but I'm removing it until we can figure it out.
- public void brokenTestGeneral() throws Exception {
- Connection cn;
- Message msg;
- AsyncResult ar;
-
- // IDLE state
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
- assertFalse(mGSMPhone.canConference());
-
- // One DIALING connection
-
- mRadioControl.setAutoProgressConnectingCall(false);
-
- mGSMPhone.dial("+13125551212");
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-
- msg = mGSMTestHandler.waitForMessage(EVENT_PHONE_STATE_CHANGED);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertEquals(Call.State.DIALING, mGSMPhone.getForegroundCall().getState());
- assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- /*do {
- mGSMTestHandler.waitForMessage(ANY_MESSAGE);
- } while (mGSMPhone.getForegroundCall().getConnections().size() == 0);*/
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DIALING,
- mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- cn = mGSMPhone.getForegroundCall().getConnections().get(0);
- assertTrue(!cn.isIncoming());
- assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
- assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
-
- assertFalse(mGSMPhone.canConference());
-
- // One ALERTING connection
-
- mRadioControl.progressConnectingCallState();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- }
- while (mGSMPhone.getForegroundCall().getState() != Call.State.ALERTING);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ALERTING, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- cn = mGSMPhone.getForegroundCall().getConnections().get(0);
- assertTrue(!cn.isIncoming());
- assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
- assertFalse(mGSMPhone.canConference());
-
- // One ACTIVE connection
-
- mRadioControl.progressConnectingCallState();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
- cn = mGSMPhone.getForegroundCall().getConnections().get(0);
- assertTrue(!cn.isIncoming());
- assertEquals(Connection.PostDialState.COMPLETE, cn.getPostDialState());
- assertFalse(mGSMPhone.canConference());
-
- // One disconnected connection
- mGSMPhone.getForegroundCall().hangup();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
- assertFalse(mGSMPhone.canConference());
-
- cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- // Back to idle state
-
- mGSMPhone.clearDisconnected();
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- assertFalse(mGSMPhone.canConference());
-
- // cn left over from before phone.clearDisconnected();
-
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- // One ringing (INCOMING) call
-
- mRadioControl.triggerRing("18005551212");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- ar = (AsyncResult) msg.obj;
- cn = (Connection) ar.result;
- assertTrue(cn.isRinging());
- assertEquals(mGSMPhone.getRingingCall(), cn.getCall());
-
- assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.INCOMING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- cn = mGSMPhone.getRingingCall().getConnections().get(0);
- assertTrue(cn.isIncoming());
- assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
- assertFalse(mGSMPhone.canConference());
-
- // One mobile terminated active call
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getConnections().size() == 1);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE,
- mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
- cn = mGSMPhone.getForegroundCall().getConnections().get(0);
- assertTrue(cn.isIncoming());
- assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
- assertFalse(mGSMPhone.canConference());
-
- // One disconnected (local hangup) call
-
- try {
- Connection conn;
- conn = mGSMPhone.getForegroundCall().getConnections().get(0);
- conn.hangup();
- } catch (CallStateException ex) {
- ex.printStackTrace();
- fail("unexpected ex");
- }
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED,
- mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
- cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
- assertFalse(mGSMPhone.canConference());
-
- // Back to idle state
-
- mGSMPhone.clearDisconnected();
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- assertFalse(mGSMPhone.canConference());
-
- // cn left over from before phone.clearDisconnected();
-
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- // One ringing call
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getConnections().isEmpty());
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.INCOMING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- assertFalse(mGSMPhone.canConference());
-
- // One rejected call
- mGSMPhone.rejectCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- cn = mGSMPhone.getRingingCall().getEarliestConnection();
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-
- assertFalse(mGSMPhone.canConference());
-
- // Back to idle state
-
- mGSMPhone.clearDisconnected();
-
- assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- assertFalse(mGSMPhone.canConference());
- assertEquals(Call.State.DISCONNECTED, cn.getState());
-
- // One ringing call
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getConnections().isEmpty());
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
- // Ringing call disconnects
-
- mRadioControl.triggerHangupForeground();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
- assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-
- // One Ringing Call
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-
- cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
- // One answered call
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // one holding call
- mGSMPhone.switchHoldingAndActive();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // one active call
- mGSMPhone.switchHoldingAndActive();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- }
- while (mGSMPhone.getBackgroundCall().getState() == Call.State.HOLDING);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // One disconnected call in the foreground slot
-
- mRadioControl.triggerHangupAll();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
- // Test missed calls
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
- mGSMPhone.rejectCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (msg.what != EVENT_DISCONNECT);
-
- ar = (AsyncResult) msg.obj;
- cn = (Connection) ar.result;
-
- assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
-
- // Test incoming not missed calls
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
- cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-
- try {
- mGSMPhone.getForegroundCall().hangup();
- } catch (CallStateException ex) {
- ex.printStackTrace();
- fail("unexpected ex");
- }
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState()
- != Call.State.DISCONNECTED);
-
- assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
- //
- // Test held and hangup held calls
- //
-
- // One ALERTING call
- mGSMPhone.dial("+13125551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- mRadioControl.progressConnectingCallState();
- mRadioControl.progressConnectingCallState();
-
- // One ACTIVE call
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- // One ACTIVE call, one ringing call
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- // One HOLDING call, one ACTIVE call
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertTrue(mGSMPhone.canConference());
-
- // Conference the two
- mGSMPhone.conference();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertTrue(mGSMPhone.getForegroundCall().isMultiparty());
- assertFalse(mGSMPhone.canConference());
-
- // Hold the multiparty call
- mGSMPhone.switchHoldingAndActive();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- }
- while (mGSMPhone.getBackgroundCall().getState() != Call.State.HOLDING);
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertTrue(mGSMPhone.getBackgroundCall().isMultiparty());
- assertFalse(mGSMPhone.canConference());
-
- // Multiparty call on hold, call waiting added
-
- mRadioControl.triggerRing("18005558355");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertTrue(mGSMPhone.getBackgroundCall().isMultiparty());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertFalse(mGSMPhone.canConference());
-
- // Hangup conference call, ringing call still around
- mGSMPhone.getBackgroundCall().hangup();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.DISCONNECTED);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getBackgroundCall().getState());
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
-
- // Reject waiting call
- mGSMPhone.rejectCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
- assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
- }
-
- public void testOutgoingCallFailImmediately() throws Exception {
- Message msg;
-
- // Test outgoing call fail-immediately edge case
- // This happens when a call terminated before ever appearing in a
- // call list
- // This should land the immediately-failing call in the
- // ForegroundCall list as an IDLE call
- mRadioControl.setNextDialFailImmediately(true);
-
- Connection cn = mGSMPhone.dial("+13125551212");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
- }
-
- public void testHangupOnOutgoing() throws Exception {
- Connection cn;
- Message msg;
-
- mRadioControl.setAutoProgressConnectingCall(false);
-
- // Test 1: local hangup in "DIALING" state
- mGSMPhone.dial("+13125551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- }
- while (mGSMPhone.getForegroundCall().getState() != Call.State.DIALING);
-
- cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
- mGSMPhone.getForegroundCall().hangup();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
- // Test 2: local hangup in "ALERTING" state
- mGSMPhone.dial("+13125551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- mRadioControl.progressConnectingCallState();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- }
- while (mGSMPhone.getForegroundCall().getState() != Call.State.ALERTING);
-
- cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
- mGSMPhone.getForegroundCall().hangup();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
- // Test 3: local immediate hangup before GSM index is
- // assigned (CallTracker.hangupPendingMO case)
-
- mRadioControl.pauseResponses();
-
- cn = mGSMPhone.dial("+13125551212");
-
- cn.hangup();
-
- mRadioControl.resumeResponses();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-
- assertEquals(DisconnectCause.LOCAL,
- mGSMPhone.getForegroundCall().getEarliestConnection().getDisconnectCause());
- }
-
- public void testHangupOnChannelClose() throws Exception {
- mGSMPhone.dial("+13125551212");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getConnections().isEmpty());
-
- mRadioControl.shutdown();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- mGSMPhone.clearDisconnected();
- } while (!mGSMPhone.getForegroundCall().getConnections().isEmpty());
- }
-
- public void testIncallMmiCallDeflection() throws Exception {
- Message msg;
-
- // establish an active call
- mGSMPhone.dial("+13125551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // establish a ringing (WAITING) call
-
- mRadioControl.triggerRing("18005551212");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering 0 followed by SEND: release all held calls
- // or sets UDUB for a waiting call.
- mGSMPhone.handleInCallMmiCommands("0");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // change the active call to holding call
- mGSMPhone.switchHoldingAndActive();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering 0 followed by SEND: release all held calls
- // or sets UDUB for a waiting call.
- mGSMPhone.handleInCallMmiCommands("0");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() == Call.State.HOLDING);
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getBackgroundCall().getState());
- }
-
- public void testIncallMmiCallWaiting() throws Exception {
- Message msg;
-
- // establish an active call
- mGSMPhone.dial("+13125551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // establish a ringing (WAITING) call
-
- mRadioControl.triggerRing("18005551212");
-
- do {
- msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
- assertNotNull("Message Time Out", msg);
- } while (msg.what != EVENT_RINGING);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering 1 followed by SEND: release all active calls
- // (if any exist) and accepts the other (held or waiting) call.
-
- mGSMPhone.handleInCallMmiCommands("1");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
- // change the active call to holding call
- mGSMPhone.switchHoldingAndActive();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
- assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering 1 followed by SEND: release all active calls
- // (if any exist) and accepts the other (held or waiting) call.
- mGSMPhone.handleInCallMmiCommands("1");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
- // at this point, the active call with number==18005551212 should
- // have the gsm index of 2
-
- mRadioControl.triggerRing("16505550100");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering "12" followed by SEND: release the call with
- // gsm index equals to 2.
- mGSMPhone.handleInCallMmiCommands("12");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertFalse(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // at this point, the call with number==16505550100 should
- // have the gsm index of 1
- mGSMPhone.dial("+13125551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE ||
- mGSMPhone.getBackgroundCall().getState() != Call.State.HOLDING);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // at this point, the active call with number==13125551212 should
- // have the gsm index of 2
-
- // Simulate entering "11" followed by SEND: release the call with
- // gsm index equals to 1. This should not be allowed, and a
- // Supplementary Service notification must be received.
- mGSMPhone.handleInCallMmiCommands("11");
-
- msg = mGSMTestHandler.waitForMessage(SUPP_SERVICE_FAILED);
- assertNotNull("Message Time Out", msg);
- assertFalse("IncallMmiCallWaiting: command should not work on holding call", msg == null);
-
- // Simulate entering "12" followed by SEND: release the call with
- // gsm index equals to 2.
- mGSMPhone.handleInCallMmiCommands("12");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // Simulate entering 1 followed by SEND: release all active calls
- // (if any exist) and accepts the other (held or waiting) call.
- mGSMPhone.handleInCallMmiCommands("1");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- assertEquals("16505550100",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
- // Simulate entering "11" followed by SEND: release the call with
- // gsm index equals to 1.
- mGSMPhone.handleInCallMmiCommands("11");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- }
-
- public void testIncallMmiCallHold() throws Exception {
- Message msg;
-
- // establish an active call
- mGSMPhone.dial("13125551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // establish a ringing (WAITING) call
-
- mRadioControl.triggerRing("18005551212");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // simulate entering 2 followed by SEND: place all active calls
- // (if any exist) on hold and accepts the other (held or waiting)
- // call
-
- mGSMPhone.handleInCallMmiCommands("2");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
-
- assertFalse(mGSMPhone.getRingingCall().isRinging());
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE,
- mGSMPhone.getForegroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertEquals("13125551212",
- mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
- // swap the active and holding calls
- mGSMPhone.handleInCallMmiCommands("2");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_PHONE_STATE_CHANGED);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("13125551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
- // merge the calls
- mGSMPhone.conference();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- assertEquals(2, mGSMPhone.getForegroundCall().getConnections().size());
-
- // at this point, we have an active conference call, with
- // call(1) = 13125551212 and call(2) = 18005551212
-
- // Simulate entering "23" followed by SEND: places all active call
- // on hold except call 3. This should fail and a supplementary service
- // failed notification should be received.
-
- mGSMPhone.handleInCallMmiCommands("23");
-
- msg = mGSMTestHandler.waitForMessage(SUPP_SERVICE_FAILED);
- assertNotNull("Message Time Out", msg);
- assertFalse("IncallMmiCallHold: separate should have failed!", msg == null);
-
- // Simulate entering "21" followed by SEND: places all active call
- // on hold except call 1.
- mGSMPhone.handleInCallMmiCommands("21");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("13125551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
- }
-
- public void testIncallMmiMultipartyServices() throws Exception {
- // establish an active call
- mGSMPhone.dial("13125551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- // dial another call
- mGSMPhone.dial("18005551212");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- mGSMPhone.handleInCallMmiCommands("3");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals("13125551212",
- mGSMPhone.getForegroundCall().getConnections().get(1).getAddress());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- }
-
- public void testCallIndex() throws Exception {
- Message msg;
-
- // establish the first call
- mGSMPhone.dial("16505550100");
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- String baseNumber = "1650555010";
-
- for (int i = 1; i < 6; i++) {
- String number = baseNumber + i;
-
- mGSMPhone.dial(number);
-
- do {
- mRadioControl.progressConnectingCallState();
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- if (mGSMPhone.getBackgroundCall().getConnections().size() >= 5) {
- break;
- }
-
- mGSMPhone.conference();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- }
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("16505550105",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // create an incoming call, this call should have the call index
- // of 7
- mRadioControl.triggerRing("18005551212");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
- assertNotNull("Message Time Out", msg);
-
- assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
- assertTrue(mGSMPhone.getRingingCall().isRinging());
- assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
- // hangup the background call and accept the ringing call
- mGSMPhone.getBackgroundCall().hangup();
- mGSMPhone.acceptCall();
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getRingingCall().getState() != Call.State.IDLE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals("18005551212",
- mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertEquals("16505550105",
- mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
- mGSMPhone.handleInCallMmiCommands("17");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
- assertEquals("16505550105",
- mGSMPhone.getBackgroundCall().getConnections().get(0).
- getAddress());
-
- mGSMPhone.handleInCallMmiCommands("1");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
- assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- mGSMPhone.handleInCallMmiCommands("16");
-
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
- }
-
- public void testPostDialSequences() throws Exception {
- Message msg;
- AsyncResult ar;
- Connection cn;
-
- mGSMPhone.dial("+13125551212,1234;5N8xx");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(',', msg.arg1);
- assertEquals("1234;5N8", cn.getRemainingPostDialString());
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('1', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('2', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('3', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('4', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals(';', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(Connection.PostDialState.WAIT, cn.getPostDialState());
- assertEquals(Connection.PostDialState.WAIT, ar.userObj);
- cn.proceedAfterWaitChar();
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('5', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertEquals('N', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(Connection.PostDialState.WILD, cn.getPostDialState());
- assertEquals(Connection.PostDialState.WILD, ar.userObj);
- cn.proceedAfterWildChar(",6;7");
-
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(',', msg.arg1);
- assertEquals("6;78", cn.getRemainingPostDialString());
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('6', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals(';', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(Connection.PostDialState.WAIT, cn.getPostDialState());
- assertEquals(Connection.PostDialState.WAIT, ar.userObj);
- cn.proceedAfterWaitChar();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('7', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals('8', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
- // Bogus chars at end should be ignored
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals(0, msg.arg1);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(Connection.PostDialState.COMPLETE,
- cn.getPostDialState());
- assertEquals(Connection.PostDialState.COMPLETE, ar.userObj);
- }
-
- public void testPostDialCancel() throws Exception {
- Message msg;
- AsyncResult ar;
- Connection cn;
-
- mGSMPhone.dial("+13125551212,N");
- mRadioControl.progressConnectingToActive();
-
- mRadioControl.progressConnectingToActive();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertNotNull("Message Time Out", msg);
- assertEquals(',', msg.arg1);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
- assertEquals('N', msg.arg1);
- ar = (AsyncResult) (msg.obj);
- cn = (Connection) (ar.result);
- assertEquals(Connection.PostDialState.WILD, cn.getPostDialState());
- cn.cancelPostDial();
-
- assertEquals(Connection.PostDialState.CANCELLED, cn.getPostDialState());
- }
-
- public void testOutgoingCallFail() throws Exception {
- Message msg;
- /*
- * normal clearing
- */
-
- mRadioControl.setNextCallFailCause(CallFailCause.NORMAL_CLEARING);
- mRadioControl.setAutoProgressConnectingCall(false);
-
- Connection cn = mGSMPhone.dial("+13125551212");
-
- mRadioControl.progressConnectingCallState();
-
- // I'm just progressing the call state to
- // ensure getCurrentCalls() gets processed...
- // Normally these failure conditions would happen in DIALING
- // not ALERTING
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (cn.getState() == Call.State.DIALING);
-
-
- mRadioControl.triggerHangupAll();
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- /*
- * busy
- */
-
- mRadioControl.setNextCallFailCause(CallFailCause.USER_BUSY);
- mRadioControl.setAutoProgressConnectingCall(false);
-
- cn = mGSMPhone.dial("+13125551212");
-
- mRadioControl.progressConnectingCallState();
-
- // I'm just progressing the call state to
- // ensure getCurrentCalls() gets processed...
- // Normally these failure conditions would happen in DIALING
- // not ALERTING
- do {
- assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
- } while (cn.getState() == Call.State.DIALING);
-
-
- mRadioControl.triggerHangupAll();
- msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
- assertNotNull("Message Time Out", msg);
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(DisconnectCause.BUSY, cn.getDisconnectCause());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED,
- mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
- /*
- * congestion
- */
-
- mRadioControl.setNextCallFailCause(CallFailCause.NO_CIRCUIT_AVAIL);
- mRadioControl.setAutoProgressConnectingCall(false);
-
- cn = mGSMPhone.dial("+13125551212");
-
- mRadioControl.progressConnectingCallState();
-
- // I'm just progressing the call state to
- // ensure getCurrentCalls() gets processed...
- // Normally these failure conditions would happen in DIALING
- // not ALERTING
- do {
- msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
- assertNotNull("Message Time Out", msg);
- } while (cn.getState() == Call.State.DIALING);
-
-
- mRadioControl.triggerHangupAll();
-
- // Unlike the while loops above, this one waits
- // for a "phone state changed" message back to "idle"
- do {
- msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
- assertNotNull("Message Time Out", msg);
- } while (!(msg.what == EVENT_PHONE_STATE_CHANGED
- && mGSMPhone.getState() == PhoneConstants.State.IDLE));
-
- assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
- assertEquals(DisconnectCause.CONGESTION, cn.getDisconnectCause());
-
- assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
- assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
- assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
- assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
- assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
- assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
- assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
- assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
- }
-
- public void testSSNotification() throws Exception {
- // MO
- runTest(0, SuppServiceNotification.MO_CODE_UNCONDITIONAL_CF_ACTIVE);
- runTest(0, SuppServiceNotification.MO_CODE_CALL_IS_WAITING);
- runTest(0, SuppServiceNotification.MO_CODE_CALL_DEFLECTED);
-
- // MT
- runTest(1, SuppServiceNotification.MT_CODE_FORWARDED_CALL);
- runTest(1, SuppServiceNotification.MT_CODE_CALL_CONNECTED_ECT);
- runTest(1, SuppServiceNotification.MT_CODE_ADDITIONAL_CALL_FORWARDED);
- }
-
- private void runTest(int type, int code) {
- Message msg;
-
- mRadioControl.triggerSsn(type, code);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_SSN);
- assertNotNull("Message Time Out", msg);
- AsyncResult ar = (AsyncResult) msg.obj;
-
- assertNull(ar.exception);
-
- SuppServiceNotification notification =
- (SuppServiceNotification) ar.result;
-
- assertEquals(type, notification.notificationType);
- assertEquals(code, notification.code);
- }
-
- public void testUssd() throws Exception {
- // Quick hack to work around a race condition in this test:
- // We may initiate a USSD MMI before GSMPhone receives its initial
- // GSMTestHandler.EVENT_RADIO_OFF_OR_NOT_AVAILABLE event. When the phone sees this
- // event, it will cancel the just issued USSD MMI, which we don't
- // want. So sleep a little first.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ex) {
- // do nothing
- }
-
- verifyNormal();
- verifyCancel();
- varifyNetworkInitiated();
- }
-
- private void varifyNetworkInitiated() {
- Message msg;
- AsyncResult ar;
- MmiCode mmi;
-
- // Receive an incoming NOTIFY
- mRadioControl.triggerIncomingUssd("0", "NOTIFY message");
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertFalse(mmi.isUssdRequest());
-
- // Receive a REQUEST and send response
- mRadioControl.triggerIncomingUssd("1", "REQUEST Message");
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertTrue(mmi.isUssdRequest());
-
- mGSMPhone.sendUssdResponse("## TEST: TEST_GSMPhone responding...");
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- GsmMmiCode gsmMmi = (GsmMmiCode) mmi;
- assertTrue(gsmMmi.isPendingUSSD());
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertNull(ar.exception);
- assertFalse(mmi.isUssdRequest());
-
- // Receive a REQUEST and cancel
- mRadioControl.triggerIncomingUssd("1", "REQUEST Message");
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertTrue(mmi.isUssdRequest());
-
- mmi.cancel();
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
-
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertNull(ar.exception);
- assertEquals(MmiCode.State.CANCELLED, mmi.getState());
-
- List mmiList = mGSMPhone.getPendingMmiCodes();
- assertEquals(0, mmiList.size());
- }
-
- private void verifyNormal() throws CallStateException {
- Message msg;
- AsyncResult ar;
- MmiCode mmi;
-
- mGSMPhone.dial("#646#");
-
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
- assertNotNull("Message Time Out", msg);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
-
- ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
- assertEquals(MmiCode.State.COMPLETE, mmi.getState());
- }
-
-
- private void verifyCancel() throws CallStateException {
- /**
- * This case makes an assumption that dial() will add the USSD
- * to the "pending MMI codes" list before it returns. This seems
- * like reasonable semantics. It also assumes that the USSD
- * request in question won't complete until we get back to the
- * event loop, thus cancel() is safe.
- */
- Message msg;
-
- mGSMPhone.dial("#646#");
-
- List<? extends MmiCode> pendingMmis = mGSMPhone.getPendingMmiCodes();
-
- assertEquals(1, pendingMmis.size());
-
- MmiCode mmi = pendingMmis.get(0);
- assertTrue(mmi.isCancelable());
- mmi.cancel();
-
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
- assertNotNull("Message Time Out", msg);
-
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
-
- AsyncResult ar = (AsyncResult) msg.obj;
- mmi = (MmiCode) ar.result;
-
- assertEquals(MmiCode.State.CANCELLED, mmi.getState());
- }
-
- public void testRilHooks() throws Exception {
- //
- // These test cases all assume the RIL OEM hooks
- // just echo back their input
- //
-
- Message msg;
- AsyncResult ar;
-
- // null byte array
-
- mGSMPhone.invokeOemRilRequestRaw(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertNull(ar.result);
- assertNull(ar.exception);
-
- // empty byte array
-
- mGSMPhone.invokeOemRilRequestRaw(new byte[0], mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertEquals(0, ((byte[]) (ar.result)).length);
- assertNull(ar.exception);
-
- // byte array with data
-
- mGSMPhone.invokeOemRilRequestRaw("Hello".getBytes("utf-8"),
- mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertEquals("Hello", new String(((byte[]) (ar.result)), "utf-8"));
- assertNull(ar.exception);
-
- // null strings
-
- mGSMPhone.invokeOemRilRequestStrings(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertNull(ar.result);
- assertNull(ar.exception);
-
- // empty byte array
-
- mGSMPhone.invokeOemRilRequestStrings(new String[0],
- mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertEquals(0, ((String[]) (ar.result)).length);
- assertNull(ar.exception);
-
- // Strings with data
-
- String s[] = new String[1];
-
- s[0] = "Hello";
-
- mGSMPhone.invokeOemRilRequestStrings(s, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
- msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
- assertNotNull("Message Time Out", msg);
-
- ar = ((AsyncResult) msg.obj);
-
- assertEquals("Hello", ((String[]) (ar.result))[0]);
- assertEquals(1, ((String[]) (ar.result)).length);
- assertNull(ar.exception);
- }
-
- public void testMmi() throws Exception {
- mRadioControl.setAutoProgressConnectingCall(false);
-
- // "valid" MMI sequences
- runValidMmi("*#67#", false);
- runValidMmi("##43*11#", false);
- runValidMmi("#33*1234*11#", false);
- runValidMmi("*21*6505551234**5#", false);
- runValidMmi("**03**1234*4321*4321#", false);
- // pound string
- runValidMmi("5308234092307540923#", true);
- // short code
- runValidMmi("22", true);
- // as part of call setup
- runValidMmiWithConnect("*31#6505551234");
-
- // invalid MMI sequences
- runNotMmi("6505551234");
- runNotMmi("1234#*12#34566654");
- runNotMmi("*#*#12#*");
- }
-
- private void runValidMmi(String dialString, boolean cancelable) throws CallStateException {
- Connection c = mGSMPhone.dial(dialString);
- assertNull(c);
- Message msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
- assertNotNull("Message Time Out", msg);
- // Should not be cancelable.
- AsyncResult ar = (AsyncResult) msg.obj;
- MmiCode mmi = (MmiCode) ar.result;
- assertEquals(cancelable, mmi.isCancelable());
-
- msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
- assertNotNull("Message Time Out", msg);
- }
-
- private void runValidMmiWithConnect(String dialString) throws CallStateException {
- mRadioControl.pauseResponses();
-
- Connection c = mGSMPhone.dial(dialString);
- assertNotNull(c);
-
- hangup(c);
- }
-
- private void hangup(Connection cn) throws CallStateException {
- cn.hangup();
-
- mRadioControl.resumeResponses();
- assertNotNull(mGSMTestHandler.waitForMessage(EVENT_DISCONNECT));
-
- }
-
- private void runNotMmi(String dialString) throws CallStateException {
- mRadioControl.pauseResponses();
-
- Connection c = mGSMPhone.dial(dialString);
- assertNotNull(c);
-
- hangup(c);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
deleted file mode 100644
index 16861fa..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.content.Context;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import com.android.telephony.Rlog;
-
-import com.android.internal.telephony.gsm.GSMPhone;
-import com.android.internal.telephony.test.SimulatedCommands;
-import com.android.internal.telephony.TestPhoneNotifier;
-
-/**
- * This class creates a HandlerThread which waits for the various messages.
- */
-public class GSMTestHandler extends HandlerThread implements Handler.Callback {
-
- private Handler mHandler;
- private Message mCurrentMessage;
-
- private Boolean mMsgConsumed;
- private SimulatedCommands sc;
- private GSMPhone mGSMPhone;
- private Context mContext;
-
- private static final int FAIL_TIMEOUT_MILLIS = 5 * 1000;
-
- public GSMTestHandler(Context context) {
- super("GSMPhoneTest");
- mMsgConsumed = false;
- mContext = context;
- }
-
- @Override
- protected void onLooperPrepared() {
- sc = new SimulatedCommands();
- mGSMPhone = new GSMPhone(mContext, sc, new TestPhoneNotifier(), true);
- mHandler = new Handler(getLooper(), this);
- synchronized (this) {
- notifyAll();
- }
- }
-
- public boolean handleMessage(Message msg) {
- synchronized (this) {
- mCurrentMessage = msg;
- this.notifyAll();
- while(!mMsgConsumed) {
- try {
- this.wait();
- } catch (InterruptedException e) {}
- }
- mMsgConsumed = false;
- }
- return true;
- }
-
-
- public void cleanup() {
- Looper looper = getLooper();
- if (looper != null) looper.quit();
- mHandler = null;
- }
-
- public Handler getHandler() {
- return mHandler;
- }
-
- public SimulatedCommands getSimulatedCommands() {
- return sc;
- }
-
- public GSMPhone getGSMPhone() {
- return mGSMPhone;
- }
-
- public Message waitForMessage(int code) {
- Message msg;
- while(true) {
- msg = null;
- synchronized (this) {
- try {
- this.wait(FAIL_TIMEOUT_MILLIS);
- } catch (InterruptedException e) {
- }
-
- // Check if timeout has occurred.
- if (mCurrentMessage != null) {
- // Consume the message
- msg = Message.obtain();
- msg.copyFrom(mCurrentMessage);
- mCurrentMessage = null;
- mMsgConsumed = true;
- this.notifyAll();
- }
- }
- if (msg == null || code == GSMPhoneTest.ANY_MESSAGE || msg.what == code) return msg;
- }
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index a847a24..c261afe 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.BroadcastOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -55,9 +56,9 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
+import com.android.ims.ImsManager;
import com.android.internal.telephony.FakeSmsContentProvider;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.InboundSmsTracker;
@@ -192,9 +193,9 @@
InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
- anyInt(), anyBoolean(),
- anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt(), anyInt());
+ anyInt(), anyBoolean(),
+ anyBoolean(), nullable(String.class), nullable(String.class),
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
createInboundSmsTrackerMultiSim();
@@ -203,12 +204,11 @@
Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider);
mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
- mSmsStorageMonitor, mPhone);
+ mSmsStorageMonitor, mPhone, mTestableLooper.getLooper());
mSmsFilters = new ArrayList<>();
mSmsFilters.add(mSmsFilter);
mSmsFilters.add(mSmsFilter2);
mGsmInboundSmsHandler.setSmsFiltersForTesting(mSmsFilters);
- monitorTestableLooper(new TestableLooper(mGsmInboundSmsHandler.getHandler().getLooper()));
doReturn(mGsmInboundSmsHandler).when(mPhone).getInboundSmsHandler(false);
doReturn(mCdmaInboundSmsHandler).when(mPhone).getInboundSmsHandler(true);
@@ -278,8 +278,8 @@
assertEquals("WaitingState", getCurrentState().getName());
if (allowBgActivityStarts) {
Bundle broadcastOptions = mContextFixture.getLastBroadcastOptions();
- assertTrue(broadcastOptions
- .getBoolean("android:broadcast.allowBackgroundActivityStarts"));
+ BroadcastOptions brOptions = new BroadcastOptions(broadcastOptions);
+ assertTrue(brOptions.allowsBackgroundActivityStarts());
}
mContextFixture.sendBroadcastToOrderedBroadcastReceivers();
@@ -310,7 +310,6 @@
processAllMessages();
}
- @FlakyTest
@Test
@MediumTest
public void testNewSms() {
@@ -330,7 +329,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
@@ -348,7 +346,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSmsWithUserLocked_notificationShown() {
@@ -375,7 +372,6 @@
any(Notification.class));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSmsFromBlockedNumberWithUserLocked_noNotificationShown() {
@@ -405,7 +401,6 @@
any(Notification.class));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSms_filterInvoked_noBroadcastsSent() {
@@ -431,7 +426,6 @@
anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSms_filterChaining_noBroadcastsSent() {
@@ -483,7 +477,6 @@
assertEquals("IdleState", getCurrentState().getName());
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testClass0Sms() {
@@ -515,7 +508,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastSms() {
@@ -556,7 +548,6 @@
verifySmsFiltersInvoked(times(2));
}
- @FlakyTest
@Test
@MediumTest
public void testInjectSms() {
@@ -617,7 +608,6 @@
InboundSmsHandler.SOURCE_NOT_INJECTED);
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartSmsWithIncompleteWAP() {
@@ -678,12 +668,13 @@
assertEquals("IdleState", getCurrentState().getName());
// verify there are three segments in the db and only one of them is not marked as deleted.
assertEquals(3, mContentProvider.getNumRows());
- assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());
+ Cursor c = mContentProvider.query(sRawUri, null, "deleted=0", null, null);
+ assertEquals(1, c.getCount());
verifySmsFiltersInvoked(times(1));
+ c.close();
}
- @FlakyTest
@Test
@MediumTest
public void testMultiPartSms() {
@@ -757,7 +748,6 @@
assertEquals("IdleState", getCurrentState().getName());
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartIncompleteSms() {
@@ -820,9 +810,9 @@
// State machine should go back to idle
assertEquals("IdleState", getCurrentState().getName());
verifySmsFiltersInvoked(never());
+ c.close();
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartSmsWithInvalidSeqNumber() {
@@ -882,7 +872,6 @@
verifySmsFiltersInvoked(never());
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() {
@@ -921,7 +910,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultipartSmsFromBlockedEmail_noBroadcastsSent() {
@@ -977,7 +965,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultipartSms_filterInvoked_noBroadcastsSent() {
@@ -1027,7 +1014,6 @@
anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredUserLocked() throws Exception {
@@ -1085,7 +1071,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredUserUnlocked() throws Exception {
@@ -1123,7 +1108,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredDeleted() throws Exception {
@@ -1164,7 +1148,6 @@
verifySmsFiltersInvoked(never());
}
- @FlakyTest
@Test
@MediumTest
public void testBroadcastUndeliveredMultiPart() throws Exception {
@@ -1180,7 +1163,7 @@
//return InboundSmsTracker objects corresponding to the 2 parts
doReturn(mInboundSmsTrackerPart1).doReturn(mInboundSmsTrackerPart2).
when(mTelephonyComponentFactory).makeInboundSmsTracker(any(Context.class),
- any(Cursor.class), anyBoolean());
+ any(Cursor.class), anyBoolean());
SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
// wait for ScanRawTableThread
@@ -1191,7 +1174,6 @@
verifySmsFiltersInvoked(times(1));
}
- @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredMultiSim() throws Exception {
@@ -1251,4 +1233,12 @@
assertEquals("IdleState", getCurrentState().getName());
}
+
+ @Test
+ public void testSetImsManager() {
+ ImsManager imsManager = Mockito.mock(ImsManager.class);
+ transitionFromStartupToIdle();
+ assertTrue(mGsmInboundSmsHandler.setImsManager(imsManager));
+ }
}
+
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 8c1caa5..1c1ca0f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -16,17 +16,23 @@
package com.android.internal.telephony.gsm;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.fail;
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.os.AsyncResult;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.GsmCdmaPhone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
@@ -36,6 +42,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.concurrent.Executor;
/**
@@ -102,6 +109,223 @@
}
}
+ // The main purpose of this test is to list all the valid use cases of getControlStrings().
+ @Test
+ public void testGetControlStrings() {
+ // Test if controlStrings list is empty when inputs are null
+ ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(null,
+ null);
+ assertThat(controlStrings).isEmpty();
+
+ // Test control strings of Call Forwarding Unconditional
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CFU);
+ assertThat(controlStrings).containsExactly("*#21", "*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CFU);
+ assertThat(controlStrings).containsExactly("**21", "**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CFU);
+ assertThat(controlStrings).containsExactly("#21", "#002");
+
+ // Test control strings of Call Forwarding Busy
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CF_BUSY);
+ assertThat(controlStrings).containsExactly("*#67", "*#004", "*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CF_BUSY);
+ assertThat(controlStrings).containsExactly("**67", "**004", "**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CF_BUSY);
+ assertThat(controlStrings).containsExactly("#67", "#004", "#002");
+
+ // Test control strings of Call Forwarding No Reply
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CF_NO_REPLY);
+ assertThat(controlStrings).containsExactly("*#61", "*#004", "*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CF_NO_REPLY);
+ assertThat(controlStrings).containsExactly("**61", "**004", "**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CF_NO_REPLY);
+ assertThat(controlStrings).containsExactly("#61", "#004", "#002");
+
+ // Test control strings of Call Forwarding Not Reachable
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CF_NOT_REACHABLE);
+ assertThat(controlStrings).containsExactly("*#62", "*#004", "*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CF_NOT_REACHABLE);
+ assertThat(controlStrings).containsExactly("**62", "**004", "**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CF_NOT_REACHABLE);
+ assertThat(controlStrings).containsExactly("#62", "#004", "#002");
+
+ // Test control strings of Call Forwarding All
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CF_ALL);
+ assertThat(controlStrings).containsExactly("*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CF_ALL);
+ assertThat(controlStrings).containsExactly("**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CF_ALL);
+ assertThat(controlStrings).containsExactly("#002");
+
+ // Test control strings of Call Forwarding ALl Conditional
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+ assertThat(controlStrings).containsExactly("*#004", "*#002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+ SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+ assertThat(controlStrings).containsExactly("**004", "**002");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+ assertThat(controlStrings).containsExactly("#004", "#002");
+
+ // Test control strings of CLIP
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CLIP);
+ assertThat(controlStrings).containsExactly("*#30");
+
+ // Test control strings of CLIR
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_CLIR);
+ assertThat(controlStrings).containsExactly("*#31");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_CLIR);
+ assertThat(controlStrings).containsExactly("*31");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_CLIR);
+ assertThat(controlStrings).containsExactly("#31");
+
+ // Test control strings of Call Waiting
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_WAIT);
+ assertThat(controlStrings).containsExactly("*#43");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_WAIT);
+ assertThat(controlStrings).containsExactly("*43");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_WAIT);
+ assertThat(controlStrings).containsExactly("#43");
+
+ // Test control strings of BAOC
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_BAOC);
+ assertThat(controlStrings).containsExactly("*#33", "*#330", "*#333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_BAOC);
+ assertThat(controlStrings).containsExactly("*33", "*330", "*333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_BAOC);
+ assertThat(controlStrings).containsExactly("#33", "#330", "#333");
+
+ // Test control strings of BAOIC
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_BAOIC);
+ assertThat(controlStrings).containsExactly("*#331", "*#330", "*#333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_BAOIC);
+ assertThat(controlStrings).containsExactly("*331", "*330", "*333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_BAOIC);
+ assertThat(controlStrings).containsExactly("#331", "#330", "#333");
+
+ // Test control strings of BAOICxH
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_BAOIC_EXC_HOME);
+ assertThat(controlStrings).containsExactly("*#332", "*#330", "*#333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_BAOIC_EXC_HOME);
+ assertThat(controlStrings).containsExactly("*332", "*330", "*333");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_BAOIC_EXC_HOME);
+ assertThat(controlStrings).containsExactly("#332", "#330", "#333");
+
+ // Test control strings of BAIC
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_BAIC);
+ assertThat(controlStrings).containsExactly("*#35", "*#330", "*#353");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_BAIC);
+ assertThat(controlStrings).containsExactly("*35", "*330", "*353");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_BAIC);
+ assertThat(controlStrings).containsExactly("#35", "#330", "#353");
+
+ // Test control strings of BAICr
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_BAIC_ROAMING);
+ assertThat(controlStrings).containsExactly("*#351", "*#330", "*#353");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_BAIC_ROAMING);
+ assertThat(controlStrings).containsExactly("*351", "*330", "*353");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_BAIC_ROAMING);
+ assertThat(controlStrings).containsExactly("#351", "#330", "#353");
+
+ // Test control strings of BA_ALL
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_ALL_BARRING);
+ assertThat(controlStrings).containsExactly("*#330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_ALL_BARRING);
+ assertThat(controlStrings).containsExactly("*330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_ALL_BARRING);
+ assertThat(controlStrings).containsExactly( "#330");
+
+ // Test control strings of BA_MO
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_OUTGOING_BARRING);
+ assertThat(controlStrings).containsExactly("*#333", "*#330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_OUTGOING_BARRING);
+ assertThat(controlStrings).containsExactly("*333", "*330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_OUTGOING_BARRING);
+ assertThat(controlStrings).containsExactly( "#333", "#330");
+
+ // Test control strings of BA_MT
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+ SsData.ServiceType.SS_INCOMING_BARRING);
+ assertThat(controlStrings).containsExactly("*#353", "*#330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+ SsData.ServiceType.SS_INCOMING_BARRING);
+ assertThat(controlStrings).containsExactly("*353", "*330");
+ controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+ SsData.ServiceType.SS_INCOMING_BARRING);
+ assertThat(controlStrings).containsExactly( "#353", "#330");
+ }
+
+ @Test
+ public void testGetControlStringsForPwd() {
+ // Test if controlStrings list is empty when inputs are null
+ ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd(null,
+ null);
+ assertThat(controlStrings).isEmpty();
+
+ // Test control strings of Call Barring Change Password
+ controlStrings = GsmMmiCode.getControlStringsForPwd(
+ SsData.RequestType.SS_REGISTRATION, SsData.ServiceType.SS_ALL_BARRING);
+ assertThat(controlStrings).containsExactly("**03*330");
+ }
+
+ @Test
+ public void testOperationNotSupported() {
+ // Contrived; this is just to get a simple instance of the class.
+ mGsmMmiCode = GsmMmiCode.newNetworkInitiatedUssd(null, true, mGsmCdmaPhoneUT, null);
+
+ assertThat(mGsmMmiCode).isNotNull();
+ // Emulate request not supported from the network.
+ AsyncResult ar = new AsyncResult(null, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ mGsmMmiCode.getErrorMessage(ar);
+ verify(mContext.getResources()).getText(
+ eq(com.android.internal.R.string.mmiErrorNotSupported));
+ }
+
private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
final PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 19cacf0..8374daa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -69,6 +69,8 @@
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.TelephonyTestUtils;
import com.android.internal.telephony.TestApplication;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.IsimUiccRecords;
import org.junit.After;
import org.junit.Before;
@@ -228,7 +230,8 @@
restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
Context realContext = TestApplication.getAppContext();
- realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
+ realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT),
+ Context.RECEIVER_EXPORTED);
}
@Test
@@ -373,6 +376,7 @@
@Test
@SmallTest
+ @Ignore("b/256282780")
public void testSendSmsByCarrierApp() throws Exception {
mockCarrierApp();
mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
@@ -380,7 +384,9 @@
registerTestIntentReceiver();
PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
- new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ new Intent(TEST_INTENT)
+ .setPackage(TestApplication.getAppContext().getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
mReceivedTestIntent = false;
mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
@@ -435,9 +441,13 @@
ArrayList<PendingIntent> sentIntents = new ArrayList<>();
PendingIntent sentIntent1 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
- new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ new Intent(TEST_INTENT)
+ .setPackage(TestApplication.getAppContext().getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
PendingIntent sentIntent2 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
- new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ new Intent(TEST_INTENT)
+ .setPackage(TestApplication.getAppContext().getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
sentIntents.add(sentIntent1);
sentIntents.add(sentIntent2);
@@ -447,6 +457,7 @@
@Test
@SmallTest
+ @Ignore("b/256282780")
public void testSendMultipartSmsByCarrierApp() throws Exception {
mockCarrierApp();
mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
@@ -503,4 +514,83 @@
verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
any(Message.class));
}
+
+
+ @Test
+ public void testSendTextWithMessageRef() throws Exception {
+ int messageRef = mGsmSmsDispatcher.nextMessageRef();
+ if (mGsmSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += 1;
+ }
+
+ mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1, false, 0L);
+
+ ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+ any(Message.class));
+ byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+ assertEquals(messageRef, pdu[1]);
+ }
+
+ @Test
+ public void testSendMultipartWithMessageRef() throws Exception {
+ ArrayList<String> parts = new ArrayList<>();
+ parts.add("segment1");
+ parts.add("segment2");
+ parts.add("segment3");
+
+ int messageRef = mGsmSmsDispatcher.nextMessageRef();
+ if (mGsmSmsDispatcher.isMessageRefIncrementViaTelephony()) {
+ messageRef += parts.size();
+ }
+ mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
+ null, null, null, null, false, -1, false, -1, 0L);
+ waitForMs(150);
+ ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+
+ verify(mSimulatedCommandsVerifier, times(parts.size() - 1)).sendSMSExpectMore(anyString(),
+ anyString(),
+ any(Message.class));
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+ any(Message.class));
+ byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+ assertEquals(messageRef, pdu[1]);
+ }
+
+ @Test
+ public void testSendTextWithMessageRefNegativeBoundaryCondition() throws Exception {
+ mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
+ mSimulatedCommands);
+ doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
+ Message msg = mGsmSmsDispatcher.obtainMessage(17);
+ mPhone.getIccRecords().setSmssTpmrValue(-1, msg);
+ mSubscriptionManagerService.setLastUsedTPMessageReference(mPhone.getSubId(), -1);
+
+ mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1, false, 0L);
+
+ ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+ any(Message.class));
+ byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+ assertEquals(0, pdu[1]);
+ }
+
+ @Test
+ public void testSendTextWithMessageRefMaxBoundaryCondition() throws Exception {
+ mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
+ mSimulatedCommands);
+ doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
+ Message msg = mGsmSmsDispatcher.obtainMessage(17);
+ mPhone.getIccRecords().setSmssTpmrValue(255, msg);
+ mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1, false, 0L);
+
+ ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+ any(Message.class));
+ byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+ assertEquals(0, pdu[1]);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
deleted file mode 100644
index a9d869c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * Copyright (C) 2011 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.gsm;
-
-import android.content.Context;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.os.SystemClock;
-import com.android.telephony.Rlog;
-
-import com.android.internal.telephony.BaseCommands;
-import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.uicc.IccIoResult;
-import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-
-import junit.framework.Assert;
-
-/**
- * Dummy BaseCommands for UsimDataDownloadTest. Only implements UICC envelope and
- * SMS acknowledgement commands.
- */
-class UsimDataDownloadCommands extends BaseCommands {
- private static final String TAG = "UsimDataDownloadCommands";
-
- private boolean mExpectingAcknowledgeGsmSms; // true if expecting ack GSM SMS
- private boolean mExpectingAcknowledgeGsmSmsSuccess; // true if expecting ack SMS success
- private int mExpectingAcknowledgeGsmSmsFailureCause; // expecting ack SMS failure cause
- private String mExpectingAcknowledgeGsmSmsPdu; // expecting ack SMS PDU
-
- private boolean mExpectingSendEnvelope; // true to expect a send envelope command
- private String mExpectingSendEnvelopeContents; // expected string for send envelope
- private int mExpectingSendEnvelopeResponseSw1; // SW1/SW2 response status
- private int mExpectingSendEnvelopeResponseSw2; // SW1/SW2 response status
- private String mExpectingSendEnvelopeResponse; // Response string for Send Envelope
-
- UsimDataDownloadCommands(Context context) {
- super(context);
- }
-
- /**
- * Expect a call to acknowledgeLastIncomingGsmSms with success flag and failure cause.
- * @param success true if expecting success; false if expecting failure
- * @param cause the failure cause, if success is false
- */
- synchronized void expectAcknowledgeGsmSms(boolean success, int cause) {
- Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
- mExpectingAcknowledgeGsmSms = true;
- mExpectingAcknowledgeGsmSmsSuccess = success;
- mExpectingAcknowledgeGsmSmsFailureCause = cause;
- }
-
- /**
- * Expect a call to acknowledgeLastIncomingGsmSmsWithPdu with success flag and PDU.
- * @param success true if expecting success; false if expecting failure
- * @param ackPdu the acknowledgement PDU to expect
- */
- synchronized void expectAcknowledgeGsmSmsWithPdu(boolean success, String ackPdu) {
- Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
- mExpectingAcknowledgeGsmSms = true;
- mExpectingAcknowledgeGsmSmsSuccess = success;
- mExpectingAcknowledgeGsmSmsPdu = ackPdu;
- }
-
- /**
- * Expect a call to sendEnvelopeWithStatus().
- * @param contents expected envelope contents to send
- * @param sw1 simulated SW1 status to return
- * @param sw2 simulated SW2 status to return
- * @param response simulated envelope response to return
- */
- synchronized void expectSendEnvelope(String contents, int sw1, int sw2, String response) {
- Assert.assertFalse("expectSendEnvelope called twice", mExpectingSendEnvelope);
- mExpectingSendEnvelope = true;
- mExpectingSendEnvelopeContents = contents;
- mExpectingSendEnvelopeResponseSw1 = sw1;
- mExpectingSendEnvelopeResponseSw2 = sw2;
- mExpectingSendEnvelopeResponse = response;
- }
-
- synchronized void assertExpectedMethodsCalled() {
- long stopTime = SystemClock.elapsedRealtime() + 5000;
- while ((mExpectingAcknowledgeGsmSms || mExpectingSendEnvelope)
- && SystemClock.elapsedRealtime() < stopTime) {
- try {
- wait();
- } catch (InterruptedException ignored) {}
- }
- Assert.assertFalse("expecting SMS acknowledge call", mExpectingAcknowledgeGsmSms);
- Assert.assertFalse("expecting send envelope call", mExpectingSendEnvelope);
- }
-
- @Override
- public synchronized void acknowledgeLastIncomingGsmSms(boolean success, int cause,
- Message response) {
- Rlog.d(TAG, "acknowledgeLastIncomingGsmSms: success=" + success + ", cause=" + cause);
- Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
- Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
- Assert.assertEquals(mExpectingAcknowledgeGsmSmsFailureCause, cause);
- mExpectingAcknowledgeGsmSms = false;
- if (response != null) {
- AsyncResult.forMessage(response);
- response.sendToTarget();
- }
- notifyAll(); // wake up assertExpectedMethodsCalled()
- }
-
- @Override
- public synchronized void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu,
- Message response) {
- Rlog.d(TAG, "acknowledgeLastIncomingGsmSmsWithPdu: success=" + success
- + ", ackPDU= " + ackPdu);
- Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
- Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
- Assert.assertEquals(mExpectingAcknowledgeGsmSmsPdu, ackPdu);
- mExpectingAcknowledgeGsmSms = false;
- if (response != null) {
- AsyncResult.forMessage(response);
- response.sendToTarget();
- }
- notifyAll(); // wake up assertExpectedMethodsCalled()
- }
-
- @Override
- public synchronized void sendEnvelopeWithStatus(String contents, Message response) {
- // Add spaces between hex bytes for readability
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < contents.length(); i += 2) {
- builder.append(contents.charAt(i)).append(contents.charAt(i+1)).append(' ');
- }
- Rlog.d(TAG, "sendEnvelopeWithStatus: " + builder.toString());
-
- Assert.assertTrue("unexpected call to send envelope", mExpectingSendEnvelope);
- Assert.assertEquals(mExpectingSendEnvelopeContents, contents);
- mExpectingSendEnvelope = false;
-
- IccIoResult result = new IccIoResult(mExpectingSendEnvelopeResponseSw1,
- mExpectingSendEnvelopeResponseSw2, mExpectingSendEnvelopeResponse);
-
- if (response != null) {
- AsyncResult.forMessage(response, result, null);
- response.sendToTarget();
- }
- notifyAll(); // wake up assertExpectedMethodsCalled()
- }
-
- @Override
- public void setSuppServiceNotifications(boolean enable, Message result) {
- }
-
- @Override
- public void supplyIccPin(String pin, Message result) {
- }
-
- @Override
- public void supplyIccPinForApp(String pin, String aid, Message result) {
- }
-
- @Override
- public void supplyIccPuk(String puk, String newPin, Message result) {
- }
-
- @Override
- public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
- }
-
- @Override
- public void supplyIccPin2(String pin2, Message result) {
- }
-
- @Override
- public void supplyIccPin2ForApp(String pin2, String aid, Message result) {
- }
-
- @Override
- public void supplyIccPuk2(String puk2, String newPin2, Message result) {
- }
-
- @Override
- public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result) {
- }
-
- @Override
- public void changeIccPin(String oldPin, String newPin, Message result) {
- }
-
- @Override
- public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result) {
- }
-
- @Override
- public void changeIccPin2(String oldPin2, String newPin2, Message result) {
- }
-
- @Override
- public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr, Message result) {
- }
-
- @Override
- public void changeBarringPassword(String facility, String oldPwd, String newPwd,
- Message result) {
- }
-
- @Override
- public void supplyNetworkDepersonalization(String netpin, Message result) {
- }
-
- @Override
- public void getCurrentCalls(Message result) {
- }
-
- @Override
- public void getPDPContextList(Message result) {
- }
-
- @Override
- public void getDataCallList(Message result) {
- }
-
- @Override
- public void dial(String address, int clirMode, Message result) {
- }
-
- @Override
- public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
- }
-
- @Override
- public void getIMSI(Message result) {
- }
-
- @Override
- public void getIMEI(Message result) {
- }
-
- @Override
- public void getIMEISV(Message result) {
- }
-
- @Override
- public void hangupConnection(int gsmIndex, Message result) {
- }
-
- @Override
- public void hangupWaitingOrBackground(Message result) {
- }
-
- @Override
- public void hangupForegroundResumeBackground(Message result) {
- }
-
- @Override
- public void switchWaitingOrHoldingAndActive(Message result) {
- }
-
- @Override
- public void conference(Message result) {
- }
-
- @Override
- public void setPreferredVoicePrivacy(boolean enable, Message result) {
- }
-
- @Override
- public void getPreferredVoicePrivacy(Message result) {
- }
-
- @Override
- public void separateConnection(int gsmIndex, Message result) {
- }
-
- @Override
- public void acceptCall(Message result) {
- }
-
- @Override
- public void rejectCall(Message result) {
- }
-
- @Override
- public void explicitCallTransfer(Message result) {
- }
-
- @Override
- public void getLastCallFailCause(Message result) {
- }
-
- @Override
- public void getLastPdpFailCause(Message result) {
- }
-
- @Override
- public void getLastDataCallFailCause(Message result) {
- }
-
- @Override
- public void setMute(boolean enableMute, Message response) {
- }
-
- @Override
- public void getMute(Message response) {
- }
-
- @Override
- public void getSignalStrength(Message response) {
- }
-
- @Override
- public void getVoiceRegistrationState(Message response) {
- }
-
- @Override
- public void getDataRegistrationState(Message response) {
- }
-
- @Override
- public void getOperator(Message response) {
- }
-
- @Override
- public void sendDtmf(char c, Message result) {
- }
-
- @Override
- public void startDtmf(char c, Message result) {
- }
-
- @Override
- public void stopDtmf(Message result) {
- }
-
- @Override
- public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
- }
-
- @Override
- public void sendSMS(String smscPDU, String pdu, Message response) {
- }
-
- @Override
- public void sendSMSExpectMore(String smscPDU, String pdu, Message response) {
- }
-
- @Override
- public void sendCdmaSms(byte[] pdu, Message response) {
- }
-
- @Override
- public void sendImsGsmSms (String smscPDU, String pdu,
- int retry, int messageRef, Message response) {
- }
-
- @Override
- public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
- Message response) {
- }
-
- @Override
- public void deleteSmsOnSim(int index, Message response) {
- }
-
- @Override
- public void deleteSmsOnRuim(int index, Message response) {
- }
-
- @Override
- public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
- }
-
- @Override
- public void writeSmsToRuim(int status, byte[] pdu, Message response) {
- }
-
- @Override
- public void setRadioPower(boolean on, Message response) {
- }
-
- @Override
- public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response) {
- }
-
- @Override
- public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
- String pin2, Message response) {
- }
-
- @Override
- public void queryCLIP(Message response) {
- }
-
- @Override
- public void getCLIR(Message response) {
- }
-
- @Override
- public void setCLIR(int clirMode, Message response) {
- }
-
- @Override
- public void queryCallWaiting(int serviceClass, Message response) {
- }
-
- @Override
- public void setCallWaiting(boolean enable, int serviceClass, Message response) {
- }
-
- @Override
- public void setCallForward(int action, int cfReason, int serviceClass, String number,
- int timeSeconds, Message response) {
- }
-
- @Override
- public void queryCallForwardStatus(int cfReason, int serviceClass, String number,
- Message response) {
- }
-
- @Override
- public void setNetworkSelectionModeAutomatic(Message response) {
- }
-
- @Override
- public void setNetworkSelectionModeManual(String operatorNumeric, Message response) {
- }
-
- @Override
- public void getNetworkSelectionMode(Message response) {
- }
-
- @Override
- public void getAvailableNetworks(Message response) {
- }
-
- @Override
- public void getBasebandVersion(Message response) {
- }
-
- @Override
- public void queryFacilityLock(String facility, String password, int serviceClass,
- Message response) {
- }
-
- @Override
- public void queryFacilityLockForApp(String facility, String password, int serviceClass,
- String appId, Message response) {
- }
-
- @Override
- public void setFacilityLock(String facility, boolean lockState, String password,
- int serviceClass, Message response) {
- }
-
- @Override
- public void setFacilityLockForApp(String facility, boolean lockState, String password,
- int serviceClass, String appId, Message response) {
- }
-
- @Override
- public void sendUSSD(String ussdString, Message response) {
- }
-
- @Override
- public void cancelPendingUssd(Message response) {
- }
-
- @Override
- public void resetRadio(Message result) {
- }
-
- @Override
- public void setBandMode(int bandMode, Message response) {
- }
-
- @Override
- public void queryAvailableBandMode(Message response) {
- }
-
- @Override
- public void setPreferredNetworkType(int networkType, Message response) {
- }
-
- @Override
- public void getPreferredNetworkType(Message response) {
- }
-
- @Override
- public void setLocationUpdates(boolean enable, Message response) {
- }
-
- @Override
- public void getSmscAddress(Message result) {
- }
-
- @Override
- public void setSmscAddress(String address, Message result) {
- }
-
- @Override
- public void reportSmsMemoryStatus(boolean available, Message result) {
- }
-
- @Override
- public void reportStkServiceIsRunning(Message result) {
- }
-
- @Override
- public void invokeOemRilRequestRaw(byte[] data, Message response) {
- }
-
- @Override
- public void invokeOemRilRequestStrings(String[] strings, Message response) {
- }
-
- @Override
- public void sendTerminalResponse(String contents, Message response) {
- }
-
- @Override
- public void sendEnvelope(String contents, Message response) {
- }
-
- @Override
- public void handleCallSetupRequestFromSim(boolean accept, Message response) {
- }
-
- @Override
- public void setGsmBroadcastActivation(boolean activate, Message result) {
- }
-
- @Override
- public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
- }
-
- @Override
- public void getGsmBroadcastConfig(Message response) {
- }
-
- @Override
- public void getDeviceIdentity(Message response) {
- }
-
- @Override
- public void getCDMASubscription(Message response) {
- }
-
- @Override
- public void getImsRegistrationState (Message result) {
- }
-
- @Override
- public void sendCDMAFeatureCode(String FeatureCode, Message response) {
- }
-
- @Override
- public void setPhoneType(int phoneType) {
- }
-
- @Override
- public void queryCdmaRoamingPreference(Message response) {
- }
-
- @Override
- public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
- }
-
- @Override
- public void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response) {
- }
-
- @Override
- public void getCdmaSubscriptionSource(Message response) {
- }
-
- @Override
- public void setTTYMode(int ttyMode, Message response) {
- }
-
- @Override
- public void queryTTYMode(Message response) {
- }
-
- @Override
- public void setupDataCall(String radioTechnology, String profile, String apn, String user,
- String password, String authType, String protocol, Message result) {
- }
-
- @Override
- public void deactivateDataCall(int cid, int reason, Message result) {
- }
-
- @Override
- public void setCdmaBroadcastActivation(boolean activate, Message result) {
- }
-
- @Override
- public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message response) {
- }
-
- @Override
- public void getCdmaBroadcastConfig(Message result) {
- }
-
- @Override
- public void exitEmergencyCallbackMode(Message response) {
- }
-
- @Override
- public void getIccCardStatus(Message result) {
- }
-
- @Override
- public void requestIsimAuthentication(String nonce, Message response) {
- }
-
- @Override
- public void requestIccSimAuthentication(String data, Message response) {
- }
-
- @Override
- public void getVoiceRadioTechnology(Message response) {
- }
-
- @Override
- public void getCellInfoList(Message result) {
- }
-
- @Override
- public void setCellInfoListRate(int rateInMillis, Message response) {
- }
-
- @Override
- public void setInitialAttachApn(String apn, String protocol, int authType, String username,
- String password, Message result) {
- }
-
- @Override
- public void setDataProfile(DataProfile[] dps, Message result) {
- }
-
- @Override
- public void getIMSIForApp(String aid, Message result) {
- }
-
- @Override
- public void iccIOForApp(int command, int fileid, String path, int p1, int p2, int p3,
- String data, String pin2, String aid, Message response) {
- }
-
- @Override
- public void iccOpenLogicalChannel(String AID, Message response) {
- }
-
- @Override
- public void iccCloseLogicalChannel(int channel, Message response) {
- }
-
- @Override
- public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
- int p1, int p2, int p3, String data, Message response) {
- }
-
- @Override
- public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
- int p3, String data, Message response) {
- }
-
- @Override
- public void nvReadItem(int itemID, Message response) {
- }
-
- @Override
- public void nvWriteItem(int itemID, String itemValue, Message response) {
- }
-
- @Override
- public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) {
- }
-
- @Override
- public void nvResetConfig(int resetType, Message response) {
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadHandlerTest.java
new file mode 100644
index 0000000..c6041c8d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadHandlerTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class UsimDataDownloadHandlerTest extends TelephonyTest {
+ // Mocked classes
+ private CommandsInterface mMockCi;
+ protected GsmCdmaPhone mMockPhone;
+ private ImsManager mMockImsManager;
+ private Resources mMockResources;
+
+ private UsimDataDownloadHandler mUsimDataDownloadHandler;
+ private Phone mPhoneObj;
+ private Phone[] mPhoneslist;
+ private int mPhoneId;
+ private int mSlotId = 0;
+ private int mToken = 0;
+ private int mSmsSource = 1;
+ private byte[] mTpdu;
+ private byte[] mSmsAckPdu;
+ //Envelope is created as per TS.131.111 for SMS-PP data downwnload operation
+ private String mEnvelope = "D11502028281060591896745F306068199201269231300";
+ //SMS TPDU for Class2 according to TS.123.040
+ private String mPdu = "07914151551512f221110A81785634121000000666B2996C2603";
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mMockCi = Mockito.mock(CommandsInterface.class);
+ mMockPhone = Mockito.mock(GsmCdmaPhone.class);
+ mMockImsManager = Mockito.mock(ImsManager.class);
+ mMockResources = Mockito.mock(Resources.class);
+
+ mPhoneslist = new Phone[] {mMockPhone};
+ mTpdu = HexDump.hexStringToByteArray(mPdu);
+
+ //Use reflection to mock
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhoneslist);
+ replaceInstance(PhoneFactory.class, "sPhone", null, mMockPhone);
+
+ mPhoneObj = PhoneFactory.getPhone(mSlotId);
+ assertNotNull(mPhoneObj);
+ mPhoneId = mPhoneObj.getPhoneId();
+ doReturn(mSmsStats).when(mPhoneObj).getSmsStats();
+
+ //new UsimDataDownloadHandlerThread(TAG).start();
+ //waitUntilReady();
+ mUsimDataDownloadHandler = new UsimDataDownloadHandler(mMockCi, mPhoneId);
+ mUsimDataDownloadHandler.setImsManager(mMockImsManager);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUsimDataDownloadHandler = null;
+ mPhoneslist = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void sendEnvelopeForException() throws Exception {
+ setSmsPPSimConfig(false);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(false,
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC);
+ }
+
+ @Test
+ public void sendEnvelopeDataDownloadSuccess() throws Exception {
+ setSmsPPSimConfig(true);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+ mSmsAckPdu = createSmsAckPdu(true, mEnvelope, sms);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ IccIoResult iir = new IccIoResult(0x90, 0x00,
+ IccUtils.hexStringToBytes(mEnvelope));
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_OK, mSmsAckPdu);
+
+ setSmsPPSimConfig(false);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeIncomingGsmSmsWithPdu(true,
+ IccUtils.bytesToHexString(mSmsAckPdu), null);
+ }
+
+ @Test
+ public void sendEnvelopeDataDownloadFailed() throws Exception {
+ setSmsPPSimConfig(false);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ IccIoResult iir = new IccIoResult(0x93, 0x00,
+ IccUtils.hexStringToBytes(mEnvelope));
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(false,
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY, null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC);
+ }
+
+ @Test
+ public void sendEnvelopeForSw1_62() throws Exception {
+ setSmsPPSimConfig(false);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+ mSmsAckPdu = createSmsAckPdu(false, mEnvelope, sms);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ IccIoResult iir = new IccIoResult(0x62, 0x63,
+ IccUtils.hexStringToBytes(mEnvelope));
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeIncomingGsmSmsWithPdu(false,
+ IccUtils.bytesToHexString(mSmsAckPdu), null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_OK, mSmsAckPdu);
+ }
+
+ @Test
+ public void smsCompleteForException() throws Exception {
+ setSmsPPSimConfig(false);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(3);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .writeSmsToSim(anyInt(), anyString(), anyString(), any(Message.class));
+
+ int[] responseInfo = {mSmsSource, mTpdu[9], mToken};
+ Message msg = mUsimDataDownloadHandler.obtainMessage(3 /* EVENT_WRITE_SMS_COMPLETE */,
+ responseInfo);
+ AsyncResult.forMessage(msg, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mUsimDataDownloadHandler.handleMessage(msg);
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(false,
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.handleMessage(msg);
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC);
+ }
+
+ @Test
+ public void smsComplete() throws Exception {
+ setSmsPPSimConfig(true);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(3);
+ IccIoResult iir = new IccIoResult(0x90, 0x00,
+ IccUtils.hexStringToBytes(mEnvelope));
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .writeSmsToSim(anyInt(), anyString(), anyString(), any(Message.class));
+
+ int[] responseInfo = {mSmsSource, mTpdu[9], mToken};
+ Message msg = mUsimDataDownloadHandler.obtainMessage(3 /* EVENT_WRITE_SMS_COMPLETE */,
+ responseInfo);
+ AsyncResult.forMessage(msg, null, null);
+ mUsimDataDownloadHandler.handleMessage(msg);
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9], ImsSmsImplBase.DELIVER_STATUS_OK);
+
+ setSmsPPSimConfig(false);
+ mUsimDataDownloadHandler.handleMessage(msg);
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(true, 0, null);
+ }
+
+ @Test
+ public void failureEnvelopeResponse() throws Exception {
+ setSmsPPSimConfig(false);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ IccIoResult iir = new IccIoResult(0x62, 0x63,
+ IccUtils.hexStringToBytes(null)); //ForNullResponseBytes
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(false,
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9],
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC);
+ }
+
+ @Test
+ public void successEnvelopeResponse() throws Exception {
+ setSmsPPSimConfig(false);
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(mTpdu);
+
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ IccIoResult iir = new IccIoResult(0x90, 0x00,
+ IccUtils.hexStringToBytes(null)); //ForNullResponseBytes
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mMockCi)
+ .sendEnvelopeWithStatus(anyString(), any(Message.class));
+
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ verify(mMockCi).acknowledgeLastIncomingGsmSms(true, 0, null);
+
+ setSmsPPSimConfig(true);
+ mUsimDataDownloadHandler.startDataDownload(sms,
+ InboundSmsHandler.SOURCE_INJECTED_FROM_IMS, mToken);
+ processAllMessages();
+ //mTpdu[9] holds messageReference value
+ verify(mMockImsManager).acknowledgeSms(mToken, mTpdu[9], ImsSmsImplBase.DELIVER_STATUS_OK);
+ }
+
+ //To set "config_smppsim_response_via_ims" for testing purpose
+ private void setSmsPPSimConfig(boolean config) {
+ mUsimDataDownloadHandler.setResourcesForTest(mMockResources);
+ doReturn(config).when(mMockResources).getBoolean(
+ com.android.internal.R.bool.config_smppsim_response_via_ims);
+ }
+
+ private byte[] createSmsAckPdu(boolean success, String envelope, SmsMessage smsMessage) {
+ byte[] responseBytes = IccUtils.hexStringToBytes(envelope);
+ int dcs = 0x00;
+ int pid = smsMessage.getProtocolIdentifier();
+ byte[] smsAckPdu;
+ int index = 0;
+ if (success) {
+ smsAckPdu = new byte[responseBytes.length + 5];
+ smsAckPdu[index++] = 0x00;
+ smsAckPdu[index++] = 0x07;
+ } else {
+ smsAckPdu = new byte[responseBytes.length + 6];
+ smsAckPdu[index++] = 0x00;
+ smsAckPdu[index++] = (byte)
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR;
+ smsAckPdu[index++] = 0x07;
+ }
+
+ smsAckPdu[index++] = (byte) pid;
+ smsAckPdu[index++] = (byte) dcs;
+
+ if (((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0)) {
+ int septetCount = responseBytes.length * 8 / 7;
+ smsAckPdu[index++] = (byte) septetCount;
+ } else {
+ smsAckPdu[index++] = (byte) responseBytes.length;
+ }
+
+ System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length);
+ return smsAckPdu;
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
deleted file mode 100644
index 9fbb86c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2011 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.gsm;
-
-import android.os.HandlerThread;
-import android.test.AndroidTestCase;
-import com.android.telephony.Rlog;
-
-import java.nio.charset.Charset;
-
-/**
- * Test SMS-PP data download to UICC.
- * Uses test messages from 3GPP TS 31.124 section 27.22.5.
- */
-public class UsimDataDownloadTest extends AndroidTestCase {
- private static final String TAG = "UsimDataDownloadTest";
-
- class TestHandlerThread extends HandlerThread {
- private UsimDataDownloadHandler mHandler;
-
- TestHandlerThread() {
- super("TestHandlerThread");
- }
-
- @Override
- protected void onLooperPrepared() {
- synchronized (this) {
- mHandler = new UsimDataDownloadHandler(mCi, 0);
- notifyAll();
- }
- }
-
- UsimDataDownloadHandler getHandler() {
- synchronized (this) {
- while (mHandler == null) {
- try {
- wait();
- } catch (InterruptedException ignored) {}
- }
- return mHandler;
- }
- }
- }
-
- private UsimDataDownloadCommands mCi;
- private TestHandlerThread mHandlerThread;
- UsimDataDownloadHandler mHandler;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mCi = new UsimDataDownloadCommands(mContext);
- mHandlerThread = new TestHandlerThread();
- mHandlerThread.start();
- mHandler = mHandlerThread.getHandler();
- Rlog.d(TAG, "mHandler is constructed");
- }
-
- @Override
- protected void tearDown() throws Exception {
- mHandlerThread.quit();
- super.tearDown();
- }
-
- // SMS-PP Message 3.1.1
- private static final byte[] SMS_PP_MESSAGE_3_1_1 = {
- // Service center address
- 0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
-
- 0x04, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, 0x16, (byte) 0x89, 0x10, 0x10, 0x00, 0x00,
- 0x00, 0x00, 0x0d, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
- 0x67, 0x65, 0x20, 0x31
- };
-
- // SMS-PP Download Envelope 3.1.1
- private static final String SMS_PP_ENVELOPE_3_1_1 = "d12d8202838106099111223344556677f88b1c04"
- + "049121437f16891010000000000d546573744d6573736167652031";
-
- // SMS-PP Message 3.1.5
- private static final byte[] SMS_PP_MESSAGE_3_1_5 = {
- // Service center address
- 0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
-
- 0x44, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, (byte) 0xf6, (byte) 0x89, 0x10, 0x10, 0x00,
- 0x00, 0x00, 0x00, 0x1e, 0x02, 0x70, 0x00, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x00,
- 0x00, 0x00, (byte) 0xbf, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
- (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc,
- (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc
- };
-
- // SMS-PP Download Envelope 3.1.5
- private static final String SMS_PP_ENVELOPE_3_1_5 = "d13e8202838106099111223344556677f88b2d44"
- + "049121437ff6891010000000001e0270000019000d00000000bfff00000000000100"
- + "dcdcdcdcdcdcdcdcdcdc";
-
- public void testDataDownloadMessage1() {
- SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_1);
- assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
-
- mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "");
- mCi.expectAcknowledgeGsmSms(true, 0);
- mHandler.startDataDownload(message);
- mCi.assertExpectedMethodsCalled();
-
- mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "0123456789");
- mCi.expectAcknowledgeGsmSmsWithPdu(true, "00077f16050123456789");
- mHandler.startDataDownload(message);
- mCi.assertExpectedMethodsCalled();
-
- mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x62, 0xff, "0123456789abcdef");
- mCi.expectAcknowledgeGsmSmsWithPdu(false, "00d5077f16080123456789abcdef");
- mHandler.startDataDownload(message);
- mCi.assertExpectedMethodsCalled();
- }
-
- public void testDataDownloadMessage5() {
- SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_5);
- assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
-
- mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x90, 0x00, "9876543210");
- mCi.expectAcknowledgeGsmSmsWithPdu(true, "00077ff6059876543210");
- mHandler.startDataDownload(message);
- mCi.assertExpectedMethodsCalled();
-
- mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x93, 0x00, "");
- mCi.expectAcknowledgeGsmSms(false, 0xd4); // SIM toolkit busy
- mHandler.startDataDownload(message);
- mCi.assertExpectedMethodsCalled();
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java
new file mode 100644
index 0000000..c6f45d9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java
@@ -0,0 +1,812 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.ims;
+
+import static com.android.internal.telephony.ims.ImsEnablementTracker.COMMAND_DISABLE_MSG;
+import static com.android.internal.telephony.ims.ImsEnablementTracker.COMMAND_ENABLE_MSG;
+import static com.android.internal.telephony.ims.ImsEnablementTracker.COMMAND_RESETTING_DONE;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for ImsEnablementTracker
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsEnablementTrackerTest extends ImsTestBase {
+
+ // default timeout values(millisecond) for handler working
+ private static final int DEFAULT_TIMEOUT = 100;
+
+ // default delay values(millisecond) for handler working
+ private static final int DEFAULT_DELAY = 150;
+
+ private static final int SLOT_1 = 0;
+ private static final int SUB_1 = 11;
+
+ private static final int SLOT_2 = 1;
+ private static final int SUB_2 = 22;
+ private static final long TEST_REQUEST_THROTTLE_TIME_MS = 3000L;
+
+ // Mocked classes
+ @Mock
+ IImsServiceController mMockServiceControllerBinder;
+
+ private TestableImsEnablementTracker mTracker;
+ private Handler mHandler;
+
+ private static long sLastImsOperationTimeMs = 0L;
+
+ private static class TestableImsEnablementTracker extends ImsEnablementTracker {
+ private ImsEnablementTrackerTest mOwner;
+
+ TestableImsEnablementTracker(Looper looper, IImsServiceController controller, int state,
+ int numSlots, ImsEnablementTrackerTest owner) {
+ super(looper, controller, state, numSlots);
+ mOwner = owner;
+ }
+
+ @Override
+ protected long getLastOperationTimeMillis() {
+ return ImsEnablementTrackerTest.sLastImsOperationTimeMs;
+ }
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ // Make sure the handler is empty before finishing the test.
+ waitForHandlerAction(mHandler, TEST_REQUEST_THROTTLE_TIME_MS);
+
+ mTracker = null;
+ super.tearDown();
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInDefaultState() throws RemoteException {
+ // Verify that when the enable command is received in the Default state and enableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DEFAULT, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInDefaultState() throws RemoteException {
+ // Verify that when the disable command is received in the Default state and disableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DEFAULT, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandInDefaultState() throws RemoteException {
+ // Verify that when reset command is received in the Default state, it should be ignored.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DEFAULT, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).resetIms(eq(SLOT_1), eq(SUB_1));
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInEnabledState() throws RemoteException {
+ // Verify that received the enable command is not handle in the Enabled state,
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLED, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder, never()).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInEnabledState() throws RemoteException {
+ // Verify that when the disable command is received in the Enabled state and disableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLED, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandInEnabledState() throws RemoteException {
+ // Verify that when the reset command is received in the Enabled state and disableIms
+ // and enableIms are called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLED, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ verify(mMockServiceControllerBinder).resetIms(eq(SLOT_1), eq(SUB_1));
+
+ waitForHandlerActionDelayed(mHandler, TEST_REQUEST_THROTTLE_TIME_MS,
+ TEST_REQUEST_THROTTLE_TIME_MS + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInDisabledState() throws RemoteException {
+ // Verify that when disable command is received in the Disabled state, it should be ignored.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLED, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verifyZeroInteractions(mMockServiceControllerBinder);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInDisabledState() throws RemoteException {
+ // Verify that when the enable command is received in the Disabled state and enableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLED, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandWithoutTimeoutInDisableState() throws RemoteException {
+ // Verify that when the enable command is received in the Disabled state. After throttle
+ // time expired, the enableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLED, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandInDisabledState() throws RemoteException {
+ // Verify that the reset command is received in the Disabled state and it`s not handled.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLED, 1, 0);
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ verify(mMockServiceControllerBinder).resetIms(eq(SLOT_1), eq(SUB_1));
+
+ // The disableIms was called. So set the last operation time to current.
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInDisablingState() throws RemoteException {
+ // Verify that when enable command is received in the Disabling state, it should be ignored.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verifyZeroInteractions(mMockServiceControllerBinder);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisablingMessageInDisablingState() throws RemoteException {
+ // Verify that when the internal disable message is received in the Disabling state and
+ // disableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).disableIms(anyInt(), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandInDisablingState() throws RemoteException {
+ // Verify when the reset command is received in the Disabling state the disableIms and
+ // enableIms are called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).resetIms(eq(SLOT_1), eq(SUB_1));
+
+ // The disableIms was called. So set the last operation time to current.
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnablingMessageInEnablingState() throws RemoteException {
+ // Verify that when the internal enable message is received in the Enabling state and
+ // enableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).enableIms(anyInt(), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInEnablingState() throws RemoteException {
+ // Verify that when the disable command is received in the Enabling state and
+ // clear pending message and disableIms is not called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandWithEnablingState() throws RemoteException {
+ // Verify that when reset command is received in the Enabling state, it should be ignored.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).resetIms(eq(SLOT_1), eq(SUB_1));
+
+ // The disableIms was called. So set the last operation time to current.
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInResettingState() throws RemoteException {
+ // Verify that when the enable command is received in the Resetting state and
+ // enableIms is not called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verifyZeroInteractions(mMockServiceControllerBinder);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_RESETTING));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInResettingState() throws RemoteException {
+ // Verify that when the disable command is received in the Resetting state and
+ // disableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verifyZeroInteractions(mMockServiceControllerBinder);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_RESETTING));
+ }
+
+ @SmallTest
+ @Test
+ public void testResettingMessageInResettingState() throws RemoteException {
+ // Verify that when the internal reset message is received in the Resetting state and
+ // disableIms and enableIms are called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+
+ // Wait for the throttle time.
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).resetIms(anyInt(), anyInt());
+
+ // Set the last operation time to current to verify the message with delay.
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), anyInt());
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableEnableMessageInResettingState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // Simulation.
+ // In Resetting state, disableIms() called before doing resetIms().
+ // After doing resetIms(), during the throttle time(before doing disableIms()),
+ // enableIms() called. Finally skip disableIms() and do enableIms().
+ mHandler.removeMessages(COMMAND_RESETTING_DONE);
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_RESETTING_DONE, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).resetIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableDisableMessageInResettingState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // Simulation.
+ // In Resetting state, enableIms() called before doing resetIms().
+ // After doing resetIms(), during the throttle time(before doing enableIms()),
+ // disableIms() called. Finally skip enableIms() and do disableIms().
+ mHandler.removeMessages(COMMAND_RESETTING_DONE);
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_RESETTING_DONE, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).resetIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, times(1)).disableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testRepeatEnableMessageInResettingState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // Simulation.
+ // In Resetting state, enableIms(), disableIms() are called repeatedly.
+ // After doing resetIms(), latest enableIms() should perform.
+ mHandler.removeMessages(COMMAND_RESETTING_DONE);
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_RESETTING_DONE, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).resetIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testRepeatDisableMessageInResettingState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_RESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // Simulation.
+ // In Resetting state, enableIms(), disableIms() are called repeatedly.
+ // After doing resetIms(), latest disableIms() should perform.
+ mHandler.removeMessages(COMMAND_RESETTING_DONE);
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_RESETTING_DONE, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_ENABLE_MSG, SLOT_1, SUB_1));
+ mHandler.sendMessage(mHandler.obtainMessage(COMMAND_DISABLE_MSG, SLOT_1, SUB_1));
+
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).resetIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, times(1)).disableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testConsecutiveCommandInEnabledState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLED, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+
+ // Set the last operation time to current to verify the message with delay.
+ sLastImsOperationTimeMs = System.currentTimeMillis();
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLING));
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLING));
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).disableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).resetIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testConsecutiveCommandInDisabledState() throws RemoteException {
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DISABLED, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // Set the last operation time to current to verify the message with delay.
+ sLastImsOperationTimeMs = System.currentTimeMillis();
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLING));
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_RESETTING));
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testSubIdChangeToInvalidAndEnableCommand() throws RemoteException {
+ // Verify that when the enable command is received in the Default state and enableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_ENABLED, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ mTracker.subIdChangedToInvalid(SLOT_1);
+ waitForHandler(mHandler);
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DEFAULT));
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandWithDifferentSlotId() throws RemoteException {
+ // Verify that when the enable command is received in the Default state and enableIms
+ // is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_DEFAULT, 2,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ Handler handlerForSlot2 = mTracker.getHandler(SLOT_2);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+ waitForHandler(handlerForSlot2);
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandler(mHandler);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ assertTrue(mTracker.isState(SLOT_2, mTracker.STATE_IMS_DEFAULT));
+
+ mTracker.enableIms(SLOT_2, SUB_2);
+ waitForHandler(handlerForSlot2);
+
+ verify(mMockServiceControllerBinder).enableIms(eq(SLOT_2), eq(SUB_2));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ assertTrue(mTracker.isState(SLOT_2, mTracker.STATE_IMS_ENABLED));
+
+ mTracker.setNumOfSlots(1);
+ sLastImsOperationTimeMs = System.currentTimeMillis();
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+
+ mTracker.setNumOfSlots(2);
+ sLastImsOperationTimeMs = System.currentTimeMillis();
+ mTracker.disableIms(SLOT_2, SUB_2);
+ waitForHandler(handlerForSlot2);
+
+ verify(mMockServiceControllerBinder).disableIms(eq(SLOT_2), eq(SUB_2));
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ assertTrue(mTracker.isState(SLOT_2, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testEnableCommandInPostResettingState() throws RemoteException {
+ // Verify that when the enable/disable commands are received in the PostResetting state
+ // and final enableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_POSTRESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // to confirm the slotId, subId for COMMAND_POST_RESETTING_DONE
+ mHandler.removeMessages(mTracker.COMMAND_POST_RESETTING_DONE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(mTracker.COMMAND_POST_RESETTING_DONE,
+ SLOT_1, SUB_1), mTracker.getRemainThrottleTime());
+
+ mTracker.enableIms(SLOT_1, SUB_1);
+ mTracker.disableIms(SLOT_1, SUB_1);
+ mTracker.enableIms(SLOT_1, SUB_1);
+ mTracker.disableIms(SLOT_1, SUB_1);
+ mTracker.enableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDisableCommandInPostResettingState() throws RemoteException {
+ // Verify that when the enable/disable commands are received in the PostResetting state
+ // and final disableIms is called.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_POSTRESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // to confirm the slotId, subId for COMMAND_POST_RESETTING_DONE
+ mHandler.removeMessages(mTracker.COMMAND_POST_RESETTING_DONE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(mTracker.COMMAND_POST_RESETTING_DONE,
+ SLOT_1, SUB_1), mTracker.getRemainThrottleTime());
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ mTracker.enableIms(SLOT_1, SUB_1);
+ mTracker.disableIms(SLOT_1, SUB_1);
+ mTracker.enableIms(SLOT_1, SUB_1);
+ mTracker.disableIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).disableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).enableIms(eq(SLOT_1), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_DISABLED));
+ }
+
+ @SmallTest
+ @Test
+ public void testResetCommandInPostResettingState() throws RemoteException {
+ // Verify that when the enable/disable/reset commands are received in the PostResetting
+ // state and final enableIms is called without calling resetIms again.
+ mTracker = createTracker(mMockServiceControllerBinder, mTracker.STATE_IMS_POSTRESETTING, 1,
+ System.currentTimeMillis());
+ mHandler = mTracker.getHandler(SLOT_1);
+ // Wait for a while for the state machine to be ready.
+ waitForHandler(mHandler);
+
+ // to confirm the slotId, subId for COMMAND_POST_RESETTING_DONE
+ mHandler.removeMessages(mTracker.COMMAND_POST_RESETTING_DONE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(mTracker.COMMAND_POST_RESETTING_DONE,
+ SLOT_1, SUB_1), mTracker.getRemainThrottleTime());
+
+ mTracker.disableIms(SLOT_1, SUB_1);
+ mTracker.enableIms(SLOT_1, SUB_1);
+ mTracker.resetIms(SLOT_1, SUB_1);
+ waitForHandlerActionDelayed(mHandler, mTracker.getRemainThrottleTime(),
+ mTracker.getRemainThrottleTime() + DEFAULT_DELAY);
+
+ verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+ verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), anyInt());
+ verify(mMockServiceControllerBinder, never()).resetIms(eq(SLOT_1), anyInt());
+ assertTrue(mTracker.isState(SLOT_1, mTracker.STATE_IMS_ENABLED));
+ }
+
+ private TestableImsEnablementTracker createTracker(IImsServiceController binder, int state,
+ int numSlots, long initLastOperationTime) {
+ sLastImsOperationTimeMs = initLastOperationTime;
+ TestableImsEnablementTracker tracker = new TestableImsEnablementTracker(
+ Looper.getMainLooper(), binder, state, numSlots, this);
+ return tracker;
+ }
+
+ private void waitForHandler(Handler h) {
+ waitForHandlerActionDelayed(h, DEFAULT_TIMEOUT, DEFAULT_DELAY);
+ }
+}
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 02484ce..abc231a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -956,7 +956,7 @@
carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, ImsFeature.FEATURE_RCS));
// Assume that there is a CarrierConfig change that kicks off query to carrier service.
sendCarrierConfigChanged(1, 1);
- setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
+ setupDynamicQueryFeaturesMultiSim(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
verify(carrierController).changeImsServiceFeatures(eq(carrierFeatures),
any(SparseIntArray.class));
deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
@@ -2021,6 +2021,7 @@
*/
private void startBindCarrierConfigAlreadySet() {
mTestImsResolver.initialize();
+ processAllMessages();
ArgumentCaptor<BroadcastReceiver> receiversCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
@@ -2042,6 +2043,7 @@
*/
private void startBindNoCarrierConfig(int numSlots) {
mTestImsResolver.initialize();
+ processAllMessages();
ArgumentCaptor<BroadcastReceiver> receiversCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
@@ -2068,6 +2070,15 @@
processAllMessages();
}
+ private void setupDynamicQueryFeaturesMultiSim(ComponentName name,
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
+ processAllFutureMessages();
+ // ensure that startQuery was called
+ verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
+ mDynamicQueryListener.onComplete(name, features);
+ processAllMessages();
+ }
+
private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
processAllMessages();
// ensure that startQuery was called
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
index b80c6b0..4a3ceaa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
@@ -140,6 +140,8 @@
SparseIntArray slotIdToSubIdMap = new SparseIntArray();
slotIdToSubIdMap.put(SLOT_0, SUB_1);
ServiceConnection conn = bindAndConnectService(slotIdToSubIdMap);
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// add the MMTelFeature
verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
@@ -149,6 +151,8 @@
validateMmTelFeatureContainerExists(SLOT_0);
// Remove the feature
conn.onBindingDied(mTestComponentName);
+ delay = REBIND_RETRY.getStartDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
verify(mMmTelCompatAdapterSpy).onFeatureRemoved();
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL));
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 113829f..adfc4a3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -634,6 +634,8 @@
conn.onServiceDisconnected(mTestComponentName);
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
@@ -660,6 +662,8 @@
conn.onServiceDisconnected(mTestComponentName);
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
@@ -762,6 +766,8 @@
conn.onBindingDied(null /*null*/);
+ long delay = REBIND_RETRY.getStartDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
verify(mMockContext).unbindService(eq(conn));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
@@ -787,6 +793,8 @@
slotIdToSubIdMap.put(SLOT_0, SUB_2);
bindAndNullServiceError(testFeatures, slotIdToSubIdMap.clone());
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName));
@@ -1304,7 +1312,7 @@
conn.onBindingDied(null /*null*/);
- long delay = mTestImsServiceController.getRebindDelay();
+ long delay = REBIND_RETRY.getStartDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// The service should autobind after rebind event occurs
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
@@ -1330,7 +1338,7 @@
// null binding should be ignored in this case.
conn.onNullBinding(null);
- long delay = mTestImsServiceController.getRebindDelay();
+ long delay = REBIND_RETRY.getStartDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// The service should autobind after rebind event occurs
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
@@ -1398,10 +1406,11 @@
slotIdToSubIdMap.put(SLOT_0, SUB_2);
ServiceConnection conn = bindAndConnectService(testFeatures, slotIdToSubIdMap.clone());
conn.onBindingDied(null /*null*/);
+
+ long delay = REBIND_RETRY.getStartDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone());
- long delay = mTestImsServiceController.getRebindDelay();
- waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// Should only see two binds, not three from the auto rebind that occurs.
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
}
@@ -1481,6 +1490,9 @@
IImsServiceController.Stub controllerStub = mock(IImsServiceController.Stub.class);
when(controllerStub.queryLocalInterface(any())).thenReturn(mMockServiceControllerBinder);
connection.onServiceConnected(mTestComponentName, controllerStub);
+
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
return connection;
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
index 63fcf10..c6b0fa1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
import android.testing.TestableLooper;
import androidx.test.InstrumentationRegistry;
@@ -29,6 +31,7 @@
import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -38,6 +41,22 @@
* Helper class to load Mockito Resources into a test.
*/
public class ImsTestBase {
+ private static final Field MESSAGE_QUEUE_FIELD;
+ private static final Field MESSAGE_WHEN_FIELD;
+ private static final Field MESSAGE_NEXT_FIELD;
+
+ static {
+ try {
+ MESSAGE_QUEUE_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Failed to initialize TelephonyTest", e);
+ }
+ }
protected Context mContext;
protected List<TestableLooper> mTestableLoopers = new ArrayList<>();
@@ -112,6 +131,52 @@
}
/**
+ * @return The longest delay from all the message queues.
+ */
+ private long getLongestDelay() {
+ long delay = 0;
+ for (TestableLooper looper : mTestableLoopers) {
+ MessageQueue queue = looper.getLooper().getQueue();
+ try {
+ Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
+ while (msg != null) {
+ delay = Math.max(msg.getWhen(), delay);
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TelephonyTest", e);
+ }
+ }
+ return delay;
+ }
+
+ /**
+ * @return {@code true} if there are any messages in the queue.
+ */
+ private boolean messagesExist() {
+ for (TestableLooper looper : mTestableLoopers) {
+ MessageQueue queue = looper.getLooper().getQueue();
+ try {
+ Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
+ if (msg != null) return true;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TelephonyTest", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle all messages including the delayed messages.
+ */
+ public void processAllFutureMessages() {
+ while (messagesExist()) {
+ moveTimeForward(getLongestDelay());
+ processAllMessages();
+ }
+ }
+
+ /**
* Check if there are any messages to be processed in any monitored TestableLooper
* Delayed messages to be handled at a later time will be ignored
* @return true if there are no messages that can be handled at the current time
@@ -123,4 +188,28 @@
}
return true;
}
+
+ /**
+ * Effectively moves time forward by reducing the time of all messages
+ * for all monitored TestableLoopers
+ * @param milliSeconds number of milliseconds to move time forward by
+ */
+ public void moveTimeForward(long milliSeconds) {
+ for (TestableLooper looper : mTestableLoopers) {
+ MessageQueue queue = looper.getLooper().getQueue();
+ try {
+ Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
+ while (msg != null) {
+ long updatedWhen = msg.getWhen() - milliSeconds;
+ if (updatedWhen < 0) {
+ updatedWhen = 0;
+ }
+ MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TelephonyTest", e);
+ }
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java
new file mode 100644
index 0000000..e3fc6d3
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.imsphone;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.ServiceState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ImsCallInfoTrackerTest extends TelephonyTest {
+
+ private ImsCallInfoTracker mImsCallInfoTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mImsCallInfoTracker = new ImsCallInfoTracker(mImsPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testDialingNormalCall() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.DIALING, info.getCallState());
+ assertFalse(info.isIncoming());
+ assertFalse(info.isEmergencyCall());
+ assertEquals(EUTRAN, info.getCallRadioTech());
+ assertFalse(info.isHeldByRemote());
+ }
+
+ @Test
+ public void testDialingEmergencyCall() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, true);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.DIALING, info.getCallState());
+ assertFalse(info.isIncoming());
+ assertTrue(info.isEmergencyCall());
+ assertEquals(EUTRAN, info.getCallRadioTech());
+ assertFalse(info.isHeldByRemote());
+ }
+
+ @Test
+ public void testIncomingCall() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.INCOMING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.INCOMING, info.getCallState());
+ assertTrue(info.isIncoming());
+ assertFalse(info.isEmergencyCall());
+ assertEquals(EUTRAN, info.getCallRadioTech());
+ assertFalse(info.isHeldByRemote());
+
+ // Answer the call
+ doReturn(Call.State.ACTIVE).when(c).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c);
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.ACTIVE, info.getCallState());
+
+ // Hold the call
+ doReturn(Call.State.HOLDING).when(c).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c);
+
+ verify(mImsPhone, times(3)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.HOLDING, info.getCallState());
+
+ // Disconnect the call
+ doReturn(Call.State.DISCONNECTING).when(c).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c);
+
+ verify(mImsPhone, times(4)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.DISCONNECTING, info.getCallState());
+
+ // Call disconnected
+ doReturn(Call.State.DISCONNECTED).when(c).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c);
+
+ verify(mImsPhone, times(5)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.IDLE, info.getCallState());
+ }
+
+ @Test
+ public void testMultiCalls() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c1 = getConnection(Call.State.INCOMING, false);
+ mImsCallInfoTracker.addImsCallStatus(c1);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ doReturn(Call.State.ACTIVE).when(c1).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c1);
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ // 1st call
+ ImsCallInfo info1 = imsCallInfos.get(0);
+
+ assertNotNull(info1);
+ assertEquals(1, info1.getIndex());
+ assertEquals(Call.State.ACTIVE, info1.getCallState());
+
+ // Add 2nd WAITING call
+ ImsPhoneConnection c2 = getConnection(Call.State.WAITING, false);
+ mImsCallInfoTracker.addImsCallStatus(c2);
+
+ verify(mImsPhone, times(3)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(2, imsCallInfos.size());
+
+ // 1st call
+ info1 = imsCallInfos.get(0);
+
+ assertNotNull(info1);
+ assertEquals(1, info1.getIndex());
+ assertEquals(Call.State.ACTIVE, info1.getCallState());
+
+ // 2nd call
+ ImsCallInfo info2 = imsCallInfos.get(1);
+
+ assertNotNull(info2);
+ assertEquals(2, info2.getIndex());
+ assertEquals(Call.State.WAITING, info2.getCallState());
+ assertTrue(info2.isIncoming());
+
+ // Disconnect 1st call
+ doReturn(Call.State.DISCONNECTED).when(c1).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c1);
+
+ verify(mImsPhone, times(4)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(2, imsCallInfos.size());
+
+ // 1st call
+ info1 = imsCallInfos.get(0);
+
+ assertNotNull(info1);
+ assertEquals(1, info1.getIndex());
+ assertEquals(Call.State.IDLE, info1.getCallState());
+
+ // 2nd call
+ info2 = imsCallInfos.get(1);
+
+ assertNotNull(info2);
+ assertEquals(2, info2.getIndex());
+ assertEquals(Call.State.WAITING, info2.getCallState());
+
+ // Answer WAITING call
+ doReturn(Call.State.ACTIVE).when(c2).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c2);
+
+ verify(mImsPhone, times(5)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ // 2nd call
+ info2 = imsCallInfos.get(0);
+
+ assertNotNull(info2);
+ assertEquals(2, info2.getIndex());
+ assertEquals(Call.State.ACTIVE, info2.getCallState());
+ }
+
+ @Test
+ public void testHeldByRemote() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.INCOMING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ doReturn(Call.State.ACTIVE).when(c).getState();
+ mImsCallInfoTracker.updateImsCallStatus(c);
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ // Hold received
+ mImsCallInfoTracker.updateImsCallStatus(c, true, false);
+
+ verify(mImsPhone, times(3)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.ACTIVE, info.getCallState());
+ assertTrue(info.isHeldByRemote());
+
+ // Resume received
+ mImsCallInfoTracker.updateImsCallStatus(c, false, true);
+
+ verify(mImsPhone, times(4)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertEquals(1, imsCallInfos.size());
+
+ info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.ACTIVE, info.getCallState());
+ assertFalse(info.isHeldByRemote());
+ }
+
+ @Test
+ public void testSortImsCallInfo() throws Exception {
+ List<ImsCallInfo> imsCallInfos = new ArrayList<>();
+ imsCallInfos.add(new ImsCallInfo(2));
+ imsCallInfos.add(new ImsCallInfo(1));
+
+ assertEquals(2, imsCallInfos.get(0).getIndex());
+ assertEquals(1, imsCallInfos.get(1).getIndex());
+
+ ImsCallInfoTracker.sort(imsCallInfos);
+
+ assertEquals(1, imsCallInfos.get(0).getIndex());
+ assertEquals(2, imsCallInfos.get(1).getIndex());
+ }
+
+ @Test
+ public void testSrvccCompleted() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ mImsCallInfoTracker.notifySrvccCompleted();
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(0, imsCallInfos.size());
+ }
+
+ @Test
+ public void testClearAllOrphanedConnections() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ mImsCallInfoTracker.clearAllOrphanedConnections();
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.IDLE, info.getCallState());
+ }
+
+ private ImsPhoneConnection getConnection(Call.State state, boolean isEmergency) {
+ ImsPhoneConnection c = mock(ImsPhoneConnection.class);
+ doReturn(isEmergency).when(c).isEmergencyCall();
+ doReturn(state).when(c).getState();
+ doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(c).getCallRadioTech();
+ switch (state) {
+ case INCOMING:
+ case WAITING:
+ doReturn(true).when(c).isIncoming();
+ break;
+ default:
+ doReturn(false).when(c).isIncoming();
+ }
+
+ return c;
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
new file mode 100644
index 0000000..7d6557d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.imsphone;
+
+import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA;
+import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA;
+import static android.telephony.CarrierConfigManager.Ims.KEY_NR_SA_DISABLE_POLICY_INT;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_NONE;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED;
+import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.telephony.CarrierConfigManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public final class ImsNrSaModeHandlerTest extends TelephonyTest{
+ @Captor ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener>
+ mCarrierConfigChangeListenerCaptor;
+ @Captor ArgumentCaptor<Handler> mPreciseCallStateHandlerCaptor;
+
+ private ImsNrSaModeHandler mTestImsNrSaModeHandler;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+ private Handler mPreciseCallStateHandler;
+
+ @Mock private ImsPhoneCall mForegroundCall;
+ @Mock private ImsPhoneCall mBackgroundCall;
+ private Call.State mActiveState = ImsPhoneCall.State.ACTIVE;
+ private Call.State mIdleState = ImsPhoneCall.State.IDLE;
+
+ private int mAnyInt = 0;
+ private final Set<String> mFeatureTags =
+ new ArraySet<String>(Arrays.asList(ImsNrSaModeHandler.MMTEL_FEATURE_TAG));
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+
+ mTestImsNrSaModeHandler = new ImsNrSaModeHandler(mImsPhone, mTestableLooper.getLooper());
+
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(
+ any(), mCarrierConfigChangeListenerCaptor.capture());
+
+ mCarrierConfigChangeListener = mCarrierConfigChangeListenerCaptor.getValue();
+
+ doReturn(mAnyInt).when(mImsPhone).getSubId();
+ doReturn(mContextFixture.getCarrierConfigBundle()).when(mCarrierConfigManager)
+ .getConfigForSubId(anyInt(), any());
+ doReturn(mPhone).when(mImsPhone).getDefaultPhone();
+
+ doReturn(mForegroundCall).when(mImsPhone).getForegroundCall();
+ doReturn(mBackgroundCall).when(mImsPhone).getBackgroundCall();
+
+ doReturn(mActiveState).when(mForegroundCall).getState();
+ doReturn(mActiveState).when(mBackgroundCall).getState();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestImsNrSaModeHandler = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testTearDown() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).registerForPreciseCallStateChanged(
+ mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
+ mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setNrSaDisabledForWfc(true);
+
+ mTestImsNrSaModeHandler.tearDown();
+
+ verify(mCarrierConfigManager).unregisterCarrierConfigChangeListener(any());
+ verify(mImsPhone).unregisterForPreciseCallStateChanged(mPreciseCallStateHandler);
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testOnImsRegisteredWithSaDisablePolicyNone() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ }
+
+ @Test
+ public void testOnImsRegisteredWithSaDisablePolicyWfcEstablished() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).registerForPreciseCallStateChanged(any(), anyInt(), any());
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testOnImsRegisteredWithSaDisablePolicyWfcEstablishedWithVonrDisabled() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT,
+ NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).registerForPreciseCallStateChanged(any(), anyInt(), any());
+
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+ mSimulatedCommands.setVonrEnabled(true);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+ processAllMessages();
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+ mSimulatedCommands.setVonrEnabled(false);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+ processAllMessages();
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+ mSimulatedCommands.setVonrEnabled(false);
+
+ mFeatureTags.remove(ImsNrSaModeHandler.MMTEL_FEATURE_TAG);
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+ processAllMessages();
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+ mSimulatedCommands.setVonrEnabled(false);
+
+ mFeatureTags.add(ImsNrSaModeHandler.MMTEL_FEATURE_TAG);
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+ processAllMessages();
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testOnImsRegisteredWithSaDisablePolicyVowifiRegistered() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testOnImsUnregisteredDoNothingIfNotVowifiRegNoti() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+
+ mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_NONE);
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ }
+
+ @Test
+ public void testOnImsUnregisteredWithSaDisablePolicyVowifiRegistered() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+
+ mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_IWLAN);
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+
+ mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_NONE);
+
+ assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testOnPreciseCallStateChangedWithSaDisablePolicyWfcEstablished() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).registerForPreciseCallStateChanged(
+ mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
+ mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+
+ mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+
+ assertTrue(mTestImsNrSaModeHandler.isImsCallOngoing());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+
+ doReturn(mActiveState).when(mForegroundCall).getState();
+ doReturn(mActiveState).when(mBackgroundCall).getState();
+ mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+
+ assertTrue(mTestImsNrSaModeHandler.isImsCallOngoing());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+ mTestImsNrSaModeHandler.setImsCallStatus(false);
+ mSimulatedCommands.setN1ModeEnabled(true, null);
+
+ doReturn(mIdleState).when(mForegroundCall).getState();
+ doReturn(mIdleState).when(mBackgroundCall).getState();
+ mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+
+ assertFalse(mTestImsNrSaModeHandler.isImsCallOngoing());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+ mTestImsNrSaModeHandler.setImsCallStatus(true);
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+
+ assertFalse(mTestImsNrSaModeHandler.isImsCallOngoing());
+ assertTrue(mSimulatedCommands.isN1ModeEnabled());
+ }
+
+ @Test
+ public void testUnregisterForPreciseCallStateChangeIfNeeded() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).registerForPreciseCallStateChanged(
+ mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
+ mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
+
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ verify(mImsPhone).unregisterForPreciseCallStateChanged(mPreciseCallStateHandler);
+ }
+
+ @Test
+ public void testNrSaModeIsNotHandledWhenNotSupported() {
+ mContextFixture.getCarrierConfigBundle().putInt(
+ KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
+ mContextFixture.getCarrierConfigBundle().putIntArray(
+ KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_NSA});
+
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+ mSimulatedCommands.setN1ModeEnabled(false, null);
+ mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+
+ mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
+
+ assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+ assertFalse(mSimulatedCommands.isN1ModeEnabled());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
index c2db93f..c4bb864 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -27,6 +28,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsStreamMediaProfile;
import android.test.suitebuilder.annotation.SmallTest;
@@ -258,6 +261,51 @@
@Test
@SmallTest
+ public void testGetCallSessionId() {
+ doReturn(mImsCall).when(mConnection1).getImsCall();
+ ImsCallSession imsForegroundCallSession = mock(ImsCallSession.class);
+ doReturn(imsForegroundCallSession).when(mImsCall).getSession();
+ doReturn("1").when(imsForegroundCallSession).getCallId();
+ mImsCallUT.attach(mConnection1, Call.State.ACTIVE);
+ assertEquals("1", mImsCallUT.getCallSessionId());
+ doReturn(null).when(mImsCall).getSession();
+ assertNull(mImsCallUT.getCallSessionId());
+ doReturn(null).when(mConnection1).getImsCall();
+ assertNull(mImsCallUT.getCallSessionId());
+ mImsCallUT.detach(mConnection1);
+ assertNull(mImsCallUT.getCallSessionId());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetServiceType() {
+ doReturn(mImsCall).when(mConnection1).getImsCall();
+ mImsCallUT.attach(mConnection1, Call.State.ACTIVE);
+ doReturn(false).when(mConnection1).isEmergencyCall();
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NORMAL, mImsCallUT.getServiceType());
+ doReturn(true).when(mConnection1).isEmergencyCall();
+ assertEquals(ImsCallProfile.SERVICE_TYPE_EMERGENCY, mImsCallUT.getServiceType());
+ mImsCallUT.detach(mConnection1);
+ assertEquals(ImsCallProfile.SERVICE_TYPE_NONE, mImsCallUT.getServiceType());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetCallType() {
+ doReturn(mImsCall).when(mConnection1).getImsCall();
+ mImsCallUT.attach(mConnection1, Call.State.ACTIVE);
+ doReturn(false).when(mImsCall).isVideoCall();
+ assertEquals(ImsCallProfile.CALL_TYPE_VOICE, mImsCallUT.getCallType());
+ doReturn(true).when(mImsCall).isVideoCall();
+ assertEquals(ImsCallProfile.CALL_TYPE_VT, mImsCallUT.getCallType());
+ doReturn(null).when(mConnection1).getImsCall();
+ assertEquals(ImsCallProfile.CALL_TYPE_NONE, mImsCallUT.getCallType());
+ mImsCallUT.detach(mConnection1);
+ assertEquals(ImsCallProfile.CALL_TYPE_NONE, mImsCallUT.getCallType());
+ }
+
+ @Test
+ @SmallTest
public void testSetMute() {
doReturn(mImsCall).when(mConnection1).getImsCall();
mImsCallUT.attach(mConnection1, Call.State.ACTIVE);
@@ -269,4 +317,24 @@
fail("Exception unexpected");
}
}
+
+ @Test
+ public void testMaybeClearRemotelyHeldStatus() {
+ mImsCallUT.attach(mConnection1, Call.State.ACTIVE);
+ when(mConnection1.isHeldByRemote()).thenReturn(true);
+ mImsCallUT.maybeClearRemotelyHeldStatus();
+ verify(mConnection1, times(1)).setRemotelyUnheld();
+
+ mImsCallUT.attach(mConnection2, Call.State.ACTIVE);
+ when(mConnection2.isHeldByRemote()).thenReturn(true);
+ mImsCallUT.maybeClearRemotelyHeldStatus();
+ verify(mConnection1, times(2)).setRemotelyUnheld();
+ verify(mConnection2, times(1)).setRemotelyUnheld();
+
+ when(mConnection1.isHeldByRemote()).thenReturn(false);
+ when(mConnection2.isHeldByRemote()).thenReturn(false);
+ mImsCallUT.maybeClearRemotelyHeldStatus();
+ verify(mConnection1, times(2)).setRemotelyUnheld();
+ verify(mConnection2, times(1)).setRemotelyUnheld();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index ec2209d..d0a2094 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -21,6 +21,23 @@
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.telephony.CarrierConfigManager.ImsVoice.ALERTING_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.BASIC_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.MIDCALL_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.PREALERTING_SRVCC_SUPPORT;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_FAILED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
+import static android.telephony.ims.ImsStreamMediaProfile.DIRECTION_INACTIVE;
+import static android.telephony.ims.ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+import static android.telephony.ims.feature.MmTelFeature.IMS_TRAFFIC_DIRECTION_INCOMING;
+import static android.telephony.ims.feature.MmTelFeature.IMS_TRAFFIC_DIRECTION_OUTGOING;
import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
@@ -53,22 +70,24 @@
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
+import android.net.Uri;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.Bundle;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsConferenceState;
@@ -76,6 +95,9 @@
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
+import android.telephony.ims.aidl.IImsTrafficSessionCallback;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -91,16 +113,21 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
+import com.android.ims.internal.ConferenceParticipant;
import com.android.ims.internal.IImsCallSession;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SrvccConnection;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.d2d.RtpTransport;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker.VtDataUsageProvider;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Assert;
@@ -109,9 +136,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -125,6 +155,7 @@
private ImsCall.Listener mImsCallListener;
private ImsCall mImsCall;
private ImsCall mSecondImsCall;
+ private ISrvccStartedCallback mSrvccStartedCallback;
private BroadcastReceiver mBroadcastReceiver;
private Bundle mBundle = new Bundle();
private static final int SUB_0 = 0;
@@ -140,6 +171,9 @@
private ImsPhoneConnection mImsPhoneConnection;
private INetworkStatsProviderCallback mVtDataUsageProviderCb;
private ImsPhoneCallTracker.ConnectorFactory mConnectorFactory;
+ private CommandsInterface mMockCi;
+ private DomainSelectionResolver mDomainSelectionResolver;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private final Executor mExecutor = Runnable::run;
@@ -200,11 +234,16 @@
mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class);
mImsPhoneConnection = mock(ImsPhoneConnection.class);
+ mMockCi = mock(CommandsInterface.class);
imsCallMocking(mImsCall);
imsCallMocking(mSecondImsCall);
doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState();
doReturn(mImsCallProfile).when(mImsManager).createCallProfile(anyInt(), anyInt());
mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
+ mDomainSelectionResolver = mock(DomainSelectionResolver.class);
+
+ doReturn(new SubscriptionInfoInternal.Builder().setSimSlotIndex(0).setId(1).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
doAnswer(invocation -> {
mMmTelListener = (MmTelFeature.Listener) invocation.getArguments()[0];
@@ -240,7 +279,16 @@
return mMockConnector;
}).when(mConnectorFactory).create(any(), anyInt(), anyString(), any(), any());
+ DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
+ doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mCTUT = new ImsPhoneCallTracker(mImsPhone, mConnectorFactory, Runnable::run);
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
mCTUT.setDataEnabled(true);
final ArgumentCaptor<VtDataUsageProvider> vtDataUsageProviderCaptor =
@@ -347,16 +395,11 @@
@Test
@SmallTest
public void testCarrierConfigLoadSubscription() throws Exception {
- // Start with there being no subId loaded, so SubscriptionController#isActiveSubId is false
- // as part of setup, connectionReady is called, which ends up calling
- // updateCarrierConfiguration. Since the carrier config is not report carrier identified
- // config, we should not see updateImsServiceConfig called yet.
verify(mImsManager, never()).updateImsServiceConfig();
// Send disconnected indication
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
// Receive a subscription loaded and IMS connection ready indication.
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
sendCarrierConfigChanged();
@@ -372,7 +415,6 @@
public void testCarrierConfigSentLocked() throws Exception {
// move to ImsService unavailable state.
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
@@ -399,7 +441,6 @@
verify(mImsManager, never()).updateImsServiceConfig();
// Receive a subscription loaded and IMS connection ready indication.
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
// CarrierConfigLoader has signalled that the carrier config has been applied for a specific
@@ -419,7 +460,6 @@
public void testCarrierConfigSentBeforeReady() throws Exception {
// move to ImsService unavailable state.
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
@@ -440,7 +480,6 @@
verify(mImsManager, never()).updateImsServiceConfig();
// Receive a subscription loaded and IMS connection ready indication.
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
// CarrierConfigLoader has signalled that the carrier config has been applied for a specific
@@ -464,7 +503,6 @@
public void testCarrierConfigSentBeforeReadyAndCrash() throws Exception {
// move to ImsService unavailable state.
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
@@ -532,7 +570,7 @@
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
assertFalse(mCTUT.mRingingCall.isRinging());
// mock a MT call
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(1)).notifyIncomingRing();
assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -684,7 +722,7 @@
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
}
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -722,7 +760,7 @@
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
}
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -914,7 +952,7 @@
try {
doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
any(ImsCall.Listener.class));
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
} catch (Exception ex) {
ex.printStackTrace();
@@ -1387,6 +1425,43 @@
@Test
@SmallTest
+ public void testAutoRejectedCauses() {
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_CALL_ON_OTHER_SUB, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_E911_CALL, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_CALL_SETUP, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_MAX_CALL_LIMIT_REACHED, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_CALL_TRANSFER, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_CONFERENCE_CALL, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_HANDOVER, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REJECT_ONGOING_CALL_UPGRADE, 0),
+ Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_BAD_REQUEST, 0), Call.State.INCOMING));
+ assertEquals(DisconnectCause.INCOMING_AUTO_REJECTED, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_BAD_REQUEST, 0), Call.State.WAITING));
+ assertEquals(DisconnectCause.SERVER_ERROR, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_BAD_REQUEST, 0), Call.State.DIALING));
+ assertEquals(DisconnectCause.SERVER_ERROR, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_BAD_REQUEST, 0), Call.State.ALERTING));
+ }
+
+ @Test
+ @SmallTest
public void testImsAlternateEmergencyDisconnect() {
assertEquals(DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL,
mCTUT.getDisconnectCauseFromReasonInfo(
@@ -1429,6 +1504,45 @@
verify(mImsPhone, never()).startOnHoldTone(nullable(Connection.class));
}
+ @Test
+ @SmallTest
+ public void testSendAnbrQuery() throws Exception {
+ logd("ImsPhoneCallTracker testSendAnbrQuery");
+
+ replaceInstance(Phone.class, "mCi", mPhone, mMockCi);
+ //establish a MT call
+ testImsMTCallAccept();
+
+ ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection();
+ ImsCall imsCall = connection.getImsCall();
+ imsCall.getImsCallSessionListenerProxy().callSessionSendAnbrQuery(1, 1, 24400);
+
+ verify(mMockCi, times(1)).sendAnbrQuery(eq(1), eq(1), eq(24400), any());
+
+ // Disconnecting and then Disconnected
+ mCTUT.hangup(connection);
+ mImsCallListener.onCallTerminated(imsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+ }
+
+ @Test
+ @SmallTest
+ public void testTriggerNotifyAnbr() throws Exception {
+ logd("ImsPhoneCallTracker testTriggerNotifyAnbr");
+
+ testImsMTCallAccept();
+ ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection();
+ ImsCall imsCall = connection.getImsCall();
+
+ mCTUT.triggerNotifyAnbr(1, 1, 24400);
+ verify(mImsCall, times(1)).callSessionNotifyAnbr(eq(1), eq(1), eq(24400));
+
+ // Disconnecting and then Disconnected
+ mCTUT.hangup(connection);
+ mImsCallListener.onCallTerminated(imsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+ }
+
/**
* Verifies that a remote hold tone is played when the call is remotely held and the media
* direction is inactive (i.e. the audio stream is not playing, so we should play the tone).
@@ -1579,7 +1693,9 @@
}
});
ImsCall call = connection.getImsCall();
- call.getListener().onCallMerged(call, null, false);
+ call.getListener().onCallTerminated(
+ call, new ImsReasonInfo(
+ ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, 0));
assertTrue(result[0]);
}
@@ -1689,7 +1805,7 @@
assertTrue(mCTUT.mForegroundCall.isRingbackTonePlaying());
// Move the connection to the handover state.
- mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
assertFalse(mCTUT.mForegroundCall.isRingbackTonePlaying());
}
@@ -1720,7 +1836,7 @@
}
// Move the connection to the handover state.
- mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
// Ensure we are no longer tracking hold.
assertFalse(mCTUT.isHoldOrSwapInProgress());
}
@@ -1732,7 +1848,7 @@
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
assertFalse(mCTUT.mRingingCall.isRinging());
// mock a MT call
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(1)).notifyIncomingRing();
assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -1743,7 +1859,7 @@
connection.addListener(mImsPhoneConnectionListener);
// Move the connection to the handover state.
- mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
assertEquals(1, mCTUT.mHandoverCall.getConnections().size());
// No need to go through all the rigamarole of the mocked termination we normally do; we
@@ -1770,7 +1886,7 @@
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
assertFalse(mCTUT.mRingingCall.isRinging());
// mock a MT call
- mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(1)).notifyIncomingRing();
assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -1781,7 +1897,7 @@
connection.addListener(mImsPhoneConnectionListener);
// Move the connection to the handover state.
- mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
assertEquals(1, mCTUT.mHandoverCall.getConnections().size());
// Make sure the tracker states it's idle.
@@ -1797,7 +1913,6 @@
@SmallTest
public void testConfigureRtpHeaderExtensionTypes() throws Exception {
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
true);
@@ -1828,7 +1943,6 @@
@SmallTest
public void testRtpButNoSdp() throws Exception {
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
true);
@@ -1858,7 +1972,6 @@
@SmallTest
public void testDontConfigureRtpHeaderExtensionTypes() throws Exception {
mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
- doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
sendCarrierConfigChanged();
ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
config.isD2DCommunicationSupported = false;
@@ -1876,11 +1989,14 @@
assertEquals(1, mCTUT.getConnections().size());
assertNotNull(mCTUT.getPendingMO());
assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
+ assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
mCTUT.cleanupAndRemoveConnection(conn);
assertEquals(0, mCTUT.getConnections().size());
assertNull(mCTUT.getPendingMO());
assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
+
+ assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
}
@Test
@@ -1889,7 +2005,7 @@
startOutgoingCall();
// Move the connection to the handover state.
- mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
try {
// When trigger CallSessionUpdated after Srvcc completes, checking no exception.
@@ -1899,11 +2015,635 @@
}
}
+ /**
+ * Tests the case where a dialed call has not yet moved beyond the "pending MO" phase, but the
+ * user then disconnects. In such a case we need to ensure that the pending MO reference is
+ * cleared so that another call can be placed.
+ */
+ @Test
+ @SmallTest
+ public void testCallDisconnectBeforeActive() {
+ ImsPhoneConnection connection = placeCall();
+ assertEquals(1, mCTUT.getConnections().size());
+ // Call is the pending MO right now.
+ assertEquals(connection, mCTUT.getPendingMO());
+ assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
+ assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
+
+ mImsCallListener.onCallTerminated(connection.getImsCall(),
+ new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0));
+ // Make sure pending MO got nulled out.
+ assertNull(mCTUT.getPendingMO());
+
+ // Try making another call; it should not fail.
+ ImsPhoneConnection connection2 = placeCall();
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoNotSupported() throws Exception {
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+ ImsPhoneConnection heldMT = getImsPhoneConnection(Call.State.HOLDING, "5678", true);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(activeMO);
+ connections.add(heldMT);
+
+ ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+ ImsCallProfile heldProfile = getImsCallProfileForSrvccSync("heldCall", heldMT, false);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+
+ // active call
+ SrvccCall srvccProfile = new SrvccCall(
+ "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {});
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoBasicSrvcc() throws Exception {
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+ ImsPhoneConnection heldMT = getImsPhoneConnection(Call.State.HOLDING, "5678", true);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(activeMO);
+ connections.add(heldMT);
+
+ ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+ ImsCallProfile heldProfile = getImsCallProfileForSrvccSync("heldCall", heldMT, false);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ // active call
+ SrvccCall srvccProfile = new SrvccCall(
+ "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+ assertEquals("1234", srvccConnections[0].getNumber());
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoMoAlerting() throws Exception {
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection alertingMO = getImsPhoneConnection(Call.State.ALERTING, "1234", false);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(alertingMO);
+
+ ImsCallProfile alertingProfile = getImsCallProfileForSrvccSync("alertingCall", null, true);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ // alerting call, with local ringback tone
+ SrvccCall srvccProfile = new SrvccCall(
+ "alertingCall", PRECISE_CALL_STATE_ALERTING, alertingProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+
+ bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ ALERTING_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.ALERTING);
+ assertTrue(srvccConnections[0].getRingbackToneType() == SrvccConnection.TONE_LOCAL);
+
+ profiles.clear();
+
+ // alerting call, with network ringback tone
+ alertingProfile = getImsCallProfileForSrvccSync("alertingCall", null, false);
+
+ srvccProfile = new SrvccCall(
+ "alertingCall", PRECISE_CALL_STATE_ALERTING, alertingProfile);
+ profiles.add(srvccProfile);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.ALERTING);
+ assertTrue(srvccConnections[0].getRingbackToneType() == SrvccConnection.TONE_NETWORK);
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoMtAlerting() throws Exception {
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection alertingMT = getImsPhoneConnection(Call.State.INCOMING, "1234", false);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(alertingMT);
+
+ ImsCallProfile incomingProfile =
+ getImsCallProfileForSrvccSync("incomingCall", alertingMT, false);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ SrvccCall srvccProfile = new SrvccCall(
+ "incomingCall", PRECISE_CALL_STATE_INCOMING, incomingProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+
+ bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ ALERTING_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.INCOMING);
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoMtPreAlerting() throws Exception {
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ ImsCallProfile incomingProfile = getImsCallProfileForSrvccSync("incomingCall", null, false);
+
+ SrvccCall srvccProfile = new SrvccCall(
+ "incomingCallSetup", PRECISE_CALL_STATE_INCOMING_SETUP, incomingProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ ALERTING_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+
+ bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ ALERTING_SRVCC_SUPPORT,
+ PREALERTING_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.INCOMING);
+ assertTrue(srvccConnections[0].getSubState() == SrvccConnection.SUBSTATE_PREALERTING);
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySrvccStateStarted() throws Exception {
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ mSrvccStartedCallback = null;
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mSrvccStartedCallback = (ISrvccStartedCallback) invocation.getArguments()[0];
+ return null;
+ }
+ }).when(mImsManager).notifySrvccStarted(any(ISrvccStartedCallback.class));
+
+ verify(mImsManager, times(0)).notifySrvccStarted(any());
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_STARTED);
+ verify(mImsManager, times(1)).notifySrvccStarted(any());
+ assertNotNull(mSrvccStartedCallback);
+
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(activeMO);
+
+ ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ // active call
+ SrvccCall srvccProfile = new SrvccCall(
+ "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+ profiles.add(srvccProfile);
+
+ mSrvccStartedCallback.onSrvccCallNotified(profiles);
+ SrvccConnection[] srvccConnections = mSimulatedCommands.getSrvccConnections();
+
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+ assertEquals("1234", srvccConnections[0].getNumber());
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySrvccStateFailed() throws Exception {
+ verify(mImsManager, times(0)).notifySrvccFailed();
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_FAILED);
+ verify(mImsManager, times(1)).notifySrvccFailed();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySrvccStateCanceled() throws Exception {
+ verify(mImsManager, times(0)).notifySrvccCanceled();
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_CANCELED);
+ verify(mImsManager, times(1)).notifySrvccCanceled();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySrvccStateCompleted() throws Exception {
+ verify(mImsManager, times(0)).notifySrvccCompleted();
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
+ verify(mImsManager, times(1)).notifySrvccCompleted();
+ }
+
+ @Test
+ @SmallTest
+ public void testConvertToSrvccConnectionInfoConferenceCall() throws Exception {
+ // setup ImsPhoneCallTracker's mConnections
+ ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+
+ ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+ replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+ connections.add(activeMO);
+
+ List<ConferenceParticipant> participants = new ArrayList<ConferenceParticipant>();
+ participants.add(new ConferenceParticipant(Uri.parse("tel:1234"), "", null,
+ android.telecom.Connection.STATE_ACTIVE,
+ android.telecom.Call.Details.DIRECTION_INCOMING));
+ participants.add(new ConferenceParticipant(Uri.parse("tel:5678"), "", null,
+ android.telecom.Connection.STATE_ACTIVE,
+ android.telecom.Call.Details.DIRECTION_OUTGOING));
+
+ ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall",
+ activeMO, false, participants);
+
+ // setup the response of notifySrvccStarted
+ List<SrvccCall> profiles = new ArrayList<>();
+
+ SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNull(srvccConnections);
+
+ // active call
+ SrvccCall srvccProfile = new SrvccCall(
+ "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+ profiles.add(srvccProfile);
+
+ PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 1);
+ assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+ assertFalse(srvccConnections[0].isMultiParty());
+ assertEquals("1234", srvccConnections[0].getNumber());
+
+ bundle = mContextFixture.getCarrierConfigBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+ new int[] {
+ BASIC_SRVCC_SUPPORT,
+ MIDCALL_SRVCC_SUPPORT
+ });
+ mCTUT.updateCarrierConfigCache(bundle);
+
+ srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+ assertNotNull(srvccConnections);
+ assertTrue(srvccConnections.length == 2);
+
+ assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+ assertTrue(srvccConnections[0].isMultiParty());
+ assertTrue(srvccConnections[0].isIncoming());
+ assertEquals("1234", srvccConnections[0].getNumber());
+
+ assertTrue(srvccConnections[1].getState() == Call.State.ACTIVE);
+ assertTrue(srvccConnections[1].isMultiParty());
+ assertFalse(srvccConnections[1].isIncoming());
+ assertEquals("5678", srvccConnections[1].getNumber());
+ }
+
+ /**
+ * Verifies that the expected access network tech and IMS features are notified
+ * to ImsPhone when capabilities are changed.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateImsRegistrationInfo() {
+ // LTE is registered.
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(
+ mImsManager).getRegistrationTech();
+
+ // enable Voice and Video
+ MmTelFeature.MmTelCapabilities caps = new MmTelFeature.MmTelCapabilities();
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ mCapabilityCallback.onCapabilitiesStatusChanged(caps);
+ processAllMessages();
+
+ verify(mImsPhone, times(1)).updateImsRegistrationInfo(
+ eq(CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE
+ | CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO));
+
+ // enable SMS
+ caps = new MmTelFeature.MmTelCapabilities();
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS);
+ mCapabilityCallback.onCapabilitiesStatusChanged(caps);
+ processAllMessages();
+
+ verify(mImsPhone, times(1)).updateImsRegistrationInfo(
+ eq(CommandsInterface.IMS_MMTEL_CAPABILITY_SMS));
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectionAlternateServiceStartFailed() {
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ startOutgoingCall();
+ ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+ mImsCallProfile.setEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ mImsCallListener.onCallStartFailed(mSecondImsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, -1));
+ processAllMessages();
+ EmergencyNumber emergencyNumber = c.getEmergencyNumberInfo();
+ assertNotNull(emergencyNumber);
+ assertEquals(EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ emergencyNumber.getEmergencyServiceCategoryBitmask());
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectionAlternateServiceStartFailedNullPendingMO() {
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ startOutgoingCall();
+ ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+ mImsCallListener.onCallProgressing(mSecondImsCall);
+ processAllMessages();
+ mImsCallProfile.setEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ mImsCallListener.onCallStartFailed(mSecondImsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+ ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY));
+ processAllMessages();
+ EmergencyNumber emergencyNumber = c.getEmergencyNumberInfo();
+ assertNotNull(emergencyNumber);
+ assertEquals(EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ emergencyNumber.getEmergencyServiceCategoryBitmask());
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectionAlternateServiceTerminated() {
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ startOutgoingCall();
+ ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+ mImsCallProfile.setEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ mImsCallListener.onCallTerminated(mSecondImsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, -1));
+ processAllMessages();
+ EmergencyNumber emergencyNumber = c.getEmergencyNumberInfo();
+ assertNotNull(emergencyNumber);
+ assertEquals(EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+ emergencyNumber.getEmergencyServiceCategoryBitmask());
+ }
+
+ @Test
+ public void testUpdateImsCallStatusIncoming() throws Exception {
+ // Incoming call
+ ImsPhoneConnection connection = setupRingingConnection();
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // Disconnect the call
+ mImsCallListener.onCallTerminated(connection.getImsCall(),
+ new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0));
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(any(), any());
+ }
+
+ @Test
+ public void testUpdateImsCallStatus() throws Exception {
+ // Dialing
+ ImsPhoneConnection connection = placeCall();
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // Alerting
+ ImsCall imsCall = connection.getImsCall();
+ imsCall.getImsCallSessionListenerProxy().callSessionProgressing(imsCall.getSession(),
+ new ImsStreamMediaProfile());
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(any(), any());
+
+ // Active
+ imsCall.getImsCallSessionListenerProxy().callSessionStarted(imsCall.getSession(),
+ new ImsCallProfile());
+
+ verify(mImsPhone, times(3)).updateImsCallStatus(any(), any());
+
+ // Held by remote
+ mCTUT.onCallHoldReceived(imsCall);
+
+ verify(mImsPhone, times(4)).updateImsCallStatus(any(), any());
+
+ // Resumed by remote
+ mImsCallListener.onCallResumeReceived(imsCall);
+
+ verify(mImsPhone, times(5)).updateImsCallStatus(any(), any());
+
+ // Disconnecting and then Disconnected
+ mCTUT.hangup(connection);
+ mImsCallListener.onCallTerminated(imsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ verify(mImsPhone, times(7)).updateImsCallStatus(any(), any());
+ }
+
+ @Test
+ public void testUpdateImsCallStatusSrvccCompleted() throws Exception {
+ // Incoming call
+ setupRingingConnection();
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // no interaction when SRVCC has started, failed, or canceled.
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_STARTED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_FAILED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_CANCELED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // interaction when SRVCC has completed
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(any(), any());
+ }
+
+ @Test
+ public void testClearAllOrphanedConnectionInfo() throws Exception {
+ verify(mImsPhone, times(0)).updateImsCallStatus(any(), any());
+
+ mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+ }
+
+ /** Verifies that the request from ImsService is passed to ImsPhone as expected. */
+ @Test
+ @SmallTest
+ public void testStartAndStopImsTrafficSession() {
+ IImsTrafficSessionCallback binder = Mockito.mock(IImsTrafficSessionCallback.class);
+ mMmTelListener.onStartImsTrafficSession(1, MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ IMS_TRAFFIC_DIRECTION_OUTGOING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(1),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY),
+ eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+
+ mMmTelListener.onStopImsTrafficSession(1);
+ verify(mImsPhone, times(1)).stopImsTraffic(eq(1), any());
+
+ mMmTelListener.onStartImsTrafficSession(2, MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY_SMS,
+ AccessNetworkConstants.AccessNetworkType.IWLAN,
+ IMS_TRAFFIC_DIRECTION_OUTGOING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(2),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_EMERGENCY_SMS),
+ eq(AccessNetworkConstants.AccessNetworkType.IWLAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+
+ mMmTelListener.onStartImsTrafficSession(3, MmTelFeature.IMS_TRAFFIC_TYPE_VOICE,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ IMS_TRAFFIC_DIRECTION_INCOMING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(3),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_VOICE),
+ eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ eq(IMS_TRAFFIC_DIRECTION_INCOMING), any());
+
+ mMmTelListener.onStartImsTrafficSession(4, MmTelFeature.IMS_TRAFFIC_TYPE_VIDEO,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ IMS_TRAFFIC_DIRECTION_OUTGOING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(4),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_VIDEO),
+ eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+
+ mMmTelListener.onStartImsTrafficSession(5, MmTelFeature.IMS_TRAFFIC_TYPE_SMS,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ IMS_TRAFFIC_DIRECTION_OUTGOING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(5),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_SMS),
+ eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+
+ mMmTelListener.onStartImsTrafficSession(6, MmTelFeature.IMS_TRAFFIC_TYPE_REGISTRATION,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ IMS_TRAFFIC_DIRECTION_OUTGOING, binder);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(6),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_REGISTRATION),
+ eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+
+ mMmTelListener.onModifyImsTrafficSession(6,
+ AccessNetworkConstants.AccessNetworkType.IWLAN);
+ verify(mImsPhone, times(1)).startImsTraffic(eq(6),
+ eq(MmTelFeature.IMS_TRAFFIC_TYPE_REGISTRATION),
+ eq(AccessNetworkConstants.AccessNetworkType.IWLAN),
+ eq(IMS_TRAFFIC_DIRECTION_OUTGOING), any());
+ }
+
private void sendCarrierConfigChanged() {
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
- mBroadcastReceiver.onReceive(mContext, intent);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mPhone.getPhoneId(), mPhone.getSubId(),
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
}
@@ -1974,5 +2714,45 @@
}
return connection;
}
+
+ private ImsPhoneConnection getImsPhoneConnection(Call.State state,
+ String number, boolean isIncoming) {
+ ImsPhoneCall call = mock(ImsPhoneCall.class);
+ doReturn(state).when(call).getState();
+
+ ImsPhoneConnection c = mock(ImsPhoneConnection.class);
+ doReturn(state).when(c).getState();
+ doReturn(isIncoming).when(c).isIncoming();
+ doReturn(call).when(c).getCall();
+ doReturn(number).when(c).getAddress();
+
+ return c;
+ }
+
+ private ImsCallProfile getImsCallProfileForSrvccSync(String callId,
+ ImsPhoneConnection c, boolean localTone) {
+ return getImsCallProfileForSrvccSync(callId, c, localTone, null);
+ }
+
+ private ImsCallProfile getImsCallProfileForSrvccSync(String callId,
+ ImsPhoneConnection c, boolean localTone, List<ConferenceParticipant> participants) {
+ ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile(0,
+ localTone ? DIRECTION_INACTIVE : DIRECTION_SEND_RECEIVE, 0, 0, 0);
+ ImsCallProfile profile = new ImsCallProfile(0, 0, null, mediaProfile);
+
+ if (c != null) {
+ ImsCallSession session = mock(ImsCallSession.class);
+ doReturn(callId).when(session).getCallId();
+
+ ImsCall imsCall = mock(ImsCall.class);
+ doReturn(profile).when(imsCall).getCallProfile();
+ doReturn(session).when(imsCall).getCallSession();
+ doReturn(participants).when(imsCall).getConferenceParticipants();
+
+ doReturn(imsCall).when(c).getImsCall();
+ }
+
+ return profile;
+ }
}
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 9779cfa..396466c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -58,6 +58,7 @@
import com.android.internal.telephony.GsmCdmaCall;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
@@ -144,7 +145,8 @@
logd("Testing initial state of MO ImsPhoneConnection");
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false);
+ PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false,
+ new ImsDialArgs.Builder().build());
assertEquals(PhoneConstants.PRESENTATION_ALLOWED, mConnectionUT.getNumberPresentation());
assertEquals(PhoneConstants.PRESENTATION_ALLOWED, mConnectionUT.getCnapNamePresentation());
assertEquals("+1 (700).555-41NN,1234", mConnectionUT.getOrigDialString());
@@ -157,7 +159,7 @@
public void testImsUpdateStateForeGround() {
// MO Foreground Connection dailing -> active
mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
- mForeGroundCall, false, false);
+ mForeGroundCall, false, false, new ImsDialArgs.Builder().build());
// initially in dialing state
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
@@ -172,7 +174,7 @@
public void testUpdateCodec() {
// MO Foreground Connection dailing -> active
mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
- mForeGroundCall, false, false);
+ mForeGroundCall, false, false, new ImsDialArgs.Builder().build());
doReturn(Call.State.ACTIVE).when(mForeGroundCall).getState();
assertTrue(mConnectionUT.updateMediaCapabilities(mImsCall));
}
@@ -195,7 +197,7 @@
@SmallTest
public void testImsUpdateStatePendingHold() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
- mForeGroundCall, false, false);
+ mForeGroundCall, false, false, new ImsDialArgs.Builder().build());
doReturn(true).when(mImsCall).isPendingHold();
assertFalse(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
verify(mForeGroundCall, times(0)).update(eq(mConnectionUT), eq(mImsCall),
@@ -240,7 +242,8 @@
@SmallTest
public void testPostDialWait() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.WAIT), mImsCT, mForeGroundCall, false, false);
+ PhoneNumberUtils.WAIT), mImsCT, mForeGroundCall, false, false,
+ new ImsDialArgs.Builder().build());
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
doAnswer(new Answer() {
@Override
@@ -263,7 +266,8 @@
@MediumTest
public void testPostDialPause() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false);
+ PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false,
+ new ImsDialArgs.Builder().build());
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
doAnswer(new Answer() {
@Override
@@ -385,7 +389,7 @@
{"12345*00000", "12346", "12346"}};
for (String[] testAddress : testAddressMappingSet) {
mConnectionUT = new ImsPhoneConnection(mImsPhone, testAddress[0], mImsCT,
- mForeGroundCall, false, false);
+ mForeGroundCall, false, false, new ImsDialArgs.Builder().build());
mConnectionUT.setIsIncoming(true);
mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, testAddress[1]);
mConnectionUT.updateAddressDisplay(mImsCall);
@@ -403,7 +407,7 @@
String updateAddress = "6789";
mConnectionUT = new ImsPhoneConnection(mImsPhone, inputAddress, mImsCT, mForeGroundCall,
- false, false);
+ false, false, new ImsDialArgs.Builder().build());
mConnectionUT.setIsIncoming(false);
mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, updateAddress);
mConnectionUT.updateAddressDisplay(mImsCall);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
index 23b6bb9..d8173a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
@@ -20,15 +20,20 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import android.os.AsyncResult;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -117,4 +122,20 @@
.KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, true);
doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
}
+
+ /**
+ * Ensure that when an operation is not supported that the correct message is returned.
+ */
+ @Test
+ @SmallTest
+ public void testOperationNotSupported() {
+ mImsPhoneMmiCode = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT);
+
+ // Emulate request not supported from the network.
+ AsyncResult ar = new AsyncResult(null, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ mImsPhoneMmiCode.getMmiErrorMessage(ar);
+ verify(mContext.getResources()).getText(
+ eq(com.android.internal.R.string.mmiErrorNotSupported));
+ }
}
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 17a63dd..b74c8af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -17,20 +17,30 @@
package com.android.internal.telephony.imsphone;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
-import static android.provider.Telephony.SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS;
import static android.telephony.CarrierConfigManager.USSD_OVER_CS_ONLY;
import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_PREFERRED;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_3G;
+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_NR;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_SMS;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO;
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyChar;
import static org.mockito.Matchers.anyInt;
@@ -65,6 +75,7 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
@@ -84,8 +95,10 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsPhone.SS;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import org.junit.After;
import org.junit.Before;
@@ -108,6 +121,7 @@
private ImsPhoneCall mBackgroundCall;
private ImsPhoneCall mRingingCall;
private Handler mTestHandler;
+ private DomainSelectionResolver mDomainSelectionResolver;
Connection mConnection;
ImsUtInterface mImsUtInterface;
@@ -133,6 +147,9 @@
mTestHandler = mock(Handler.class);
mConnection = mock(Connection.class);
mImsUtInterface = mock(ImsUtInterface.class);
+ mDomainSelectionResolver = mock(DomainSelectionResolver.class);
+ doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
mImsCT.mForegroundCall = mForegroundCall;
mImsCT.mBackgroundCall = mBackgroundCall;
@@ -176,6 +193,7 @@
public void tearDown() throws Exception {
mImsPhoneUT = null;
mBundle = null;
+ DomainSelectionResolver.setDomainSelectionResolver(null);
super.tearDown();
}
@@ -629,6 +647,21 @@
@Test
@SmallTest
+ public void testEcbmWhenDomainSelectionEnabled() {
+ DomainSelectionResolver dsResolver = mock(DomainSelectionResolver.class);
+ doReturn(true).when(dsResolver).isDomainSelectionSupported();
+ DomainSelectionResolver.setDomainSelectionResolver(dsResolver);
+
+ ImsEcbmStateListener imsEcbmStateListener = mImsPhoneUT.getImsEcbmStateListener();
+ imsEcbmStateListener.onECBMEntered();
+ imsEcbmStateListener.onECBMExited();
+
+ verify(mPhone, never()).setIsInEcm(anyBoolean());
+ verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
+ }
+
+ @Test
+ @SmallTest
public void testProcessDisconnectReason() throws Exception {
// set up CarrierConfig
mBundle.putStringArray(CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY,
@@ -734,7 +767,6 @@
@Test
@SmallTest
public void testRoamingToAirplanModeIwlanInService() throws Exception {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
doReturn(true).when(mPhone).isRadioOn();
@@ -762,7 +794,6 @@
@Test
@SmallTest
public void testRoamingToOutOfService() throws Exception {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
doReturn(true).when(mPhone).isRadioOn();
@@ -786,83 +817,6 @@
}
@Test
- @SmallTest
- public void testRoamingChangeForLteInLegacyMode() throws Exception {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
- doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
- doReturn(true).when(mPhone).isRadioOn();
-
- //roaming - data registration only on LTE
- Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
- // Inject the message synchronously instead of waiting for the thread to do it.
- mImsPhoneUT.handleMessage(m);
- m.recycle();
-
- verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));
-
- // not roaming - data registration on LTE
- m = getServiceStateChangedMessage(getServiceStateDataOnly(
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, false));
- mImsPhoneUT.handleMessage(m);
- m.recycle();
-
- verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(false));
- }
-
- @Test
- @SmallTest
- public void testDataOnlyRoamingCellToIWlanInLegacyMode() throws Exception {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
- doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
- doReturn(true).when(mPhone).isRadioOn();
-
- //roaming - data registration only on LTE
- Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
- // Inject the message synchronously instead of waiting for the thread to do it.
- mImsPhoneUT.handleMessage(m);
- m.recycle();
-
- verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));
-
- // not roaming - data registration onto IWLAN
- m = getServiceStateChangedMessage(getServiceStateDataOnly(
- ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.STATE_IN_SERVICE, false));
- mImsPhoneUT.handleMessage(m);
- m.recycle();
-
- // Verify that it hasn't been called again.
- verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
- }
-
- @Test
- @SmallTest
- public void testCellVoiceDataChangeToWlanInLegacyMode() throws Exception {
- doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
- doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
- doReturn(true).when(mPhone).isRadioOn();
-
- //roaming - voice/data registration on LTE
- ServiceState ss = getServiceStateDataAndVoice(
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true);
- Message m = getServiceStateChangedMessage(ss);
- // Inject the message synchronously instead of waiting for the thread to do it.
- mImsPhoneUT.handleMessage(m);
-
- verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));
-
- // roaming - voice LTE, data registration onto IWLAN
- modifyServiceStateData(ss, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
- ServiceState.STATE_IN_SERVICE, false);
- mImsPhoneUT.handleMessage(m);
- m.recycle();
-
- // Verify that it hasn't been called again.
- verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
- }
-
- @Test
public void testNonNullTrackersInImsPhone() throws Exception {
assertNotNull(mImsPhoneUT.getEmergencyNumberTracker());
assertNotNull(mImsPhoneUT.getServiceStateTracker());
@@ -881,6 +835,9 @@
mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
verify(mImsCT).sendUSSD(eq("*135#"), any());
+
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ verify(mImsCT).sendUSSD(eq("91"), any());
}
@Test
@@ -901,6 +858,13 @@
errorCode = e.getMessage();
}
assertEquals(Phone.CS_FALLBACK, errorCode);
+
+ try {
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ } catch (CallStateException e) {
+ errorCode = e.getMessage();
+ }
+ assertEquals(Phone.CS_FALLBACK, errorCode);
}
@Test
@@ -918,11 +882,18 @@
errorCode = e.getMessage();
}
assertEquals(Phone.CS_FALLBACK, errorCode);
+
+ try {
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ } catch (CallStateException e) {
+ errorCode = e.getMessage();
+ }
+ assertEquals(Phone.CS_FALLBACK, errorCode);
}
@Test
@SmallTest
- public void testSendUssdAllowUssdOverImswithIMSPreferred() throws Exception {
+ public void testSendUssdAllowUssdOverImsWithImsPreferred() throws Exception {
Resources resources = mContext.getResources();
mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
@@ -933,11 +904,14 @@
mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
verify(mImsCT).sendUSSD(eq("*135#"), any());
+
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ verify(mImsCT).sendUSSD(eq("91"), any());
}
@Test
@SmallTest
- public void testSendUssdAllowUssdOverImswithCSOnly() throws Exception {
+ public void testSendUssdAllowUssdOverImsWithCsOnly() throws Exception {
String errorCode = "";
Resources resources = mContext.getResources();
@@ -953,11 +927,18 @@
errorCode = e.getMessage();
}
assertEquals(Phone.CS_FALLBACK, errorCode);
+
+ try {
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ } catch (CallStateException e) {
+ errorCode = e.getMessage();
+ }
+ assertEquals(Phone.CS_FALLBACK, errorCode);
}
@Test
@SmallTest
- public void testSendUssdAllowUssdOverImswithIMSOnly() throws Exception {
+ public void testSendUssdAllowUssdOverImsWithImsOnly() throws Exception {
Resources resources = mContext.getResources();
mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
@@ -968,6 +949,9 @@
mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
verify(mImsCT).sendUSSD(eq("*135#"), any());
+
+ mImsPhoneUT.dial("91", new ImsPhone.ImsDialArgs.Builder().build());
+ verify(mImsCT).sendUSSD(eq("91"), any());
}
@Test
@@ -986,7 +970,9 @@
doReturn(subId).when(mPhone).getSubId();
SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
doReturn("gb").when(subInfo).getCountryIso();
- doReturn(subInfo).when(mSubscriptionController).getSubscriptionInfo(subId);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(subId).setSimSlotIndex(0)
+ .setCountryIso("gb").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(subId);
// 1. Two valid phone number; 1st is set.
Uri[] associatedUris = new Uri[] {
@@ -995,8 +981,7 @@
};
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController).setSubscriptionProperty(
- subId, COLUMN_PHONE_NUMBER_SOURCE_IMS, "+447539447777");
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "+447539447777");
// 2. 1st invalid and 2nd valid: 2nd is set.
associatedUris = new Uri[] {
@@ -1005,8 +990,7 @@
};
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController).setSubscriptionProperty(
- subId, COLUMN_PHONE_NUMBER_SOURCE_IMS, "+447539446666");
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "+447539446666");
// 3. 1st sip-uri is not phone number and 2nd valid: 2nd is set.
associatedUris = new Uri[] {
@@ -1016,8 +1000,7 @@
};
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController).setSubscriptionProperty(
- subId, COLUMN_PHONE_NUMBER_SOURCE_IMS, "+447539446677");
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "+447539446677");
// Clean up
mContextFixture.addCallingOrSelfPermission("");
@@ -1032,7 +1015,9 @@
doReturn(subId).when(mPhone).getSubId();
SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
doReturn("gb").when(subInfo).getCountryIso();
- doReturn(subInfo).when(mSubscriptionController).getSubscriptionInfo(subId);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(subId).setSimSlotIndex(0)
+ .setCountryIso("gb").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(0);
// 1. No valid phone number; do not set
Uri[] associatedUris = new Uri[] {
@@ -1041,33 +1026,367 @@
};
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController, never()).setSubscriptionProperty(
- anyInt(), any(), any());
+ verify(mSubscriptionManagerService, never()).setNumberFromIms(anyInt(), anyString());
// 2. no URI; do not set
associatedUris = new Uri[] {};
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController, never()).setSubscriptionProperty(
- anyInt(), any(), any());
+ verify(mSubscriptionManagerService, never()).setNumberFromIms(anyInt(), anyString());
// 3. null URI; do not set
associatedUris = new Uri[] { null };
mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
- verify(mSubscriptionController, never()).setSubscriptionProperty(
- anyInt(), any(), any());
+ verify(mSubscriptionManagerService, never()).setNumberFromIms(anyInt(), anyString());
// 4. null pointer; do not set
mImsPhoneUT.setPhoneNumberForSourceIms(null);
- verify(mSubscriptionController, never()).setSubscriptionProperty(
- anyInt(), any(), any());
+ verify(mSubscriptionManagerService, never()).setNumberFromIms(anyInt(), anyString());
// Clean up
mContextFixture.addCallingOrSelfPermission("");
}
+ /**
+ * Verifies that valid radio technology is passed to RIL
+ * when IMS registration state changes to registered.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateImsRegistrationInfoRadioTech() {
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+
+ int[] regInfo = mSimulatedCommands.getImsRegistrationInfo();
+ assertNotNull(regInfo);
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ RegistrationManager.RegistrationCallback registrationCallback =
+ mImsPhoneUT.getImsMmTelRegistrationCallback();
+
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ REGISTRATION_TECH_LTE).build();
+ registrationCallback.onRegistered(attr);
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_VOICE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VOICE);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same radio technology
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_LTE).build();
+ registrationCallback.onRegistered(attr);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // radio technology changed
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NR).build();
+ registrationCallback.onRegistered(attr);
+
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_NR
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VOICE);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same radio technology
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NR).build();
+ registrationCallback.onRegistered(attr);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // radio technology changed
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_IWLAN).build();
+ registrationCallback.onRegistered(attr);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_IWLAN
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VOICE);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same radio technology
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_IWLAN).build();
+ registrationCallback.onRegistered(attr);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // radio technology changed
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_3G).build();
+ registrationCallback.onRegistered(attr);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_3G
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VOICE);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same radio technology
+ attr = new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_3G).build();
+ registrationCallback.onRegistered(attr);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+ }
+
+ /**
+ * Verifies that valid capabilities is passed to RIL
+ * when IMS registration state changes to registered.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateImsRegistrationInfoCapabilities() {
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+
+ int[] regInfo = mSimulatedCommands.getImsRegistrationInfo();
+ assertNotNull(regInfo);
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ RegistrationManager.RegistrationCallback registrationCallback =
+ mImsPhoneUT.getImsMmTelRegistrationCallback();
+
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ REGISTRATION_TECH_LTE).build();
+ registrationCallback.onRegistered(attr);
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_VOICE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VOICE);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same capability
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_VOICE);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // capability changed
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_VIDEO);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_VIDEO);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same capability
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_VIDEO);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // capability changed
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_SMS);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[3] == IMS_MMTEL_CAPABILITY_SMS);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // duplicated notification with the same capability
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_SMS);
+
+ // verify that there is no change in SimulatedCommands
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+
+ // capability changed, but no capability
+ mImsPhoneUT.updateImsRegistrationInfo(IMS_MMTEL_CAPABILITY_SMS);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ // verify that there is no change in SimulatedCommands.
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[3] == 0);
+ }
+
+ /**
+ * Verifies that valid state and reason is passed to RIL
+ * when IMS registration state changes to unregistered.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateImsRegistrationInfo() {
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+
+ int[] regInfo = mSimulatedCommands.getImsRegistrationInfo();
+ assertNotNull(regInfo);
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ RegistrationManager.RegistrationCallback registrationCallback =
+ mImsPhoneUT.getImsMmTelRegistrationCallback();
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR,
+ ImsReasonInfo.CODE_UNSPECIFIED, "");
+
+ // unregistered with fatal error
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_NR);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_NR
+ && regInfo[2] == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ // unregistered with fatal error but rat changed
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_LTE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[2] == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ // duplicated notification with the same suggested action
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_LTE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ // verify that there is no update in the SimulatedCommands
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ // unregistered with repeated error
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+ REGISTRATION_TECH_LTE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[2] == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT);
+
+ // reset the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ // duplicated notification with the same suggested action
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+ REGISTRATION_TECH_LTE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ // verify that there is no update in the SimulatedCommands
+ assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+ // unregistered with temporary error
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_NONE, REGISTRATION_TECH_LTE);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_LTE
+ && regInfo[2] == SUGGESTED_ACTION_NONE);
+
+ // verifies that reason codes except ImsReasonInfo.CODE_REGISTRATION_ERROR are discarded.
+ reasonInfo = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE,
+ ImsReasonInfo.CODE_UNSPECIFIED, "");
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_NR);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+ && regInfo[1] == REGISTRATION_TECH_NR
+ && regInfo[2] == SUGGESTED_ACTION_NONE);
+
+ // change the registration info saved in the SimulatedCommands
+ mSimulatedCommands.updateImsRegistrationInfo(1, 1, 1, 1, null);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ assertTrue(regInfo[0] == 1 && regInfo[1] == 1 && regInfo[2] == 1);
+
+ // duplicated notification with the same suggested action
+ registrationCallback.onUnregistered(reasonInfo,
+ SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NR);
+ regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+ // verify that there is no update in the SimulatedCommands
+ assertTrue(regInfo[0] == 1 && regInfo[1] == 1 && regInfo[2] == 1);
+ }
+
+ @Test
+ @SmallTest
+ public void testImsDialArgsBuilderFromForAlternateService() {
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIsEmergency(true)
+ .setEccCategory(2)
+ .build();
+
+ ImsPhone.ImsDialArgs copiedDialArgs =
+ ImsPhone.ImsDialArgs.Builder.from(dialArgs).build();
+
+ assertTrue(copiedDialArgs.isEmergency);
+ assertEquals(2, copiedDialArgs.eccCategory);
+ }
+
private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
ServiceState ss = new ServiceState();
ss.setStateOutOfService();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelperTest.java
index cce0646..93fbfdc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelperTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelperTest.java
@@ -16,10 +16,16 @@
package com.android.internal.telephony.imsphone;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -27,8 +33,10 @@
import android.net.Uri;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.RegistrationManager.RegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.TelephonyTest;
@@ -121,11 +129,13 @@
// When onRegistered is called, the registration state should be
// REGISTRATION_STATE_REGISTERED
- callback.onRegistered(AccessNetworkType.IWLAN);
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).build();
+ callback.onRegistered(attr);
assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED,
mRegistrationCallbackHelper.getImsRegistrationState());
- verify(mMockRegistrationUpdate).handleImsRegistered(anyInt());
+ verify(mMockRegistrationUpdate).handleImsRegistered(attr);
}
@Test
@@ -158,7 +168,25 @@
// The registration state should be REGISTRATION_STATE_NOT_REGISTERED
assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED,
mRegistrationCallbackHelper.getImsRegistrationState());
- verify(mMockRegistrationUpdate).handleImsUnregistered(reasonInfo);
+ verify(mMockRegistrationUpdate).handleImsUnregistered(eq(reasonInfo),
+ eq(SUGGESTED_ACTION_NONE), eq(REGISTRATION_TECH_NONE));
+ }
+
+ @Test
+ @SmallTest
+ public void testImsUnRegisteredWithSuggestedAction() {
+ // Verify the RegistrationCallback should not be null
+ RegistrationCallback callback = mRegistrationCallbackHelper.getCallback();
+ assertNotNull(callback);
+
+ ImsReasonInfo reasonInfo = new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0);
+ callback.onUnregistered(reasonInfo, SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
+ REGISTRATION_TECH_LTE);
+
+ assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED,
+ mRegistrationCallbackHelper.getImsRegistrationState());
+ verify(mMockRegistrationUpdate).handleImsUnregistered(eq(reasonInfo),
+ eq(SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK), eq(REGISTRATION_TECH_LTE));
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
index 8b2fe92..504782b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
@@ -869,4 +869,40 @@
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, mImsStats.getImsVoiceRadioTech());
}
+
+ @Test
+ @SmallTest
+ public void getImsVoiceRadioTech_serviceStateChanged() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+ doReturn(
+ new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+ mImsStats.onServiceStateChanged(mServiceState);
+ assertEquals(TelephonyManager.NETWORK_TYPE_NR, mImsStats.getImsVoiceRadioTech());
+ }
+
+ @Test
+ @SmallTest
+ public void getImsVoiceRadioTech_serviceStateChanged_wlan() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+ doReturn(
+ new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+ mImsStats.onServiceStateChanged(mServiceState);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, mImsStats.getImsVoiceRadioTech());
+ }
}
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 a4e2574..d4e1b86 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -18,6 +18,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.OUTGOING_SHORT_CODE_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -43,6 +44,7 @@
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -102,7 +104,7 @@
mActivePort = mock(UiccPort.class);
mServiceStateStats = mock(ServiceStateStats.class);
mMetricsCollector =
- new MetricsCollector(mContext, mPersistAtomsStorage);
+ new MetricsCollector(mContext, mPersistAtomsStorage, mDeviceStateHelper);
doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
}
@@ -412,4 +414,45 @@
assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
// TODO(b/153196254): verify atom contents
}
+
+ @Test
+ public void onPullAtom_outgoingShortCodeSms_empty() {
+ doReturn(new OutgoingShortCodeSms[0]).when(mPersistAtomsStorage)
+ .getOutgoingShortCodeSms(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ }
+
+ @Test
+ public void onPullAtom_outgoingShortCodeSms_tooFrequent() {
+ doReturn(null).when(mPersistAtomsStorage).getOutgoingShortCodeSms(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+ verify(mPersistAtomsStorage, times(1))
+ .getOutgoingShortCodeSms(eq(MIN_COOLDOWN_MILLIS));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onPullAtom_outgoingShortCodeSms_multipleSms() {
+ OutgoingShortCodeSms outgoingShortCodeSms = new OutgoingShortCodeSms();
+ doReturn(new OutgoingShortCodeSms[] {outgoingShortCodeSms, outgoingShortCodeSms,
+ outgoingShortCodeSms, outgoingShortCodeSms})
+ .when(mPersistAtomsStorage)
+ .getOutgoingShortCodeSms(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(4);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
index 3d5cf481..dc9be1c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
@@ -19,6 +19,7 @@
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_IMS;
import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_UICC;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A;
import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_B;
@@ -42,8 +43,9 @@
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.UiccSlot;
import org.junit.After;
@@ -81,17 +83,20 @@
doReturn(1).when(mPhone).getSubId();
doReturn(100).when(mPhone).getCarrierId();
doReturn("6506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_UICC, null, null);
doReturn("")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER, null, null);
doReturn("+16506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS, null, null);
SubscriptionInfo subscriptionInfo1 = mock(SubscriptionInfo.class);
doReturn("us").when(subscriptionInfo1).getCountryIso();
- doReturn(subscriptionInfo1).when(mSubscriptionController).getSubscriptionInfo(1);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).setSimSlotIndex(0)
+ .setCountryIso("us").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(1);
+
ImsManager imsManager = mContext.getSystemService(ImsManager.class);
ImsMmTelManager imsMmTelManager1 = mock(ImsMmTelManager.class);
doReturn(imsMmTelManager1).when(imsManager).getImsMmTelManager(1);
@@ -116,23 +121,28 @@
UiccSlot uiccSlot1 = mock(UiccSlot.class);
doReturn(UiccSlot.VOLTAGE_CLASS_A).when(uiccSlot1).getMinimumVoltageClass();
doReturn(uiccSlot1).when(mUiccController).getUiccSlotForPhone(0);
+ doReturn(NETWORK_TYPE_BITMASK_GSM).when(mPersistAtomsStorage).getUnmeteredNetworks(0, 100);
+ doReturn(false).when(mTelephonyManager).isVoNrEnabled();
// phone 1 setup
doReturn(mContext).when(mSecondPhone).getContext();
doReturn(1).when(mSecondPhone).getPhoneId();
doReturn(2).when(mSecondPhone).getSubId();
doReturn(101).when(mSecondPhone).getCarrierId();
doReturn("0123")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(2, PHONE_NUMBER_SOURCE_UICC, null, null);
doReturn("16506950123")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(2, PHONE_NUMBER_SOURCE_CARRIER, null, null);
doReturn("+16506950123")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(2, PHONE_NUMBER_SOURCE_IMS, null, null);
SubscriptionInfo subscriptionInfo2 = mock(SubscriptionInfo.class);
doReturn("us").when(subscriptionInfo2).getCountryIso();
- doReturn(subscriptionInfo2).when(mSubscriptionController).getSubscriptionInfo(2);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(2).setSimSlotIndex(1)
+ .setCountryIso("us").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(2);
+
ImsMmTelManager imsMmTelManager2 = mock(ImsMmTelManager.class);
doReturn(imsMmTelManager2).when(imsManager).getImsMmTelManager(2);
doReturn(true).when(imsMmTelManager2).isAdvancedCallingSettingEnabled();
@@ -156,6 +166,7 @@
UiccSlot uiccSlot2 = mock(UiccSlot.class);
doReturn(UiccSlot.VOLTAGE_CLASS_B).when(uiccSlot2).getMinimumVoltageClass();
doReturn(uiccSlot2).when(mUiccController).getUiccSlotForPhone(1);
+ doReturn(NETWORK_TYPE_BITMASK_GSM).when(mPersistAtomsStorage).getUnmeteredNetworks(1, 101);
PerSimStatus perSimStatus1 = PerSimStatus.getCurrentState(mPhone);
PerSimStatus perSimStatus2 = PerSimStatus.getCurrentState(mSecondPhone);
@@ -178,6 +189,8 @@
assertEquals(
PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A,
perSimStatus1.minimumVoltageClass);
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus1.unmeteredNetworks);
+ assertEquals(false, perSimStatus1.vonrEnabled);
assertEquals(101, perSimStatus2.carrierId);
assertEquals(1, perSimStatus2.phoneNumberSourceUicc);
assertEquals(2, perSimStatus2.phoneNumberSourceCarrier);
@@ -195,12 +208,14 @@
assertEquals(
PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_B,
perSimStatus2.minimumVoltageClass);
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus2.unmeteredNetworks);
+ assertEquals(false, perSimStatus2.vonrEnabled);
}
@Test
@SmallTest
- public void onPullAtom_perSimStatus_noSubscriptionController() throws Exception {
- replaceInstance(SubscriptionController.class, "sInstance", null, null);
+ public void onPullAtom_perSimStatus_noSubscriptionManagerService() throws Exception {
+ replaceInstance(SubscriptionManagerService.class, "sInstance", null, null);
PerSimStatus perSimStatus = PerSimStatus.getCurrentState(mPhone);
@@ -214,17 +229,19 @@
doReturn(1).when(mPhone).getSubId();
doReturn(100).when(mPhone).getCarrierId();
doReturn("6506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_UICC, null, null);
doReturn("")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER, null, null);
doReturn("+16506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS, null, null);
SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
doReturn("us").when(subscriptionInfo).getCountryIso();
- doReturn(subscriptionInfo).when(mSubscriptionController).getSubscriptionInfo(1);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).setSimSlotIndex(0)
+ .setCountryIso("us").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(1);
doReturn(null).when(mContext).getSystemService(ImsManager.class);
doReturn(1L)
.when(mPhone)
@@ -239,6 +256,8 @@
UiccSlot uiccSlot1 = mock(UiccSlot.class);
doReturn(UiccSlot.VOLTAGE_CLASS_A).when(uiccSlot1).getMinimumVoltageClass();
doReturn(uiccSlot1).when(mUiccController).getUiccSlotForPhone(0);
+ doReturn(NETWORK_TYPE_BITMASK_GSM).when(mPersistAtomsStorage).getUnmeteredNetworks(0, 100);
+ doReturn(true).when(mTelephonyManager).isVoNrEnabled();
PerSimStatus perSimStatus = PerSimStatus.getCurrentState(mPhone);
@@ -258,6 +277,8 @@
assertEquals(
PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A,
perSimStatus.minimumVoltageClass);
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
+ assertEquals(true, perSimStatus.vonrEnabled);
}
@Test
@@ -267,17 +288,19 @@
doReturn(1).when(mPhone).getSubId();
doReturn(100).when(mPhone).getCarrierId();
doReturn("6506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_UICC, null, null);
doReturn("")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER, null, null);
doReturn("+16506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS, null, null);
SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
doReturn("us").when(subscriptionInfo).getCountryIso();
- doReturn(subscriptionInfo).when(mSubscriptionController).getSubscriptionInfo(1);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).setSimSlotIndex(0)
+ .setCountryIso("us").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(1);
ImsManager imsManager = mContext.getSystemService(ImsManager.class);
doThrow(new IllegalArgumentException()).when(imsManager).getImsMmTelManager(1);
doReturn(false).when(mPhone).getDataRoamingEnabled();
@@ -294,6 +317,8 @@
UiccSlot uiccSlot1 = mock(UiccSlot.class);
doReturn(UiccSlot.VOLTAGE_CLASS_A).when(uiccSlot1).getMinimumVoltageClass();
doReturn(uiccSlot1).when(mUiccController).getUiccSlotForPhone(0);
+ doReturn(NETWORK_TYPE_BITMASK_GSM).when(mPersistAtomsStorage).getUnmeteredNetworks(0, 100);
+ doReturn(true).when(mTelephonyManager).isVoNrEnabled();
PerSimStatus perSimStatus = PerSimStatus.getCurrentState(mPhone);
@@ -313,6 +338,8 @@
assertEquals(
PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A,
perSimStatus.minimumVoltageClass);
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
+ assertEquals(true, perSimStatus.vonrEnabled);
}
@Test
@@ -322,17 +349,19 @@
doReturn(1).when(mPhone).getSubId();
doReturn(100).when(mPhone).getCarrierId();
doReturn("6506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_UICC, null, null);
doReturn("")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_CARRIER, null, null);
doReturn("+16506953210")
- .when(mSubscriptionController)
+ .when(mSubscriptionManagerService)
.getPhoneNumber(1, PHONE_NUMBER_SOURCE_IMS, null, null);
SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
doReturn("us").when(subscriptionInfo).getCountryIso();
- doReturn(subscriptionInfo).when(mSubscriptionController).getSubscriptionInfo(1);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1).setSimSlotIndex(0)
+ .setCountryIso("us").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(1);
doReturn(null).when(mContext).getSystemService(ImsManager.class);
doReturn(1L)
.when(mPhone)
@@ -345,6 +374,8 @@
doReturn(true).when(iccCard).getIccLockEnabled();
doReturn(iccCard).when(mPhone).getIccCard();
doReturn(null).when(mUiccController).getUiccSlotForPhone(0);
+ doReturn(NETWORK_TYPE_BITMASK_GSM).when(mPersistAtomsStorage).getUnmeteredNetworks(0, 100);
+ doReturn(true).when(mTelephonyManager).isVoNrEnabled();
PerSimStatus perSimStatus = PerSimStatus.getCurrentState(mPhone);
@@ -364,5 +395,7 @@
assertEquals(
PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_UNKNOWN,
perSimStatus.minimumVoltageClass);
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
+ assertEquals(true, perSimStatus.vonrEnabled);
}
}
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 d2e30db..3307813 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -16,7 +16,10 @@
package com.android.internal.telephony.metrics;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__UNKNOWN;
@@ -28,10 +31,8 @@
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__DIRECTION__CALL_DIRECTION_MO;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -48,6 +49,7 @@
import android.content.Context;
import android.os.Build;
import android.telephony.DisconnectCause;
+import android.telephony.SatelliteProtoEnums;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyProtoEnums;
@@ -67,10 +69,17 @@
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
@@ -139,7 +148,7 @@
private CellularDataServiceSwitch mServiceSwitch1Proto;
private CellularDataServiceSwitch mServiceSwitch2Proto;
- // service states for slot 0 and 1
+ // Service states for slot 0 and 1
private CellularServiceState mServiceState1Proto;
private CellularServiceState mServiceState2Proto;
private CellularServiceState mServiceState3Proto;
@@ -223,14 +232,40 @@
private SipTransportSession mSipTransportSession2;
private SipTransportSession[] mSipTransportSession;
+ private OutgoingShortCodeSms mOutgoingShortCodeSms1;
+ private OutgoingShortCodeSms mOutgoingShortCodeSms2;
+ private OutgoingShortCodeSms[] mOutgoingShortCodeSms;
+
+ private SatelliteController mSatelliteController1;
+ private SatelliteController mSatelliteController2;
+ private SatelliteController[] mSatelliteControllers;
+
+ private SatelliteSession mSatelliteSession1;
+ private SatelliteSession mSatelliteSession2;
+ private SatelliteSession[] mSatelliteSessions;
+
+ private SatelliteIncomingDatagram mSatelliteIncomingDatagram1;
+ private SatelliteIncomingDatagram mSatelliteIncomingDatagram2;
+ private SatelliteIncomingDatagram[] mSatelliteIncomingDatagrams;
+
+ private SatelliteOutgoingDatagram mSatelliteOutgoingDatagram1;
+ private SatelliteOutgoingDatagram mSatelliteOutgoingDatagram2;
+ private SatelliteOutgoingDatagram[] mSatelliteOutgoingDatagrams;
+
+ private SatelliteProvision mSatelliteProvision1;
+ private SatelliteProvision mSatelliteProvision2;
+ private SatelliteProvision[] mSatelliteProvisions;
+
+ private SatelliteSosMessageRecommender mSatelliteSosMessageRecommender1;
+ private SatelliteSosMessageRecommender mSatelliteSosMessageRecommender2;
+ private SatelliteSosMessageRecommender[] mSatelliteSosMessageRecommenders;
+
private void makeTestData() {
// MO call with SRVCC (LTE to UMTS)
mCall1Proto = new VoiceCallSession();
mCall1Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
mCall1Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
mCall1Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
- mCall1Proto.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
mCall1Proto.setupFailed = false;
mCall1Proto.disconnectReasonCode = DisconnectCause.LOCAL;
mCall1Proto.disconnectExtraCode = 0;
@@ -259,8 +294,6 @@
mCall2Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
mCall2Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
mCall2Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
- mCall2Proto.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
mCall2Proto.setupFailed = false;
mCall2Proto.disconnectReasonCode = ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE;
mCall2Proto.disconnectExtraCode = 0;
@@ -288,8 +321,6 @@
mCall3Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
mCall3Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
mCall3Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
- mCall3Proto.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
mCall3Proto.setupFailed = false;
mCall3Proto.disconnectReasonCode = ImsReasonInfo.CODE_USER_TERMINATED;
mCall3Proto.disconnectExtraCode = 0;
@@ -316,8 +347,6 @@
mCall4Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
mCall4Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
mCall4Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
- mCall4Proto.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
mCall4Proto.setupFailed = true;
mCall4Proto.disconnectReasonCode = DisconnectCause.NORMAL;
mCall4Proto.disconnectExtraCode = 0;
@@ -552,6 +581,8 @@
mDataCallSession0.setupFailed = false;
mDataCallSession0.durationMinutes = 20;
mDataCallSession0.ongoing = true;
+ mDataCallSession0.handoverFailureCauses = new int[]{3, 2, 1};
+ mDataCallSession0.handoverFailureRat = new int[]{5, 5, 6};
mDataCallSession1 = new DataCallSession();
mDataCallSession1.dimension = 222;
@@ -864,6 +895,158 @@
mSipTransportSession =
new SipTransportSession[] {mSipTransportSession1, mSipTransportSession2};
+
+ mOutgoingShortCodeSms1 = new OutgoingShortCodeSms();
+ mOutgoingShortCodeSms1.category = 1;
+ mOutgoingShortCodeSms1.xmlVersion = 30;
+ mOutgoingShortCodeSms1.shortCodeSmsCount = 1;
+
+ mOutgoingShortCodeSms2 = new OutgoingShortCodeSms();
+ mOutgoingShortCodeSms2.category = 2;
+ mOutgoingShortCodeSms2.xmlVersion = 31;
+ mOutgoingShortCodeSms2.shortCodeSmsCount = 1;
+
+ mOutgoingShortCodeSms = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1,
+ mOutgoingShortCodeSms2};
+
+ generateTestSatelliteData();
+ }
+
+ private void generateTestSatelliteData() {
+ mSatelliteController1 = new SatelliteController();
+ mSatelliteController1.countOfSatelliteServiceEnablementsSuccess = 2;
+ mSatelliteController1.countOfSatelliteServiceEnablementsFail = 0;
+ mSatelliteController1.countOfOutgoingDatagramSuccess = 8;
+ mSatelliteController1.countOfOutgoingDatagramFail = 9;
+ mSatelliteController1.countOfIncomingDatagramSuccess = 10;
+ mSatelliteController1.countOfIncomingDatagramFail = 11;
+ mSatelliteController1.countOfDatagramTypeSosSmsSuccess = 5;
+ mSatelliteController1.countOfDatagramTypeSosSmsFail = 5;
+ mSatelliteController1.countOfDatagramTypeLocationSharingSuccess = 6;
+ mSatelliteController1.countOfDatagramTypeLocationSharingFail = 6;
+ mSatelliteController1.countOfProvisionSuccess = 3;
+ mSatelliteController1.countOfProvisionFail = 4;
+ mSatelliteController1.countOfDeprovisionSuccess = 5;
+ mSatelliteController1.countOfDeprovisionFail = 6;
+ mSatelliteController1.totalServiceUptimeSec = 60 * 60 * 24 * 7;
+ mSatelliteController1.totalBatteryConsumptionPercent = 7;
+ mSatelliteController1.totalBatteryChargedTimeSec = 60 * 60 * 3 * 1;
+
+ mSatelliteController2 = new SatelliteController();
+ mSatelliteController2.countOfSatelliteServiceEnablementsSuccess = 2 + 1;
+ mSatelliteController2.countOfSatelliteServiceEnablementsFail = 0 + 1;
+ mSatelliteController2.countOfOutgoingDatagramSuccess = 8 + 1;
+ mSatelliteController2.countOfOutgoingDatagramFail = 9 + 1;
+ mSatelliteController2.countOfIncomingDatagramSuccess = 10 + 1;
+ mSatelliteController2.countOfIncomingDatagramFail = 11 + 1;
+ mSatelliteController2.countOfDatagramTypeSosSmsSuccess = 5 + 1;
+ mSatelliteController2.countOfDatagramTypeSosSmsFail = 5 + 1;
+ mSatelliteController2.countOfDatagramTypeLocationSharingSuccess = 6 + 1;
+ mSatelliteController2.countOfDatagramTypeLocationSharingFail = 6 + 1;
+ mSatelliteController2.countOfProvisionSuccess = 13;
+ mSatelliteController2.countOfProvisionFail = 14;
+ mSatelliteController2.countOfDeprovisionSuccess = 15;
+ mSatelliteController2.countOfDeprovisionFail = 16;
+ mSatelliteController2.totalServiceUptimeSec = 60 * 60 * 12;
+ mSatelliteController2.totalBatteryConsumptionPercent = 14;
+ mSatelliteController1.totalBatteryChargedTimeSec = 60 * 60 * 3;
+
+ // SatelliteController atom has one data point
+ mSatelliteControllers =
+ new SatelliteController[] {
+ mSatelliteController1
+ };
+
+ mSatelliteSession1 = new SatelliteSession();
+ mSatelliteSession1.satelliteServiceInitializationResult =
+ SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+ mSatelliteSession1.satelliteTechnology =
+ SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_PROPRIETARY;
+ mSatelliteSession1.count = 1;
+
+ mSatelliteSession2 = new SatelliteSession();
+ mSatelliteSession2.satelliteServiceInitializationResult =
+ SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+ mSatelliteSession2.satelliteTechnology =
+ SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
+ mSatelliteSession2.count = 1;
+
+ mSatelliteSessions =
+ new SatelliteSession[] {
+ mSatelliteSession1, mSatelliteSession2
+ };
+
+ mSatelliteIncomingDatagram1 = new SatelliteIncomingDatagram();
+ mSatelliteIncomingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+ mSatelliteIncomingDatagram1.datagramSizeBytes = 1 * 1024;
+ mSatelliteIncomingDatagram1.datagramTransferTimeMillis = 3 * 1000;
+
+ mSatelliteIncomingDatagram2 = new SatelliteIncomingDatagram();
+ mSatelliteIncomingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+ mSatelliteIncomingDatagram2.datagramSizeBytes = 512;
+ mSatelliteIncomingDatagram2.datagramTransferTimeMillis = 1 * 1000;
+
+ mSatelliteIncomingDatagrams =
+ new SatelliteIncomingDatagram[] {
+ mSatelliteIncomingDatagram1, mSatelliteIncomingDatagram2
+ };
+
+ mSatelliteOutgoingDatagram1 = new SatelliteOutgoingDatagram();
+ mSatelliteOutgoingDatagram1.datagramType =
+ SatelliteProtoEnums.DATAGRAM_TYPE_LOCATION_SHARING;
+ mSatelliteOutgoingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+ mSatelliteOutgoingDatagram1.datagramSizeBytes = 1 * 1024;
+ mSatelliteOutgoingDatagram1.datagramTransferTimeMillis = 3 * 1000;
+
+ mSatelliteOutgoingDatagram2 = new SatelliteOutgoingDatagram();
+ mSatelliteOutgoingDatagram2.datagramType =
+ SatelliteProtoEnums.DATAGRAM_TYPE_SOS_MESSAGE;
+ mSatelliteOutgoingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+ mSatelliteOutgoingDatagram2.datagramSizeBytes = 512;
+ mSatelliteOutgoingDatagram2.datagramTransferTimeMillis = 1 * 1000;
+
+ mSatelliteOutgoingDatagrams =
+ new SatelliteOutgoingDatagram[] {
+ mSatelliteOutgoingDatagram1, mSatelliteOutgoingDatagram2
+ };
+
+ mSatelliteProvision1 = new SatelliteProvision();
+ mSatelliteProvision1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+ mSatelliteProvision1.provisioningTimeSec = 3 * 60;
+ mSatelliteProvision1.isProvisionRequest = true;
+ mSatelliteProvision1.isCanceled = false;
+
+ mSatelliteProvision2 = new SatelliteProvision();
+ mSatelliteProvision2.resultCode = SatelliteProtoEnums.SATELLITE_SERVICE_NOT_PROVISIONED;
+ mSatelliteProvision2.provisioningTimeSec = 0;
+ mSatelliteProvision2.isProvisionRequest = false;
+ mSatelliteProvision2.isCanceled = true;
+
+ mSatelliteProvisions =
+ new SatelliteProvision[] {
+ mSatelliteProvision1, mSatelliteProvision2
+ };
+
+ mSatelliteSosMessageRecommender1 = new SatelliteSosMessageRecommender();
+ mSatelliteSosMessageRecommender1.isDisplaySosMessageSent = true;
+ mSatelliteSosMessageRecommender1.countOfTimerStarted = 5;
+ mSatelliteSosMessageRecommender1.isImsRegistered = false;
+ mSatelliteSosMessageRecommender1.cellularServiceState =
+ TelephonyProtoEnums.SERVICE_STATE_OUT_OF_SERVICE;
+ mSatelliteSosMessageRecommender1.count = 1;
+
+ mSatelliteSosMessageRecommender2 = new SatelliteSosMessageRecommender();
+ mSatelliteSosMessageRecommender2.isDisplaySosMessageSent = false;
+ mSatelliteSosMessageRecommender2.countOfTimerStarted = 3;
+ mSatelliteSosMessageRecommender2.isImsRegistered = true;
+ mSatelliteSosMessageRecommender2.cellularServiceState =
+ TelephonyProtoEnums.SERVICE_STATE_POWER_OFF;
+ mSatelliteSosMessageRecommender2.count = 1;
+
+ mSatelliteSosMessageRecommenders =
+ new SatelliteSosMessageRecommender[] {
+ mSatelliteSosMessageRecommender1, mSatelliteSosMessageRecommender2
+ };
}
private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
@@ -1002,6 +1185,27 @@
mSipTransportSession1 = null;
mSipTransportSession2 = null;
mSipTransportSession = null;
+ mOutgoingShortCodeSms1 = null;
+ mOutgoingShortCodeSms2 = null;
+ mOutgoingShortCodeSms = null;
+ mSatelliteController1 = null;
+ mSatelliteController2 = null;
+ mSatelliteControllers = null;
+ mSatelliteSession1 = null;
+ mSatelliteSession2 = null;
+ mSatelliteSessions = null;
+ mSatelliteIncomingDatagram1 = null;
+ mSatelliteIncomingDatagram2 = null;
+ mSatelliteIncomingDatagrams = null;
+ mSatelliteOutgoingDatagram1 = null;
+ mSatelliteOutgoingDatagram2 = null;
+ mSatelliteOutgoingDatagrams = null;
+ mSatelliteProvision1 = null;
+ mSatelliteProvision2 = null;
+ mSatelliteProvisions = null;
+ mSatelliteSosMessageRecommender1 = null;
+ mSatelliteSosMessageRecommender2 = null;
+ mSatelliteSosMessageRecommenders = null;
super.tearDown();
}
@@ -1231,7 +1435,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L);
- // should be denied
+ // Should be denied
assertNull(voiceCallRatUsage);
}
@@ -1249,7 +1453,7 @@
mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis;
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage1);
assertProtoArrayIsEmpty(voiceCallRatUsage2);
@@ -1280,7 +1484,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(100L);
- // should be denied
+ // Should be denied
assertNull(voiceCallSession);
}
@@ -1298,7 +1502,7 @@
mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis;
VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession1);
assertProtoArrayIsEmpty(voiceCallSession2);
@@ -1330,7 +1534,7 @@
mServiceState1Proto, mServiceSwitch1Proto);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
CellularDataServiceSwitch[] serviceSwitches =
@@ -1353,7 +1557,7 @@
mServiceState2Proto, mServiceSwitch2Proto);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
CellularDataServiceSwitch[] serviceSwitches =
@@ -1447,7 +1651,7 @@
CellularDataServiceSwitch[] serviceSwitches =
mPersistAtomsStorage.getCellularDataServiceSwitches(100L);
- // should be denied
+ // Should be denied
assertNull(serviceSwitches);
}
@@ -1464,7 +1668,7 @@
CellularDataServiceSwitch[] serviceSwitches2 =
mPersistAtomsStorage.getCellularDataServiceSwitches(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto},
@@ -1492,7 +1696,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(100L);
- // should be denied
+ // Should be denied
assertNull(serviceStates);
}
@@ -1507,7 +1711,7 @@
mPersistAtomsStorage.incTimeMillis(100L);
CellularServiceState[] serviceStates2 = mPersistAtomsStorage.getCellularServiceStates(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new CellularServiceState[] {
@@ -1541,7 +1745,7 @@
mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
assertProtoArrayEquals(new ImsRegistrationStats[] {mImsRegistrationStatsLte0}, regStats);
@@ -1557,7 +1761,7 @@
mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsWifi0));
mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
assertProtoArrayEqualsIgnoringOrder(
@@ -1629,7 +1833,7 @@
mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationLte);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationTermination[] terminations =
mPersistAtomsStorage.getImsRegistrationTerminations(0L);
@@ -1647,7 +1851,7 @@
mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationWifi);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationTermination[] terminations =
mPersistAtomsStorage.getImsRegistrationTerminations(0L);
@@ -1712,7 +1916,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
ImsRegistrationStats[] stats = mPersistAtomsStorage.getImsRegistrationStats(100L);
- // should be denied
+ // Should be denied
assertNull(stats);
}
@@ -1727,7 +1931,7 @@
mPersistAtomsStorage.incTimeMillis(100L);
ImsRegistrationStats[] stats2 = mPersistAtomsStorage.getImsRegistrationStats(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new ImsRegistrationStats[] {
@@ -1758,7 +1962,7 @@
ImsRegistrationTermination[] terminations =
mPersistAtomsStorage.getImsRegistrationTerminations(100L);
- // should be denied
+ // Should be denied
assertNull(terminations);
}
@@ -1775,7 +1979,7 @@
ImsRegistrationTermination[] terminations2 =
mPersistAtomsStorage.getImsRegistrationTerminations(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new ImsRegistrationTermination[] {
@@ -1813,6 +2017,12 @@
assertProtoArrayEqualsIgnoringOrder(
new DataCallSession[]{mDataCallSession0, mDataCallSession1},
dataCalls);
+ for (DataCallSession dataCallSession : dataCalls) {
+ if (dataCallSession.dimension == mDataCallSession0.dimension) {
+ assertArrayEquals(new int[]{1, 2, 3}, dataCallSession.handoverFailureCauses);
+ assertArrayEquals(new int[]{6, 5, 5}, dataCallSession.handoverFailureRat);
+ }
+ }
}
@Test
@@ -1846,11 +2056,15 @@
newDataCallSession0.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
newDataCallSession0.durationMinutes = 10;
newDataCallSession0.ratSwitchCount = 5;
+ newDataCallSession0.handoverFailureCauses = new int[]{4};
+ newDataCallSession0.handoverFailureRat = new int[]{4};
DataCallSession totalDataCallSession0 = copyOf(newDataCallSession0);
totalDataCallSession0.durationMinutes =
mDataCallSession0.durationMinutes + newDataCallSession0.durationMinutes;
totalDataCallSession0.ratSwitchCount =
mDataCallSession0.ratSwitchCount + newDataCallSession0.ratSwitchCount;
+ totalDataCallSession0.handoverFailureCauses = new int[]{1, 2, 3, 4};
+ totalDataCallSession0.handoverFailureRat = new int[]{6, 5, 5, 4};
mPersistAtomsStorage.addDataCallSession(mDataCallSession0);
mPersistAtomsStorage.addDataCallSession(newDataCallSession0);
@@ -1930,7 +2144,7 @@
ImsRegistrationFeatureTagStats[] result =
mPersistAtomsStorage.getImsRegistrationFeatureTagStats(100L);
- // should be denied
+ // Should be denied
assertNull(result);
}
@@ -1947,7 +2161,7 @@
ImsRegistrationFeatureTagStats[] statses2 =
mPersistAtomsStorage.getImsRegistrationFeatureTagStats(50L);
- // first results of get should have two atoms, second should be empty
+ // First results of get should have two atoms, second should be empty
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(mImsRegistrationFeatureTagStatses, statses1);
assertProtoArrayEquals(new ImsRegistrationFeatureTagStats[0], statses2);
@@ -1997,13 +2211,13 @@
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
- // store 11 same atoms, but only 1 atoms stored with count 11
+ // Store 11 same atoms, but only 1 atoms stored with count 11
for (int i = 0; i < 11; i++) {
mPersistAtomsStorage
.addRcsClientProvisioningStats(mRcsClientProvisioningStats1Proto);
mPersistAtomsStorage.incTimeMillis(100L);
}
- // store 1 different atom and count 1
+ // Store 1 different atom and count 1
mPersistAtomsStorage
.addRcsClientProvisioningStats(mRcsClientProvisioningStats2Proto);
@@ -2012,7 +2226,7 @@
RcsClientProvisioningStats[] result =
mPersistAtomsStorage.getRcsClientProvisioningStats(0L);
- // first atom has count 11, the other has 1
+ // First atom has count 11, the other has 1
assertHasStatsAndCount(result, mRcsClientProvisioningStats1Proto, 11);
assertHasStatsAndCount(result, mRcsClientProvisioningStats2Proto, 1);
}
@@ -2027,7 +2241,7 @@
RcsClientProvisioningStats[] result =
mPersistAtomsStorage.getRcsClientProvisioningStats(100L);
- // should be denied
+ // Should be denied
assertNull(result);
}
@@ -2044,7 +2258,7 @@
RcsClientProvisioningStats[] statses2 =
mPersistAtomsStorage.getRcsClientProvisioningStats(50L);
- // first results of get should have two atoms, second should be empty
+ // First results of get should have two atoms, second should be empty
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(mRcsClientProvisioningStatses, statses1);
assertProtoArrayEquals(new RcsClientProvisioningStats[0], statses2);
@@ -2090,13 +2304,16 @@
@Test
@SmallTest
public void addRcsAcsProvisioningStats_updateExistingEntries() throws Exception {
+ final int maxCount = 5;
+ final long duration = START_TIME_MILLIS;
createEmptyTestFile();
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
- // store 5 same atoms (1Proto), but only 1 atoms stored with count 5, total time 2000L * 5
- // store 5 same atoms (2Proto), but only 1 atoms stored with count 5, total time 2000L * 5
- for (int i = 0; i < 5; i++) {
+ // Store 5 same atoms (1Proto), but only 1 atoms stored with count 5, total time 2000L * 5
+ // Store 5 same atoms (2Proto), but only 1 atoms stored with count 5, total time 2000L * 5
+
+ for (int i = 0; i < maxCount; i++) {
mPersistAtomsStorage
.addRcsAcsProvisioningStats(copyOf(mRcsAcsProvisioningStats1Proto));
mPersistAtomsStorage.incTimeMillis(100L);
@@ -2107,6 +2324,7 @@
// add one more atoms (2Proto), count 6, total time 2000L * 6
mPersistAtomsStorage
.addRcsAcsProvisioningStats(copyOf(mRcsAcsProvisioningStats2Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
verifyCurrentStateSavedToFileOnce();
@@ -2114,9 +2332,11 @@
mPersistAtomsStorage.getRcsAcsProvisioningStats(0L);
// atom (1Proto) : count = 5, time = 2000L * 5
- assertHasStatsAndCountDuration(result, mRcsAcsProvisioningStats1Proto, 5, 2000L * 5);
+ assertHasStatsAndCountDuration(
+ result, mRcsAcsProvisioningStats1Proto, 5, duration * maxCount);
// atom (2Proto) : count = 6, time = 2000L * 6
- assertHasStatsAndCountDuration(result, mRcsAcsProvisioningStats2Proto, 6, 2000L * 6);
+ assertHasStatsAndCountDuration(
+ result, mRcsAcsProvisioningStats2Proto, 6, duration * (maxCount + 1));
}
@Test
@@ -2129,7 +2349,7 @@
RcsAcsProvisioningStats[] result =
mPersistAtomsStorage.getRcsAcsProvisioningStats(100L);
- // should be denied
+ // Should be denied
assertNull(result);
}
@@ -2139,27 +2359,27 @@
createTestFile(START_TIME_MILLIS);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
- mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
RcsAcsProvisioningStats[] statses1 =
- mPersistAtomsStorage.getRcsAcsProvisioningStats(50L);
- mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(DAY_IN_MILLIS - HOUR_IN_MILLIS);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
RcsAcsProvisioningStats[] statses2 =
- mPersistAtomsStorage.getRcsAcsProvisioningStats(50L);
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(DAY_IN_MILLIS - HOUR_IN_MILLIS);
- // first results of get should have two atoms, second should be empty
+ // First results of get should have two atoms, second should be empty
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(mRcsAcsProvisioningStatses, statses1);
assertProtoArrayEquals(new RcsAcsProvisioningStats[0], statses2);
assertEquals(
- START_TIME_MILLIS + 200L,
+ START_TIME_MILLIS + 2 * DAY_IN_MILLIS,
mPersistAtomsStorage.getAtomsProto()
.rcsAcsProvisioningStatsPullTimestampMillis);
InOrder inOrder = inOrder(mTestFileOutputStream);
assertEquals(
- START_TIME_MILLIS + 100L,
+ START_TIME_MILLIS + DAY_IN_MILLIS,
getAtomsWritten(inOrder).rcsAcsProvisioningStatsPullTimestampMillis);
assertEquals(
- START_TIME_MILLIS + 200L,
+ START_TIME_MILLIS + 2 * DAY_IN_MILLIS,
getAtomsWritten(inOrder).rcsAcsProvisioningStatsPullTimestampMillis);
inOrder.verifyNoMoreInteractions();
}
@@ -2172,7 +2392,7 @@
mPersistAtomsStorage.addImsRegistrationServiceDescStats(mImsRegistrationServiceIm);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationServiceDescStats[] outputs =
mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
@@ -2190,7 +2410,7 @@
mPersistAtomsStorage.addImsRegistrationServiceDescStats(mImsRegistrationServiceFt);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationServiceDescStats[] output =
mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
@@ -2237,7 +2457,7 @@
ImsRegistrationServiceDescStats[] output =
mPersistAtomsStorage.getImsRegistrationServiceDescStats(100L);
- // should be denied
+ // Should be denied
assertNull(output);
}
@@ -2254,7 +2474,7 @@
ImsRegistrationServiceDescStats[] output2 =
mPersistAtomsStorage.getImsRegistrationServiceDescStats(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new ImsRegistrationServiceDescStats[] {
@@ -2303,6 +2523,121 @@
}
@Test
+ public void getRcsAcsProvisioningStats_24h_normalization() throws Exception {
+ // in case pulling interval is greater than a day
+ final long stateTimer = HOUR_IN_MILLIS;
+ final long weightFactor = 2;
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ RcsAcsProvisioningStats mSubjectStats = copyOf(mRcsAcsProvisioningStats1Proto);
+
+ mSubjectStats.stateTimerMillis = stateTimer;
+ mPersistAtomsStorage.addRcsAcsProvisioningStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS * weightFactor);
+
+ RcsAcsProvisioningStats[] savedStats =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(0L);
+
+ assertEquals(
+ (START_TIME_MILLIS + stateTimer) / weightFactor, savedStats[0].stateTimerMillis);
+
+ // in case pulling interval is smaller than a day
+ long incTimeMillis = DAY_IN_MILLIS * 23 / 24 + 1;
+ mSubjectStats = copyOf(mRcsAcsProvisioningStats1Proto);
+ mSubjectStats.stateTimerMillis = stateTimer;
+ mPersistAtomsStorage.addRcsAcsProvisioningStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(incTimeMillis);
+ savedStats =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(0L);
+
+
+ assertEquals(stateTimer, savedStats[0].stateTimerMillis);
+ }
+
+ @Test
+ public void getSipDelegateStats_24h_normalization() throws Exception {
+ final long stateTimer = HOUR_IN_MILLIS;
+ final long weightFactor = 2;
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ SipDelegateStats mSubjectStats = copyOf(mSipDelegateStats1);
+ mSubjectStats.uptimeMillis = stateTimer;
+ mPersistAtomsStorage.addSipDelegateStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS * weightFactor);
+ SipDelegateStats[] savedStats =
+ mPersistAtomsStorage.getSipDelegateStats(0L);
+ for (SipDelegateStats stat : savedStats) {
+ if (stat.destroyReason
+ == SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP) {
+ assertEquals(stateTimer / weightFactor, stat.uptimeMillis);
+ }
+ }
+
+ long incTimeMillis = DAY_IN_MILLIS * 23 / 24 + 1;
+ mSubjectStats = copyOf(mSipDelegateStats1);
+ mSubjectStats.uptimeMillis = stateTimer;
+ mPersistAtomsStorage.addSipDelegateStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(incTimeMillis);
+ savedStats =
+ mPersistAtomsStorage.getSipDelegateStats(0L);
+ for (SipDelegateStats stat : savedStats) {
+ if (stat.destroyReason
+ == SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP) {
+ assertEquals(stateTimer, stat.uptimeMillis);
+ }
+ }
+ }
+
+ @Test
+ public void getSipTransportFeatureTagStats_24h_normalization() throws Exception {
+ final long stateTimer = HOUR_IN_MILLIS;
+ final long weightFactor = 2;
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ SipTransportFeatureTagStats mSubjectStats = copyOf(mSipTransportFeatureTagStats1);
+ mSubjectStats.associatedMillis = stateTimer;
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS * weightFactor);
+ SipTransportFeatureTagStats[] savedStats =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+ assertEquals((stateTimer) / weightFactor, savedStats[0].associatedMillis);
+
+ long incTimeMillis = DAY_IN_MILLIS * 23 / 24 + 1;
+ mSubjectStats = copyOf(mSipTransportFeatureTagStats1);
+ mSubjectStats.associatedMillis = stateTimer;
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(incTimeMillis);
+ savedStats =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+ assertEquals(stateTimer, savedStats[0].associatedMillis);
+ }
+
+ @Test
+ public void getImsRegistrationServiceDescStats_24h_normalization() throws Exception {
+ final long stateTimer = HOUR_IN_MILLIS;
+ final long weightFactor = 2;
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ ImsRegistrationServiceDescStats mSubjectStats = copyOf(mImsRegistrationServiceIm);
+ mSubjectStats.publishedMillis = stateTimer;
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS * weightFactor);
+ ImsRegistrationServiceDescStats[] savedStats =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
+ assertEquals(
+ (START_TIME_MILLIS + stateTimer) / weightFactor, savedStats[0].publishedMillis);
+
+ long incTimeMillis = DAY_IN_MILLIS * 23 / 24 + 1;
+ mSubjectStats = copyOf(mImsRegistrationServiceIm);
+ mSubjectStats.publishedMillis = stateTimer;
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(mSubjectStats);
+ mPersistAtomsStorage.incTimeMillis(incTimeMillis);
+ savedStats =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
+ assertEquals(stateTimer, savedStats[0].publishedMillis);
+ }
+
+ @Test
@SmallTest
public void addImsDedicatedBearerListenerEvent_emptyProto() throws Exception {
createEmptyTestFile();
@@ -2310,7 +2645,7 @@
mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent1);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsDedicatedBearerListenerEvent[] outputs =
mPersistAtomsStorage.getImsDedicatedBearerListenerEvent(0L);
@@ -2328,7 +2663,7 @@
mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent2);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsDedicatedBearerListenerEvent[] output =
mPersistAtomsStorage.getImsDedicatedBearerListenerEvent(0L);
@@ -2369,7 +2704,7 @@
mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsDedicatedBearerEvent[] outputs =
mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
@@ -2387,7 +2722,7 @@
mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent2);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsDedicatedBearerEvent[] output =
mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
@@ -2462,7 +2797,6 @@
mImsDedicatedBearerEvent2, newStats}, stats);
}
-
@Test
@SmallTest
public void addUceEventStats_emptyProto() throws Exception {
@@ -2471,7 +2805,7 @@
mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
UceEventStats[] outputs = mPersistAtomsStorage.getUceEventStats(0L);
assertProtoArrayEquals(
@@ -2488,7 +2822,7 @@
mPersistAtomsStorage.addUceEventStats(mUceEventStats2);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
UceEventStats[] output = mPersistAtomsStorage.getUceEventStats(0L);
assertProtoArrayEqualsIgnoringOrder(
@@ -2562,7 +2896,7 @@
mPersistAtomsStorage.addPresenceNotifyEvent(mPresenceNotifyEvent2);
mPersistAtomsStorage.incTimeMillis(100L);
- // service state and service switch should be added successfully
+ // Service state and service switch should be added successfully
verifyCurrentStateSavedToFileOnce();
PresenceNotifyEvent[] output = mPersistAtomsStorage.getPresenceNotifyEvent(0L);
assertProtoArrayEqualsIgnoringOrder(
@@ -2578,7 +2912,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
PresenceNotifyEvent[] output = mPersistAtomsStorage.getPresenceNotifyEvent(100L);
- // should be denied
+ // Should be denied
assertNull(output);
}
@@ -2593,7 +2927,7 @@
mPersistAtomsStorage.incTimeMillis(100L);
PresenceNotifyEvent[] output2 = mPersistAtomsStorage.getPresenceNotifyEvent(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new PresenceNotifyEvent[] {mPresenceNotifyEvent1, mPresenceNotifyEvent2}, output1);
@@ -2720,7 +3054,7 @@
SipTransportFeatureTagStats[] outputs =
mPersistAtomsStorage.getSipTransportFeatureTagStats(100L);
- // should be denied
+ // Should be denied
assertNull(outputs);
}
@@ -2739,7 +3073,7 @@
SipTransportFeatureTagStats[] output2 =
mPersistAtomsStorage.getSipTransportFeatureTagStats(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new SipTransportFeatureTagStats[] {
@@ -2862,7 +3196,7 @@
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
SipDelegateStats[] outputs = mPersistAtomsStorage.getSipDelegateStats(100L);
- // should be denied
+ // Should be denied
assertNull(outputs);
}
@@ -2879,7 +3213,7 @@
mPersistAtomsStorage.incTimeMillis(100L);
SipDelegateStats[] output2 = mPersistAtomsStorage.getSipDelegateStats(50L);
- // first set of results should equal to file contents, second should be empty, corresponding
+ // First set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new SipDelegateStats[] {
@@ -3005,18 +3339,18 @@
createEmptyTestFile();
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
- // store 11 same atoms, but only 1 atoms stored with count 11
+ // Store 11 same atoms, but only 1 atoms stored with count 11
for (int i = 0; i < 11; i++) {
mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse1);
mPersistAtomsStorage.incTimeMillis(100L);
}
- // store 1 different atom and count 1
+ // Store 1 different atom and count 1
mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse2);
verifyCurrentStateSavedToFileOnce();
SipMessageResponse[] result = mPersistAtomsStorage.getSipMessageResponse(0L);
- // first atom has count 11, the other has 1
+ // First atom has count 11, the other has 1
assertHasStats(result, mSipMessageResponse1, 11);
assertHasStats(result, mSipMessageResponse2, 1);
}
@@ -3074,18 +3408,18 @@
createEmptyTestFile();
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
- // store 11 same atoms, but only 1 atoms stored with count 11
+ // Store 11 same atoms, but only 1 atoms stored with count 11
for (int i = 0; i < 11; i++) {
mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession1);
mPersistAtomsStorage.incTimeMillis(100L);
}
- // store 1 different atom and count 1
+ // Store 1 different atom and count 1
mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession2);
verifyCurrentStateSavedToFileOnce();
SipTransportSession[] result = mPersistAtomsStorage.getSipTransportSession(0L);
- // first atom has count 11, the other has 1
+ // First atom has count 11, the other has 1
assertHasStats(result, mSipTransportSession1, 11);
assertHasStats(result, mSipTransportSession2, 1);
}
@@ -3108,6 +3442,784 @@
newSipTransportSession}, outputs);
}
+ @Test
+ @SmallTest
+ public void getUnmeteredNetworks_noExistingEntry() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ assertEquals(0L, mPersistAtomsStorage.getUnmeteredNetworks(1, 0));
+
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void getUnmeteredNetworks() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(NETWORK_TYPE_BITMASK_GPRS, mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0L, mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void getUnmeteredNetworks_carrierIdMismatch() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0L, mPersistAtomsStorage.getUnmeteredNetworks(0, 1));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0L, mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addUnmeteredNetworks() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GSM);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(
+ NETWORK_TYPE_BITMASK_GPRS | NETWORK_TYPE_BITMASK_GSM,
+ mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0, mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verifyNoMoreInteractions();
+
+ mPersistAtomsStorage.addUnmeteredNetworks(1, 2, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ mPersistAtomsStorage.addUnmeteredNetworks(1, 2, NETWORK_TYPE_BITMASK_GSM);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(
+ NETWORK_TYPE_BITMASK_GPRS | NETWORK_TYPE_BITMASK_GSM,
+ mPersistAtomsStorage.getUnmeteredNetworks(1, 2));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0, mPersistAtomsStorage.getUnmeteredNetworks(1, 2));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addUnmeteredNetworks_carrierIdMismatch() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 1, NETWORK_TYPE_BITMASK_GSM);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(NETWORK_TYPE_BITMASK_GSM, mPersistAtomsStorage.getUnmeteredNetworks(0, 1));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(0L, mPersistAtomsStorage.getUnmeteredNetworks(0, 1));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addUnmeteredNetworks_sameBitmask() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+
+ mPersistAtomsStorage.addUnmeteredNetworks(0, 0, NETWORK_TYPE_BITMASK_GPRS);
+ inOrder.verifyNoMoreInteractions();
+
+ assertEquals(NETWORK_TYPE_BITMASK_GPRS, mPersistAtomsStorage.getUnmeteredNetworks(0, 0));
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void addOutgoingShortCodeSms_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // OutgoingShortCodeSms should be added successfully, changes should be saved.
+ verifyCurrentStateSavedToFileOnce();
+ OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1};
+ assertProtoArrayEquals(expectedList,
+ mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+ }
+
+ @Test
+ public void addOutgoingShortCodeSms_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+ mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // OutgoingShortCodeSms should be added successfully.
+ verifyCurrentStateSavedToFileOnce();
+ OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1,
+ mOutgoingShortCodeSms2};
+ assertProtoArrayEqualsIgnoringOrder(expectedList,
+ mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+ }
+
+ @Test
+ public void addOutgoingShortCodeSms_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ // Add copy of mOutgoingShortCodeSms1.
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addOutgoingShortCodeSms(copyOf(mOutgoingShortCodeSms1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mOutgoingShortCodeSms1's short code sms count should be increased by 1.
+ verifyCurrentStateSavedToFileOnce();
+ OutgoingShortCodeSms newOutgoingShortCodeSms1 = copyOf(mOutgoingShortCodeSms1);
+ newOutgoingShortCodeSms1.shortCodeSmsCount = 2;
+ OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {newOutgoingShortCodeSms1,
+ mOutgoingShortCodeSms2};
+ assertProtoArrayEqualsIgnoringOrder(expectedList,
+ mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+ }
+
+ @Test
+ public void addOutgoingShortCodeSms_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store mOutgoingShortCodeSms1 11 times.
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ // Store mOutgoingShortCodeSms2 1 time.
+ mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms2);
+
+ verifyCurrentStateSavedToFileOnce();
+ OutgoingShortCodeSms[] result = mPersistAtomsStorage
+ .getOutgoingShortCodeSms(0L);
+ assertHasStatsAndCount(result, mOutgoingShortCodeSms1, 11);
+ assertHasStatsAndCount(result, mOutgoingShortCodeSms2, 1);
+ }
+
+ @Test
+ public void getOutgoingShortCodeSms_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ // Pull interval less than minimum.
+ mPersistAtomsStorage.incTimeMillis(50L);
+ OutgoingShortCodeSms[] outgoingShortCodeSmsList = mPersistAtomsStorage
+ .getOutgoingShortCodeSms(100L);
+ // Should be denied.
+ assertNull(outgoingShortCodeSmsList);
+ }
+
+ @Test
+ public void getOutgoingShortCodeSms_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ OutgoingShortCodeSms[] outgoingShortCodeSmsList1 = mPersistAtomsStorage
+ .getOutgoingShortCodeSms(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ OutgoingShortCodeSms[] outgoingShortCodeSmsList2 = mPersistAtomsStorage
+ .getOutgoingShortCodeSms(50L);
+
+ // First set of results should be equal to file contents.
+ OutgoingShortCodeSms[] expectedOutgoingShortCodeSmsList =
+ new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1, mOutgoingShortCodeSms2};
+ assertProtoArrayEqualsIgnoringOrder(expectedOutgoingShortCodeSmsList,
+ outgoingShortCodeSmsList1);
+ // Second set of results should be empty.
+ assertProtoArrayEquals(new OutgoingShortCodeSms[0], outgoingShortCodeSmsList2);
+ // Corresponding pull timestamp should be updated and saved.
+ assertEquals(START_TIME_MILLIS + 200L, mPersistAtomsStorage
+ .getAtomsProto().outgoingShortCodeSmsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).outgoingShortCodeSmsPullTimestampMillis);
+ assertEquals(START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).outgoingShortCodeSmsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void addSatelliteControllerStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteController[] output =
+ mPersistAtomsStorage.getSatelliteControllerStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteController[] {mSatelliteController1}, output);
+ }
+
+ @Test
+ public void addSatelliteControllerStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1);
+ mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ SatelliteController expected = new SatelliteController();
+ expected.countOfSatelliteServiceEnablementsSuccess =
+ mSatelliteController1.countOfSatelliteServiceEnablementsSuccess
+ + mSatelliteController2.countOfSatelliteServiceEnablementsSuccess;
+ expected.countOfSatelliteServiceEnablementsFail =
+ mSatelliteController1.countOfSatelliteServiceEnablementsFail
+ + mSatelliteController2.countOfSatelliteServiceEnablementsFail;
+ expected.countOfOutgoingDatagramSuccess =
+ mSatelliteController1.countOfOutgoingDatagramSuccess
+ + mSatelliteController2.countOfOutgoingDatagramSuccess;
+ expected.countOfOutgoingDatagramFail =
+ mSatelliteController1.countOfOutgoingDatagramFail
+ + mSatelliteController2.countOfOutgoingDatagramFail;
+ expected.countOfIncomingDatagramSuccess =
+ mSatelliteController1.countOfIncomingDatagramSuccess
+ + mSatelliteController2.countOfIncomingDatagramSuccess;
+ expected.countOfIncomingDatagramFail =
+ mSatelliteController1.countOfIncomingDatagramFail
+ + mSatelliteController2.countOfIncomingDatagramFail;
+ expected.countOfDatagramTypeSosSmsSuccess =
+ mSatelliteController1.countOfDatagramTypeSosSmsSuccess
+ + mSatelliteController2.countOfDatagramTypeSosSmsSuccess;
+ expected.countOfDatagramTypeSosSmsFail =
+ mSatelliteController1.countOfDatagramTypeSosSmsFail
+ + mSatelliteController2.countOfDatagramTypeSosSmsFail;
+ expected.countOfDatagramTypeLocationSharingSuccess =
+ mSatelliteController1.countOfDatagramTypeLocationSharingSuccess
+ + mSatelliteController2.countOfDatagramTypeLocationSharingSuccess;
+ expected.countOfDatagramTypeLocationSharingFail =
+ mSatelliteController1.countOfDatagramTypeLocationSharingFail
+ + mSatelliteController2.countOfDatagramTypeLocationSharingFail;
+ expected.countOfProvisionSuccess =
+ mSatelliteController1.countOfProvisionSuccess
+ + mSatelliteController2.countOfProvisionSuccess;
+ expected.countOfProvisionFail =
+ mSatelliteController1.countOfProvisionFail
+ + mSatelliteController2.countOfProvisionFail;
+ expected.countOfDeprovisionSuccess =
+ mSatelliteController1.countOfDeprovisionSuccess
+ + mSatelliteController2.countOfDeprovisionSuccess;
+ expected.countOfDeprovisionFail =
+ mSatelliteController1.countOfDeprovisionFail
+ + mSatelliteController2.countOfDeprovisionFail;
+ expected.totalServiceUptimeSec =
+ mSatelliteController1.totalServiceUptimeSec
+ + mSatelliteController2.totalServiceUptimeSec;
+ expected.totalBatteryConsumptionPercent =
+ mSatelliteController1.totalBatteryConsumptionPercent
+ + mSatelliteController2.totalBatteryConsumptionPercent;
+ expected.totalBatteryChargedTimeSec =
+ mSatelliteController1.totalBatteryChargedTimeSec
+ + mSatelliteController2.totalBatteryChargedTimeSec;
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteController[] output =
+ mPersistAtomsStorage.getSatelliteControllerStats(0L);
+ assertHasStats(output, expected);
+ }
+
+ @Test
+ public void getSatelliteControllerStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteController[] output =
+ mPersistAtomsStorage.getSatelliteControllerStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+ @Test
+ public void addSatelliteSessionStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteSessionStats(
+ mSatelliteSession1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteSession[] output =
+ mPersistAtomsStorage.getSatelliteSessionStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteSession[] {mSatelliteSession1}, output);
+ }
+
+ @Test
+ public void addSatelliteSessionStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteSessionStats(
+ mSatelliteSession1);
+ mPersistAtomsStorage.addSatelliteSessionStats(
+ mSatelliteSession2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteSession[] output =
+ mPersistAtomsStorage.getSatelliteSessionStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new SatelliteSession[] {
+ mSatelliteSession1, mSatelliteSession2},
+ output);
+ }
+
+ @Test
+ public void addSatelliteSessionStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store atoms up to maximum number + 1
+ int maxCount = 15 + 1;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addSatelliteSessionStats(
+ copyOf(mSatelliteSession1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // Store 1 different atom
+ mPersistAtomsStorage
+ .addSatelliteSessionStats(mSatelliteSession2);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ SatelliteSession[] result =
+ mPersistAtomsStorage.getSatelliteSessionStats(0L);
+
+ // First atom has count 16, the other has 1
+ assertHasStatsAndCount(result, mSatelliteSession1, 16);
+ assertHasStatsAndCount(result, mSatelliteSession2, 1);
+
+ }
+
+ @Test
+ public void getSatelliteSessionStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteSession[] output =
+ mPersistAtomsStorage.getSatelliteSessionStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+
+
+ @Test
+ public void addSatelliteIncomingDatagramStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteIncomingDatagramStats(mSatelliteIncomingDatagram1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteIncomingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteIncomingDatagramStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteIncomingDatagram[] {mSatelliteIncomingDatagram1}, output);
+ }
+
+ @Test
+ public void addSatelliteIncomingDatagramStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteIncomingDatagramStats(mSatelliteIncomingDatagram1);
+ mPersistAtomsStorage.addSatelliteIncomingDatagramStats(mSatelliteIncomingDatagram2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteIncomingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteIncomingDatagramStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new SatelliteIncomingDatagram[] {
+ mSatelliteIncomingDatagram1, mSatelliteIncomingDatagram2}, output);
+ }
+
+ @Test
+ public void addSatelliteIncomingDatagramStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store atoms up to maximum number + 1
+ int maxCount = 15 + 1;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addSatelliteIncomingDatagramStats(copyOf(mSatelliteIncomingDatagram1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // Store 1 different atom
+ mPersistAtomsStorage
+ .addSatelliteIncomingDatagramStats(mSatelliteIncomingDatagram2);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ SatelliteIncomingDatagram[] result =
+ mPersistAtomsStorage.getSatelliteIncomingDatagramStats(0L);
+
+ // First atom has count 14, the other has 1
+ assertHasStatsAndCount(result, mSatelliteIncomingDatagram1, 14);
+ assertHasStatsAndCount(result, mSatelliteIncomingDatagram2, 1);
+
+ }
+
+ @Test
+ public void getSatelliteIncomingDatagramStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteIncomingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteIncomingDatagramStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+ @Test
+ public void addSatelliteOutgoingDatagramStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteOutgoingDatagramStats(mSatelliteOutgoingDatagram1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteOutgoingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteOutgoingDatagramStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteOutgoingDatagram[] {mSatelliteOutgoingDatagram1}, output);
+ }
+
+ @Test
+ public void addSatelliteOutgoingDatagramStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteOutgoingDatagramStats(mSatelliteOutgoingDatagram1);
+ mPersistAtomsStorage.addSatelliteOutgoingDatagramStats(mSatelliteOutgoingDatagram2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteOutgoingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteOutgoingDatagramStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new SatelliteOutgoingDatagram[] {
+ mSatelliteOutgoingDatagram1, mSatelliteOutgoingDatagram2}, output);
+ }
+
+ @Test
+ public void addSatelliteOutgoingDatagramStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store atoms up to maximum number + 1
+ int maxCount = 15 + 1;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addSatelliteOutgoingDatagramStats(copyOf(mSatelliteOutgoingDatagram1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // Store 1 different atom
+ mPersistAtomsStorage
+ .addSatelliteOutgoingDatagramStats(mSatelliteOutgoingDatagram2);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ SatelliteOutgoingDatagram[] result =
+ mPersistAtomsStorage.getSatelliteOutgoingDatagramStats(0L);
+
+ // First atom has count 14, the other has 1
+ assertHasStatsAndCount(result, mSatelliteOutgoingDatagram1, 14);
+ assertHasStatsAndCount(result, mSatelliteOutgoingDatagram2, 1);
+
+ }
+
+ @Test
+ public void getSatelliteOutgoingDatagramStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteOutgoingDatagram[] output =
+ mPersistAtomsStorage.getSatelliteOutgoingDatagramStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+ @Test
+ public void addSatelliteProvisionStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteProvisionStats(mSatelliteProvision1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteProvision[] output =
+ mPersistAtomsStorage.getSatelliteProvisionStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteProvision[] {mSatelliteProvision1}, output);
+ }
+
+ @Test
+ public void addSatelliteProvisionStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteProvisionStats(mSatelliteProvision1);
+ mPersistAtomsStorage.addSatelliteProvisionStats(mSatelliteProvision2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteProvision[] output =
+ mPersistAtomsStorage.getSatelliteProvisionStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new SatelliteProvision[] {
+ mSatelliteProvision1, mSatelliteProvision2}, output);
+ }
+
+ @Test
+ public void addSatelliteProvisionStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store atoms up to maximum number + 1
+ int maxCount = 15 + 1;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addSatelliteProvisionStats(copyOf(mSatelliteProvision1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // Store 1 different atom
+ mPersistAtomsStorage
+ .addSatelliteProvisionStats(mSatelliteProvision2);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ SatelliteProvision[] result =
+ mPersistAtomsStorage.getSatelliteProvisionStats(0L);
+
+ // First atom has count 14, the other has 1
+ assertHasStatsAndCount(result, mSatelliteProvision1, 14);
+ assertHasStatsAndCount(result, mSatelliteProvision2, 1);
+
+ }
+
+ @Test
+ public void getSatelliteProvisionStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteProvision[] output =
+ mPersistAtomsStorage.getSatelliteProvisionStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+ @Test
+ public void addSatelliteSosMessageRecommenderStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteSosMessageRecommenderStats(
+ mSatelliteSosMessageRecommender1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteSosMessageRecommender[] output =
+ mPersistAtomsStorage.getSatelliteSosMessageRecommenderStats(0L);
+ assertProtoArrayEquals(
+ new SatelliteSosMessageRecommender[] {mSatelliteSosMessageRecommender1}, output);
+ }
+
+ @Test
+ public void addSatelliteSosMessageRecommenderStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSatelliteSosMessageRecommenderStats(
+ mSatelliteSosMessageRecommender1);
+ mPersistAtomsStorage.addSatelliteSosMessageRecommenderStats(
+ mSatelliteSosMessageRecommender2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // Service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SatelliteSosMessageRecommender[] output =
+ mPersistAtomsStorage.getSatelliteSosMessageRecommenderStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new SatelliteSosMessageRecommender[] {
+ mSatelliteSosMessageRecommender1, mSatelliteSosMessageRecommender2},
+ output);
+ }
+
+ @Test
+ public void addSatelliteSosMessageRecommenderStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Store atoms up to maximum number + 1
+ int maxCount = 15 + 1;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addSatelliteSosMessageRecommenderStats(
+ copyOf(mSatelliteSosMessageRecommender1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // Store 1 different atom
+ mPersistAtomsStorage
+ .addSatelliteSosMessageRecommenderStats(mSatelliteSosMessageRecommender2);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ SatelliteSosMessageRecommender[] result =
+ mPersistAtomsStorage.getSatelliteSosMessageRecommenderStats(0L);
+
+ // First atom has count 16, the other has 1
+ assertHasStatsAndCount(result, mSatelliteSosMessageRecommender1, 16);
+ assertHasStatsAndCount(result, mSatelliteSosMessageRecommender2, 1);
+
+ }
+
+ @Test
+ public void getSatelliteSosMessageRecommenderStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ SatelliteSosMessageRecommender[] output =
+ mPersistAtomsStorage.getSatelliteSosMessageRecommenderStats(100L);
+
+ // Should be denied
+ assertNull(output);
+ }
+
+ @Test
+ @SmallTest
+ public void clearAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCompleteSipTransportSession(copyOf(mSipTransportSession1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ mPersistAtomsStorage.clearAtoms();
+ verifyCurrentStateSavedToFileOnce();
+ UceEventStats[] uceEventStats = mPersistAtomsStorage.getUceEventStats(0L);
+ assertEquals(null, uceEventStats);
+ SipTransportSession[] sipTransportSession = mPersistAtomsStorage.getSipTransportSession(0L);
+ assertEquals(null, sipTransportSession);
+ }
+
/* Utilities */
private void createEmptyTestFile() throws Exception {
@@ -3158,6 +4270,20 @@
atoms.sipMessageResponse = mSipMessageResponse;
atoms.sipTransportSessionPullTimestampMillis = lastPullTimeMillis;
atoms.sipTransportSession = mSipTransportSession;
+ atoms.outgoingShortCodeSms = mOutgoingShortCodeSms;
+ atoms.outgoingShortCodeSmsPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteController = mSatelliteControllers;
+ atoms.satelliteControllerPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteSession = mSatelliteSessions;
+ atoms.satelliteSessionPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteIncomingDatagram = mSatelliteIncomingDatagrams;
+ atoms.satelliteIncomingDatagramPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteOutgoingDatagram = mSatelliteOutgoingDatagrams;
+ atoms.satelliteOutgoingDatagramPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteProvision = mSatelliteProvisions;
+ atoms.satelliteProvisionPullTimestampMillis = lastPullTimeMillis;
+ atoms.satelliteSosMessageRecommender = mSatelliteSosMessageRecommenders;
+ atoms.satelliteSosMessageRecommenderPullTimestampMillis = lastPullTimeMillis;
FileOutputStream stream = new FileOutputStream(mTestFile);
stream.write(PersistAtoms.toByteArray(atoms));
stream.close();
@@ -3276,6 +4402,41 @@
return SipTransportSession.parseFrom(MessageNano.toByteArray(source));
}
+ private static OutgoingShortCodeSms copyOf(OutgoingShortCodeSms source)
+ throws Exception {
+ return OutgoingShortCodeSms.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteController copyOf(SatelliteController source)
+ throws Exception {
+ return SatelliteController.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteSession copyOf(SatelliteSession source)
+ throws Exception {
+ return SatelliteSession.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteIncomingDatagram copyOf(SatelliteIncomingDatagram source)
+ throws Exception {
+ return SatelliteIncomingDatagram.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteOutgoingDatagram copyOf(SatelliteOutgoingDatagram source)
+ throws Exception {
+ return SatelliteOutgoingDatagram.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteProvision copyOf(SatelliteProvision source)
+ throws Exception {
+ return SatelliteProvision.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SatelliteSosMessageRecommender copyOf(SatelliteSosMessageRecommender source)
+ throws Exception {
+ return SatelliteSosMessageRecommender.parseFrom(MessageNano.toByteArray(source));
+ }
+
private void assertAllPullTimestampEquals(long timestamp) {
assertEquals(
timestamp,
@@ -3421,6 +4582,119 @@
assertEquals(expectedCount, actualCount);
}
+ private static void assertHasStats(
+ SatelliteController[] tested,
+ @Nullable SatelliteController expectedStats) {
+ assertNotNull(tested);
+ assertEquals(tested[0].countOfSatelliteServiceEnablementsSuccess,
+ expectedStats.countOfSatelliteServiceEnablementsSuccess);
+ assertEquals(tested[0].countOfSatelliteServiceEnablementsFail,
+ expectedStats.countOfSatelliteServiceEnablementsFail);
+ assertEquals(tested[0].countOfOutgoingDatagramSuccess,
+ expectedStats.countOfOutgoingDatagramSuccess);
+ assertEquals(tested[0].countOfOutgoingDatagramFail,
+ expectedStats.countOfOutgoingDatagramFail);
+ assertEquals(tested[0].countOfIncomingDatagramSuccess,
+ expectedStats.countOfIncomingDatagramSuccess);
+ assertEquals(tested[0].countOfIncomingDatagramFail,
+ expectedStats.countOfIncomingDatagramFail);
+ assertEquals(tested[0].countOfDatagramTypeSosSmsSuccess,
+ expectedStats.countOfDatagramTypeSosSmsSuccess);
+ assertEquals(tested[0].countOfDatagramTypeSosSmsFail,
+ expectedStats.countOfDatagramTypeSosSmsFail);
+ assertEquals(tested[0].countOfDatagramTypeLocationSharingSuccess,
+ expectedStats.countOfDatagramTypeLocationSharingSuccess);
+ assertEquals(tested[0].countOfDatagramTypeLocationSharingFail,
+ expectedStats.countOfDatagramTypeLocationSharingFail);
+ assertEquals(tested[0].totalServiceUptimeSec,
+ expectedStats.totalServiceUptimeSec);
+ assertEquals(tested[0].totalBatteryConsumptionPercent,
+ expectedStats.totalBatteryConsumptionPercent);
+ assertEquals(tested[0].totalBatteryChargedTimeSec,
+ expectedStats.totalBatteryChargedTimeSec);
+ }
+
+ private static void assertHasStatsAndCount(
+ SatelliteSession[] tested,
+ @Nullable SatelliteSession expectedStats, int expectedCount) {
+ assertNotNull(tested);
+ int actualCount = 0;
+ for (SatelliteSession stats : tested) {
+ if (stats.satelliteServiceInitializationResult
+ == expectedStats.satelliteServiceInitializationResult
+ && stats.satelliteTechnology == expectedStats.satelliteTechnology) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ SatelliteIncomingDatagram[] tested,
+ @Nullable SatelliteIncomingDatagram expectedStats, int expectedCount) {
+ assertNotNull(tested);
+ int actualCount = 0;
+ for (SatelliteIncomingDatagram stats : tested) {
+ if (stats.resultCode == expectedStats.resultCode
+ && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
+ && stats.datagramTransferTimeMillis
+ == expectedStats.datagramTransferTimeMillis) {
+ actualCount++;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ SatelliteOutgoingDatagram[] tested,
+ @Nullable SatelliteOutgoingDatagram expectedStats, int expectedCount) {
+ assertNotNull(tested);
+ int actualCount = 0;
+ for (SatelliteOutgoingDatagram stats : tested) {
+ if (stats.datagramType == expectedStats.datagramType
+ && stats.resultCode == expectedStats.resultCode
+ && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
+ && stats.datagramTransferTimeMillis
+ == expectedStats.datagramTransferTimeMillis) {
+ actualCount++;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ SatelliteProvision[] tested,
+ @Nullable SatelliteProvision expectedStats, int expectedCount) {
+ assertNotNull(tested);
+ int actualCount = 0;
+ for (SatelliteProvision stats : tested) {
+ if (stats.resultCode == expectedStats.resultCode
+ && stats.provisioningTimeSec == expectedStats.provisioningTimeSec
+ && stats.isProvisionRequest == expectedStats.isProvisionRequest
+ && stats.isCanceled == expectedStats.isCanceled) {
+ actualCount++;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ SatelliteSosMessageRecommender[] tested,
+ @Nullable SatelliteSosMessageRecommender expectedStats, int expectedCount) {
+ assertNotNull(tested);
+ int actualCount = 0;
+ for (SatelliteSosMessageRecommender stats : tested) {
+ if (stats.isDisplaySosMessageSent
+ == expectedStats.isDisplaySosMessageSent
+ && stats.countOfTimerStarted == expectedStats.countOfTimerStarted
+ && stats.isImsRegistered == expectedStats.isImsRegistered
+ && stats.cellularServiceState == expectedStats.cellularServiceState) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
private static void assertHasStatsAndCountDuration(
RcsAcsProvisioningStats[] statses,
@Nullable RcsAcsProvisioningStats expectedStats, int count, long duration) {
@@ -3555,4 +4829,18 @@
}
assertEquals(expectedCount, actualCount);
}
+
+ private static void assertHasStatsAndCount(
+ OutgoingShortCodeSms[] outgoingShortCodeSmsList,
+ @Nullable OutgoingShortCodeSms expectedOutgoingShortCodeSms, int expectedCount) {
+ assertNotNull(outgoingShortCodeSmsList);
+ int actualCount = -1;
+ for (OutgoingShortCodeSms outgoingShortCodeSms : outgoingShortCodeSmsList) {
+ if (outgoingShortCodeSms.category == expectedOutgoingShortCodeSms.category
+ && outgoingShortCodeSms.xmlVersion == expectedOutgoingShortCodeSms.xmlVersion) {
+ actualCount = outgoingShortCodeSms.shortCodeSmsCount;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
new file mode 100644
index 0000000..22032ae
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.SatelliteProtoEnums;
+import android.telephony.TelephonyProtoEnums;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class SatelliteStatsTest extends TelephonyTest {
+ private static final String TAG = SatelliteStatsTest.class.getSimpleName();
+
+ private TestableSatelliteStats mSatelliteStats;
+
+ private class TestableSatelliteStats extends SatelliteStats {
+ TestableSatelliteStats() {
+ super();
+ }
+ }
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mSatelliteStats = new TestableSatelliteStats();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSatelliteStats = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void onSatelliteControllerMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteControllerParams param =
+ new SatelliteStats.SatelliteControllerParams.Builder()
+ .setCountOfSatelliteServiceEnablementsSuccess(2)
+ .setCountOfSatelliteServiceEnablementsFail(2)
+ .setCountOfOutgoingDatagramSuccess(8)
+ .setCountOfOutgoingDatagramFail(9)
+ .setCountOfIncomingDatagramSuccess(10)
+ .setCountOfIncomingDatagramFail(11)
+ .setCountOfDatagramTypeSosSmsSuccess(5)
+ .setCountOfDatagramTypeSosSmsFail(5)
+ .setCountOfDatagramTypeLocationSharingSuccess(6)
+ .setCountOfDatagramTypeLocationSharingFail(6)
+ .setCountOfProvisionSuccess(3)
+ .setCountOfProvisionFail(4)
+ .setCountOfDeprovisionSuccess(5)
+ .setCountOfDeprovisionFail(6)
+ .setTotalServiceUptimeSec(60 * 60 * 24 * 7)
+ .setTotalBatteryConsumptionPercent(7)
+ .setTotalBatteryChargedTimeSec(60 * 60 * 3)
+ .build();
+
+ mSatelliteStats.onSatelliteControllerMetrics(param);
+
+ ArgumentCaptor<SatelliteController> captor =
+ ArgumentCaptor.forClass(SatelliteController.class);
+ verify(mPersistAtomsStorage).addSatelliteControllerStats(captor.capture());
+ SatelliteController stats = captor.getValue();
+ assertEquals(param.getCountOfSatelliteServiceEnablementsSuccess(),
+ stats.countOfSatelliteServiceEnablementsSuccess);
+ assertEquals(param.getCountOfSatelliteServiceEnablementsFail(),
+ stats.countOfSatelliteServiceEnablementsFail);
+ assertEquals(param.getCountOfOutgoingDatagramSuccess(),
+ stats.countOfOutgoingDatagramSuccess);
+ assertEquals(param.getCountOfOutgoingDatagramFail(),
+ stats.countOfOutgoingDatagramFail);
+ assertEquals(param.getCountOfIncomingDatagramSuccess(),
+ stats.countOfIncomingDatagramSuccess);
+ assertEquals(param.getCountOfIncomingDatagramFail(),
+ stats.countOfIncomingDatagramFail);
+ assertEquals(param.getCountOfDatagramTypeSosSmsSuccess(),
+ stats.countOfDatagramTypeSosSmsSuccess);
+ assertEquals(param.getCountOfDatagramTypeSosSmsFail(),
+ stats.countOfDatagramTypeSosSmsFail);
+ assertEquals(param.getCountOfDatagramTypeLocationSharingSuccess(),
+ stats.countOfDatagramTypeLocationSharingSuccess);
+ assertEquals(param.getCountOfDatagramTypeLocationSharingFail(),
+ stats.countOfDatagramTypeLocationSharingFail);
+ assertEquals(param.getCountOfProvisionSuccess(),
+ stats.countOfProvisionSuccess);
+ assertEquals(param.getCountOfProvisionFail(),
+ stats.countOfProvisionFail);
+ assertEquals(param.getCountOfDeprovisionSuccess(),
+ stats.countOfDeprovisionSuccess);
+ assertEquals(param.getCountOfDeprovisionFail(),
+ stats.countOfDeprovisionFail);
+ assertEquals(param.getTotalServiceUptimeSec(),
+ stats.totalServiceUptimeSec);
+ assertEquals(param.getTotalBatteryConsumptionPercent(),
+ stats.totalBatteryConsumptionPercent);
+ assertEquals(param.getTotalBatteryChargedTimeSec(),
+ stats.totalBatteryChargedTimeSec);
+
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onSatelliteSessionMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteSessionParams param =
+ new SatelliteStats.SatelliteSessionParams.Builder()
+ .setSatelliteServiceInitializationResult(
+ SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+ .setSatelliteTechnology(SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_PROPRIETARY)
+ .build();
+
+ mSatelliteStats.onSatelliteSessionMetrics(param);
+
+ ArgumentCaptor<SatelliteSession> captor =
+ ArgumentCaptor.forClass(SatelliteSession.class);
+ verify(mPersistAtomsStorage).addSatelliteSessionStats(captor.capture());
+ SatelliteSession stats = captor.getValue();
+ assertEquals(param.getSatelliteServiceInitializationResult(),
+ stats.satelliteServiceInitializationResult);
+ assertEquals(param.getSatelliteTechnology(), stats.satelliteTechnology);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onSatelliteIncomingDatagramMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteIncomingDatagramParams param =
+ new SatelliteStats.SatelliteIncomingDatagramParams.Builder()
+ .setResultCode(SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+ .setDatagramSizeBytes(1 * 1024)
+ .setDatagramTransferTimeMillis(3 * 1000)
+ .build();
+
+ mSatelliteStats.onSatelliteIncomingDatagramMetrics(param);
+
+ ArgumentCaptor<SatelliteIncomingDatagram> captor =
+ ArgumentCaptor.forClass(SatelliteIncomingDatagram.class);
+ verify(mPersistAtomsStorage).addSatelliteIncomingDatagramStats(captor.capture());
+ SatelliteIncomingDatagram stats = captor.getValue();
+ assertEquals(param.getResultCode(), stats.resultCode);
+ assertEquals(param.getDatagramSizeBytes(), stats.datagramSizeBytes);
+ assertEquals(param.getDatagramTransferTimeMillis(), stats.datagramTransferTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onSatelliteOutgoingDatagramMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteOutgoingDatagramParams param =
+ new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
+ .setDatagramType(SatelliteProtoEnums.DATAGRAM_TYPE_LOCATION_SHARING)
+ .setResultCode(SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+ .setDatagramSizeBytes(1 * 1024)
+ .setDatagramTransferTimeMillis(3 * 1000)
+ .build();
+
+ mSatelliteStats.onSatelliteOutgoingDatagramMetrics(param);
+
+ ArgumentCaptor<SatelliteOutgoingDatagram> captor =
+ ArgumentCaptor.forClass(SatelliteOutgoingDatagram.class);
+ verify(mPersistAtomsStorage).addSatelliteOutgoingDatagramStats(captor.capture());
+ SatelliteOutgoingDatagram stats = captor.getValue();
+ assertEquals(param.getDatagramType(), stats.datagramType);
+ assertEquals(param.getResultCode(), stats.resultCode);
+ assertEquals(param.getDatagramSizeBytes(), stats.datagramSizeBytes);
+ assertEquals(param.getDatagramTransferTimeMillis(), stats.datagramTransferTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onSatelliteProvisionMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteProvisionParams param =
+ new SatelliteStats.SatelliteProvisionParams.Builder()
+ .setResultCode(SatelliteProtoEnums.SATELLITE_SERVICE_PROVISION_IN_PROGRESS)
+ .setProvisioningTimeSec(5 * 1000)
+ .setIsProvisionRequest(true)
+ .setIsCanceled(false)
+ .build();
+
+ mSatelliteStats.onSatelliteProvisionMetrics(param);
+
+ ArgumentCaptor<SatelliteProvision> captor =
+ ArgumentCaptor.forClass(SatelliteProvision.class);
+ verify(mPersistAtomsStorage).addSatelliteProvisionStats(captor.capture());
+ SatelliteProvision stats = captor.getValue();
+ assertEquals(param.getResultCode(), stats.resultCode);
+ assertEquals(param.getProvisioningTimeSec(), stats.provisioningTimeSec);
+ assertEquals(param.getIsProvisionRequest(), stats.isProvisionRequest);
+ assertEquals(param.getIsCanceled(), stats.isCanceled);
+
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ public void onSatelliteSosMessageRecommenderMetrics_withAtoms() throws Exception {
+ SatelliteStats.SatelliteSosMessageRecommenderParams param =
+ new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
+ .setDisplaySosMessageSent(true)
+ .setCountOfTimerStarted(5)
+ .setImsRegistered(false)
+ .setCellularServiceState(TelephonyProtoEnums.SERVICE_STATE_OUT_OF_SERVICE)
+ .build();
+
+ mSatelliteStats.onSatelliteSosMessageRecommender(param);
+
+ ArgumentCaptor<SatelliteSosMessageRecommender> captor =
+ ArgumentCaptor.forClass(SatelliteSosMessageRecommender.class);
+ verify(mPersistAtomsStorage).addSatelliteSosMessageRecommenderStats(captor.capture());
+ SatelliteSosMessageRecommender stats = captor.getValue();
+ assertEquals(param.isDisplaySosMessageSent(),
+ stats.isDisplaySosMessageSent);
+ assertEquals(param.getCountOfTimerStarted(), stats.countOfTimerStarted);
+ assertEquals(param.isImsRegistered(), stats.isImsRegistered);
+ assertEquals(param.getCellularServiceState(), stats.cellularServiceState);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
index 96db966..7b66a52 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -16,6 +16,15 @@
package com.android.internal.telephony.metrics;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
+import static android.telephony.TelephonyManager.DATA_UNKNOWN;
+
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN;
+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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
@@ -105,6 +114,9 @@
mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(DATA_CONNECTED).when(mDataNetworkController).getInternetDataNetworkState();
+ doReturn(mDataNetworkController).when(mSecondPhone).getDataNetworkController();
+
mServiceStateStats = new TestableServiceStateStats(mPhone);
}
@@ -139,6 +151,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -149,7 +162,9 @@
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanCsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(DATA_UNKNOWN).when(mDataNetworkController).getInternetDataNetworkState();
mServiceStateStats.onServiceStateChanged(mServiceState);
mServiceStateStats.incTimeMillis(100L);
@@ -171,6 +186,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(false, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -216,6 +232,7 @@
doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
mockLimitedService(TelephonyManager.NETWORK_TYPE_UMTS);
doReturn(-1).when(mPhone).getCarrierId();
+ doReturn(DATA_UNKNOWN).when(mDataNetworkController).getInternetDataNetworkState();
mServiceStateStats.onServiceStateChanged(mServiceState);
mServiceStateStats.incTimeMillis(100L);
@@ -237,6 +254,7 @@
assertEquals(-1, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(true, state.isEmergencyOnly);
+ assertEquals(false, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -248,8 +266,10 @@
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanCsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
doReturn(-1).when(mPhone).getCarrierId();
+ doReturn(DATA_UNKNOWN).when(mDataNetworkController).getInternetDataNetworkState();
mServiceStateStats.onServiceStateChanged(mServiceState);
mServiceStateStats.incTimeMillis(100L);
@@ -271,6 +291,7 @@
assertEquals(-1, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(false, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -302,6 +323,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -313,6 +335,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -349,6 +372,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
@@ -360,6 +384,50 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onInternetDataNetworkDisconnected() throws Exception {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.onInternetDataNetworkDisconnected();
+ mServiceStateStats.incTimeMillis(200L);
+ mServiceStateStats.conclude();
+
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ assertNotSame(captor.getAllValues().get(0), captor.getAllValues().get(1));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ assertEquals(false, state.isEmergencyOnly);
+ assertEquals(false, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -388,6 +456,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -395,6 +464,7 @@
@SmallTest
public void onServiceStateChanged_differentDataRats() throws Exception {
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanCsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
mServiceStateStats.onServiceStateChanged(mServiceState);
@@ -424,6 +494,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -435,6 +506,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratTo);
@@ -475,6 +547,7 @@
assertFalse(state.isMultiSim);
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -485,6 +558,7 @@
assertFalse(state.isMultiSim);
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -513,6 +587,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(0L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -553,6 +628,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -564,6 +640,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -575,6 +652,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(400L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(3);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -586,6 +664,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(800L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -630,6 +709,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
@@ -641,6 +721,7 @@
assertEquals(-1, state.carrierId);
assertEquals(5000L, state.totalTimeMillis);
assertEquals(true, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -652,6 +733,7 @@
assertEquals(CARRIER2_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -696,6 +778,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -707,6 +790,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -718,6 +802,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(400L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
@@ -774,6 +859,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -785,6 +871,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -796,6 +883,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = serviceStateCaptor.getAllValues().get(3);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -807,6 +895,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
@@ -867,6 +956,7 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -878,6 +968,71 @@
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void getVoiceRat_bearer() throws Exception {
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ mockWwanCsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mImsStats).getImsVoiceRadioTech();
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS));
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS));
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN));
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ mockWwanCsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS));
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS));
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, mServiceStateStats.getVoiceRat(
+ mPhone, mServiceState, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN));
+ }
+
+ @Test
+ @SmallTest
+ public void onFoldStateChanged_modemOff() throws Exception {
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+
+ mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onFoldStateChanged_LTEMode() throws Exception {
+ // Using default service state for LTE with fold state unknown
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED);
+ mServiceStateStats.incTimeMillis(1000L);
+ // Same fold state as before should not generate a new atom
+ mServiceStateStats.onFoldStateChanged(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED);
+ mServiceStateStats.incTimeMillis(1000L);
+
+ // There should be 2 service state updates
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN, state.foldState);
+ state = captor.getAllValues().get(1);
+ assertEquals(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_CLOSED, state.foldState);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 111e5d2..c26dd22 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -25,11 +25,6 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
import static com.android.internal.telephony.data.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
import static org.junit.Assert.assertArrayEquals;
@@ -89,6 +84,11 @@
import java.util.Arrays;
public class TelephonyMetricsTest extends TelephonyTest {
+ private static final String FAKE_ADDRESS = "99.88.77.66";
+ private static final String FAKE_DNS = "55.66.77.88";
+ private static final String FAKE_GATEWAY = "11.22.33.44";
+ private static final String FAKE_IFNAME = "FAKE IFNAME";
+ private static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
// Mocked classes
private ImsCallSession mImsCallSession;
private ServiceState mServiceState;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index 5ae6d39..2ca0b16 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -30,10 +30,6 @@
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -217,6 +213,8 @@
@SmallTest
public void singleImsCall_moRejected() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(0L).when(mImsConnection0).getDurationMillis();
@@ -228,8 +226,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = true;
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
@@ -272,6 +268,8 @@
@SmallTest
public void singleImsCall_moFailed() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(0L).when(mImsConnection0).getDurationMillis();
@@ -284,6 +282,7 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_SIP_FORBIDDEN);
expectedCall.setupFailed = true;
+ expectedCall.setupDurationMillis = 200;
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
VoiceCallRatUsage expectedRatUsage =
@@ -311,8 +310,54 @@
@Test
@SmallTest
+ public void singleImsCall_moStartFailed() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(false).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(0L).when(mImsConnection0).getDurationMillis();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED);
+ expectedCall.setupFailed = true;
+ expectedCall.setupDurationMillis = 200;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 2200L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.DIALING).when(mImsCall0).getState();
+ doReturn(Call.State.DIALING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsDial(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsCallStartFailed(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
public void singleImsCall_moAccepted() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(1000L).when(mImsConnection0).getDurationMillis();
@@ -324,8 +369,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
@@ -374,6 +417,8 @@
@SmallTest
public void singleImsCall_mtRejected() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(0L).when(mImsConnection0).getDurationMillis();
@@ -419,8 +464,59 @@
@Test
@SmallTest
+ public void singleImsCall_mtStartFailed() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(0L).when(mImsConnection0).getDurationMillis();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED);
+ expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 8000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(8000L);
+ mVoiceCallSessionStats0.onImsCallStartFailed(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
public void singleImsCall_mtAccepted() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -432,8 +528,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -482,6 +576,8 @@
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -527,6 +623,8 @@
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -572,6 +670,8 @@
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
setServiceState(mSecondServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mSecondPhone).getImsPhone();
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(2000L).when(mImsConnection1).getCreateTime();
doReturn(mImsCall1).when(mImsConnection1).getCall();
@@ -611,6 +711,8 @@
@SmallTest
public void singleImsCall_emergency() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(true).when(mImsConnection0).isEmergencyCall();
@@ -652,6 +754,8 @@
@SmallTest
public void singleImsCall_roaming() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mServiceState).getVoiceRoaming();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -693,6 +797,8 @@
@SmallTest
public void singleImsCall_codecSwitch() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(300000L).when(mImsConnection0).getDurationMillis();
@@ -704,8 +810,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask =
@@ -747,6 +851,8 @@
@SmallTest
public void singleImsCall_ratSwitch() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(600001L).when(mImsConnection0).getDurationMillis();
@@ -758,8 +864,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -770,6 +874,7 @@
expectedCall.bandAtEnd = 0;
expectedCall.callDuration =
VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
@@ -796,9 +901,11 @@
mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
mVoiceCallSessionStats0.setTimeMillis(4000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_HSPA);
+ doReturn(TelephonyManager.NETWORK_TYPE_HSPA).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(6000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(8000L);
mVoiceCallSessionStats0.onImsCallTerminated(
@@ -819,8 +926,101 @@
@Test
@SmallTest
+ public void singleImsCall_ratSwitchToUnknown() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(600001L).when(mImsConnection0).getDurationMillis();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_USER_TERMINATED);
+ expectedCall.setupDurationMillis = 80;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.ratSwitchCount = 3L;
+ expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.bandAtEnd = 0;
+ expectedCall.callDuration =
+ VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ VoiceCallRatUsage expectedRatUsageLte =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
+ VoiceCallRatUsage expectedRatUsageHspa =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 4000L, 6000L, 1L);
+ VoiceCallRatUsage expectedRatUsageUmts =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 6000L, 8000L, 1L);
+ VoiceCallRatUsage expectedRatUsageUnknown =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ 8000L,
+ 10000L,
+ 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsAcceptCall(List.of(mImsConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(2280L);
+ doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+ mVoiceCallSessionStats0.setTimeMillis(4000L);
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_HSPA);
+ doReturn(TelephonyManager.NETWORK_TYPE_HSPA).when(mImsStats).getImsVoiceRadioTech();
+ mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
+ mVoiceCallSessionStats0.setTimeMillis(6000L);
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
+ mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
+ mVoiceCallSessionStats0.setTimeMillis(8000L);
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mImsStats).getImsVoiceRadioTech();
+ mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
+ mVoiceCallSessionStats0.setTimeMillis(10000L);
+ mVoiceCallSessionStats0.onImsCallTerminated(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertSortedProtoArrayEquals(
+ new VoiceCallRatUsage[] {
+ expectedRatUsageLte,
+ expectedRatUsageHspa,
+ expectedRatUsageUmts,
+ expectedRatUsageUnknown
+ },
+ ratUsage.get());
+ }
+
+ @Test
+ @SmallTest
public void singleImsCall_rttOnDial() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(true).when(mImsConnection0).hasRttTextStream();
@@ -865,6 +1065,8 @@
@SmallTest
public void singleImsCall_rttStartedMidCall() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -876,8 +1078,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -918,6 +1118,8 @@
@SmallTest
public void concurrentImsCalls_firstCallHangupFirst() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
// call 0 starts first, MO
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -929,8 +1131,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
- expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -941,6 +1141,7 @@
expectedCall0.ratSwitchCount = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall0.bandAtEnd = 0;
+ expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -952,8 +1153,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall1.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -964,6 +1163,7 @@
expectedCall1.ratSwitchCount = 2L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall1.bandAtEnd = 0;
+ expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1012,6 +1212,7 @@
// RAT change, LTE to HSPA
mVoiceCallSessionStats0.setTimeMillis(80000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_HSPA);
+ doReturn(TelephonyManager.NETWORK_TYPE_HSPA).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
// call 0 hangup by remote
mVoiceCallSessionStats0.setTimeMillis(90000L);
@@ -1021,6 +1222,7 @@
// RAT change, HSPA to UMTS
mVoiceCallSessionStats0.setTimeMillis(100000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
// call 1 hangup by local
mVoiceCallSessionStats0.setTimeMillis(120000L);
@@ -1046,6 +1248,8 @@
@SmallTest
public void concurrentImsCalls_firstCallHangupLast() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
// call 0 starts first, MO
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -1057,8 +1261,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1069,6 +1271,7 @@
expectedCall0.ratSwitchCount = 2L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall0.bandAtEnd = 0;
+ expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -1080,8 +1283,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
- expectedCall1.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1092,6 +1293,7 @@
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall1.bandAtEnd = 0;
+ expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1140,6 +1342,7 @@
// RAT change, LTE to HSPA
mVoiceCallSessionStats0.setTimeMillis(80000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_HSPA);
+ doReturn(TelephonyManager.NETWORK_TYPE_HSPA).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
// call 1 hangup by remote
mVoiceCallSessionStats0.setTimeMillis(90000L);
@@ -1149,6 +1352,7 @@
// RAT change, HSPA to UMTS
mVoiceCallSessionStats0.setTimeMillis(100000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
// call 0 hangup by local
mVoiceCallSessionStats0.setTimeMillis(120000L);
@@ -1174,6 +1378,8 @@
@SmallTest
public void concurrentImsCalls_firstCallHangupDuringSecondCallSetup() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
// call 0 starts first, MO
doReturn(false).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -1185,8 +1391,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1207,8 +1411,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
- expectedCall1.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1219,6 +1421,7 @@
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall1.bandAtEnd = 0;
+ expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1264,6 +1467,7 @@
// RAT change, LTE to HSPA
mVoiceCallSessionStats0.setTimeMillis(80000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_HSPA);
+ doReturn(TelephonyManager.NETWORK_TYPE_HSPA).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
// call 1 hangup by remote
mVoiceCallSessionStats0.setTimeMillis(90000L);
@@ -1298,8 +1502,6 @@
DisconnectCause.NORMAL);
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.ratSwitchCount = 1L;
@@ -1308,6 +1510,7 @@
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1365,10 +1568,12 @@
expectedCall.bandAtEnd = 0;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = true;
+ expectedCall.setupDurationMillis = 13000;
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 0L;
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1379,12 +1584,14 @@
mVoiceCallSessionStats0.setTimeMillis(2000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(Call.State.DIALING).when(mCsCall0).getState();
doReturn(Call.State.DIALING).when(mGsmConnection0).getState();
doReturn(DisconnectCause.NOT_DISCONNECTED).when(mGsmConnection0).getDisconnectCause();
mVoiceCallSessionStats0.onRilDial(mGsmConnection0);
mVoiceCallSessionStats0.setTimeMillis(3000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(15000L);
doReturn(DisconnectCause.LOST_SIGNAL).when(mGsmConnection0).getDisconnectCause();
@@ -1416,8 +1623,6 @@
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.ratSwitchCount = 1L;
@@ -1425,6 +1630,7 @@
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1481,8 +1687,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_UMTS,
DisconnectCause.NORMAL);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
expectedCall.setupDurationMillis = 0;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.setupFailed = true;
@@ -1538,8 +1742,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_UMTS,
DisconnectCause.NORMAL);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
expectedCall.setupDurationMillis = 500;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.setupFailed = false;
@@ -1586,6 +1788,7 @@
@SmallTest
public void singleCall_srvccFailed() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -1598,8 +1801,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccFailureCount = 2L;
@@ -1609,6 +1810,7 @@
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 10000L, 1L);
@@ -1635,6 +1837,7 @@
TelephonyManager.SRVCC_STATE_HANDOVER_STARTED);
mVoiceCallSessionStats0.setTimeMillis(10000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(11000L);
mVoiceCallSessionStats0.onRilSrvccStateChanged(
@@ -1664,6 +1867,7 @@
@SmallTest
public void singleCall_srvccCanceled() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -1676,8 +1880,6 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 2L;
@@ -1732,6 +1934,7 @@
@SmallTest
public void singleCall_srvccSuccess() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -1747,8 +1950,6 @@
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
- expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 1L;
@@ -1761,6 +1962,7 @@
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 7000L, 1L);
@@ -1796,6 +1998,7 @@
TelephonyManager.SRVCC_STATE_HANDOVER_STARTED);
mVoiceCallSessionStats0.setTimeMillis(7000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(8000L);
mVoiceCallSessionStats0.onRilSrvccStateChanged(
@@ -1825,6 +2028,7 @@
@SmallTest
public void concurrentCalls_srvcc() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(mImsPhone).when(mPhone).getImsPhone();
doReturn(new ArrayList(List.of(mImsConnection0, mImsConnection1)))
.when(mImsPhone)
@@ -1843,8 +2047,6 @@
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
expectedCall0.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
- expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1857,6 +2059,7 @@
expectedCall0.bandAtEnd = 0;
expectedCall0.srvccCompleted = true;
expectedCall0.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
+ expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -1871,8 +2074,6 @@
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
expectedCall1.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
- expectedCall1.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1885,6 +2086,7 @@
expectedCall1.bandAtEnd = 0;
expectedCall1.srvccCompleted = true;
expectedCall1.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
+ expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1930,6 +2132,7 @@
// RAT change, LTE to UMTS
mVoiceCallSessionStats0.setTimeMillis(80000L);
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mImsStats).getImsVoiceRadioTech();
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats0.setTimeMillis(85000L);
mVoiceCallSessionStats0.onRilSrvccStateChanged(
@@ -2115,7 +2318,6 @@
call.bearerAtStart = bearer;
call.bearerAtEnd = bearer;
call.direction = direction;
- call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
@@ -2124,6 +2326,7 @@
call.ratAtStart = rat;
call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.lastKnownRat = rat;
call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
@@ -2149,7 +2352,6 @@
call.bearerAtStart = bearer;
call.bearerAtEnd = bearer;
call.direction = direction;
- call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
@@ -2158,6 +2360,7 @@
call.ratAtStart = rat;
call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.lastKnownRat = rat;
call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
index 98e27cb..580d533 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
@@ -316,7 +316,8 @@
script.toggleAirplaneMode(true);
// Verify the state machine did the right thing.
- // Check the time zone suggestion was withdrawn (time is not currently withdrawn).
+ // Check the previous time and time zone suggestions based on cleared signals were
+ // withdrawn.
script.verifyTimeAndTimeZoneSuggestedAndReset(
EMPTY_TIME_SUGGESTION, EMPTY_TIME_ZONE_SUGGESTION);
@@ -374,6 +375,57 @@
}
/**
+ * Regression test for b/227047106: If only country state was actually cleared (i.e. if there
+ * was no NITZ signal to clear) then the existing tz suggestion wasn't withdrawn. Simulates a
+ * flight from the UK to the US.
+ */
+ @Test
+ public void test_airplaneModeClearsState_onlyCountryCleared_b227047106() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO.mutableCopy();
+ int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3);
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Pre-flight: Simulate a device receiving signals that allow it to detect the time zone.
+ String preFlightCountryIsoCode = scenario.getNetworkCountryIsoCode();
+
+ // Simulate receiving the country.
+ script.countryReceived(preFlightCountryIsoCode);
+
+ // Verify the state machine did the right thing.
+ TelephonyTimeZoneSuggestion expectedPreFlightTimeZoneSuggestion =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ SLOT_INDEX, preFlightCountryIsoCode, null);
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedPreFlightTimeZoneSuggestion);
+
+ // Check NitzStateMachineImpl internal state exposed for tests.
+ assertNull(mNitzStateMachineImpl.getLatestNitzData());
+ assertNull(mNitzStateMachineImpl.getLastNitzDataCleared());
+
+ // Boarded flight: Airplane mode turned on / time zone detection still enabled.
+ // The NitzStateMachine must lose all state and stop having an opinion about time zone.
+
+ // Simulate the passage of time and update the device realtime clock.
+ scenario.incrementTime(timeStepMillis);
+ script.incrementTime(timeStepMillis);
+
+ // Simulate airplane mode being turned on.
+ script.toggleAirplaneMode(true);
+
+ // Verify the state machine did the right thing. Check the previous time zone suggestion
+ // was withdrawn. An empty time suggestion is also made, but this is for simplicity in the
+ // implementation, not a requirement.
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ EMPTY_TIME_SUGGESTION, EMPTY_TIME_ZONE_SUGGESTION);
+
+ // Check NitzStateMachineImpl internal state exposed for tests.
+ assertNull(mNitzStateMachineImpl.getLatestNitzData());
+ assertNull(mNitzStateMachineImpl.getLastNitzDataCleared());
+ }
+
+ /**
* Confirm losing the network / NITZ doesn't clear country state.
*/
@Test
@@ -858,7 +910,8 @@
suggestedTimes.set(timeSuggestion);
if (timeSuggestion.getUnixEpochTime() != null) {
// The fake time service just uses the latest suggestion.
- mFakeDeviceState.currentTimeMillis = timeSuggestion.getUnixEpochTime().getValue();
+ mFakeDeviceState.currentTimeMillis =
+ timeSuggestion.getUnixEpochTime().getUnixEpochTimeMillis();
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/nitz/OWNERS
new file mode 100644
index 0000000..be0513d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 847766
+include platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaDirectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaDirectionTest.java
new file mode 100644
index 0000000..6a91d95
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaDirectionTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.telephony.satellite.AntennaDirection;
+
+import org.junit.Test;
+
+public class AntennaDirectionTest {
+
+ @Test
+ public void testParcel() {
+ AntennaDirection antennaDirection = new AntennaDirection(1, 2, 3);
+
+ Parcel p = Parcel.obtain();
+ antennaDirection.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ AntennaDirection fromParcel = AntennaDirection.CREATOR.createFromParcel(p);
+ assertThat(antennaDirection).isEqualTo(fromParcel);
+ }
+
+ @Test
+ public void testEquals() {
+ AntennaDirection antennaDirection1 = new AntennaDirection(1.12f, 1.13f, 1.14f);
+ AntennaDirection antennaDirection2 = new AntennaDirection(1.12f, 1.13f, 1.14f);
+ assertEquals(antennaDirection1, antennaDirection2);
+
+ AntennaDirection antennaDirection3 = new AntennaDirection(1.121f, 1.131f, 1.141f);
+ assertNotEquals(antennaDirection1, antennaDirection3);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaPositionTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaPositionTest.java
new file mode 100644
index 0000000..919ab83
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/AntennaPositionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.telephony.satellite.AntennaDirection;
+import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.SatelliteManager;
+
+import org.junit.Test;
+
+public class AntennaPositionTest {
+
+ private AntennaDirection mAntennaDirection = new AntennaDirection(1,1,1);
+
+ @Test
+ public void testParcel() {
+ AntennaPosition antennaPosition = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+
+ Parcel p = Parcel.obtain();
+ antennaPosition.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ AntennaPosition fromParcel = AntennaPosition.CREATOR.createFromParcel(p);
+ assertThat(antennaPosition).isEqualTo(fromParcel);
+ }
+
+ @Test
+ public void testEquals() {
+ AntennaPosition antennaPosition1 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+ AntennaPosition antennaPosition2 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+ assertEquals(antennaPosition1, antennaPosition2);
+
+ AntennaPosition antennaPosition3 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_LANDSCAPE_LEFT);
+ assertNotEquals(antennaPosition1, antennaPosition3);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
new file mode 100644
index 0000000..baa00c1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.telephony.satellite.SatelliteManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+import com.android.telephony.Rlog;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ControllerMetricsStatsTest extends TelephonyTest {
+ private static final String TAG = "ControllerMetricsStatsTest";
+
+ private static final long MODEM_ENABLED_TIME = 1000L;
+
+ private TestControllerMetricsStats mControllerMetricsStatsUT;
+ private TestSatelliteStats mTestStats;
+
+ @Mock private Context mMockContext;
+ @Spy private ControllerMetricsStats mSpyControllerMetricsStats;
+ @Mock private SatelliteStats mMockSatelliteStats;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ Rlog.d(TAG, "setUp()");
+ mTestStats = new TestSatelliteStats();
+ mControllerMetricsStatsUT =
+ new TestControllerMetricsStats(mMockContext, mTestStats);
+ mMockContext = mock(Context.class);
+ mMockSatelliteStats = mock(SatelliteStats.class);
+ mSpyControllerMetricsStats =
+ Mockito.spy(ControllerMetricsStats.make(mMockContext, mMockSatelliteStats));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Rlog.d(TAG, "tearDown()");
+ mTestStats = null;
+ mControllerMetricsStatsUT = null;
+ mMockSatelliteStats = null;
+ mSpyControllerMetricsStats = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testReportServiceEnablementSuccessCount() {
+ mTestStats.initializeParams();
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportServiceEnablementSuccessCount();
+ }
+ assertEquals(10, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testReportServiceEnablementFailCount() {
+ mTestStats.initializeParams();
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportServiceEnablementFailCount();
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(10, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testReportOutgoingDatagramSuccessCount() {
+ mTestStats.initializeParams();
+ int datagramType = SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramSuccessCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(10, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ datagramType = SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramSuccessCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ datagramType = SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramSuccessCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void reportOutgoingDatagramFailCount() {
+ mTestStats.initializeParams();
+ int datagramType = SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramFailCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(10, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ datagramType = SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramFailCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ datagramType = SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportOutgoingDatagramFailCount(datagramType);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(10, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testReportIncomingDatagramCount() {
+ mTestStats.initializeParams();
+
+ int result = SatelliteManager.SATELLITE_ERROR_NONE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(10, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_SERVER_ERROR;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(10, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(10, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testReportProvisionCount() {
+ mTestStats.initializeParams();
+
+ int result = SatelliteManager.SATELLITE_ERROR_NONE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportProvisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(10, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_SERVER_ERROR;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportProvisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(10, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportProvisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(10, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testReportDeprovisionCount() {
+ mTestStats.initializeParams();
+
+ int result = SatelliteManager.SATELLITE_ERROR_NONE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportDeprovisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(10, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(0, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_SERVER_ERROR;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportDeprovisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(10, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+
+ result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+ for (int i = 0; i < 10; i++) {
+ mControllerMetricsStatsUT.reportDeprovisionCount(result);
+ }
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsSuccess);
+ assertEquals(0, mTestStats.mCountOfSatelliteServiceEnablementsFail);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfOutgoingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramSuccess);
+ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+ assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+ assertEquals(0, mTestStats.mCountOfProvisionSuccess);
+ assertEquals(0, mTestStats.mCountOfProvisionFail);
+ assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
+ assertEquals(10, mTestStats.mCountOfDeprovisionFail);
+ assertEquals(0, mTestStats.mTotalServiceUptimeSec);
+ assertEquals(0, mTestStats.mTotalBatteryConsumptionPercent);
+ assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
+ mTestStats.initializeParams();
+ }
+
+ @Test
+ public void testOnSatelliteEnabled() {
+ // set precondition
+ doReturn(false).when(mSpyControllerMetricsStats).isSatelliteModemOn();
+
+ doNothing().when(mSpyControllerMetricsStats).startCaptureBatteryLevel();
+ doReturn(MODEM_ENABLED_TIME).when(mSpyControllerMetricsStats).getCurrentTime();
+
+ // test object
+ mSpyControllerMetricsStats.onSatelliteEnabled();
+
+ // verification
+ verify(mSpyControllerMetricsStats).startCaptureBatteryLevel();
+ verify(mSpyControllerMetricsStats).getCurrentTime();
+ }
+
+ @Test
+ public void testOnSatelliteDisabled() {
+ // set precondition
+ doNothing().when(mMockSatelliteStats).onSatelliteControllerMetrics(any());
+
+ doReturn(true).when(mSpyControllerMetricsStats).isSatelliteModemOn();
+
+ doReturn(0).when(mSpyControllerMetricsStats).captureTotalServiceUpTimeSec();
+ doReturn(0).when(mSpyControllerMetricsStats).captureTotalBatteryConsumptionPercent(any());
+ doReturn(0).when(mSpyControllerMetricsStats).captureTotalBatteryChargeTimeSec();
+
+ // test object
+ mSpyControllerMetricsStats.onSatelliteDisabled();
+
+ // verification
+ verify(mSpyControllerMetricsStats).captureTotalServiceUpTimeSec();
+ verify(mSpyControllerMetricsStats).captureTotalBatteryConsumptionPercent(any());
+ verify(mSpyControllerMetricsStats).captureTotalBatteryChargeTimeSec();
+ }
+
+ static class TestControllerMetricsStats extends ControllerMetricsStats {
+ TestControllerMetricsStats(Context context, SatelliteStats satelliteStats) {
+ super(context, satelliteStats);
+ }
+ }
+
+ static class TestSatelliteStats extends SatelliteStats {
+ public int mCountOfSatelliteServiceEnablementsSuccess;
+ public int mCountOfSatelliteServiceEnablementsFail;
+ public int mCountOfOutgoingDatagramSuccess;
+ public int mCountOfOutgoingDatagramFail;
+ public int mCountOfIncomingDatagramSuccess;
+ public int mCountOfIncomingDatagramFail;
+ public int mCountOfDatagramTypeSosSmsSuccess;
+ public int mCountOfDatagramTypeSosSmsFail;
+ public int mCountOfDatagramTypeLocationSharingSuccess;
+ public int mCountOfDatagramTypeLocationSharingFail;
+ public int mCountOfProvisionSuccess;
+ public int mCountOfProvisionFail;
+ public int mCountOfDeprovisionSuccess;
+ public int mCountOfDeprovisionFail;
+ public int mTotalServiceUptimeSec;
+ public int mTotalBatteryConsumptionPercent;
+ public int mTotalBatteryChargedTimeSec;
+
+ @Override
+ public synchronized void onSatelliteControllerMetrics(SatelliteControllerParams param) {
+ mCountOfSatelliteServiceEnablementsSuccess +=
+ param.getCountOfSatelliteServiceEnablementsSuccess();
+ mCountOfSatelliteServiceEnablementsFail +=
+ param.getCountOfSatelliteServiceEnablementsFail();
+ mCountOfOutgoingDatagramSuccess += param.getCountOfOutgoingDatagramSuccess();
+ mCountOfOutgoingDatagramFail += param.getCountOfOutgoingDatagramFail();
+ mCountOfIncomingDatagramSuccess += param.getCountOfIncomingDatagramSuccess();
+ mCountOfIncomingDatagramFail += param.getCountOfIncomingDatagramFail();
+ mCountOfDatagramTypeSosSmsSuccess += param.getCountOfDatagramTypeSosSmsSuccess();
+ mCountOfDatagramTypeSosSmsFail += param.getCountOfDatagramTypeSosSmsFail();
+ mCountOfDatagramTypeLocationSharingSuccess +=
+ param.getCountOfDatagramTypeLocationSharingSuccess();
+ mCountOfDatagramTypeLocationSharingFail +=
+ param.getCountOfDatagramTypeLocationSharingFail();
+ mCountOfProvisionSuccess += param.getCountOfProvisionSuccess();
+ mCountOfProvisionFail += param.getCountOfProvisionFail();
+ mCountOfDeprovisionSuccess += param.getCountOfDeprovisionSuccess();
+ mCountOfDeprovisionFail += param.getCountOfDeprovisionFail();
+ mTotalServiceUptimeSec += param.getTotalServiceUptimeSec();
+ mTotalBatteryConsumptionPercent += param.getTotalBatteryConsumptionPercent();
+ mTotalBatteryChargedTimeSec += param.getTotalBatteryChargedTimeSec();
+ }
+
+ public void initializeParams() {
+ mCountOfSatelliteServiceEnablementsSuccess = 0;
+ mCountOfSatelliteServiceEnablementsFail = 0;
+ mCountOfOutgoingDatagramSuccess = 0;
+ mCountOfOutgoingDatagramFail = 0;
+ mCountOfIncomingDatagramSuccess = 0;
+ mCountOfIncomingDatagramFail = 0;
+ mCountOfDatagramTypeSosSmsSuccess = 0;
+ mCountOfDatagramTypeSosSmsFail = 0;
+ mCountOfDatagramTypeLocationSharingSuccess = 0;
+ mCountOfDatagramTypeLocationSharingFail = 0;
+ mCountOfProvisionSuccess = 0;
+ mCountOfProvisionFail = 0;
+ mCountOfDeprovisionSuccess = 0;
+ mCountOfDeprovisionFail = 0;
+ mTotalServiceUptimeSec = 0;
+ mTotalBatteryConsumptionPercent = 0;
+ mTotalBatteryChargedTimeSec = 0;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
new file mode 100644
index 0000000..bc1d767
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DatagramDispatcherTest extends TelephonyTest {
+ private static final String TAG = "DatagramDispatcherTest";
+ private static final int SUB_ID = 0;
+ private static final int DATAGRAM_TYPE1 = SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
+ private static final int DATAGRAM_TYPE2 = SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING;
+ private static final String TEST_MESSAGE = "This is a test datagram message";
+ private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
+
+ private DatagramDispatcher mDatagramDispatcherUT;
+ private TestDatagramDispatcher mTestDemoModeDatagramDispatcher;
+
+ @Mock private DatagramController mMockDatagramController;
+ @Mock private DatagramReceiver mMockDatagramReceiver;
+ @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
+ @Mock private ControllerMetricsStats mMockControllerMetricsStats;
+
+ /** Variables required to send datagram in the unit tests. */
+ LinkedBlockingQueue<Integer> mResultListener;
+ SatelliteDatagram mDatagram;
+ InOrder mInOrder;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+
+ replaceInstance(DatagramController.class, "sInstance", null,
+ mMockDatagramController);
+ replaceInstance(DatagramReceiver.class, "sInstance", null,
+ mMockDatagramReceiver);
+ replaceInstance(SatelliteModemInterface.class, "sInstance", null,
+ mMockSatelliteModemInterface);
+ replaceInstance(ControllerMetricsStats.class, "sInstance", null,
+ mMockControllerMetricsStats);
+
+ mDatagramDispatcherUT = DatagramDispatcher.make(mContext, Looper.myLooper(),
+ mMockDatagramController);
+ mTestDemoModeDatagramDispatcher = new TestDatagramDispatcher(mContext, Looper.myLooper(),
+ mMockDatagramController);
+
+ mResultListener = new LinkedBlockingQueue<>(1);
+ mDatagram = new SatelliteDatagram(TEST_MESSAGE.getBytes());
+ mInOrder = inOrder(mMockDatagramController);
+ when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ mDatagramDispatcherUT.destroy();
+ mDatagramDispatcherUT = null;
+ mTestDemoModeDatagramDispatcher = null;
+ mResultListener = null;
+ mDatagram = null;
+ mInOrder = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_usingSatelliteModemInterface_success() throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[3];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
+ anyBoolean(), anyBoolean(), any(Message.class));
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verifyNoMoreInteractions(mMockDatagramController);
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_usingSatelliteModemInterface_failure() throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[3];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null,
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_SERVICE_ERROR)))
+ .sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
+ anyBoolean(), anyBoolean(), any(Message.class));
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE2, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
+ eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verifyNoMoreInteractions(mMockDatagramController);
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_usingCommandsInterface_phoneNull() throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {null});
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
+ eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verifyNoMoreInteractions(mMockDatagramController);
+
+ assertThat(mResultListener.peek())
+ .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_usingCommandsInterface_success() throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
+ anyBoolean());
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE2, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verifyNoMoreInteractions(mMockDatagramController);
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_usingCommandsInterface_failure() throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null,
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_SERVICE_ERROR)))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
+ anyBoolean());
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
+ eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verifyNoMoreInteractions(mMockDatagramController);
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_DemoMode_Align_Success() throws Exception {
+ mTestDemoModeDatagramDispatcher.setDemoMode(true);
+ mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(true);
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
+ anyBoolean());
+
+ mTestDemoModeDatagramDispatcher.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ mTestDemoModeDatagramDispatcher.setDemoMode(false);
+ mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_DemoMode_Align_failed() throws Exception {
+ long previousTimer = mTestDemoModeDatagramDispatcher.getSatelliteAlignedTimeoutDuration();
+ mTestDemoModeDatagramDispatcher.setDemoMode(true);
+ mTestDemoModeDatagramDispatcher.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
+ mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mTestDemoModeDatagramDispatcher.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
+ anyBoolean());
+
+ mTestDemoModeDatagramDispatcher.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ processAllFutureMessages();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+ anyInt(), eq(SatelliteManager.SATELLITE_NOT_REACHABLE));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_NOT_REACHABLE);
+ mTestDemoModeDatagramDispatcher.setDemoMode(false);
+ mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+ mTestDemoModeDatagramDispatcher.setDuration(previousTimer);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_DemoMode_data_type_location_sharing() throws Exception {
+ mTestDemoModeDatagramDispatcher.setDemoMode(true);
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
+ anyBoolean());
+
+ mTestDemoModeDatagramDispatcher.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE2, mDatagram,
+ true, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ mTestDemoModeDatagramDispatcher.setDemoMode(false);
+ mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+ }
+
+ @Test
+ public void testSatelliteModemBusy_modemPollingDatagram_sendingDelayed() {
+ when(mMockDatagramController.isPollingInIdleState()).thenReturn(false);
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+ processAllMessages();
+ // As modem is busy receiving datagrams, sending datagram did not proceed further.
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ verifyNoMoreInteractions(mMockDatagramController);
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateListening() {
+ mDatagramDispatcherUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+ processAllMessages();
+ verifyNoMoreInteractions(mMockDatagramController);
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateOff_modemSendingDatagrams() {
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+
+ mDatagramDispatcherUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+ eq(1), eq(SatelliteManager.SATELLITE_REQUEST_ABORTED));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateOff_modemNotSendingDatagrams() {
+ mDatagramDispatcherUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ private static class TestDatagramDispatcher extends DatagramDispatcher {
+ private long mLong = SATELLITE_ALIGN_TIMEOUT;
+
+ TestDatagramDispatcher(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ super(context, looper, datagramController);
+ }
+
+ @Override
+ protected void setDemoMode(boolean isDemoMode) {
+ super.setDemoMode(isDemoMode);
+ }
+
+ @Override
+ protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+ super.onDeviceAlignedWithSatellite(isAligned);
+ }
+
+ @Override
+ protected long getSatelliteAlignedTimeoutDuration() {
+ return mLong;
+ }
+
+ public void setDuration(long duration) {
+ mLong = duration;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
new file mode 100644
index 0000000..1c3777d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.provider.Telephony;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.AsyncResult;
+import android.os.Looper;
+import android.os.Message;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Pair;
+
+import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DatagramReceiverTest extends TelephonyTest {
+ private static final String TAG = "DatagramReceiverTest";
+ private static final int SUB_ID = 0;
+ private static final String TEST_MESSAGE = "This is a test datagram message";
+ private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
+
+ private DatagramReceiver mDatagramReceiverUT;
+ private DatagramReceiver.SatelliteDatagramListenerHandler mSatelliteDatagramListenerHandler;
+ private TestDatagramReceiver mTestDemoModeDatagramReceiver;
+
+ @Mock private SatelliteController mMockSatelliteController;
+ @Mock private DatagramController mMockDatagramController;
+ @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
+ @Mock private ControllerMetricsStats mMockControllerMetricsStats;
+
+ /** Variables required to receive datagrams in the unit tests. */
+ LinkedBlockingQueue<Integer> mResultListener;
+ SatelliteDatagram mDatagram;
+ InOrder mInOrder;
+ private FakeSatelliteProvider mFakeSatelliteProvider;
+ private MockContentResolver mMockContentResolver;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+
+ // Setup mock satellite provider DB.
+ mFakeSatelliteProvider = new FakeSatelliteProvider();
+ mMockContentResolver = new MockContentResolver();
+ mMockContentResolver.addProvider(
+ Telephony.SatelliteDatagrams.PROVIDER_NAME, mFakeSatelliteProvider);
+ doReturn(mMockContentResolver).when(mContext).getContentResolver();
+
+ replaceInstance(SatelliteController.class, "sInstance", null, mMockSatelliteController);
+ replaceInstance(DatagramController.class, "sInstance", null,
+ mMockDatagramController);
+ replaceInstance(SatelliteModemInterface.class, "sInstance", null,
+ mMockSatelliteModemInterface);
+ replaceInstance(ControllerMetricsStats.class, "sInstance", null,
+ mMockControllerMetricsStats);
+
+ mDatagramReceiverUT = DatagramReceiver.make(mContext, Looper.myLooper(),
+ mMockDatagramController);
+ mTestDemoModeDatagramReceiver = new TestDatagramReceiver(mContext, Looper.myLooper(),
+ mMockDatagramController);
+ mSatelliteDatagramListenerHandler = new DatagramReceiver.SatelliteDatagramListenerHandler(
+ Looper.myLooper(), SUB_ID);
+
+ mResultListener = new LinkedBlockingQueue<>(1);
+ mDatagram = new SatelliteDatagram(TEST_MESSAGE.getBytes());
+ mInOrder = inOrder(mMockDatagramController);
+
+ when(mMockDatagramController.isSendingInIdleState()).thenReturn(true);
+ when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
+ processAllMessages();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ mFakeSatelliteProvider.shutdown();
+ mDatagramReceiverUT.destroy();
+ mDatagramReceiverUT = null;
+ mTestDemoModeDatagramReceiver = null;
+ mResultListener = null;
+ mDatagram = null;
+ mInOrder = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_usingSatelliteModemInterface_success()
+ throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_usingSatelliteModemInterface_failure()
+ throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
+ new AsyncResult(message.obj, null,
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_SERVICE_ERROR)))
+ .sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ eq(0), eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_usingCommandsInterface_phoneNull()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {null});
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ eq(0), eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ assertThat(mResultListener.peek())
+ .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_usingCommandsInterface_success()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).pollPendingSatelliteDatagrams(any(Message.class));
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_usingCommandsInterface_failure()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+
+ mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
+ new AsyncResult(message.obj, null,
+ new SatelliteManager.SatelliteException(
+ SatelliteManager.SATELLITE_SERVICE_ERROR)))
+ .sendToTarget();
+ return null;
+ }).when(mPhone).pollPendingSatelliteDatagrams(any(Message.class));
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ eq(0), eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+ }
+
+ @Test
+ public void testSatelliteDatagramReceived_receiveNone() {
+ mSatelliteDatagramListenerHandler.obtainMessage(1 /*EVENT_SATELLITE_DATAGRAM_RECEIVED*/,
+ new AsyncResult(null, new Pair<>(null, 0), null))
+ .sendToTarget();
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testSatelliteDatagramReceived_success_zeroPendingCount() {
+ mSatelliteDatagramListenerHandler.obtainMessage(1 /*EVENT_SATELLITE_DATAGRAM_RECEIVED*/,
+ new AsyncResult(null, new Pair<>(mDatagram, 0), null))
+ .sendToTarget();
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testSatelliteDatagramReceived_success_nonZeroPendingCount() {
+ mSatelliteDatagramListenerHandler.obtainMessage(1 /*EVENT_SATELLITE_DATAGRAM_RECEIVED*/,
+ new AsyncResult(null, new Pair<>(mDatagram, 10), null))
+ .sendToTarget();
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS),
+ eq(10), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_DemoMode_Align_succeed() throws Exception {
+ // Checks invalid case only as SatelliteController does not exist in unit test
+ mTestDemoModeDatagramReceiver.setDemoMode(true);
+ mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(true);
+ when(mMockDatagramController.getDemoModeDatagram()).thenReturn(mDatagram);
+ mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+ processAllMessages();
+ verify(mMockDatagramController, times(1)).getDemoModeDatagram();
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ assertThat(mResultListener.peek())
+ .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams_DemoMode_Align_failed() throws Exception {
+ // Checks invalid case only as SatelliteController does not exist in unit test
+ long previousTimer = mTestDemoModeDatagramReceiver.getSatelliteAlignedTimeoutDuration();
+ mTestDemoModeDatagramReceiver.setDemoMode(true);
+ mTestDemoModeDatagramReceiver.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
+ mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(false);
+ when(mMockDatagramController.getDemoModeDatagram()).thenReturn(mDatagram);
+ mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+ processAllMessages();
+ verify(mMockDatagramController, never()).getDemoModeDatagram();
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ processAllFutureMessages();
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_NOT_REACHABLE));
+ verify(mMockDatagramController)
+ .updateReceiveStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ anyInt(),
+ eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ assertThat(mResultListener.peek())
+ .isEqualTo(SatelliteManager.SATELLITE_NOT_REACHABLE);
+
+ mTestDemoModeDatagramReceiver.setDemoMode(false);
+ mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(false);
+ mTestDemoModeDatagramReceiver.setDuration(previousTimer);
+ }
+
+ @Test
+ public void testSatelliteModemBusy_modemSendingDatagram_pollingFailure() {
+ when(mMockDatagramController.isSendingInIdleState()).thenReturn(false);
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+ processAllMessages();
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_MODEM_BUSY);
+ }
+
+ @Test
+ public void testSatelliteModemBusy_modemPollingDatagrams_pollingFailure() {
+ when(mMockDatagramController.isSendingInIdleState()).thenReturn(false);
+ when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
+
+ mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+ processAllMessages();
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_MODEM_BUSY);
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateIdle() {
+ mDatagramReceiverUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ processAllMessages();
+ verifyNoMoreInteractions(mMockDatagramController);
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateOff_modemReceivingDatagrams() {
+ when(mMockDatagramController.isReceivingDatagrams()).thenReturn(true);
+ when(mMockDatagramController.getReceivePendingCount()).thenReturn(10);
+
+ mDatagramReceiverUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
+ eq(10), eq(SatelliteManager.SATELLITE_REQUEST_ABORTED));
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testOnSatelliteModemStateChanged_modemStateOff_modemNotReceivingDatagrams() {
+ when(mMockDatagramController.isReceivingDatagrams()).thenReturn(false);
+
+ mDatagramReceiverUT.onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+
+ processAllMessages();
+
+ mInOrder.verify(mMockDatagramController)
+ .updateReceiveStatus(anyInt(),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
+ eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+ }
+
+ @Test
+ public void testRegisterForSatelliteDatagram_satelliteNotSupported() {
+ when(mMockSatelliteController.isSatelliteSupported()).thenReturn(false);
+
+ ISatelliteDatagramCallback callback = new ISatelliteDatagramCallback() {
+ @Override
+ public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+ int pendingCount, IVoidConsumer callback) throws RemoteException {
+ logd("onSatelliteDatagramReceived");
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+
+ assertThat(mDatagramReceiverUT.registerForSatelliteDatagram(SUB_ID, callback))
+ .isEqualTo(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+ }
+
+ private static class TestDatagramReceiver extends DatagramReceiver {
+ private long mLong = SATELLITE_ALIGN_TIMEOUT;
+
+ TestDatagramReceiver(@NonNull Context context, @NonNull Looper looper,
+ @NonNull DatagramController datagramController) {
+ super(context, looper, datagramController);
+ }
+
+ @Override
+ protected void setDemoMode(boolean isDemoMode) {
+ super.setDemoMode(isDemoMode);
+ }
+
+ @Override
+ protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+ super.onDeviceAlignedWithSatellite(isAligned);
+ }
+
+ @Override
+ protected long getSatelliteAlignedTimeoutDuration() {
+ return mLong;
+ }
+
+ public void setDuration(long duration) {
+ mLong = duration;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/FakeSatelliteProvider.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/FakeSatelliteProvider.java
new file mode 100644
index 0000000..b0c6a81
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/FakeSatelliteProvider.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.test.mock.MockContentProvider;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+public class FakeSatelliteProvider extends MockContentProvider {
+ private static final String TAG = "FakeSatelliteProvider";
+
+ private InMemorySatelliteProviderDbHelper mDbHelper = new InMemorySatelliteProviderDbHelper();
+
+ private class InMemorySatelliteProviderDbHelper extends SQLiteOpenHelper {
+
+ InMemorySatelliteProviderDbHelper() {
+ super(InstrumentationRegistry.getTargetContext(),
+ null, // db file name is null for in-memory db
+ null, // CursorFactory is null by default
+ 1); // db version is no-op for tests
+ Log.d(TAG, "InMemorySatelliteProviderDbHelper creating in-memory database");
+ }
+ public static String getStringForDatagramTableCreation(String tableName) {
+ return "CREATE TABLE " + tableName + "("
+ + Telephony.SatelliteDatagrams.COLUMN_UNIQUE_KEY_DATAGRAM_ID
+ + " INTEGER PRIMARY KEY,"
+ + Telephony.SatelliteDatagrams.COLUMN_DATAGRAM + " BLOB DEFAULT ''" +
+ ");";
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d(TAG, "InMemoryTelephonyProviderDbHelper onCreate:"
+ + " creating satellite incoming datagram table");
+ db.execSQL(getStringForDatagramTableCreation(Telephony.SatelliteDatagrams.TABLE_NAME));
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // Do nothing.
+ }
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ Log.d(TAG, "insert. values=" + values);
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ long id = db.insert(Telephony.SatelliteDatagrams.TABLE_NAME, null, values);
+ return ContentUris.withAppendedId(Telephony.SatelliteDatagrams.CONTENT_URI, id);
+ }
+
+ @Override
+ public synchronized int delete(Uri url, String where, String[] whereArgs) {
+ return mDbHelper.getWritableDatabase()
+ .delete(Telephony.SatelliteDatagrams.TABLE_NAME, where, whereArgs);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mDbHelper.getReadableDatabase().query(Telephony.SatelliteDatagrams.TABLE_NAME,
+ projection, selection, selectionArgs, null, null, sortOrder);
+ }
+
+ @Override
+ public Bundle call(String method, String request, Bundle args) {
+ return null;
+ }
+
+ @Override
+ public final int update(Uri uri, ContentValues values, String where, String[] selectionArgs) {
+ // Do nothing.
+ return 0;
+ }
+
+ @Override
+ public void shutdown() {
+ mDbHelper.close();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteCapabilitiesTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteCapabilitiesTest.java
new file mode 100644
index 0000000..29473c9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteCapabilitiesTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.telephony.satellite.AntennaDirection;
+import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteManager;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class SatelliteCapabilitiesTest {
+
+ private AntennaDirection mAntennaDirection = new AntennaDirection(1,1,1);
+
+ @Test
+ public void testParcel() {
+ Set<Integer> satelliteRadioTechnologies = new HashSet<>();
+ satelliteRadioTechnologies.add(SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN);
+ satelliteRadioTechnologies.add(SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN);
+
+ Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>();
+ AntennaPosition antennaPosition = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+ antennaPositionMap.put(SatelliteManager.DISPLAY_MODE_OPENED, antennaPosition);
+
+ SatelliteCapabilities capabilities = new SatelliteCapabilities(satelliteRadioTechnologies,
+ true, 10, antennaPositionMap);
+
+ Parcel p = Parcel.obtain();
+ capabilities.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SatelliteCapabilities fromParcel = SatelliteCapabilities.CREATOR.createFromParcel(p);
+ assertThat(capabilities).isEqualTo(fromParcel);
+ }
+
+ @Test
+ public void testParcel_emptySatelliteRadioTechnologies() {
+ Set<Integer> satelliteRadioTechnologies = new HashSet<>();
+
+ Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>();
+ AntennaPosition antennaPosition1 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+ AntennaPosition antennaPosition2 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_LANDSCAPE_LEFT);
+ antennaPositionMap.put(SatelliteManager.DISPLAY_MODE_OPENED, antennaPosition1);
+ antennaPositionMap.put(SatelliteManager.DISPLAY_MODE_CLOSED, antennaPosition2);
+
+ SatelliteCapabilities capabilities = new SatelliteCapabilities(satelliteRadioTechnologies,
+ false, 100, antennaPositionMap);
+
+ Parcel p = Parcel.obtain();
+ capabilities.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SatelliteCapabilities fromParcel = SatelliteCapabilities.CREATOR.createFromParcel(p);
+ assertThat(capabilities).isEqualTo(fromParcel);
+ }
+
+
+ @Test
+ public void testParcel_emptyAntennaPosition() {
+ Set<Integer> satelliteRadioTechnologies = new HashSet<>();
+ satelliteRadioTechnologies.add(SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN);
+
+ SatelliteCapabilities capabilities = new SatelliteCapabilities(satelliteRadioTechnologies,
+ true, 0, new HashMap<>());
+
+ Parcel p = Parcel.obtain();
+ capabilities.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SatelliteCapabilities fromParcel = SatelliteCapabilities.CREATOR.createFromParcel(p);
+ assertThat(capabilities).isEqualTo(fromParcel);
+ }
+
+ @Test
+ public void testEquals() {
+ Set<Integer> satelliteRadioTechnologies = new HashSet<>();
+ satelliteRadioTechnologies.add(SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN);
+ satelliteRadioTechnologies.add(SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN);
+
+ AntennaPosition antennaPosition1 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT);
+ AntennaPosition antennaPosition2 = new AntennaPosition(mAntennaDirection,
+ SatelliteManager.DEVICE_HOLD_POSITION_LANDSCAPE_LEFT);
+
+ Map<Integer, AntennaPosition> antennaPositionMap1 = new HashMap<>();
+ antennaPositionMap1.put(SatelliteManager.DISPLAY_MODE_OPENED, antennaPosition1);
+ antennaPositionMap1.put(SatelliteManager.DISPLAY_MODE_CLOSED, antennaPosition2);
+
+ SatelliteCapabilities satelliteCapabilities1 =
+ new SatelliteCapabilities(satelliteRadioTechnologies, true, 10,
+ antennaPositionMap1);
+ SatelliteCapabilities satelliteCapabilities2 =
+ new SatelliteCapabilities(satelliteRadioTechnologies, true, 10,
+ antennaPositionMap1);
+ assertEquals(satelliteCapabilities1, satelliteCapabilities2);
+
+ Map<Integer, AntennaPosition> antennaPositionMap2 = new HashMap<>();
+ antennaPositionMap2.put(SatelliteManager.DISPLAY_MODE_CLOSED, antennaPosition1);
+ antennaPositionMap2.put(SatelliteManager.DISPLAY_MODE_OPENED, antennaPosition2);
+ satelliteCapabilities2 =
+ new SatelliteCapabilities(satelliteRadioTechnologies, true, 10,
+ antennaPositionMap2);
+ assertNotEquals(satelliteCapabilities1, satelliteCapabilities2);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
new file mode 100644
index 0000000..f6ed2e2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -0,0 +1,2025 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static android.telephony.satellite.SatelliteManager.KEY_DEMO_MODE_ENABLED;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_CAPABILITIES;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ENABLED;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN;
+import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN;
+import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_NOT_AUTHORIZED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_NO_RESOURCES;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS;
+
+import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_FALSE;
+import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_TRUE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ResultReceiver;
+import android.telephony.Rlog;
+import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
+import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SatelliteControllerTest extends TelephonyTest {
+ private static final String TAG = "SatelliteControllerTest";
+ private static final long TIMEOUT = 500;
+ private static final int SUB_ID = 0;
+ private static final int MAX_BYTES_PER_OUT_GOING_DATAGRAM = 339;
+ private static final String TEST_SATELLITE_TOKEN = "TEST_SATELLITE_TOKEN";
+ private static final String TEST_NEXT_SATELLITE_TOKEN = "TEST_NEXT_SATELLITE_TOKEN";
+
+ private TestSatelliteController mSatelliteControllerUT;
+ private TestSharedPreferences mSharedPreferences;
+
+ @Mock private DatagramController mMockDatagramController;
+ @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
+ @Mock private SatelliteSessionController mMockSatelliteSessionController;
+ @Mock private PointingAppController mMockPointingAppController;
+ @Mock private ControllerMetricsStats mMockControllerMetricsStats;
+ @Mock private ProvisionMetricsStats mMockProvisionMetricsStats;
+ @Mock private SessionMetricsStats mMockSessionMetricsStats;
+ private List<Integer> mIIntegerConsumerResults = new ArrayList<>();
+ @Mock private ISatelliteTransmissionUpdateCallback mStartTransmissionUpdateCallback;
+ @Mock private ISatelliteTransmissionUpdateCallback mStopTransmissionUpdateCallback;
+ private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
+ private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("mIIntegerConsumer: result=" + result);
+ mIIntegerConsumerResults.add(result);
+ try {
+ mIIntegerConsumerSemaphore.release();
+ } catch (Exception ex) {
+ loge("mIIntegerConsumer: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private boolean mIsSatelliteServiceSupported = true;
+ private boolean mIsPointingRequired = true;
+ private Set<Integer> mSupportedRadioTechnologies = new HashSet<>(Arrays.asList(
+ NT_RADIO_TECHNOLOGY_NR_NTN,
+ NT_RADIO_TECHNOLOGY_EMTC_NTN,
+ NT_RADIO_TECHNOLOGY_PROPRIETARY));
+ private SatelliteCapabilities mSatelliteCapabilities = new SatelliteCapabilities(
+ mSupportedRadioTechnologies, mIsPointingRequired, MAX_BYTES_PER_OUT_GOING_DATAGRAM,
+ new HashMap<>());
+ private Semaphore mSatelliteCapabilitiesSemaphore = new Semaphore(0);
+ private SatelliteCapabilities mQueriedSatelliteCapabilities = null;
+ private int mQueriedSatelliteCapabilitiesResultCode = SATELLITE_ERROR_NONE;
+ private ResultReceiver mSatelliteCapabilitiesReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedSatelliteCapabilitiesResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_CAPABILITIES)) {
+ mQueriedSatelliteCapabilities = resultData.getParcelable(
+ KEY_SATELLITE_CAPABILITIES, SatelliteCapabilities.class);
+ } else {
+ loge("KEY_SATELLITE_SUPPORTED does not exist.");
+ mQueriedSatelliteCapabilities = null;
+ }
+ } else {
+ logd("mSatelliteSupportReceiver: resultCode=" + resultCode);
+ mQueriedSatelliteCapabilities = null;
+ }
+ try {
+ mSatelliteCapabilitiesSemaphore.release();
+ } catch (Exception ex) {
+ loge("mSatelliteSupportReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private boolean mQueriedSatelliteSupported = false;
+ private int mQueriedSatelliteSupportedResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mSatelliteSupportSemaphore = new Semaphore(0);
+ private ResultReceiver mSatelliteSupportReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedSatelliteSupportedResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
+ mQueriedSatelliteSupported = resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
+ } else {
+ loge("KEY_SATELLITE_SUPPORTED does not exist.");
+ mQueriedSatelliteSupported = false;
+ }
+ } else {
+ logd("mSatelliteSupportReceiver: resultCode=" + resultCode);
+ mQueriedSatelliteSupported = false;
+ }
+ try {
+ mSatelliteSupportSemaphore.release();
+ } catch (Exception ex) {
+ loge("mSatelliteSupportReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private boolean mQueriedIsSatelliteEnabled = false;
+ private int mQueriedIsSatelliteEnabledResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mIsSatelliteEnabledSemaphore = new Semaphore(0);
+ private ResultReceiver mIsSatelliteEnabledReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedIsSatelliteEnabledResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_ENABLED)) {
+ mQueriedIsSatelliteEnabled = resultData.getBoolean(KEY_SATELLITE_ENABLED);
+ } else {
+ loge("KEY_SATELLITE_ENABLED does not exist.");
+ mQueriedIsSatelliteEnabled = false;
+ }
+ } else {
+ logd("mIsSatelliteEnableReceiver: resultCode=" + resultCode);
+ mQueriedIsSatelliteEnabled = false;
+ }
+ try {
+ mIsSatelliteEnabledSemaphore.release();
+ } catch (Exception ex) {
+ loge("mIsSatelliteEnableReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private boolean mQueriedIsDemoModeEnabled = false;
+ private int mQueriedIsDemoModeEnabledResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mIsDemoModeEnabledSemaphore = new Semaphore(0);
+ private ResultReceiver mIsDemoModeEnabledReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedIsDemoModeEnabledResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_DEMO_MODE_ENABLED)) {
+ mQueriedIsDemoModeEnabled = resultData.getBoolean(KEY_DEMO_MODE_ENABLED);
+ } else {
+ loge("KEY_DEMO_MODE_ENABLED does not exist.");
+ mQueriedIsDemoModeEnabled = false;
+ }
+ } else {
+ logd("mIsSatelliteEnableReceiver: resultCode=" + resultCode);
+ mQueriedIsDemoModeEnabled = false;
+ }
+ try {
+ mIsDemoModeEnabledSemaphore.release();
+ } catch (Exception ex) {
+ loge("mIsDemoModeEnabledReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private boolean mQueriedIsSatelliteProvisioned = false;
+ private int mQueriedIsSatelliteProvisionedResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mIsSatelliteProvisionedSemaphore = new Semaphore(0);
+ private ResultReceiver mIsSatelliteProvisionedReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedIsSatelliteProvisionedResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
+ mQueriedIsSatelliteProvisioned =
+ resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
+ } else {
+ loge("KEY_SATELLITE_PROVISIONED does not exist.");
+ mQueriedIsSatelliteProvisioned = false;
+ }
+ } else {
+ mQueriedIsSatelliteProvisioned = false;
+ }
+ try {
+ mIsSatelliteProvisionedSemaphore.release();
+ } catch (Exception ex) {
+ loge("mIsSatelliteProvisionedReceiver: Got exception in releasing semaphore ex="
+ + ex);
+ }
+ }
+ };
+
+ private boolean mQueriedSatelliteAllowed = false;
+ private int mQueriedSatelliteAllowedResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mSatelliteAllowedSemaphore = new Semaphore(0);
+ private ResultReceiver mSatelliteAllowedReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedSatelliteAllowedResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+ mQueriedSatelliteAllowed = resultData.getBoolean(
+ KEY_SATELLITE_COMMUNICATION_ALLOWED);
+ } else {
+ loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+ mQueriedSatelliteAllowed = false;
+ }
+ } else {
+ logd("mSatelliteSupportReceiver: resultCode=" + resultCode);
+ mQueriedSatelliteAllowed = false;
+ }
+ try {
+ mSatelliteAllowedSemaphore.release();
+ } catch (Exception ex) {
+ loge("mSatelliteAllowedReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+ private int mQueriedSatelliteVisibilityTime = -1;
+ private int mSatelliteNextVisibilityTime = 3600;
+ private int mQueriedSatelliteVisibilityTimeResultCode = SATELLITE_ERROR_NONE;
+ private Semaphore mSatelliteVisibilityTimeSemaphore = new Semaphore(0);
+ private ResultReceiver mSatelliteVisibilityTimeReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mQueriedSatelliteVisibilityTimeResultCode = resultCode;
+ if (resultCode == SATELLITE_ERROR_NONE) {
+ if (resultData.containsKey(KEY_SATELLITE_NEXT_VISIBILITY)) {
+ mQueriedSatelliteVisibilityTime = resultData.getInt(
+ KEY_SATELLITE_NEXT_VISIBILITY);
+ } else {
+ loge("KEY_SATELLITE_NEXT_VISIBILITY does not exist.");
+ mQueriedSatelliteVisibilityTime = -1;
+ }
+ } else {
+ logd("mSatelliteSupportReceiver: resultCode=" + resultCode);
+ mQueriedSatelliteVisibilityTime = -1;
+ }
+ try {
+ mSatelliteVisibilityTimeSemaphore.release();
+ } catch (Exception ex) {
+ loge("mSatelliteAllowedReceiver: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+ };
+
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+
+ replaceInstance(DatagramController.class, "sInstance", null,
+ mMockDatagramController);
+ replaceInstance(SatelliteModemInterface.class, "sInstance", null,
+ mMockSatelliteModemInterface);
+ replaceInstance(SatelliteSessionController.class, "sInstance", null,
+ mMockSatelliteSessionController);
+ replaceInstance(PointingAppController.class, "sInstance", null,
+ mMockPointingAppController);
+ replaceInstance(ControllerMetricsStats.class, "sInstance", null,
+ mMockControllerMetricsStats);
+ replaceInstance(ProvisionMetricsStats.class, "sInstance", null,
+ mMockProvisionMetricsStats);
+ replaceInstance(SessionMetricsStats.class, "sInstance", null,
+ mMockSessionMetricsStats);
+
+ mSharedPreferences = new TestSharedPreferences();
+ when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
+ doReturn(mIsSatelliteServiceSupported)
+ .when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ setUpResponseForRequestSatelliteCapabilities(
+ mSatelliteCapabilities, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+ doNothing().when(mMockDatagramController).setDemoMode(anyBoolean());
+ doNothing().when(mMockSatelliteSessionController)
+ .onSatelliteEnabledStateChanged(anyBoolean());
+ doNothing().when(mMockSatelliteSessionController).setDemoMode(anyBoolean());
+ doNothing().when(mMockControllerMetricsStats).onSatelliteEnabled();
+ doNothing().when(mMockControllerMetricsStats).reportServiceEnablementSuccessCount();
+ doNothing().when(mMockControllerMetricsStats).reportServiceEnablementFailCount();
+ doReturn(mMockSessionMetricsStats)
+ .when(mMockSessionMetricsStats).setInitializationResult(anyInt());
+ doReturn(mMockSessionMetricsStats)
+ .when(mMockSessionMetricsStats).setRadioTechnology(anyInt());
+ doNothing().when(mMockSessionMetricsStats).reportSessionMetrics();
+
+ doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
+ .setResultCode(anyInt());
+ doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
+ .setIsProvisionRequest(eq(false));
+ doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
+ doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
+ mSatelliteControllerUT = new TestSatelliteController(mContext, Looper.myLooper());
+ verify(mMockSatelliteModemInterface).registerForSatelliteProvisionStateChanged(
+ any(Handler.class),
+ eq(26) /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */,
+ eq(null));
+ verify(mMockSatelliteModemInterface).registerForPendingDatagrams(
+ any(Handler.class),
+ eq(27) /* EVENT_PENDING_DATAGRAMS */,
+ eq(null));
+ verify(mMockSatelliteModemInterface).registerForSatelliteModemStateChanged(
+ any(Handler.class),
+ eq(28) /* EVENT_SATELLITE_MODEM_STATE_CHANGED */,
+ eq(null));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ mSatelliteControllerUT = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testRequestIsSatelliteCommunicationAllowedForCurrentLocation() {
+ mSatelliteAllowedSemaphore.drainPermits();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
+ mSatelliteAllowedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode);
+
+ resetSatelliteControllerUT();
+ mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
+ mSatelliteAllowedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteAllowedForCurrentLocation(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
+ mSatelliteAllowedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteAllowedResultCode);
+ assertTrue(mQueriedSatelliteAllowed);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
+ mSatelliteAllowedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(
+ SATELLITE_INVALID_MODEM_STATE);
+ mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
+ mSatelliteAllowedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteAllowedResultCode);
+ }
+
+ @Test
+ public void testRequestTimeForNextSatelliteVisibility() {
+ mSatelliteVisibilityTimeSemaphore.drainPermits();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteVisibilityTimeResultCode);
+
+ resetSatelliteControllerUT();
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
+ SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
+ SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, mQueriedSatelliteVisibilityTimeResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
+ SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteVisibilityTimeResultCode);
+ assertEquals(mSatelliteNextVisibilityTime, mQueriedSatelliteVisibilityTime);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestTimeForNextSatelliteVisibility(
+ SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestTimeForNextSatelliteVisibility(
+ SATELLITE_INVALID_MODEM_STATE);
+ mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+ mSatelliteVisibilityTimeReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+ }
+
+ @Test
+ public void testRequestSatelliteEnabled() {
+ mIsSatelliteEnabledSemaphore.drainPermits();
+
+ // Fail to enable satellite when SatelliteController is not fully loaded yet.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ // Fail to enable satellite when the device does not support satellite.
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+
+ // Fail to enable satellite when the device is not provisioned yet.
+ mIIntegerConsumerResults.clear();
+ resetSatelliteControllerUT();
+ verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(false));
+ verify(mMockSatelliteSessionController, times(1)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(1)).setDemoMode(eq(false));
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+
+ // Successfully disable satellite when radio is turned off.
+ mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+ setRadioPower(false);
+ processAllMessages();
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+ assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+ assertEquals(
+ SATELLITE_MODE_ENABLED_FALSE, mSatelliteControllerUT.satelliteModeSettingValue);
+ verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(false));
+ verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
+ verify(mMockControllerMetricsStats, times(1)).onSatelliteDisabled();
+
+ // Fail to enable satellite when radio is off.
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ // Radio is not on, can not enable satellite
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ setRadioPower(true);
+ processAllMessages();
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+
+ // Fail to enable satellite with an error response from modem when radio is on.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_INVALID_MODEM_STATE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+ verify(mMockPointingAppController, never()).startPointingUI(anyBoolean());
+ assertFalse(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+ verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementFailCount();
+
+ // Successfully enable satellite when radio is on.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+ assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+ assertEquals(SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
+ verify(mMockPointingAppController).startPointingUI(eq(false));
+ verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
+ verify(mMockSatelliteSessionController, times(3)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
+ verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled();
+ verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
+ verify(mMockSessionMetricsStats, times(2)).setInitializationResult(anyInt());
+ verify(mMockSessionMetricsStats, times(2)).setRadioTechnology(anyInt());
+ verify(mMockSessionMetricsStats, times(2)).reportSessionMetrics();
+
+ // Successfully enable satellite when it is already enabled.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+
+ // Fail to enable satellite with a different demo mode when it is already enabled.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, true, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_ARGUMENTS, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+
+ // Disable satellite.
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+
+ // Disable satellite when satellite is already disabled.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+
+ // Disable satellite with a different demo mode when satellite is already disabled.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, true, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+
+ // Send a second request while the first request in progress
+ mIIntegerConsumerResults.clear();
+ setUpNoResponseForRequestSatelliteEnabled(true, false);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_REQUEST_IN_PROGRESS, (long) mIIntegerConsumerResults.get(0));
+
+ mIIntegerConsumerResults.clear();
+ resetSatelliteControllerUTToSupportedAndProvisionedState();
+ // Should receive callback for the above request when satellite modem is turned off.
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ // Move to satellite-disabling in progress.
+ setUpNoResponseForRequestSatelliteEnabled(false, false);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+
+ // Disable is in progress. Thus, a new request to enable satellite will be rejected.
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR, (long) mIIntegerConsumerResults.get(0));
+
+ mIIntegerConsumerResults.clear();
+ resetSatelliteControllerUTToOffAndProvisionedState();
+ // Should receive callback for the above request when satellite modem is turned off.
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ /**
+ * Make areAllRadiosDisabled return false and move mWaitingForRadioDisabled to true, which
+ * will lead to no response for requestSatelliteEnabled.
+ */
+ mSatelliteControllerUT.allRadiosDisabled = false;
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+
+ resetSatelliteControllerUTEnabledState();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ // We should receive 2 callbacks for the above 2 requests.
+ assertTrue(waitForIIntegerConsumerResult(2));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(1));
+
+ resetSatelliteControllerUTToOffAndProvisionedState();
+
+ // Repeat the same test as above but with error response from modem for the second request
+ mSatelliteControllerUT.allRadiosDisabled = false;
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+
+ resetSatelliteControllerUTEnabledState();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_NO_RESOURCES);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ // We should receive 2 callbacks for the above 2 requests.
+ assertTrue(waitForIIntegerConsumerResult(2));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ assertEquals(SATELLITE_NO_RESOURCES, (long) mIIntegerConsumerResults.get(1));
+ mSatelliteControllerUT.allRadiosDisabled = true;
+ }
+
+ @Test
+ public void testRequestSatelliteCapabilities() {
+ mSatelliteCapabilitiesSemaphore.drainPermits();
+ mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteCapabilitiesResultCode);
+
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteCapabilitiesResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestSatelliteCapabilities(mSatelliteCapabilities, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteCapabilitiesResultCode);
+ assertEquals(mSatelliteCapabilities, mQueriedSatelliteCapabilities);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteCapabilitiesResultCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_INVALID_MODEM_STATE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteCapabilitiesResultCode);
+ }
+
+ @Test
+ public void testStartSatelliteTransmissionUpdates() {
+ mIIntegerConsumerSemaphore.drainPermits();
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ verify(mMockPointingAppController).registerForSatelliteTransmissionUpdates(anyInt(),
+ eq(mStartTransmissionUpdateCallback), any());
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockPointingAppController).startSatelliteTransmissionUpdates(any(Message.class),
+ any(Phone.class));
+ verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(true));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_INVALID_TELEPHONY_STATE);
+ mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStartTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
+ any(), eq(mStartTransmissionUpdateCallback), any(Phone.class));
+ verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(false));
+ }
+
+ @Test
+ public void testStopSatelliteTransmissionUpdates() {
+ mIIntegerConsumerSemaphore.drainPermits();
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
+ any(), eq(mStopTransmissionUpdateCallback), any(Phone.class));
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockPointingAppController).stopSatelliteTransmissionUpdates(any(Message.class),
+ any(Phone.class));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_INVALID_TELEPHONY_STATE);
+ mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+ mStopTransmissionUpdateCallback);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+ }
+
+ @Test
+ public void testRequestIsDemoModeEnabled() {
+ mIsDemoModeEnabledSemaphore.drainPermits();
+ resetSatelliteControllerUT();
+ mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+ assertTrue(waitForRequestIsDemoModeEnabledResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
+ assertFalse(mQueriedIsDemoModeEnabled);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+ assertTrue(waitForRequestIsDemoModeEnabledResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedIsDemoModeEnabledResultCode);
+ assertFalse(mQueriedIsDemoModeEnabled);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+ assertTrue(waitForRequestIsDemoModeEnabledResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
+ assertFalse(mQueriedIsDemoModeEnabled);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+ assertTrue(waitForRequestIsDemoModeEnabledResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, mQueriedIsDemoModeEnabledResultCode);
+ assertFalse(mQueriedIsDemoModeEnabled);
+
+ resetSatelliteControllerUT();
+ boolean isDemoModeEnabled = mSatelliteControllerUT.isDemoModeEnabled();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+ assertTrue(waitForRequestIsDemoModeEnabledResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, mQueriedIsDemoModeEnabledResultCode);
+ assertEquals(isDemoModeEnabled, mQueriedIsDemoModeEnabled);
+ }
+
+ @Test
+ public void testIsSatelliteEnabled() {
+ assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
+ setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_ERROR_NONE);
+ mIsSatelliteEnabledSemaphore.drainPermits();
+ mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteEnabledResult(1));
+ assertEquals(mSatelliteControllerUT.isSatelliteEnabled(), mQueriedIsSatelliteEnabled);
+ }
+
+ @Test
+ public void testOnSatelliteServiceConnected() {
+ verifySatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+ verifySatelliteEnabled(false, SATELLITE_INVALID_TELEPHONY_STATE);
+ verifySatelliteProvisioned(false, SATELLITE_INVALID_TELEPHONY_STATE);
+
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+
+ mSatelliteControllerUT.onSatelliteServiceConnected();
+ processAllMessages();
+
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ }
+
+ @Test
+ public void testRegisterForSatelliteModemStateChanged() {
+ ISatelliteStateCallback callback = new ISatelliteStateCallback.Stub() {
+ @Override
+ public void onSatelliteModemStateChanged(int state) {
+ logd("onSatelliteModemStateChanged: state=" + state);
+ }
+ };
+ int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
+ SUB_ID, callback);
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, errorCode);
+ verify(mMockSatelliteSessionController, never())
+ .registerForSatelliteModemStateChanged(callback);
+
+ resetSatelliteControllerUTToSupportedAndProvisionedState();
+
+ errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
+ SUB_ID, callback);
+ assertEquals(SATELLITE_ERROR_NONE, errorCode);
+ verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback);
+ }
+
+ @Test
+ public void testUnregisterForSatelliteModemStateChanged() {
+ ISatelliteStateCallback callback = new ISatelliteStateCallback.Stub() {
+ @Override
+ public void onSatelliteModemStateChanged(int state) {
+ logd("onSatelliteModemStateChanged: state=" + state);
+ }
+ };
+ mSatelliteControllerUT.unregisterForSatelliteModemStateChanged(SUB_ID, callback);
+ verify(mMockSatelliteSessionController, never())
+ .unregisterForSatelliteModemStateChanged(callback);
+
+ resetSatelliteControllerUTToSupportedAndProvisionedState();
+
+ mSatelliteControllerUT.unregisterForSatelliteModemStateChanged(SUB_ID, callback);
+ verify(mMockSatelliteSessionController).unregisterForSatelliteModemStateChanged(callback);
+ }
+
+ @Test
+ public void testRegisterForSatelliteProvisionStateChanged() {
+ Semaphore semaphore = new Semaphore(0);
+ ISatelliteProvisionStateCallback callback =
+ new ISatelliteProvisionStateCallback.Stub() {
+ @Override
+ public void onSatelliteProvisionStateChanged(boolean provisioned) {
+ logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
+ try {
+ semaphore.release();
+ } catch (Exception ex) {
+ loge("onSatelliteProvisionStateChanged: Got exception in releasing "
+ + "semaphore, ex=" + ex);
+ }
+ }
+ };
+ int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
+ SUB_ID, callback);
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, errorCode);
+
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
+ SUB_ID, callback);
+ assertEquals(SATELLITE_NOT_SUPPORTED, errorCode);
+
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
+ SUB_ID, callback);
+ assertEquals(SATELLITE_ERROR_NONE, errorCode);
+
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ assertTrue(waitForForEvents(
+ semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
+
+ mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(SUB_ID, callback);
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ assertFalse(waitForForEvents(
+ semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
+ }
+
+ @Test
+ public void testRegisterForSatelliteDatagram() {
+ ISatelliteDatagramCallback callback =
+ new ISatelliteDatagramCallback.Stub() {
+ @Override
+ public void onSatelliteDatagramReceived(long datagramId,
+ @NonNull SatelliteDatagram datagram, int pendingCount,
+ @NonNull IVoidConsumer internalAck) {
+ logd("onSatelliteDatagramReceived");
+ }
+ };
+ when(mMockDatagramController.registerForSatelliteDatagram(eq(SUB_ID), eq(callback)))
+ .thenReturn(SATELLITE_ERROR_NONE);
+ int errorCode = mSatelliteControllerUT.registerForSatelliteDatagram(SUB_ID, callback);
+ assertEquals(SATELLITE_ERROR_NONE, errorCode);
+ verify(mMockDatagramController).registerForSatelliteDatagram(eq(SUB_ID), eq(callback));
+ }
+
+ @Test
+ public void testUnregisterForSatelliteDatagram() {
+ ISatelliteDatagramCallback callback =
+ new ISatelliteDatagramCallback.Stub() {
+ @Override
+ public void onSatelliteDatagramReceived(long datagramId,
+ @NonNull SatelliteDatagram datagram, int pendingCount,
+ @NonNull IVoidConsumer internalAck) {
+ logd("onSatelliteDatagramReceived");
+ }
+ };
+ doNothing().when(mMockDatagramController)
+ .unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
+ mSatelliteControllerUT.unregisterForSatelliteDatagram(SUB_ID, callback);
+ verify(mMockDatagramController).unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
+ }
+
+ @Test
+ public void testSendSatelliteDatagram() {
+ String mText = "This is a test datagram message from user";
+ SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
+
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+ SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockDatagramController, never()).sendSatelliteDatagram(anyInt(),
+ eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(datagram), eq(true),
+ any());
+
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ sendProvisionedStateChangedEvent(false, null);
+ processAllMessages();
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+ SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockDatagramController, never()).sendSatelliteDatagram(anyInt(),
+ eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(datagram), eq(true),
+ any());
+
+ mIIntegerConsumerResults.clear();
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+ SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+ verify(mMockDatagramController, times(1)).sendSatelliteDatagram(anyInt(),
+ eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(datagram), eq(true),
+ any());
+ verify(mMockPointingAppController, times(1)).startPointingUI(eq(true));
+ }
+
+ @Test
+ public void testPollPendingSatelliteDatagrams() {
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
+
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ sendProvisionedStateChangedEvent(false, null);
+ processAllMessages();
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
+
+ mIIntegerConsumerResults.clear();
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+ verify(mMockDatagramController, times(1)).pollPendingSatelliteDatagrams(anyInt(), any());
+ }
+
+ @Test
+ public void testProvisionSatelliteService() {
+ String mText = "This is test provision data.";
+ byte[] testProvisionData = mText.getBytes();
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ ICancellationSignal cancelRemote = null;
+ mIIntegerConsumerResults.clear();
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+ assertNull(cancelRemote);
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+ assertNull(cancelRemote);
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ assertNull(cancelRemote);
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
+ SATELLITE_ERROR_NONE);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ assertNotNull(cancelRemote);
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
+ SATELLITE_NOT_AUTHORIZED);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_AUTHORIZED, (long) mIIntegerConsumerResults.get(0));
+ assertNotNull(cancelRemote);
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
+ SATELLITE_ERROR_NONE);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_NEXT_SATELLITE_TOKEN, testProvisionData, mIIntegerConsumer);
+ cancellationSignal.setRemote(cancelRemote);
+ cancellationSignal.cancel();
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockSatelliteModemInterface).deprovisionSatelliteService(
+ eq(TEST_NEXT_SATELLITE_TOKEN), any(Message.class));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpNoResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN);
+ setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
+ SATELLITE_ERROR_NONE);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+ TEST_NEXT_SATELLITE_TOKEN,
+ testProvisionData, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
+ (long) mIIntegerConsumerResults.get(0));
+ }
+
+ @Test
+ public void testDeprovisionSatelliteService() {
+ mIIntegerConsumerSemaphore.drainPermits();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+ setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+
+ resetSatelliteControllerUT();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN,
+ SATELLITE_INVALID_MODEM_STATE);
+ mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+ TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+
+ }
+
+ private void resetSatelliteControllerUTEnabledState() {
+ logd("resetSatelliteControllerUTEnabledState");
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+ doReturn(true).when(mMockSatelliteModemInterface)
+ .setSatelliteServicePackageName(anyString());
+ mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
+ processAllMessages();
+
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ }
+
+ private void resetSatelliteControllerUT() {
+ logd("resetSatelliteControllerUT");
+ // Trigger cleanUpResources
+ sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
+ processAllMessages();
+
+ // Reset all cached states
+ setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+ doReturn(true).when(mMockSatelliteModemInterface)
+ .setSatelliteServicePackageName(anyString());
+ mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
+ processAllMessages();
+ }
+
+ private void resetSatelliteControllerUTToSupportedAndProvisionedState() {
+ resetSatelliteControllerUT();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
+ verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ }
+
+ private void resetSatelliteControllerUTToOffAndProvisionedState() {
+ resetSatelliteControllerUTToSupportedAndProvisionedState();
+ sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
+ processAllMessages();
+ verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+ }
+
+ private void resetSatelliteControllerUTToOnAndProvisionedState() {
+ resetSatelliteControllerUTToOffAndProvisionedState();
+ setRadioPower(true);
+ processAllMessages();
+
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+ }
+
+ private void setUpResponseForRequestIsSatelliteEnabled(boolean isSatelliteEnabled,
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, isSatelliteEnabled, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).requestIsSatelliteEnabled(any(Message.class));
+ }
+
+ private void setUpResponseForRequestIsSatelliteSupported(
+ boolean isSatelliteSupported, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, isSatelliteSupported, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).requestIsSatelliteSupported(any(Message.class));
+ }
+
+ private void setUpResponseForRequestIsSatelliteAllowedForCurrentLocation(
+ boolean isSatelliteAllowed, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, isSatelliteAllowed, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .requestIsSatelliteCommunicationAllowedForCurrentLocation(any(Message.class));
+ }
+
+ private void setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .requestIsSatelliteCommunicationAllowedForCurrentLocation(any(Message.class));
+ }
+
+ private void setUpResponseForRequestTimeForNextSatelliteVisibility(
+ int satelliteVisibilityTime, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ int[] visibilityTime = new int[] {satelliteVisibilityTime};
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, visibilityTime, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .requestTimeForNextSatelliteVisibility(any(Message.class));
+ }
+
+ private void setUpNullResponseForRequestTimeForNextSatelliteVisibility(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .requestTimeForNextSatelliteVisibility(any(Message.class));
+ }
+
+ private void setUpResponseForRequestIsSatelliteProvisioned(
+ boolean isSatelliteProvisioned, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ int[] provisioned = new int[] {isSatelliteProvisioned ? 1 : 0};
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, provisioned, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).requestIsSatelliteProvisioned(any(Message.class));
+ }
+
+ private void setUpResponseForRequestSatelliteEnabled(
+ boolean enabled, boolean demoMode, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .requestSatelliteEnabled(eq(enabled), eq(demoMode), any(Message.class));
+ }
+
+ private void setUpNoResponseForRequestSatelliteEnabled(boolean enabled, boolean demoMode) {
+ doNothing().when(mMockSatelliteModemInterface)
+ .requestSatelliteEnabled(eq(enabled), eq(demoMode), any(Message.class));
+ }
+
+ private void setUpResponseForProvisionSatelliteService(
+ String token, byte[] provisionData, @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .provisionSatelliteService(eq(token), any(byte[].class), any(Message.class));
+ }
+
+ private void setUpNoResponseForProvisionSatelliteService(String token) {
+ doNothing().when(mMockSatelliteModemInterface)
+ .provisionSatelliteService(eq(token), any(), any(Message.class));
+ }
+
+ private void setUpResponseForDeprovisionSatelliteService(String token,
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[1];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface)
+ .deprovisionSatelliteService(eq(token), any(Message.class));
+ }
+
+ private void setUpResponseForRequestSatelliteCapabilities(
+ SatelliteCapabilities satelliteCapabilities,
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, satelliteCapabilities, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).requestSatelliteCapabilities(any(Message.class));
+ }
+
+ private boolean waitForForEvents(
+ Semaphore semaphore, int expectedNumberOfEvents, String caller) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge(caller + ": Timeout to receive the expected event");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge(caller + ": Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setUpNullResponseForRequestSatelliteCapabilities(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).requestSatelliteCapabilities(any(Message.class));
+ }
+
+ private void setUpResponseForStartSatelliteTransmissionUpdates(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockPointingAppController).startSatelliteTransmissionUpdates(any(Message.class),
+ any());
+ }
+
+ private void setUpResponseForStopSatelliteTransmissionUpdates(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockPointingAppController).stopSatelliteTransmissionUpdates(any(Message.class),
+ any());
+ }
+
+ private boolean waitForRequestIsSatelliteSupportedResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mSatelliteSupportSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive requestIsSatelliteSupported() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestIsSatelliteSupportedResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+ int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mSatelliteAllowedSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive "
+ + "requestIsSatelliteCommunicationAllowedForCurrentLocation()"
+ + " callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestIsSatelliteSupportedResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestTimeForNextSatelliteVisibilityResult(
+ int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mSatelliteVisibilityTimeSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive "
+ + "requestTimeForNextSatelliteVisibility() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestTimeForNextSatelliteVisibilityResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestIsSatelliteEnabledResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mIsSatelliteEnabledSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive requestIsSatelliteEnabled() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestIsSatelliteEnabledResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestIsSatelliteProvisionedResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mIsSatelliteProvisionedSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive requestIsSatelliteProvisioned() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestIsSatelliteProvisionedResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestSatelliteCapabilitiesResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mSatelliteCapabilitiesSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive requestSatelliteCapabilities() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestSatelliteCapabilitiesResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForRequestIsDemoModeEnabledResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mIsDemoModeEnabledSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive requestIsDemoModeEnabled() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForRequestIsDemoModeEnabled: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForIIntegerConsumerResult(int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mIIntegerConsumerSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive IIntegerConsumer() callback");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForIIntegerConsumerResult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void verifySatelliteSupported(boolean supported, int expectedErrorCode) {
+ mSatelliteSupportSemaphore.drainPermits();
+ mSatelliteControllerUT.requestIsSatelliteSupported(SUB_ID, mSatelliteSupportReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteSupportedResult(1));
+ assertEquals(expectedErrorCode, mQueriedSatelliteSupportedResultCode);
+ assertEquals(supported, mQueriedSatelliteSupported);
+ }
+
+ private void verifySatelliteEnabled(boolean enabled, int expectedErrorCode) {
+ mIsSatelliteEnabledSemaphore.drainPermits();
+ mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteEnabledResult(1));
+ assertEquals(expectedErrorCode, mQueriedIsSatelliteEnabledResultCode);
+ assertEquals(enabled, mQueriedIsSatelliteEnabled);
+ }
+
+ private void verifySatelliteProvisioned(boolean provisioned, int expectedErrorCode) {
+ mIsSatelliteProvisionedSemaphore.drainPermits();
+ mSatelliteControllerUT.requestIsSatelliteProvisioned(
+ SUB_ID, mIsSatelliteProvisionedReceiver);
+ processAllMessages();
+ assertTrue(waitForRequestIsSatelliteProvisionedResult(1));
+ assertEquals(expectedErrorCode, mQueriedIsSatelliteProvisionedResultCode);
+ assertEquals(provisioned, mQueriedIsSatelliteProvisioned);
+ }
+
+ private void sendProvisionedStateChangedEvent(boolean provisioned, Throwable exception) {
+ Message msg = mSatelliteControllerUT.obtainMessage(
+ 26 /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */);
+ msg.obj = new AsyncResult(null, provisioned, exception);
+ msg.sendToTarget();
+ }
+
+ private void sendSatelliteModemStateChangedEvent(int state, Throwable exception) {
+ Message msg = mSatelliteControllerUT.obtainMessage(
+ 28 /* EVENT_SATELLITE_MODEM_STATE_CHANGED */);
+ msg.obj = new AsyncResult(null, state, exception);
+ msg.sendToTarget();
+ }
+
+ private void setRadioPower(boolean on) {
+ mSimulatedCommands.setRadioPower(on, false, false, null);
+ }
+
+ private static void loge(String message) {
+ Rlog.e(TAG, message);
+ }
+
+ private static class TestSharedPreferences
+ implements SharedPreferences, SharedPreferences.Editor {
+ private HashMap<String, Object> mValues = new HashMap<String, Object>();
+
+ public int getValueCount() {
+ return mValues.size();
+ }
+
+ @Override
+ public Editor edit() {
+ return this;
+ }
+
+ @Override
+ public boolean contains(String key) {
+ return mValues.containsKey(key);
+ }
+
+ @Override
+ public Map<String, ?> getAll() {
+ return new HashMap<String, Object>(mValues);
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Boolean) mValues.get(key)).booleanValue();
+ }
+ return defValue;
+ }
+
+ @Override
+ public float getFloat(String key, float defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Float) mValues.get(key)).floatValue();
+ }
+ return defValue;
+ }
+
+ @Override
+ public int getInt(String key, int defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Integer) mValues.get(key)).intValue();
+ }
+ return defValue;
+ }
+
+ @Override
+ public long getLong(String key, long defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Long) mValues.get(key)).longValue();
+ }
+ return defValue;
+ }
+
+ @Override
+ public String getString(String key, String defValue) {
+ if (mValues.containsKey(key)) return (String) mValues.get(key);
+ else return defValue;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Set<String> getStringSet(String key, Set<String> defValues) {
+ if (mValues.containsKey(key)) {
+ return (Set<String>) mValues.get(key);
+ }
+ return defValues;
+ }
+
+ @Override
+ public void registerOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unregisterOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Editor putBoolean(String key, boolean value) {
+ mValues.put(key, Boolean.valueOf(value));
+ return this;
+ }
+
+ @Override
+ public Editor putFloat(String key, float value) {
+ mValues.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putInt(String key, int value) {
+ mValues.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putLong(String key, long value) {
+ mValues.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putString(String key, String value) {
+ mValues.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putStringSet(String key, Set<String> values) {
+ mValues.put(key, values);
+ return this;
+ }
+
+ @Override
+ public Editor remove(String key) {
+ mValues.remove(key);
+ return this;
+ }
+
+ @Override
+ public Editor clear() {
+ mValues.clear();
+ return this;
+ }
+
+ @Override
+ public boolean commit() {
+ return true;
+ }
+
+ @Override
+ public void apply() {
+ commit();
+ }
+ }
+
+ private static class TestSatelliteController extends SatelliteController {
+ public boolean setSettingsKeyForSatelliteModeCalled = false;
+ public boolean allRadiosDisabled = true;
+ public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
+
+ TestSatelliteController(Context context, Looper looper) {
+ super(context, looper);
+ logd("Constructing TestSatelliteController");
+ }
+
+ @Override
+ protected void initializeSatelliteModeRadios() {
+ logd("initializeSatelliteModeRadios");
+ }
+
+ @Override
+ protected void setSettingsKeyForSatelliteMode(int val) {
+ logd("setSettingsKeyForSatelliteMode: val=" + val);
+ satelliteModeSettingValue = val;
+ setSettingsKeyForSatelliteModeCalled = true;
+ }
+
+ @Override
+ protected boolean areAllRadiosDisabled() {
+ return allRadiosDisabled;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
new file mode 100644
index 0000000..418d0aa
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telephony.BinderCacheManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.telephony.satellite.SatelliteManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for SatelliteSOSMessageRecommender
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SatelliteSOSMessageRecommenderTest extends TelephonyTest {
+ private static final String TAG = "SatelliteSOSMessageRecommenderTest";
+ private static final long TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS = 500;
+ private static final int PHONE_ID = 0;
+ private static final String CALL_ID = "CALL_ID";
+ private static final String WRONG_CALL_ID = "WRONG_CALL_ID";
+ private TestSatelliteController mTestSatelliteController;
+ private TestImsManager mTestImsManager;
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private ImsManager.MmTelFeatureConnectionFactory mMmTelFeatureConnectionFactory;
+ private TestConnection mTestConnection;
+ private TestSOSMessageRecommender mTestSOSMessageRecommender;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockContext.getMainLooper()).thenReturn(Looper.myLooper());
+ when(mMockContext.getResources()).thenReturn(mResources);
+ when(mResources.getString(com.android.internal.R.string.config_satellite_service_package))
+ .thenReturn("");
+ mTestSatelliteController = new TestSatelliteController(mMockContext,
+ Looper.myLooper());
+ mTestImsManager = new TestImsManager(
+ mMockContext, PHONE_ID, mMmTelFeatureConnectionFactory, null, null, null);
+ mTestConnection = new TestConnection(CALL_ID);
+ when(mPhone.getServiceState()).thenReturn(mServiceState);
+ mTestSOSMessageRecommender = new TestSOSMessageRecommender(Looper.myLooper(),
+ mTestSatelliteController, mTestImsManager,
+ TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mPhone.isImsRegistered()).thenReturn(false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testTimeoutBeforeEmergencyCallEnd() {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+
+ // Wait for the timeout to expires
+ moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ processAllMessages();
+
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ }
+
+ @Test
+ public void testStopTrackingCallBeforeTimeout_ConnectionActive() {
+ testStopTrackingCallBeforeTimeout(Connection.STATE_ACTIVE);
+ }
+
+ @Test
+ public void testStopTrackingCallBeforeTimeout_ConnectionDisconnected() {
+ testStopTrackingCallBeforeTimeout(Connection.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void testImsRegistrationStateChangedBeforeTimeout() {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestImsManager.sendImsRegistrationStateChangedEvent(true);
+ processAllMessages();
+
+ assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
+
+ mTestImsManager.sendImsRegistrationStateChangedEvent(false);
+ processAllMessages();
+ assertEquals(2, mTestSOSMessageRecommender.getCountOfTimerStarted());
+
+ // Wait for the timeout to expires
+ moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ processAllMessages();
+
+ assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ }
+
+ @Test
+ public void testSatelliteProvisionStateChangedBeforeTimeout() {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestSatelliteController.sendProvisionStateChangedEvent(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false);
+ processAllMessages();
+
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 2, 2, 2);
+
+ mTestSatelliteController.sendProvisionStateChangedEvent(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true);
+
+ // Wait for the timeout to expires
+ moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ processAllMessages();
+
+ assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 2, 2);
+ }
+
+ @Test
+ public void testEmergencyCallRedialBeforeTimeout() {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ Phone newPhone = Mockito.mock(Phone.class);
+ when(newPhone.getServiceState()).thenReturn(mServiceState);
+ when(newPhone.isImsRegistered()).thenReturn(false);
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, newPhone);
+ processAllMessages();
+
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ /**
+ * Since {@link SatelliteSOSMessageRecommender} always uses
+ * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} when registering for provision state
+ * changed events with {@link SatelliteController}, registerForProvisionCount does
+ * not depend on Phone.
+ * <p>
+ * Since we use a single mocked ImsManager instance, registerForImsCount does not depend on
+ * Phone.
+ */
+ assertRegisterForStateChangedEventsTriggered(newPhone, 2, 2, 1);
+
+ // Wait for the timeout to expires
+ moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ processAllMessages();
+
+ assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ /**
+ * Since {@link SatelliteSOSMessageRecommender} always uses
+ * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} when unregistering for provision
+ * state changed events with {@link SatelliteController}, unregisterForProvisionCount does
+ * not depend on Phone.
+ * <p>
+ * Since we use a single mocked ImsManager instance, unregisterForImsCount does not depend
+ * on Phone.
+ */
+ assertUnregisterForStateChangedEventsTriggered(newPhone, 2, 2, 1);
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ }
+
+ @Test
+ public void testCellularServiceStateChangedBeforeTimeout_InServiceToOutOfService() {
+ testCellularServiceStateChangedBeforeTimeout(
+ ServiceState.STATE_IN_SERVICE, ServiceState.STATE_OUT_OF_SERVICE);
+ }
+
+ @Test
+ public void testCellularServiceStateChangedBeforeTimeout_InServiceToPowerOff() {
+ testCellularServiceStateChangedBeforeTimeout(
+ ServiceState.STATE_IN_SERVICE, ServiceState.STATE_POWER_OFF);
+ }
+
+ @Test
+ public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToOutOfService() {
+ testCellularServiceStateChangedBeforeTimeout(
+ ServiceState.STATE_EMERGENCY_ONLY, ServiceState.STATE_OUT_OF_SERVICE);
+ }
+
+ @Test
+ public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToPowerOff() {
+ testCellularServiceStateChangedBeforeTimeout(
+ ServiceState.STATE_EMERGENCY_ONLY, ServiceState.STATE_POWER_OFF);
+ }
+
+ @Test
+ public void testOnEmergencyCallConnectionStateChangedWithWrongCallId() {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestSOSMessageRecommender.onEmergencyCallConnectionStateChanged(
+ WRONG_CALL_ID, Connection.STATE_ACTIVE);
+ processAllMessages();
+
+ assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ }
+
+ @Test
+ public void testSatelliteNotAllowedInCurrentLocation() {
+ mTestSatelliteController.setIsSatelliteCommunicationAllowed(false);
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ /**
+ * We should have registered for the state change events abd started the timer when
+ * receiving the event onEmergencyCallStarted. After getting the callback for the result of
+ * the request requestIsSatelliteCommunicationAllowedForCurrentLocation, the resources
+ * should be cleaned up.
+ */
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ }
+
+ @Test
+ public void testOnEmergencyCallStarted() {
+ SatelliteController satelliteController = new SatelliteController(
+ mMockContext, Looper.myLooper());
+ TestSOSMessageRecommender testSOSMessageRecommender = new TestSOSMessageRecommender(
+ Looper.myLooper(),
+ satelliteController, mTestImsManager,
+ TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertFalse(testSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, testSOSMessageRecommender.getCountOfTimerStarted());
+ }
+
+ private void testStopTrackingCallBeforeTimeout(
+ @Connection.ConnectionState int connectionState) {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestSOSMessageRecommender.onEmergencyCallConnectionStateChanged(CALL_ID, connectionState);
+ processAllMessages();
+
+ assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ }
+
+ private void testCellularServiceStateChangedBeforeTimeout(
+ @ServiceState.RegState int availableServiceState,
+ @ServiceState.RegState int unavailableServiceState) {
+ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+ processAllMessages();
+
+ assertTrue(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+
+ mTestSOSMessageRecommender.sendServiceStateChangedEvent(availableServiceState);
+ processAllMessages();
+
+ assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
+
+ mTestSOSMessageRecommender.sendServiceStateChangedEvent(unavailableServiceState);
+ processAllMessages();
+ assertEquals(2, mTestSOSMessageRecommender.getCountOfTimerStarted());
+
+ // Wait for the timeout to expires
+ moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+ processAllMessages();
+
+ assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ }
+
+ private void assertRegisterForStateChangedEventsTriggered(
+ Phone phone, int registerForProvisionCount, int registerForImsCount,
+ int registerForCellularCount) {
+ assertEquals(registerForProvisionCount,
+ mTestSatelliteController.getRegisterForSatelliteProvisionStateChangedCalls());
+ assertEquals(registerForImsCount, mTestImsManager.getAddRegistrationCallbackCalls());
+ verify(phone, times(registerForCellularCount))
+ .registerForServiceStateChanged(any(), anyInt(), any());
+ }
+
+ private void assertUnregisterForStateChangedEventsTriggered(
+ Phone phone, int unregisterForProvisionCount, int unregisterForImsCount,
+ int unregisterForCellularCount) {
+ assertEquals(unregisterForProvisionCount,
+ mTestSatelliteController.getUnregisterForSatelliteProvisionStateChangedCalls());
+ assertEquals(unregisterForImsCount, mTestImsManager.getRemoveRegistrationListenerCalls());
+ verify(phone, times(unregisterForCellularCount)).unregisterForServiceStateChanged(any());
+ }
+
+ private static class TestSatelliteController extends SatelliteController {
+
+ private static final String TAG = "TestSatelliteController";
+ private final Map<Integer, Set<ISatelliteProvisionStateCallback>>
+ mProvisionStateChangedCallbacks;
+ private int mRegisterForSatelliteProvisionStateChangedCalls = 0;
+ private int mUnregisterForSatelliteProvisionStateChangedCalls = 0;
+ private boolean mIsSatelliteProvisioned = true;
+ private boolean mIsSatelliteCommunicationAllowed = true;
+
+ /**
+ * Create a SatelliteController to act as a backend service of
+ * {@link SatelliteManager}
+ *
+ * @param context The Context for the SatelliteController.
+ */
+ protected TestSatelliteController(Context context, Looper looper) {
+ super(context, looper);
+ mProvisionStateChangedCallbacks = new HashMap<>();
+ }
+
+ @Override
+ public Boolean isSatelliteProvisioned() {
+ return mIsSatelliteProvisioned;
+ }
+
+ @Override
+ public boolean isSatelliteSupported() {
+ return true;
+ }
+
+ @Override
+ @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(
+ int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+ mRegisterForSatelliteProvisionStateChangedCalls++;
+ Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
+ mProvisionStateChangedCallbacks.getOrDefault(subId, new HashSet<>());
+ perSubscriptionCallbacks.add(callback);
+ mProvisionStateChangedCallbacks.put(subId, perSubscriptionCallbacks);
+ return SatelliteManager.SATELLITE_ERROR_NONE;
+ }
+
+ @Override
+ public void unregisterForSatelliteProvisionStateChanged(
+ int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+ mUnregisterForSatelliteProvisionStateChangedCalls++;
+ Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
+ mProvisionStateChangedCallbacks.get(subId);
+ if (perSubscriptionCallbacks != null) {
+ perSubscriptionCallbacks.remove(callback);
+ }
+ }
+
+ @Override
+ public void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId,
+ @NonNull ResultReceiver result) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
+ mIsSatelliteCommunicationAllowed);
+ result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+ }
+
+ public void setIsSatelliteCommunicationAllowed(boolean allowed) {
+ mIsSatelliteCommunicationAllowed = allowed;
+ }
+
+ public int getRegisterForSatelliteProvisionStateChangedCalls() {
+ return mRegisterForSatelliteProvisionStateChangedCalls;
+ }
+
+ public int getUnregisterForSatelliteProvisionStateChangedCalls() {
+ return mUnregisterForSatelliteProvisionStateChangedCalls;
+ }
+
+ public void sendProvisionStateChangedEvent(int subId, boolean provisioned) {
+ mIsSatelliteProvisioned = provisioned;
+ Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
+ mProvisionStateChangedCallbacks.get(subId);
+ if (perSubscriptionCallbacks != null) {
+ for (ISatelliteProvisionStateCallback callback : perSubscriptionCallbacks) {
+ try {
+ callback.onSatelliteProvisionStateChanged(provisioned);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "sendProvisionStateChangedEvent: ex=" + ex);
+ }
+ }
+ }
+ }
+ }
+
+ private static class TestImsManager extends ImsManager {
+
+ private final Set<RegistrationManager.RegistrationCallback> mCallbacks;
+ private int mAddRegistrationCallbackCalls = 0;
+ private int mRemoveRegistrationListenerCalls = 0;
+
+ /**
+ * Used for testing only to inject dependencies.
+ */
+ TestImsManager(Context context, int phoneId, MmTelFeatureConnectionFactory factory,
+ SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy,
+ BinderCacheManager binderCacheManager) {
+ super(context, phoneId, factory, subManagerProxy, settingsProxy, binderCacheManager);
+ mCallbacks = new HashSet<>();
+ }
+
+ @Override
+ public void addRegistrationCallback(RegistrationManager.RegistrationCallback callback,
+ Executor executor)
+ throws ImsException {
+ mAddRegistrationCallbackCalls++;
+
+ if (callback == null) {
+ throw new NullPointerException("registration callback can't be null");
+ }
+ if (executor == null) {
+ throw new NullPointerException("registration executor can't be null");
+ }
+
+ callback.setExecutor(executor);
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeRegistrationListener(RegistrationManager.RegistrationCallback callback) {
+ mRemoveRegistrationListenerCalls++;
+
+ if (callback == null) {
+ throw new NullPointerException("registration callback can't be null");
+ }
+ mCallbacks.remove(callback);
+ }
+
+ public int getAddRegistrationCallbackCalls() {
+ return mAddRegistrationCallbackCalls;
+ }
+
+ public int getRemoveRegistrationListenerCalls() {
+ return mRemoveRegistrationListenerCalls;
+ }
+
+ public void sendImsRegistrationStateChangedEvent(boolean registered) {
+ if (registered) {
+ for (RegistrationManager.RegistrationCallback callback : mCallbacks) {
+ callback.onRegistered(null);
+ }
+ } else {
+ for (RegistrationManager.RegistrationCallback callback : mCallbacks) {
+ callback.onUnregistered(null);
+ }
+ }
+ }
+ }
+
+ private static class TestSOSMessageRecommender extends SatelliteSOSMessageRecommender {
+
+ /**
+ * Create an instance of SatelliteSOSMessageRecommender.
+ *
+ * @param looper The looper used with the handler of this class.
+ * @param satelliteController The SatelliteController singleton instance.
+ * @param imsManager The ImsManager instance associated with the phone, which is
+ * used for making the emergency call. This argument is not
+ * null only in unit tests.
+ * @param timeoutMillis The timeout duration of the timer.
+ */
+ TestSOSMessageRecommender(Looper looper, SatelliteController satelliteController,
+ ImsManager imsManager, long timeoutMillis) {
+ super(looper, satelliteController, imsManager, timeoutMillis);
+ }
+
+ public boolean isTimerStarted() {
+ return hasMessages(EVENT_TIME_OUT);
+ }
+
+ public int getCountOfTimerStarted() {
+ return mCountOfTimerStarted;
+ }
+
+ public void sendServiceStateChangedEvent(@ServiceState.RegState int state) {
+ ServiceState serviceState = new ServiceState();
+ serviceState.setState(state);
+ sendMessage(obtainMessage(EVENT_CELLULAR_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, serviceState, null)));
+ }
+ }
+
+ private static class TestConnection extends Connection {
+ private final Set<String> mSentEvents;
+ TestConnection(String callId) {
+ setTelecomCallId(callId);
+ mSentEvents = new HashSet<>();
+ }
+
+ @Override
+ public void sendConnectionEvent(String event, Bundle extras) {
+ mSentEvents.add(event);
+ }
+
+ public boolean isEventSent(String event) {
+ return mSentEvents.contains(event);
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
new file mode 100644
index 0000000..3ccf512
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.SatelliteManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit tests for SatelliteSessionController
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SatelliteSessionControllerTest extends TelephonyTest {
+ private static final String TAG = "SatelliteSessionControllerTest";
+ private static final long TEST_SATELLITE_STAY_AT_LISTENING_MILLIS = 200;
+ private static final long EVENT_PROCESSING_TIME_MILLIS = 100;
+
+ private static final String STATE_UNAVAILABLE = "UnavailableState";
+ private static final String STATE_POWER_OFF = "PowerOffState";
+ private static final String STATE_IDLE = "IdleState";
+ private static final String STATE_TRANSFERRING = "TransferringState";
+ private static final String STATE_LISTENING = "ListeningState";
+
+ private TestSatelliteModemInterface mSatelliteModemInterface;
+ private TestSatelliteSessionController mTestSatelliteSessionController;
+ private TestSatelliteStateCallback mTestSatelliteStateCallback;
+
+ @Mock
+ private SatelliteController mSatelliteController;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+
+ mSatelliteModemInterface = new TestSatelliteModemInterface(
+ mContext, mSatelliteController, Looper.myLooper());
+ mTestSatelliteSessionController = new TestSatelliteSessionController(mContext,
+ Looper.myLooper(), true, mSatelliteModemInterface,
+ TEST_SATELLITE_STAY_AT_LISTENING_MILLIS,
+ TEST_SATELLITE_STAY_AT_LISTENING_MILLIS);
+ processAllMessages();
+
+ mTestSatelliteStateCallback = new TestSatelliteStateCallback();
+ mTestSatelliteSessionController.registerForSatelliteModemStateChanged(
+ mTestSatelliteStateCallback);
+ assertSuccessfulModemStateChangedCallback(
+ mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testInitialState() {
+ /**
+ * Since satellite is not supported, SatelliteSessionController should move to UNAVAILABLE
+ * state.
+ */
+ TestSatelliteSessionController sessionController1 = new TestSatelliteSessionController(
+ mContext, Looper.myLooper(), false,
+ mSatelliteModemInterface, 100, 100);
+ assertNotNull(sessionController1);
+ processAllMessages();
+ assertEquals(STATE_UNAVAILABLE, sessionController1.getCurrentStateName());
+
+ /**
+ * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+ */
+ TestSatelliteSessionController sessionController2 = new TestSatelliteSessionController(
+ mContext, Looper.myLooper(), true,
+ mSatelliteModemInterface, 100, 100);
+ assertNotNull(sessionController2);
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, sessionController2.getCurrentStateName());
+ }
+
+ @Test
+ public void testUnavailableState() throws Exception {
+ /**
+ * Since satellite is not supported, SatelliteSessionController should move to UNAVAILABLE
+ * state.
+ */
+ TestSatelliteSessionController sessionController = new TestSatelliteSessionController(
+ mContext, Looper.myLooper(), false,
+ mSatelliteModemInterface, 100, 100);
+ assertNotNull(sessionController);
+ processAllMessages();
+ assertEquals(STATE_UNAVAILABLE, sessionController.getCurrentStateName());
+
+ /**
+ * SatelliteSessionController should stay at UNAVAILABLE state even after it receives the
+ * satellite radio powered-on state changed event.
+ */
+ sessionController.onSatelliteEnabledStateChanged(true);
+ processAllMessages();
+ assertEquals(STATE_UNAVAILABLE, sessionController.getCurrentStateName());
+ }
+
+ @Test
+ public void testStateTransition() {
+ /**
+ * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+ */
+ assertNotNull(mTestSatelliteSessionController);
+ assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+ // Power on the modem.
+ mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+ processAllMessages();
+
+ // SatelliteSessionController should move to IDLE state after the modem is powered on.
+ assertSuccessfulModemStateChangedCallback(
+ mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Power off the modem.
+ mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+ processAllMessages();
+
+ // SatelliteSessionController should move back to POWER_OFF state.
+ assertSuccessfulModemStateChangedCallback(
+ mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+ assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Power on the modem.
+ mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+ processAllMessages();
+
+ // SatelliteSessionController should move to IDLE state after radio is turned on.
+ assertSuccessfulModemStateChangedCallback(
+ mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start sending datagrams
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Sending datagrams failed
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to IDLE state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start sending datagrams again
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Sending datagrams is successful and done.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to LISTENING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+ assertEquals(STATE_LISTENING, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(1, mSatelliteModemInterface.getListeningEnabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start receiving datagrams
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(1, mSatelliteModemInterface.getListeningDisabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Receiving datagrams is successful and done.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to LISTENING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+ assertEquals(STATE_LISTENING, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(2, mSatelliteModemInterface.getListeningEnabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start receiving datagrams again
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(2, mSatelliteModemInterface.getListeningDisabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Receiving datagrams failed.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED);
+ processAllMessages();
+
+ // SatelliteSessionController should move to IDLE state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start receiving datagrams again
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Receiving datagrams is successful and done.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to LISTENING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+ assertEquals(STATE_LISTENING, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(3, mSatelliteModemInterface.getListeningEnabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Wait for timeout
+ moveTimeForward(TEST_SATELLITE_STAY_AT_LISTENING_MILLIS);
+ processAllMessages();
+
+ // SatelliteSessionController should move to IDLE state after timeout
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+ assertEquals(3, mSatelliteModemInterface.getListeningDisabledCount());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start receiving datagrams again
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start sending datagrams
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should stay at TRANSFERRING state.
+ assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Receiving datagrams failed.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED);
+ processAllMessages();
+
+ // SatelliteSessionController should stay at TRANSFERRING state instead of moving to IDLE
+ // state.
+ assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Start receiving datagrams again.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should stay at TRANSFERRING state.
+ assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Sending datagrams failed.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+ processAllMessages();
+
+ // SatelliteSessionController should stay at TRANSFERRING state instead of moving to IDLE
+ // state.
+ assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+
+ // Power off the modem.
+ mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+ processAllMessages();
+
+ // SatelliteSessionController should move to POWER_OFF state.
+ assertSuccessfulModemStateChangedCallback(
+ mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+ assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
+ }
+
+ private static class TestSatelliteModemInterface extends SatelliteModemInterface {
+ private final AtomicInteger mListeningEnabledCount = new AtomicInteger(0);
+ private final AtomicInteger mListeningDisabledCount = new AtomicInteger(0);
+
+ TestSatelliteModemInterface(@NonNull Context context,
+ SatelliteController satelliteController, @NonNull Looper looper) {
+ super(context, satelliteController, looper);
+ mExponentialBackoff.stop();
+ }
+
+ @Override
+ protected void bindService() {
+ logd("TestSatelliteModemInterface: bindService");
+ }
+
+ @Override
+ protected void unbindService() {
+ logd("TestSatelliteModemInterface: unbindService");
+ }
+
+ @Override
+ public void requestSatelliteListeningEnabled(boolean enable, int timeout,
+ @Nullable Message message) {
+ if (enable) mListeningEnabledCount.incrementAndGet();
+ else mListeningDisabledCount.incrementAndGet();
+ }
+
+ public int getListeningEnabledCount() {
+ return mListeningEnabledCount.get();
+ }
+
+ public int getListeningDisabledCount() {
+ return mListeningDisabledCount.get();
+ }
+ }
+
+ private static class TestSatelliteSessionController extends SatelliteSessionController {
+ TestSatelliteSessionController(Context context, Looper looper, boolean isSatelliteSupported,
+ SatelliteModemInterface satelliteModemInterface,
+ long satelliteStayAtListeningFromSendingMillis,
+ long satelliteStayAtListeningFromReceivingMillis) {
+ super(context, looper, isSatelliteSupported, satelliteModemInterface,
+ satelliteStayAtListeningFromSendingMillis,
+ satelliteStayAtListeningFromReceivingMillis);
+ }
+
+ public String getCurrentStateName() {
+ return getCurrentState().getName();
+ }
+
+ public boolean isSendingTriggeredDuringTransferringState() {
+ return mIsSendingTriggeredDuringTransferringState.get();
+ }
+ }
+
+ private static class TestSatelliteStateCallback extends ISatelliteStateCallback.Stub {
+ private final AtomicInteger mModemState = new AtomicInteger(
+ SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+ private final Semaphore mSemaphore = new Semaphore(0);
+
+ @Override
+ public void onSatelliteModemStateChanged(int state) {
+ logd("onSatelliteModemStateChanged: state=" + state);
+ mModemState.set(state);
+ try {
+ mSemaphore.release();
+ } catch (Exception ex) {
+ logd("onSatelliteModemStateChanged: Got exception, ex=" + ex);
+ }
+ }
+
+ public boolean waitUntilResult() {
+ try {
+ if (!mSemaphore.tryAcquire(EVENT_PROCESSING_TIME_MILLIS, TimeUnit.MILLISECONDS)) {
+ logd("Timeout to receive onSatelliteModemStateChanged");
+ return false;
+ }
+ return true;
+ } catch (Exception ex) {
+ logd("onSatelliteModemStateChanged: Got exception=" + ex);
+ return false;
+ }
+ }
+
+ public int getModemState() {
+ return mModemState.get();
+ }
+ }
+
+ private static void assertSuccessfulModemStateChangedCallback(
+ TestSatelliteStateCallback callback,
+ @SatelliteManager.SatelliteModemState int expectedModemState) {
+ boolean successful = callback.waitUntilResult();
+ assertTrue(successful);
+ assertEquals(expectedModemState, callback.getModemState());
+ }
+
+ private static void assertModemStateChangedCallbackNotCalled(
+ TestSatelliteStateCallback callback) {
+ boolean successful = callback.waitUntilResult();
+ assertFalse(successful);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
new file mode 100644
index 0000000..9898353
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -0,0 +1,2038 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Looper;
+import android.provider.Telephony;
+import android.provider.Telephony.SimInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SubscriptionDatabaseManagerTest extends TelephonyTest {
+
+ 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_CARRIER_NAME1 = "A-Mobile";
+ static final String FAKE_CARRIER_NAME2 = "B-Mobile";
+ static final int FAKE_COLOR1 = 1;
+ static final int FAKE_COLOR2 = 3;
+ static final int FAKE_CARRIER_ID1 = 1234;
+ static final int FAKE_CARRIER_ID2 = 5678;
+ static final String FAKE_COUNTRY_CODE1 = "TW";
+ static final String FAKE_COUNTRY_CODE2 = "US";
+ static final String FAKE_MCC1 = "466";
+ static final String FAKE_MCC2 = "310";
+ static final String FAKE_MNC1 = "01";
+ static final String FAKE_MNC2 = "410";
+ static final String FAKE_EHPLMNS1 = "46602,46603";
+ static final String FAKE_EHPLMNS2 = "310411,310412";
+ static final String FAKE_HPLMNS1 = "46601,46604";
+ static final String FAKE_HPLMNS2 = "310410,310413";
+ static final byte[] FAKE_NATIVE_ACCESS_RULES1 = UiccAccessRule.encodeRules(
+ new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package1", 12345L)});
+ static final byte[] FAKE_NATIVE_ACCESS_RULES2 = UiccAccessRule.encodeRules(
+ new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package2", 45678L)});
+ static final byte[] FAKE_CARRIER_CONFIG_ACCESS_RULES1 = UiccAccessRule.encodeRules(
+ new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package1", 54321L)});
+ static final byte[] FAKE_CARRIER_CONFIG_ACCESS_RULES2 = UiccAccessRule.encodeRules(
+ new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package2", 84954L)});
+ static final String FAKE_UUID1 = "a684e31a-5998-4670-abdd-0561252c58a5";
+ static final String FAKE_UUID2 = "cf6d7a9d-e712-4b3c-a600-7a2d4961b5b9";
+ static final String FAKE_OWNER1 = "owner1";
+ static final String FAKE_OWNER2 = "owner2";
+ static final String FAKE_MOBILE_DATA_POLICY1 = "1,2";
+ static final String FAKE_MOBILE_DATA_POLICY2 = "1";
+ static final String FAKE_IMSI1 = "1234";
+ static final String FAKE_IMSI2 = "5678";
+ static final byte[] FAKE_RCS_CONFIG1 = new byte[]{0x01, 0x02, 0x03};
+ static final byte[] FAKE_RCS_CONFIG2 = new byte[]{0x04, 0x05, 0x06};
+ static final String FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1 = "carrier=123456, power=3";
+ static final String FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2 = "user=1256, enable_2g=3";
+ static final String FAKE_CONTACT1 = "John Smith, Tesla Forrest";
+ static final String FAKE_CONTACT2 = "Mary Jane, Teresa Mill";
+ static final int FAKE_TP_MESSAGE_REFERENCE1 = 123;
+ static final int FAKE_TP_MESSAGE_REFERENCE2 = 456;
+ static final int FAKE_USER_ID1 = 10;
+ static final int FAKE_USER_ID2 = 11;
+
+ static final String FAKE_MAC_ADDRESS1 = "DC:E5:5B:38:7D:40";
+ static final String FAKE_MAC_ADDRESS2 = "DC:B5:4F:47:F3:4C";
+
+ static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO1 =
+ new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setIccId(FAKE_ICCID1)
+ .setSimSlotIndex(0)
+ .setDisplayName(FAKE_CARRIER_NAME1)
+ .setCarrierName(FAKE_CARRIER_NAME1)
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_SIM_SPN)
+ .setIconTint(FAKE_COLOR1)
+ .setNumber(FAKE_PHONE_NUMBER1)
+ .setDataRoaming(SubscriptionManager.DATA_ROAMING_ENABLE)
+ .setMcc(FAKE_MCC1)
+ .setMnc(FAKE_MNC1)
+ .setEhplmns(FAKE_EHPLMNS1)
+ .setHplmns(FAKE_HPLMNS1)
+ .setEmbedded(1)
+ .setCardString(FAKE_ICCID1)
+ .setCardId(1)
+ .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES1)
+ .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES1)
+ .setRemovableEmbedded(0)
+ .setCellBroadcastExtremeThreatAlertEnabled(1)
+ .setCellBroadcastSevereThreatAlertEnabled(1)
+ .setCellBroadcastAmberAlertEnabled(1)
+ .setCellBroadcastEmergencyAlertEnabled(1)
+ .setCellBroadcastAlertSoundDuration(4)
+ .setCellBroadcastAlertReminderInterval(1)
+ .setCellBroadcastAlertVibrationEnabled(1)
+ .setCellBroadcastAlertSpeechEnabled(1)
+ .setCellBroadcastEtwsTestAlertEnabled(1)
+ .setCellBroadcastAreaInfoMessageEnabled(1)
+ .setCellBroadcastTestAlertEnabled(1)
+ .setCellBroadcastOptOutDialogEnabled(1)
+ .setEnhanced4GModeEnabled(1)
+ .setVideoTelephonyEnabled(1)
+ .setWifiCallingEnabled(1)
+ .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED)
+ .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
+ .setWifiCallingEnabledForRoaming(1)
+ .setOpportunistic(0)
+ .setGroupUuid(FAKE_UUID1)
+ .setCountryIso(FAKE_COUNTRY_CODE1)
+ .setCarrierId(FAKE_CARRIER_ID1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setType(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)
+ .setGroupOwner(FAKE_OWNER1)
+ .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY1)
+ .setImsi(FAKE_IMSI1)
+ .setUiccApplicationsEnabled(1)
+ .setRcsUceEnabled(1)
+ .setCrossSimCallingEnabled(1)
+ .setRcsConfig(FAKE_RCS_CONFIG1)
+ .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1)
+ .setDeviceToDeviceStatusSharingPreference(
+ SubscriptionManager.D2D_SHARING_ALL_CONTACTS)
+ .setVoImsOptInEnabled(1)
+ .setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT1)
+ .setNrAdvancedCallingEnabled(1)
+ .setNumberFromCarrier(FAKE_PHONE_NUMBER1)
+ .setNumberFromIms(FAKE_PHONE_NUMBER1)
+ .setPortIndex(0)
+ .setUsageSetting(SubscriptionManager.USAGE_SETTING_DEFAULT)
+ .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE1)
+ .setUserId(FAKE_USER_ID1)
+ .setSatelliteEnabled(0)
+ .setGroupDisabled(false)
+ .build();
+
+ static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO2 =
+ new SubscriptionInfoInternal.Builder()
+ .setId(2)
+ .setIccId(FAKE_ICCID2)
+ .setSimSlotIndex(1)
+ .setDisplayName(FAKE_CARRIER_NAME2)
+ .setCarrierName(FAKE_CARRIER_NAME2)
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+ .setIconTint(FAKE_COLOR2)
+ .setNumber(FAKE_PHONE_NUMBER2)
+ .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE)
+ .setMcc(FAKE_MCC2)
+ .setMnc(FAKE_MNC2)
+ .setEhplmns(FAKE_EHPLMNS2)
+ .setHplmns(FAKE_HPLMNS2)
+ .setEmbedded(0)
+ .setCardString(FAKE_ICCID2)
+ .setCardId(2)
+ .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES2)
+ .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES2)
+ .setRemovableEmbedded(1)
+ .setCellBroadcastExtremeThreatAlertEnabled(0)
+ .setCellBroadcastSevereThreatAlertEnabled(0)
+ .setCellBroadcastAmberAlertEnabled(0)
+ .setCellBroadcastEmergencyAlertEnabled(0)
+ .setCellBroadcastAlertSoundDuration(0)
+ .setCellBroadcastAlertReminderInterval(0)
+ .setCellBroadcastAlertVibrationEnabled(0)
+ .setCellBroadcastAlertSpeechEnabled(0)
+ .setCellBroadcastEtwsTestAlertEnabled(0)
+ .setCellBroadcastAreaInfoMessageEnabled(0)
+ .setCellBroadcastTestAlertEnabled(0)
+ .setCellBroadcastOptOutDialogEnabled(0)
+ .setEnhanced4GModeEnabled(0)
+ .setVideoTelephonyEnabled(0)
+ .setWifiCallingEnabled(0)
+ .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
+ .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED)
+ .setWifiCallingEnabledForRoaming(0)
+ .setOpportunistic(1)
+ .setGroupUuid(FAKE_UUID2)
+ .setCountryIso(FAKE_COUNTRY_CODE2)
+ .setCarrierId(FAKE_CARRIER_ID2)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING)
+ .setType(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)
+ .setGroupOwner(FAKE_OWNER2)
+ .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY2)
+ .setImsi(FAKE_IMSI2)
+ .setUiccApplicationsEnabled(0)
+ .setRcsUceEnabled(0)
+ .setCrossSimCallingEnabled(0)
+ .setRcsConfig(FAKE_RCS_CONFIG2)
+ .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2)
+ .setDeviceToDeviceStatusSharingPreference(
+ SubscriptionManager.D2D_SHARING_SELECTED_CONTACTS)
+ .setVoImsOptInEnabled(0)
+ .setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT2)
+ .setNrAdvancedCallingEnabled(0)
+ .setNumberFromCarrier(FAKE_PHONE_NUMBER2)
+ .setNumberFromIms(FAKE_PHONE_NUMBER2)
+ .setPortIndex(1)
+ .setUsageSetting(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC)
+ .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE2)
+ .setUserId(FAKE_USER_ID2)
+ .setSatelliteEnabled(1)
+ .setGroupDisabled(false)
+ .build();
+
+ private SubscriptionDatabaseManager mDatabaseManagerUT;
+
+ private final SubscriptionProvider mSubscriptionProvider = new SubscriptionProvider();
+
+ //mock
+ private SubscriptionDatabaseManagerCallback mSubscriptionDatabaseManagerCallback;
+
+ static class SubscriptionProvider extends MockContentProvider {
+ private final List<ContentValues> mDatabase = new ArrayList<>();
+
+ private final List<String> mAllColumns;
+
+ SubscriptionProvider() {
+ mAllColumns = SimInfo.getAllColumns();
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ logd("SubscriptionProvider: query. uri=" + uri);
+ if (!SimInfo.CONTENT_URI.equals(uri)) {
+ throw new UnsupportedOperationException("Unsupported uri=" + uri);
+ }
+ if (projection != null || selection != null || selectionArgs != null) {
+ throw new UnsupportedOperationException("Only support full database query. "
+ + "projection=" + Arrays.toString(projection) + ", selection=" + selection
+ + ", selectionArgs=" + Arrays.toString(selectionArgs));
+ }
+
+ MatrixCursor mc = new MatrixCursor(mAllColumns.toArray(new String[0]));
+
+ // Only support full database query
+ for (int row = 0; row < mDatabase.size(); row++) {
+ List<Object> values = new ArrayList<>();
+ for (String column : mAllColumns) {
+ values.add(mDatabase.get(row).get(column));
+ }
+ mc.addRow(values);
+ }
+
+ return mc;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
+ if (!uri.isPathPrefixMatch(SimInfo.CONTENT_URI)) {
+ throw new UnsupportedOperationException("Unsupported uri=" + uri);
+ }
+
+ int subId = Integer.parseInt(uri.getLastPathSegment());
+ logd("update: subId=" + subId + ", contentValues=" + values);
+
+ ContentValues existingValues = mDatabase.stream()
+ .filter(contentValues -> contentValues.get(
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId))
+ .findFirst()
+ .orElse(null);
+ if (existingValues == null) {
+ throw new IllegalArgumentException("Invalid sub id " + subId);
+ }
+
+ for (Map.Entry<String, Object> entry : values.valueSet()) {
+ String column = entry.getKey();
+ Object value = entry.getValue();
+ if (!mAllColumns.contains(column)) {
+ throw new IllegalArgumentException("Update with unknown column " + column);
+ }
+ existingValues.putObject(column, value);
+ }
+ return 1;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (!uri.isPathPrefixMatch(SimInfo.CONTENT_URI)) {
+ throw new UnsupportedOperationException("Unsupported uri=" + uri);
+ }
+
+ logd("delete: uri=" + uri + ", selection=" + selection + ", selectionArgs="
+ + Arrays.toString(selectionArgs));
+ if (!selection.equals(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?")) {
+ throw new UnsupportedOperationException("Only support delete by sub id.");
+ }
+
+ int rowsRemoved = 0;
+ for (String selectionArg : selectionArgs) {
+ int subId = Integer.parseInt(selectionArg);
+ // Clear it to null instead of removing it.
+ rowsRemoved += mDatabase.removeIf(contentValues -> contentValues.get(
+ SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId)) ? 1 : 0;
+ }
+ return rowsRemoved;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ logd("SubscriptionProvider: insert. uri=" + uri + ", values=" + values);
+ if (!SimInfo.CONTENT_URI.equals(uri)) {
+ throw new UnsupportedOperationException("Unsupported uri=" + uri);
+ }
+
+ for (String column : values.keySet()) {
+ if (!mAllColumns.contains(column)) {
+ throw new IllegalArgumentException("Insert with unknown column " + column);
+ }
+ }
+ // The last row's subId + 1
+ int subId;
+ if (mDatabase.isEmpty()) {
+ subId = 1;
+ } else {
+ subId = (int) mDatabase.get(mDatabase.size() - 1)
+ .get(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID) + 1;
+ }
+ values.put(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, subId);
+ mDatabase.add(values);
+ return ContentUris.withAppendedId(SimInfo.CONTENT_URI, subId);
+ }
+
+ @Override
+ public Bundle call(String method, @Nullable String args, @Nullable Bundle bundle) {
+ return new Bundle();
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("SubscriptionDatabaseManagerTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+ mContextFixture.putBooleanResource(com.android.internal.R.bool
+ .config_subscription_database_async_update, true);
+ mSubscriptionDatabaseManagerCallback =
+ Mockito.mock(SubscriptionDatabaseManagerCallback.class);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArguments()[0]).run();
+ return null;
+ }).when(mSubscriptionDatabaseManagerCallback).invokeFromExecutor(any(Runnable.class));
+
+ ((MockContentResolver) mContext.getContentResolver()).addProvider(
+ Telephony.Carriers.CONTENT_URI.getAuthority(), mSubscriptionProvider);
+
+ doReturn(1).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID1));
+ doReturn(2).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID2));
+ mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
+ mSubscriptionDatabaseManagerCallback);
+ logd("SubscriptionDatabaseManagerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Verify the subscription from the cache and database is same as the provided one.
+ *
+ * @param subInfo The subscription to compare.
+ */
+ private void verifySubscription(@NonNull SubscriptionInfoInternal subInfo) throws Exception {
+ int subId = subInfo.getSubscriptionId();
+ // Verify the cache value is same as the inserted one.
+ assertWithMessage("Subscription info cache value is different.")
+ .that(mDatabaseManagerUT.getSubscriptionInfoInternal(subId)).isEqualTo(subInfo);
+
+ // Load subscription info from the database.
+ mDatabaseManagerUT.reloadDatabase();
+ processAllMessages();
+
+ // Verify the database value is same as the inserted one.
+ assertWithMessage("Subscription info database value is different.")
+ .that(mDatabaseManagerUT.getSubscriptionInfoInternal(subId)).isEqualTo(subInfo);
+ }
+
+ /**
+ * Insert a subscription info into the database and verify it's in the cache and database.
+ *
+ * @param subInfo The subscription info to insert.
+ * @return The inserted subscription info.
+ */
+ private SubscriptionInfoInternal insertSubscriptionAndVerify(
+ @NonNull SubscriptionInfoInternal subInfo) throws Exception {
+ int subId = mDatabaseManagerUT.insertSubscriptionInfo(
+ new SubscriptionInfoInternal.Builder(subInfo)
+ .setId(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .build());
+ assertThat(SubscriptionManager.isValidSubscriptionId(subId)).isTrue();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setId(subId).build();
+ verifySubscription(subInfo);
+ return subInfo;
+ }
+
+ @Test
+ public void testGetAllColumns() throws Exception {
+ Field[] declaredFields = SimInfo.class.getDeclaredFields();
+ List<String> columnNames = new ArrayList<>();
+ for (Field field : declaredFields) {
+ if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("COLUMN_")) {
+ columnNames.add((String) field.get(null));
+ }
+ }
+ // When you add a new column in Telephony.SimInfo, did you remember to modify
+ // Telephony.SimInfo.getAllColumns() as well?
+ assertThat(SimInfo.getAllColumns()).containsExactlyElementsIn(columnNames);
+ }
+
+ @Test
+ public void testInsertSubscription() throws Exception {
+ assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1).getSubscriptionId())
+ .isEqualTo(1);
+ processAllMessages();
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+
+ assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2).getSubscriptionId())
+ .isEqualTo(2);
+ processAllMessages();
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(2));
+ }
+
+ @Test
+ public void testUpdateSubscription() throws Exception {
+ SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal
+ .Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setId(1)
+ .build();
+
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.updateSubscription(subInfo));
+
+ assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1).getSubscriptionId())
+ .isEqualTo(1);
+ mDatabaseManagerUT.updateSubscription(subInfo);
+ processAllMessages();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+
+ // Same sub info again. Should not trigger callback
+ mDatabaseManagerUT.updateSubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, never()).onSubscriptionChanged(anyInt());
+ }
+
+ @Test
+ public void testUpdateSubscriptionSync() throws Exception {
+ mContextFixture.putBooleanResource(com.android.internal.R.bool
+ .config_subscription_database_async_update, false);
+ mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
+ mSubscriptionDatabaseManagerCallback);
+
+ assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1).getSubscriptionId())
+ .isEqualTo(1);
+ SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal
+ .Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setId(1)
+ .build();
+ mDatabaseManagerUT.updateSubscription(subInfo);
+
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+
+ // Same sub info again. Should not trigger callback
+ mDatabaseManagerUT.updateSubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, never()).onSubscriptionChanged(anyInt());
+ }
+
+ @Test
+ public void testUpdateIccId() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setIccId(1, FAKE_ICCID2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setIccId(subInfo.getSubscriptionId(), FAKE_ICCID2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setIccId(FAKE_ICCID2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_ICC_ID)).isEqualTo(FAKE_ICCID2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_ICC_ID, FAKE_ICCID1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getIccId())
+ .isEqualTo(FAKE_ICCID1);
+ }
+
+ @Test
+ public void testUpdateSimSlotIndex() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setSimSlotIndex(1,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setSimSlotIndex(subInfo.getSubscriptionId(),
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setSimSlotIndex(
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_SIM_SLOT_INDEX))
+ .isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_SIM_SLOT_INDEX, 123);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getSimSlotIndex())
+ .isEqualTo(123);
+ }
+
+ @Test
+ public void testUpdateDisplayName() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setDisplayName(1, FAKE_CARRIER_NAME2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setDisplayName(subInfo.getSubscriptionId(), FAKE_CARRIER_NAME2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setDisplayName(
+ FAKE_CARRIER_NAME2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_DISPLAY_NAME))
+ .isEqualTo(FAKE_CARRIER_NAME2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_DISPLAY_NAME, FAKE_CARRIER_NAME1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getDisplayName())
+ .isEqualTo(FAKE_CARRIER_NAME1);
+ }
+
+ @Test
+ public void testUpdateCarrierName() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCarrierName(1, FAKE_CARRIER_NAME2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCarrierName(subInfo.getSubscriptionId(), FAKE_CARRIER_NAME2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setCarrierName(
+ FAKE_CARRIER_NAME2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_CARRIER_NAME))
+ .isEqualTo(FAKE_CARRIER_NAME2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_CARRIER_NAME, FAKE_CARRIER_NAME1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getCarrierName())
+ .isEqualTo(FAKE_CARRIER_NAME1);
+ }
+
+ @Test
+ public void testUpdateDisplayNameSource() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setDisplayNameSource(1,
+ SubscriptionManager.NAME_SOURCE_USER_INPUT));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setDisplayNameSource(subInfo.getSubscriptionId(),
+ SubscriptionManager.NAME_SOURCE_USER_INPUT);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setDisplayNameSource(
+ SubscriptionManager.NAME_SOURCE_USER_INPUT).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_NAME_SOURCE))
+ .isEqualTo(SubscriptionManager.NAME_SOURCE_USER_INPUT);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_NAME_SOURCE, SubscriptionManager.NAME_SOURCE_SIM_PNN);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getDisplayNameSource())
+ .isEqualTo(SubscriptionManager.NAME_SOURCE_SIM_PNN);
+ }
+
+ @Test
+ public void testUpdateIconTint() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setIconTint(1, FAKE_COLOR2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setIconTint(subInfo.getSubscriptionId(), FAKE_COLOR2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setIconTint(FAKE_COLOR2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_COLOR))
+ .isEqualTo(FAKE_COLOR2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_COLOR, FAKE_COLOR1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getIconTint())
+ .isEqualTo(FAKE_COLOR1);
+ }
+
+ @Test
+ public void testUpdateNumber() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setNumber(1, FAKE_PHONE_NUMBER2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setNumber(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setNumber(FAKE_PHONE_NUMBER2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_NUMBER))
+ .isEqualTo(FAKE_PHONE_NUMBER2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_NUMBER, FAKE_PHONE_NUMBER1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getNumber())
+ .isEqualTo(FAKE_PHONE_NUMBER1);
+ }
+
+ @Test
+ public void testUpdateDataRoaming() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setDataRoaming(1,
+ SubscriptionManager.DATA_ROAMING_DISABLE));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setDataRoaming(subInfo.getSubscriptionId(),
+ SubscriptionManager.DATA_ROAMING_DISABLE);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_DATA_ROAMING))
+ .isEqualTo(SubscriptionManager.DATA_ROAMING_DISABLE);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_DATA_ROAMING, SubscriptionManager.DATA_ROAMING_ENABLE);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getDataRoaming())
+ .isEqualTo(SubscriptionManager.DATA_ROAMING_ENABLE);
+ }
+
+ @Test
+ public void testUpdateMcc() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setMcc(1, FAKE_MCC2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setMcc(subInfo.getSubscriptionId(), FAKE_MCC2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setMcc(FAKE_MCC2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_MCC_STRING))
+ .isEqualTo(FAKE_MCC2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_MCC_STRING, FAKE_MCC1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getMcc())
+ .isEqualTo(FAKE_MCC1);
+ }
+
+ @Test
+ public void testUpdateMnc() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setMnc(1, FAKE_MNC2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setMnc(subInfo.getSubscriptionId(), FAKE_MNC2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setMnc(FAKE_MNC2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_MNC_STRING))
+ .isEqualTo(FAKE_MNC2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_MNC_STRING, FAKE_MNC1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getMnc())
+ .isEqualTo(FAKE_MNC1);
+ }
+
+ @Test
+ public void testUpdateEhplmns() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setEhplmns(1, FAKE_EHPLMNS2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setEhplmns(subInfo.getSubscriptionId(), FAKE_EHPLMNS2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEhplmns(FAKE_EHPLMNS2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_EHPLMNS))
+ .isEqualTo(FAKE_EHPLMNS2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_EHPLMNS, FAKE_EHPLMNS1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getEhplmns())
+ .isEqualTo(FAKE_EHPLMNS1);
+ }
+
+ @Test
+ public void testUpdateHplmns() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setHplmns(1, FAKE_HPLMNS2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setHplmns(subInfo.getSubscriptionId(), FAKE_HPLMNS2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setHplmns(FAKE_HPLMNS2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_HPLMNS))
+ .isEqualTo(FAKE_HPLMNS2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_HPLMNS, FAKE_HPLMNS1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getHplmns())
+ .isEqualTo(FAKE_HPLMNS1);
+ }
+
+ @Test
+ public void testUpdateEmbedded() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setEmbedded(1, false));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setEmbedded(subInfo.getSubscriptionId(), false);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEmbedded(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_IS_EMBEDDED))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_IS_EMBEDDED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getEmbedded())
+ .isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCardString() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCardString(1, FAKE_ICCID2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCardString(subInfo.getSubscriptionId(), FAKE_ICCID2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCardString(FAKE_ICCID2)
+ .setCardId(2)
+ .build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_CARD_ID))
+ .isEqualTo(FAKE_ICCID2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_CARD_ID, FAKE_ICCID1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getCardString())
+ .isEqualTo(FAKE_ICCID1);
+ }
+
+ @Test
+ public void testUpdateNativeAccessRules() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setNativeAccessRules(1, FAKE_NATIVE_ACCESS_RULES2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setNativeAccessRules(subInfo.getSubscriptionId(),
+ FAKE_NATIVE_ACCESS_RULES2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_ACCESS_RULES))
+ .isEqualTo(FAKE_NATIVE_ACCESS_RULES2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_ACCESS_RULES, FAKE_NATIVE_ACCESS_RULES1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getNativeAccessRules())
+ .isEqualTo(FAKE_NATIVE_ACCESS_RULES1);
+ }
+
+ @Test
+ public void testUpdateCarrierConfigAccessRules() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCarrierConfigAccessRules(1,
+ FAKE_CARRIER_CONFIG_ACCESS_RULES2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCarrierConfigAccessRules(subInfo.getSubscriptionId(),
+ FAKE_CARRIER_CONFIG_ACCESS_RULES2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS))
+ .isEqualTo(FAKE_CARRIER_CONFIG_ACCESS_RULES2);
+ mDatabaseManagerUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ FAKE_CARRIER_CONFIG_ACCESS_RULES2);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getCarrierConfigAccessRules())
+ .isEqualTo(FAKE_CARRIER_CONFIG_ACCESS_RULES2);
+ }
+
+ @Test
+ public void testUpdateRemovableEmbedded() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setRemovableEmbedded(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setRemovableEmbedded(subInfo.getSubscriptionId(), 1);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setRemovableEmbedded(1).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_IS_REMOVABLE))
+ .isEqualTo(1);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_IS_REMOVABLE, 0);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getRemovableEmbedded())
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastExtremeThreatAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastExtremeThreatAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastExtremeThreatAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastExtremeThreatAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastExtremeThreatAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastSevereThreatAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastSevereThreatAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastSevereThreatAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastSevereThreatAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastSevereThreatAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAmberAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAmberAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAmberAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAmberAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_AMBER_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_AMBER_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAmberAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastEmergencyAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastEmergencyAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastEmergencyAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastEmergencyAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_EMERGENCY_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_EMERGENCY_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastEmergencyAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAlertSoundDuration() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAlertSoundDuration(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAlertSoundDuration(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAlertSoundDuration(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_ALERT_SOUND_DURATION))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_ALERT_SOUND_DURATION, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAlertSoundDuration()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAlertReminderInterval() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAlertReminderInterval(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAlertReminderInterval(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAlertReminderInterval(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAlertReminderInterval()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAlertVibrationEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAlertVibrationEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAlertVibrationEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAlertVibrationEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_ALERT_VIBRATE))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_ALERT_VIBRATE, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAlertVibrationEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAlertSpeechEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAlertSpeechEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAlertSpeechEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAlertSpeechEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_ALERT_SPEECH))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_ALERT_SPEECH, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAlertSpeechEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastEtwsTestAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastEtwsTestAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastEtwsTestAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastEtwsTestAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_ETWS_TEST_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_ETWS_TEST_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastEtwsTestAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastAreaInfoMessageEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastAreaInfoMessageEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastAreaInfoMessageEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastAreaInfoMessageEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_CHANNEL_50_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_CHANNEL_50_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastAreaInfoMessageEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastTestAlertEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastTestAlertEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastTestAlertEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastTestAlertEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_CMAS_TEST_ALERT))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_CMAS_TEST_ALERT, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastTestAlertEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateCellBroadcastOptOutDialogEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCellBroadcastOptOutDialogEnabled(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCellBroadcastOptOutDialogEnabled(
+ subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCellBroadcastOptOutDialogEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CB_OPT_OUT_DIALOG))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CB_OPT_OUT_DIALOG, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCellBroadcastOptOutDialogEnabled()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateEnhanced4GModeEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setEnhanced4GModeEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setEnhanced4GModeEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEnhanced4GModeEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getEnhanced4GModeEnabled())
+ .isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).isEnhanced4GModeEnabled())
+ .isTrue();
+ }
+
+ @Test
+ public void testUpdateVideoTelephonyEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setVideoTelephonyEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setVideoTelephonyEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setVideoTelephonyEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_VT_IMS_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_VT_IMS_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getVideoTelephonyEnabled())
+ .isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).isVideoTelephonyEnabled())
+ .isTrue();
+ }
+
+ @Test
+ public void testUpdateWifiCallingEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setWifiCallingEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setWifiCallingEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setWifiCallingEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ENABLED))
+ .isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getWifiCallingEnabled())
+ .isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateWifiCallingMode() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setWifiCallingMode(
+ 1, ImsMmTelManager.WIFI_MODE_WIFI_ONLY));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setWifiCallingMode(subInfo.getSubscriptionId(),
+ ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_WIFI_ONLY).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_MODE))
+ .isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_MODE,
+ ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getWifiCallingMode())
+ .isEqualTo(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
+ }
+
+ @Test
+ public void testUpdateWifiCallingModeForRoaming() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setWifiCallingModeForRoaming(
+ 1, ImsMmTelManager.WIFI_MODE_WIFI_ONLY));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setWifiCallingModeForRoaming(subInfo.getSubscriptionId(),
+ ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_WIFI_ONLY).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE))
+ .isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+ ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getWifiCallingModeForRoaming())
+ .isEqualTo(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
+ }
+
+ @Test
+ public void testUpdateWifiCallingEnabledForRoaming() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setWifiCallingEnabledForRoaming(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setWifiCallingEnabledForRoaming(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setWifiCallingEnabledForRoaming(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getWifiCallingEnabledForRoaming()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateVoImsOptInEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setVoImsOptInEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setVoImsOptInEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setVoImsOptInEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getVoImsOptInEnabled()).isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).isVoImsOptInEnabled())
+ .isTrue();
+ }
+
+
+ @Test
+ public void testUpdateOpportunistic() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setOpportunistic(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setOpportunistic(subInfo.getSubscriptionId(), 1);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setOpportunistic(1).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_IS_OPPORTUNISTIC)).isEqualTo(1);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_IS_OPPORTUNISTIC, 0);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getOpportunistic()).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateGroupUuid() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setGroupUuid(1, FAKE_UUID2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setGroupUuid(subInfo.getSubscriptionId(), FAKE_UUID2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setGroupUuid(FAKE_UUID2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_GROUP_UUID)).isEqualTo(FAKE_UUID2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_GROUP_UUID, FAKE_UUID1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getGroupUuid()).isEqualTo(FAKE_UUID1);
+ }
+
+ @Test
+ public void testUpdateCountryIso() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCountryIso(1, FAKE_COUNTRY_CODE2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCountryIso(subInfo.getSubscriptionId(), FAKE_COUNTRY_CODE2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCountryIso(FAKE_COUNTRY_CODE2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_ISO_COUNTRY_CODE)).isEqualTo(FAKE_COUNTRY_CODE2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_ISO_COUNTRY_CODE, FAKE_COUNTRY_CODE1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getCountryIso()).isEqualTo(FAKE_COUNTRY_CODE1);
+ }
+
+ @Test
+ public void testUpdateCarrierId() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCarrierId(1, FAKE_CARRIER_ID2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCarrierId(subInfo.getSubscriptionId(), FAKE_CARRIER_ID2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCarrierId(FAKE_CARRIER_ID2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_CARRIER_ID))
+ .isEqualTo(FAKE_CARRIER_ID2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CARRIER_ID, FAKE_CARRIER_ID1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getCarrierId())
+ .isEqualTo(FAKE_CARRIER_ID1);
+ }
+
+ @Test
+ public void testUpdateProfileClass() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setProfileClass(
+ 1, SubscriptionManager.PROFILE_CLASS_TESTING));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setProfileClass(subInfo.getSubscriptionId(),
+ SubscriptionManager.PROFILE_CLASS_TESTING);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_TESTING).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_PROFILE_CLASS))
+ .isEqualTo(SubscriptionManager.PROFILE_CLASS_TESTING);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_PROFILE_CLASS,
+ SubscriptionManager.PROFILE_CLASS_PROVISIONING);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getProfileClass())
+ .isEqualTo(SubscriptionManager.PROFILE_CLASS_PROVISIONING);
+ }
+
+ @Test
+ public void testUpdateSubscriptionType() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setSubscriptionType(
+ 1, SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setSubscriptionType(subInfo.getSubscriptionId(),
+ SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setType(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_SUBSCRIPTION_TYPE))
+ .isEqualTo(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_SUBSCRIPTION_TYPE,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getSubscriptionType())
+ .isEqualTo(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ }
+
+ @Test
+ public void testUpdateGroupOwner() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setGroupOwner(1, FAKE_OWNER2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setGroupOwner(subInfo.getSubscriptionId(), FAKE_OWNER2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setGroupOwner(FAKE_OWNER2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_GROUP_OWNER))
+ .isEqualTo(FAKE_OWNER2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_GROUP_OWNER, FAKE_OWNER1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getGroupOwner())
+ .isEqualTo(FAKE_OWNER1);
+ }
+
+ @Test
+ public void testUpdateEnabledMobileDataPolicies() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setEnabledMobileDataPolicies(1, FAKE_MOBILE_DATA_POLICY2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setEnabledMobileDataPolicies(subInfo.getSubscriptionId(),
+ FAKE_MOBILE_DATA_POLICY2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES)).isEqualTo(FAKE_MOBILE_DATA_POLICY2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES, FAKE_MOBILE_DATA_POLICY1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getEnabledMobileDataPolicies())
+ .isEqualTo(FAKE_MOBILE_DATA_POLICY1);
+ }
+
+ @Test
+ public void testUpdateImsi() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setImsi(1, FAKE_IMSI2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setImsi(subInfo.getSubscriptionId(), FAKE_IMSI2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setImsi(FAKE_IMSI2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_IMSI))
+ .isEqualTo(FAKE_IMSI2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_IMSI, FAKE_IMSI1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getImsi())
+ .isEqualTo(FAKE_IMSI1);
+ }
+
+ @Test
+ public void testUpdateUiccApplicationsEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setUiccApplicationsEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setUiccApplicationsEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setUiccApplicationsEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getUiccApplicationsEnabled())
+ .isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateRcsUceEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setRcsUceEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setRcsUceEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setRcsUceEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getRcsUceEnabled())
+ .isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).isRcsUceEnabled())
+ .isTrue();
+ }
+
+ @Test
+ public void testUpdateCrossSimCallingEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setCrossSimCallingEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setCrossSimCallingEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setCrossSimCallingEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getCrossSimCallingEnabled())
+ .isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).isCrossSimCallingEnabled())
+ .isTrue();
+ }
+
+ @Test
+ public void testUpdateRcsConfig() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setRcsConfig(1, FAKE_RCS_CONFIG2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setRcsConfig(subInfo.getSubscriptionId(), FAKE_RCS_CONFIG2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setRcsConfig(FAKE_RCS_CONFIG2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_RCS_CONFIG)).isEqualTo(FAKE_RCS_CONFIG2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_RCS_CONFIG, FAKE_RCS_CONFIG1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getRcsConfig())
+ .isEqualTo(FAKE_RCS_CONFIG1);
+ }
+
+ @Test
+ public void testUpdateAllowedNetworkTypesForReasons() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setAllowedNetworkTypesForReasons(
+ 1, FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setAllowedNetworkTypesForReasons(subInfo.getSubscriptionId(),
+ FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS))
+ .isEqualTo(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2);
+ mDatabaseManagerUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+ FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getAllowedNetworkTypesForReasons())
+ .isEqualTo(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1);
+ }
+
+ @Test
+ public void testUpdateDeviceToDeviceStatusSharingPreference() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setDeviceToDeviceStatusSharingPreference(
+ 1, SubscriptionManager.D2D_SHARING_DISABLED));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setDeviceToDeviceStatusSharingPreference(subInfo.getSubscriptionId(),
+ SubscriptionManager.D2D_SHARING_DISABLED);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setDeviceToDeviceStatusSharingPreference(
+ SubscriptionManager.D2D_SHARING_DISABLED).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_D2D_STATUS_SHARING))
+ .isEqualTo(SubscriptionManager.D2D_SHARING_DISABLED);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_D2D_STATUS_SHARING,
+ SubscriptionManager.D2D_SHARING_ALL);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getDeviceToDeviceStatusSharingPreference())
+ .isEqualTo(SubscriptionManager.D2D_SHARING_ALL);
+ }
+
+ @Test
+ public void testUpdateNrAdvancedCallingEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setNrAdvancedCallingEnabled(1, 0));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setNrAdvancedCallingEnabled(subInfo.getSubscriptionId(), 0);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setNrAdvancedCallingEnabled(0).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED)).isEqualTo(0);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getNrAdvancedCallingEnabled()).isEqualTo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .isNrAdvancedCallingEnabled()).isTrue();
+ }
+
+ @Test
+ public void testUpdateNumberFromCarrier() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setNumberFromCarrier(1, FAKE_PHONE_NUMBER2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setNumberFromCarrier(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setNumberFromCarrier(FAKE_PHONE_NUMBER2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER)).isEqualTo(FAKE_PHONE_NUMBER2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER, FAKE_PHONE_NUMBER1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getNumberFromCarrier()).isEqualTo(FAKE_PHONE_NUMBER1);
+ }
+
+ @Test
+ public void testUpdateNumberFromIms() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setNumberFromIms(1, FAKE_PHONE_NUMBER2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setNumberFromIms(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setNumberFromIms(FAKE_PHONE_NUMBER2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ 1, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS)).isEqualTo(FAKE_PHONE_NUMBER2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS, FAKE_PHONE_NUMBER1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getNumberFromIms()).isEqualTo(FAKE_PHONE_NUMBER1);
+ }
+
+ @Test
+ public void testUpdatePortIndex() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setPortIndex(1, 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setPortIndex(subInfo.getSubscriptionId(), 1);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setPortIndex(1).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_PORT_INDEX))
+ .isEqualTo(1);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_PORT_INDEX, 2);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getPortIndex())
+ .isEqualTo(2);
+ }
+
+ @Test
+ public void testUpdateUsageSetting() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setUsageSetting(
+ 1, SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setUsageSetting(subInfo.getSubscriptionId(),
+ SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setUsageSetting(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_USAGE_SETTING))
+ .isEqualTo(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_USAGE_SETTING, SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1).getUsageSetting())
+ .isEqualTo(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
+ }
+
+ @Test
+ public void testUpdateLastUsedTPMessageReference() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setLastUsedTPMessageReference(
+ 1, FAKE_TP_MESSAGE_REFERENCE2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setLastUsedTPMessageReference(subInfo.getSubscriptionId(),
+ FAKE_TP_MESSAGE_REFERENCE2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_TP_MESSAGE_REF))
+ .isEqualTo(FAKE_TP_MESSAGE_REFERENCE2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_TP_MESSAGE_REF, FAKE_TP_MESSAGE_REFERENCE1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getLastUsedTPMessageReference()).isEqualTo(FAKE_TP_MESSAGE_REFERENCE1);
+ }
+
+ @Test
+ public void testUpdateUserId() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setUserId(1, FAKE_USER_ID2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setUserId(subInfo.getSubscriptionId(), FAKE_USER_ID2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setUserId(FAKE_USER_ID2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1, SimInfo.COLUMN_USER_HANDLE))
+ .isEqualTo(FAKE_USER_ID2);
+ mDatabaseManagerUT.setSubscriptionProperty(1, SimInfo.COLUMN_USER_HANDLE, FAKE_USER_ID1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getUserId()).isEqualTo(FAKE_USER_ID1);
+ }
+
+ @Test
+ public void testUpdateSatelliteEnabled() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class, () -> mDatabaseManagerUT.setSatelliteEnabled(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(), 1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setSatelliteEnabled(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ 1);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setSatelliteEnabled(1).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(), SimInfo.COLUMN_SATELLITE_ENABLED))
+ .isEqualTo(1);
+
+ mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_SATELLITE_ENABLED, 0);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId()).getSatelliteEnabled())
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateSubscriptionsInGroup() throws Exception {
+ insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2);
+ // Two subs are now in the same group
+ mDatabaseManagerUT.setGroupUuid(2, FAKE_UUID1);
+
+ mDatabaseManagerUT.setWifiCallingEnabled(1, 1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .isWifiCallingEnabled()).isTrue();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .isWifiCallingEnabled()).isTrue();
+
+ mDatabaseManagerUT.setWifiCallingEnabled(1, 0);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .isWifiCallingEnabled()).isFalse();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .isWifiCallingEnabled()).isFalse();
+
+ mDatabaseManagerUT.setUserId(1, 5678);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getUserId()).isEqualTo(5678);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .getUserId()).isEqualTo(5678);
+
+ mDatabaseManagerUT.setWifiCallingEnabledForRoaming(1, 0);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .isWifiCallingEnabledForRoaming()).isFalse();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .isWifiCallingEnabledForRoaming()).isFalse();
+
+ mDatabaseManagerUT.setDisplayName(1, "Pokemon");
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getDisplayName()).isEqualTo("Pokemon");
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .getDisplayName()).isEqualTo("Pokemon");
+
+ // ICCID is not the field that will be synced to all subs in the group.
+ mDatabaseManagerUT.setIccId(1, "0987");
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)
+ .getIccId()).isEqualTo("0987");
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)
+ .getIccId()).isEqualTo(FAKE_ICCID2);
+ }
+
+ @Test
+ public void testRemoveSubscriptionInfo() throws Exception {
+ insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2);
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+
+ mDatabaseManagerUT.removeSubscriptionInfo(1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2))
+ .isEqualTo(FAKE_SUBSCRIPTION_INFO2);
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(1));
+
+ // Insert a new one. Should become sub 3.
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+ insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2))
+ .isEqualTo(FAKE_SUBSCRIPTION_INFO2);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3))
+ .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1)
+ .setId(3).build());
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3));
+
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+ mDatabaseManagerUT.removeSubscriptionInfo(2);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3))
+ .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1)
+ .setId(3).build());
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(2));
+
+ Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback);
+ mDatabaseManagerUT.removeSubscriptionInfo(3);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull();
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)).isNull();
+ verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3));
+ }
+
+ @Test
+ public void testCallback() {
+ CountDownLatch latch = new CountDownLatch(2);
+ Executor executor = Runnable::run;
+ SubscriptionDatabaseManagerCallback callback =
+ new SubscriptionDatabaseManagerCallback(executor) {
+ @Override
+ public void onInitialized() {
+ latch.countDown();
+ logd("onInitialized");
+ }
+
+ @Override
+ public void onSubscriptionChanged(int subId) {
+ latch.countDown();
+ logd("onSubscriptionChanged");
+ }
+ };
+ assertThat(callback.getExecutor()).isEqualTo(executor);
+ mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(), callback);
+ processAllMessages();
+
+ assertThat(latch.getCount()).isEqualTo(1);
+
+ mDatabaseManagerUT.insertSubscriptionInfo(
+ new SubscriptionInfoInternal.Builder()
+ .setId(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .setIccId(FAKE_ICCID1)
+ .setSimSlotIndex(0)
+ .build());
+ processAllMessages();
+ assertThat(latch.getCount()).isEqualTo(0);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
new file mode 100644
index 0000000..e03256b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.subscription;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class SubscriptionInfoInternalTest {
+ private final SubscriptionInfoInternal mSubInfo =
+ new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setIccId(SubscriptionDatabaseManagerTest.FAKE_ICCID1)
+ .setSimSlotIndex(0)
+ .setDisplayName(SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1)
+ .setCarrierName(SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1)
+ .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_SIM_SPN)
+ .setIconTint(SubscriptionDatabaseManagerTest.FAKE_COLOR1)
+ .setNumber(SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1)
+ .setDataRoaming(SubscriptionManager.DATA_ROAMING_ENABLE)
+ .setMcc(SubscriptionDatabaseManagerTest.FAKE_MCC1)
+ .setMnc(SubscriptionDatabaseManagerTest.FAKE_MNC1)
+ .setEhplmns(SubscriptionDatabaseManagerTest.FAKE_EHPLMNS1)
+ .setHplmns(SubscriptionDatabaseManagerTest.FAKE_HPLMNS1)
+ .setEmbedded(1)
+ .setCardString(SubscriptionDatabaseManagerTest.FAKE_ICCID1)
+ .setCardId(1)
+ .setNativeAccessRules(SubscriptionDatabaseManagerTest
+ .FAKE_NATIVE_ACCESS_RULES1)
+ .setCarrierConfigAccessRules(SubscriptionDatabaseManagerTest
+ .FAKE_CARRIER_CONFIG_ACCESS_RULES1)
+ .setRemovableEmbedded(0)
+ .setCellBroadcastExtremeThreatAlertEnabled(1)
+ .setCellBroadcastSevereThreatAlertEnabled(1)
+ .setCellBroadcastAmberAlertEnabled(1)
+ .setCellBroadcastEmergencyAlertEnabled(1)
+ .setCellBroadcastAlertSoundDuration(4)
+ .setCellBroadcastAlertReminderInterval(1)
+ .setCellBroadcastAlertVibrationEnabled(1)
+ .setCellBroadcastAlertSpeechEnabled(1)
+ .setCellBroadcastEtwsTestAlertEnabled(1)
+ .setCellBroadcastAreaInfoMessageEnabled(1)
+ .setCellBroadcastTestAlertEnabled(1)
+ .setCellBroadcastOptOutDialogEnabled(1)
+ .setEnhanced4GModeEnabled(1)
+ .setVideoTelephonyEnabled(1)
+ .setWifiCallingEnabled(1)
+ .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED)
+ .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
+ .setWifiCallingEnabledForRoaming(1)
+ .setOpportunistic(0)
+ .setGroupUuid(SubscriptionDatabaseManagerTest.FAKE_UUID1)
+ .setCountryIso(SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE1)
+ .setCarrierId(SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setType(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)
+ .setGroupOwner(SubscriptionDatabaseManagerTest.FAKE_OWNER1)
+ .setEnabledMobileDataPolicies(SubscriptionDatabaseManagerTest
+ .FAKE_MOBILE_DATA_POLICY1)
+ .setImsi(SubscriptionDatabaseManagerTest.FAKE_IMSI1)
+ .setUiccApplicationsEnabled(1)
+ .setRcsUceEnabled(1)
+ .setCrossSimCallingEnabled(1)
+ .setRcsConfig(SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG1)
+ .setAllowedNetworkTypesForReasons(SubscriptionDatabaseManagerTest
+ .FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1)
+ .setDeviceToDeviceStatusSharingPreference(
+ SubscriptionManager.D2D_SHARING_ALL_CONTACTS)
+ .setVoImsOptInEnabled(1)
+ .setDeviceToDeviceStatusSharingContacts(
+ SubscriptionDatabaseManagerTest.FAKE_CONTACT1)
+ .setNrAdvancedCallingEnabled(1)
+ .setNumberFromCarrier(SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1)
+ .setNumberFromIms(SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1)
+ .setPortIndex(0)
+ .setUsageSetting(SubscriptionManager.USAGE_SETTING_DEFAULT)
+ .setLastUsedTPMessageReference(SubscriptionDatabaseManagerTest
+ .FAKE_TP_MESSAGE_REFERENCE1)
+ .setUserId(SubscriptionDatabaseManagerTest.FAKE_USER_ID1)
+ .setSatelliteEnabled(1)
+ .setGroupDisabled(false)
+ .build();
+
+ private final SubscriptionInfoInternal mSubInfoNull =
+ new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setIccId("123")
+ .setSimSlotIndex(0)
+ .setDisplayName("")
+ .setCarrierName("")
+ .setNumber("")
+ .setMcc("")
+ .setMnc("")
+ .setEhplmns("")
+ .setHplmns("")
+ .setEmbedded(1)
+ .setCardId(1)
+ .setNativeAccessRules(new byte[0])
+ .setCarrierConfigAccessRules(new byte[0])
+ .setGroupUuid("")
+ .setCountryIso("")
+ .setGroupOwner("")
+ .setEnabledMobileDataPolicies("")
+ .setImsi("")
+ .setRcsConfig(new byte[0])
+ .setAllowedNetworkTypesForReasons("")
+ .setDeviceToDeviceStatusSharingContacts("")
+ .build();
+
+ @Test
+ public void testSubscriptionInfoInternalSetAndGet() {
+ assertThat(mSubInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(mSubInfo.getIccId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1);
+ assertThat(mSubInfo.getSimSlotIndex()).isEqualTo(0);
+ assertThat(mSubInfo.getDisplayName()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1);
+ assertThat(mSubInfo.getCarrierName()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1);
+ assertThat(mSubInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_SIM_SPN);
+ assertThat(mSubInfo.getIconTint()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_COLOR1);
+ assertThat(mSubInfo.getNumber()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1);
+ assertThat(mSubInfo.getDataRoaming()).isEqualTo(SubscriptionManager.DATA_ROAMING_ENABLE);
+ assertThat(mSubInfo.getMcc()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_MCC1);
+ assertThat(mSubInfo.getMnc()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_MNC1);
+ assertThat(mSubInfo.getEhplmns()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_EHPLMNS1);
+ assertThat(mSubInfo.getHplmns()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_HPLMNS1);
+ assertThat(mSubInfo.getEmbedded()).isEqualTo(1);
+ assertThat(mSubInfo.getCardString()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1);
+ assertThat(mSubInfo.getCardId()).isEqualTo(1);
+ assertThat(mSubInfo.getNativeAccessRules()).isEqualTo(SubscriptionDatabaseManagerTest
+ .FAKE_NATIVE_ACCESS_RULES1);
+ assertThat(mSubInfo.getCarrierConfigAccessRules()).isEqualTo(SubscriptionDatabaseManagerTest
+ .FAKE_CARRIER_CONFIG_ACCESS_RULES1);
+ assertThat(mSubInfo.getRemovableEmbedded()).isEqualTo(0);
+ assertThat(mSubInfo.getCellBroadcastExtremeThreatAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastSevereThreatAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastAmberAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastEmergencyAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastAlertSoundDuration()).isEqualTo(4);
+ assertThat(mSubInfo.getCellBroadcastAlertReminderInterval()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastAlertVibrationEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastAlertSpeechEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastEtwsTestAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastAreaInfoMessageEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastTestAlertEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCellBroadcastOptOutDialogEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getEnhanced4GModeEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getVideoTelephonyEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getWifiCallingEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getWifiCallingMode()).isEqualTo(
+ ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
+ assertThat(mSubInfo.getWifiCallingModeForRoaming()).isEqualTo(
+ ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
+ assertThat(mSubInfo.getOpportunistic()).isEqualTo(0);
+ assertThat(mSubInfo.getGroupUuid()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_UUID1);
+ assertThat(mSubInfo.getCountryIso()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE1);
+ assertThat(mSubInfo.getCarrierId()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1);
+ assertThat(mSubInfo.getProfileClass()).isEqualTo(
+ SubscriptionManager.PROFILE_CLASS_OPERATIONAL);
+ assertThat(mSubInfo.getSubscriptionType()).isEqualTo(
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ assertThat(mSubInfo.getGroupOwner()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_OWNER1);
+ assertThat(mSubInfo.getEnabledMobileDataPolicies()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_MOBILE_DATA_POLICY1);
+ assertThat(mSubInfo.getImsi()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_IMSI1);
+ assertThat(mSubInfo.getUiccApplicationsEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getRcsUceEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getCrossSimCallingEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getRcsConfig()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG1);
+ assertThat(mSubInfo.getAllowedNetworkTypesForReasons()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1);
+ assertThat(mSubInfo.getVoImsOptInEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getDeviceToDeviceStatusSharingContacts()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CONTACT1);
+ assertThat(mSubInfo.getNrAdvancedCallingEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.getNumberFromCarrier()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1);
+ assertThat(mSubInfo.getNumberFromIms()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1);
+ assertThat(mSubInfo.getPortIndex()).isEqualTo(
+ SubscriptionManager.USAGE_SETTING_DEFAULT);
+ assertThat(mSubInfo.getLastUsedTPMessageReference()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_TP_MESSAGE_REFERENCE1);
+ assertThat(mSubInfo.getUserId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_USER_ID1);
+ assertThat(mSubInfo.getSatelliteEnabled()).isEqualTo(1);
+ assertThat(mSubInfo.isGroupDisabled()).isFalse();
+ }
+
+ @Test
+ public void testEquals() {
+ SubscriptionInfoInternal another = new SubscriptionInfoInternal.Builder(mSubInfo).build();
+ assertThat(another).isEqualTo(mSubInfo);
+ assertThat(another.hashCode()).isEqualTo(mSubInfo.hashCode());
+ }
+
+ @Test
+ public void testConvertToSubscriptionInfo() {
+ SubscriptionInfo subInfo = mSubInfo.toSubscriptionInfo();
+
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getIccId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
+ assertThat(subInfo.getDisplayName()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getCarrierName()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_SIM_SPN);
+ assertThat(subInfo.getIconTint()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_COLOR1);
+ assertThat(subInfo.getNumber()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1);
+ assertThat(subInfo.getDataRoaming()).isEqualTo(SubscriptionManager.DATA_ROAMING_ENABLE);
+ assertThat(subInfo.getMccString()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_MCC1);
+ assertThat(subInfo.getMncString()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_MNC1);
+ assertThat(subInfo.getEhplmns()).isEqualTo(Arrays.asList(
+ SubscriptionDatabaseManagerTest.FAKE_EHPLMNS1.split(",")));
+ assertThat(subInfo.getHplmns()).isEqualTo(Arrays.asList(
+ SubscriptionDatabaseManagerTest.FAKE_HPLMNS1.split(",")));
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.getCardString()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1);
+ assertThat(subInfo.getCardId()).isEqualTo(1);
+
+
+ List<UiccAccessRule> rules = new ArrayList<>();
+
+ rules.addAll(Arrays.asList(UiccAccessRule.decodeRules(
+ SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES1)));
+ rules.addAll(Arrays.asList(UiccAccessRule.decodeRules(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_CONFIG_ACCESS_RULES1)));
+
+ assertThat(subInfo.getAccessRules()).containsExactlyElementsIn(rules);
+
+ assertThat(subInfo.isOpportunistic()).isFalse();
+ assertThat(subInfo.getGroupUuid()).isEqualTo(ParcelUuid.fromString(
+ SubscriptionDatabaseManagerTest.FAKE_UUID1));
+ assertThat(subInfo.getCountryIso()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE1);
+ assertThat(subInfo.getCarrierId()).isEqualTo(
+ SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1);
+ assertThat(subInfo.getProfileClass()).isEqualTo(
+ SubscriptionManager.PROFILE_CLASS_OPERATIONAL);
+ assertThat(subInfo.getSubscriptionType()).isEqualTo(
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ assertThat(subInfo.getGroupOwner()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_OWNER1);
+ assertThat(subInfo.areUiccApplicationsEnabled()).isTrue();
+ assertThat(subInfo.getPortIndex()).isEqualTo(
+ SubscriptionManager.USAGE_SETTING_DEFAULT);
+ assertThat(subInfo.isGroupDisabled()).isFalse();
+ }
+
+ @Test
+ public void testNullability() {
+ SubscriptionInfo subInfoNull = mSubInfoNull.toSubscriptionInfo();
+ assertThat(subInfoNull.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfoNull.getIccId()).isEqualTo("123");
+ assertThat(subInfoNull.getSimSlotIndex()).isEqualTo(0);
+ assertThat(subInfoNull.getDisplayName()).isEqualTo("");
+ assertThat(subInfoNull.getCarrierName()).isEqualTo("");
+ assertThat(subInfoNull.getNumber()).isEqualTo("");
+ assertThat(subInfoNull.getMccString()).isEqualTo("");
+ assertThat(subInfoNull.getMncString()).isEqualTo("");
+ assertThat(subInfoNull.getEhplmns()).isEmpty();
+ assertThat(subInfoNull.getHplmns()).isEmpty();
+ assertThat(subInfoNull.isEmbedded()).isTrue();
+ assertThat(subInfoNull.getCardId()).isEqualTo(1);
+ assertThat(subInfoNull.getAccessRules()).isNull();
+ assertThat(subInfoNull.getGroupUuid()).isNull();
+ assertThat(subInfoNull.getCountryIso()).isEqualTo("");
+ assertThat(subInfoNull.getGroupOwner()).isEqualTo("");
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
new file mode 100644
index 0000000..3abfac7
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -0,0 +1,2397 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CONTACT1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CONTACT2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_DEFAULT_CARD_NAME;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_EHPLMNS1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_HPLMNS1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_IMSI1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MAC_ADDRESS1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MAC_ADDRESS2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MOBILE_DATA_POLICY1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MOBILE_DATA_POLICY2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER2;
+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_SUBSCRIPTION_INFO1;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO2;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_UUID1;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+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.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.app.PropertyInvalidatedCache;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.provider.Telephony.SimInfo;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.service.euicc.EuiccService;
+import android.service.euicc.GetEuiccProfileInfoListResult;
+import android.telephony.RadioAccessFamily;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+import android.util.Base64;
+
+import com.android.internal.telephony.ContextFixture;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.SubscriptionProvider;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionMap;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SubscriptionManagerServiceTest extends TelephonyTest {
+
+ private static final String CALLING_PACKAGE = "calling_package";
+
+ private static final String CALLING_FEATURE = "calling_feature";
+
+ private static final String GROUP_UUID = "6adbc864-691c-45dc-b698-8fc9a2176fae";
+
+ private SubscriptionManagerService mSubscriptionManagerServiceUT;
+
+ private final SubscriptionProvider mSubscriptionProvider = new SubscriptionProvider();
+
+ private static final UserHandle FAKE_USER_HANDLE = new UserHandle(12);
+
+ private static final UserHandle FAKE_MANAGED_PROFILE_USER_HANDLE = new UserHandle(13);
+
+ // mocked
+ private SubscriptionManagerServiceCallback mMockedSubscriptionManagerServiceCallback;
+ private EuiccController mEuiccController;
+
+ private Set<Integer> mActiveSubs = new ArraySet<>();
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ @Before
+ public void setUp() throws Exception {
+ logd("SubscriptionManagerServiceTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+
+ // Dual-SIM configuration
+ mPhones = new Phone[] {mPhone, mPhone2};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(2).when(mTelephonyManager).getSupportedModemCount();
+ doReturn(mUiccProfile).when(mPhone2).getIccCard();
+ doReturn(new UiccSlot[]{mUiccSlot}).when(mUiccController).getUiccSlots();
+
+ mContextFixture.putBooleanResource(com.android.internal.R.bool
+ .config_subscription_database_async_update, true);
+ mContextFixture.putIntArrayResource(com.android.internal.R.array.sim_colors, new int[0]);
+
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC);
+ setupMocksForTelephonyPermissions(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ PropertyInvalidatedCache.disableForCurrentProcess("cache_key.is_compat_change_enabled");
+
+ doReturn(true).when(mTelephonyManager).isVoiceCapable();
+ mEuiccController = Mockito.mock(EuiccController.class);
+ replaceInstance(EuiccController.class, "sInstance", null, mEuiccController);
+ mMockedSubscriptionManagerServiceCallback = Mockito.mock(
+ SubscriptionManagerServiceCallback.class);
+ doReturn(FAKE_ICCID1).when(mUiccCard).getCardId();
+ doReturn(FAKE_ICCID1).when(mUiccPort).getIccId();
+ doReturn(true).when(mUiccSlot).isActive();
+ doReturn(FAKE_ICCID1).when(mUiccController).convertToCardString(eq(1));
+ doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(2));
+
+ doReturn(new int[0]).when(mSubscriptionManager).getCompleteActiveSubscriptionIdList();
+
+ ((MockContentResolver) mContext.getContentResolver()).addProvider(
+ Telephony.Carriers.CONTENT_URI.getAuthority(), mSubscriptionProvider);
+
+ mSubscriptionManagerServiceUT = new SubscriptionManagerService(mContext, Looper.myLooper());
+
+ monitorTestableLooper(new TestableLooper(getBackgroundHandler().getLooper()));
+ monitorTestableLooper(new TestableLooper(getSubscriptionDatabaseManager().getLooper()));
+
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArguments()[0]).run();
+ return null;
+ }).when(mMockedSubscriptionManagerServiceCallback).invokeFromExecutor(any(Runnable.class));
+
+ mSubscriptionManagerServiceUT.registerCallback(mMockedSubscriptionManagerServiceCallback);
+ processAllFutureMessages();
+
+ // Revoke all permissions.
+ mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+ doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).noteOpNoThrow(anyString(),
+ anyInt(), nullable(String.class), nullable(String.class), nullable(String.class));
+ setIdentifierAccess(false);
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
+
+ doReturn(true).when(mUserManager)
+ .isManagedProfile(eq(FAKE_MANAGED_PROFILE_USER_HANDLE.getIdentifier()));
+
+ logd("SubscriptionManagerServiceTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ super.tearDown();
+ }
+
+ private Handler getBackgroundHandler() throws Exception {
+ Field field = SubscriptionManagerService.class.getDeclaredField(
+ "mBackgroundHandler");
+ field.setAccessible(true);
+ return (Handler) field.get(mSubscriptionManagerServiceUT);
+ }
+
+ private SubscriptionDatabaseManager getSubscriptionDatabaseManager() throws Exception {
+ Field field = SubscriptionManagerService.class.getDeclaredField(
+ "mSubscriptionDatabaseManager");
+ field.setAccessible(true);
+ return (SubscriptionDatabaseManager) field.get(mSubscriptionManagerServiceUT);
+ }
+
+ /**
+ * Insert the subscription info to the database. This is an instant insertion method. For real
+ * insertion sequence please use {@link #testInsertNewSim()}.
+ *
+ * @param subInfo The subscription to be inserted.
+ * @return The new sub id.
+ */
+ private int insertSubscription(@NonNull SubscriptionInfoInternal subInfo) {
+ try {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setId(SubscriptionManager.INVALID_SUBSCRIPTION_ID).build();
+ int subId = getSubscriptionDatabaseManager().insertSubscriptionInfo(subInfo);
+
+ // Insertion is sync, but the onSubscriptionChanged callback is handled by the handler.
+ processAllMessages();
+
+ Field field = SubscriptionManagerService.class.getDeclaredField("mSlotIndexToSubId");
+ field.setAccessible(true);
+ SubscriptionMap<Integer, Integer> map = (SubscriptionMap<Integer, Integer>)
+ field.get(mSubscriptionManagerServiceUT);
+ Class[] cArgs = new Class[2];
+ cArgs[0] = Object.class;
+ cArgs[1] = Object.class;
+
+ if (subInfo.getSimSlotIndex() >= 0) {
+ // Change the slot -> subId mapping
+ map.put(subInfo.getSimSlotIndex(), subId);
+ }
+
+ mContextFixture.removeCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(subId));
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+
+ if (subInfo.getSimSlotIndex() >= 0) {
+ mActiveSubs.add(subId);
+
+ // Change the SIM state
+ field = SubscriptionManagerService.class.getDeclaredField("mSimState");
+ field.setAccessible(true);
+ Object array = field.get(mSubscriptionManagerServiceUT);
+ Array.set(array, subInfo.getSimSlotIndex(), TelephonyManager.SIM_STATE_LOADED);
+ } else {
+ mActiveSubs.remove(subId);
+ }
+
+ doReturn(mActiveSubs.stream().mapToInt(i->i).toArray()).when(mSubscriptionManager)
+ .getCompleteActiveSubscriptionIdList();
+ return subId;
+ } catch (Exception e) {
+ fail("Failed to insert subscription. e=" + e);
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ @Test
+ public void testBroadcastOnInitialization() {
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(3)).sendBroadcastAsUser(
+ captorIntent.capture(), eq(UserHandle.ALL));
+ assertThat(captorIntent.getAllValues().stream().map(Intent::getAction).toList())
+ .containsExactly(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,
+ TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED,
+ SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
+ }
+
+ @Test
+ public void testAddSubInfo() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
+ assertThat(subInfo.getSubscriptionType()).isEqualTo(
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ }
+
+ @Test
+ public void testSetMccMnc() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+ mSubscriptionManagerServiceUT.setMccMnc(1, FAKE_MCC2 + FAKE_MNC2);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC2);
+ verify(mMockedSubscriptionManagerServiceCallback, times(2)).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
+ public void testSetCountryIso() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+ mSubscriptionManagerServiceUT.setCountryIso(1, FAKE_COUNTRY_CODE2);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getCountryIso()).isEqualTo(FAKE_COUNTRY_CODE2);
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
+ public void testSetCarrierId() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+ mSubscriptionManagerServiceUT.setCarrierId(1, FAKE_CARRIER_ID2);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getCarrierId()).isEqualTo(FAKE_CARRIER_ID2);
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
+ public void testSetPhoneNumber() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+
+ // Caller does not have carrier privilege
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.setPhoneNumber(1,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, FAKE_PHONE_NUMBER2,
+ CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant carrier privilege
+ setCarrierPrivilegesForSubId(true, 1);
+
+ // Source IMS is not acceptable
+ assertThrows(IllegalArgumentException.class,
+ () -> mSubscriptionManagerServiceUT.setPhoneNumber(1,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS, FAKE_PHONE_NUMBER2,
+ CALLING_PACKAGE, CALLING_FEATURE));
+
+ mSubscriptionManagerServiceUT.setPhoneNumber(1,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, FAKE_PHONE_NUMBER2,
+ CALLING_PACKAGE, CALLING_FEATURE);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getNumberFromCarrier()).isEqualTo(FAKE_PHONE_NUMBER2);
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
+ public void testGetAllSubInfoList() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ // Should throw security exception if the caller does not have permission.
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant carrier privilege for sub 1
+ setCarrierPrivilegesForSubId(true, 1);
+ // Grant carrier privilege for sub 2
+ setCarrierPrivilegesForSubId(true, 2);
+
+ List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(2);
+
+ assertThat(subInfos.get(0)).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+
+ assertThat(subInfos.get(1)).isEqualTo(FAKE_SUBSCRIPTION_INFO2.toSubscriptionInfo());
+
+ // Revoke carrier privilege for sub 2
+ setCarrierPrivilegesForSubId(false, 2);
+
+ subInfos = mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE);
+ // Should only have access to one sub.
+ assertThat(subInfos).hasSize(1);
+
+ assertThat(subInfos.get(0).getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfos.get(0).getCardString()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfos.get(0).getNumber()).isEqualTo(FAKE_PHONE_NUMBER1);
+
+ // Grant READ_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ // Grant identifier access
+ setIdentifierAccess(true);
+ // Revoke carrier privileges.
+ setCarrierPrivilegesForSubId(false, 1);
+ setCarrierPrivilegesForSubId(false, 2);
+
+ subInfos = mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(2);
+
+ assertThat(subInfos.get(0).getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfos.get(0).getCardString()).isEqualTo(FAKE_ICCID1);
+ // Phone number should be empty
+ assertThat(subInfos.get(0).getNumber()).isEmpty();
+ assertThat(subInfos.get(1).getIccId()).isEqualTo(FAKE_ICCID2);
+ assertThat(subInfos.get(1).getCardString()).isEqualTo(FAKE_ICCID2);
+ // Phone number should be empty
+ assertThat(subInfos.get(1).getNumber()).isEmpty();
+
+ // Grant phone number access
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mMockLegacyPermissionManager)
+ .checkPhoneNumberAccess(anyString(), anyString(), anyString(), anyInt(), anyInt());
+
+ subInfos = mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(2);
+ assertThat(subInfos.get(0).getNumber()).isEqualTo(FAKE_PHONE_NUMBER1);
+ assertThat(subInfos.get(1).getNumber()).isEqualTo(FAKE_PHONE_NUMBER2);
+ }
+
+ @Test
+ @EnableCompatChanges({SubscriptionManagerService.REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
+ public void testGetSubscriptionsInGroup() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ SubscriptionInfoInternal anotherSubInfo =
+ new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setGroupUuid(FAKE_UUID1)
+ .build();
+ insertSubscription(anotherSubInfo);
+
+ // Throw exception is the new behavior.
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getSubscriptionsInGroup(
+ ParcelUuid.fromString(FAKE_UUID1), CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant carrier privilege on sub 1 and 2
+ setCarrierPrivilegesForSubId(true, 1);
+ setCarrierPrivilegesForSubId(true, 2);
+ List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT.getSubscriptionsInGroup(
+ ParcelUuid.fromString(FAKE_UUID1), CALLING_PACKAGE, CALLING_FEATURE);
+
+ assertThat(subInfos).hasSize(2);
+ assertThat(subInfos.get(0)).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ assertThat(subInfos.get(1)).isEqualTo(anotherSubInfo.toSubscriptionInfo());
+
+ // Grant READ_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ setIdentifierAccess(false);
+ setCarrierPrivilegesForSubId(false, 1);
+ setCarrierPrivilegesForSubId(false, 2);
+ doNothing().when(mContext).enforcePermission(
+ eq(android.Manifest.permission.READ_PHONE_STATE), anyInt(), anyInt(), anyString());
+
+ // Throw exception is the new behavior. Only has READ_PHONE_STATE is not enough. Need
+ // identifier access as well.
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getSubscriptionsInGroup(
+ ParcelUuid.fromString(FAKE_UUID1), CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant identifier access
+ setIdentifierAccess(true);
+ // Grant phone number access
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
+
+ subInfos = mSubscriptionManagerServiceUT.getSubscriptionsInGroup(
+ ParcelUuid.fromString(FAKE_UUID1), CALLING_PACKAGE, CALLING_FEATURE);
+
+ assertThat(subInfos).hasSize(2);
+ assertThat(subInfos).containsExactlyElementsIn(
+ List.of(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo(),
+ anotherSubInfo.toSubscriptionInfo()));
+ }
+
+ @Test
+ public void testGetAvailableSubscriptionInfoList() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ SubscriptionInfoInternal anotherSubInfo =
+ new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setSimSlotIndex(SubscriptionManager.INVALID_SIM_SLOT_INDEX)
+ .setType(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)
+ .build();
+ insertSubscription(anotherSubInfo);
+
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getAvailableSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE));
+ // Grant carrier privilege for sub 1
+ setCarrierPrivilegesForSubId(true, 1);
+
+ // Not yet planned for carrier apps to access this API.
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getAvailableSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant READ_PHONE_STATE permission, which is not enough for this API.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.getAvailableSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant READ_PRIVILEGED_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT
+ .getAvailableSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(1);
+ assertThat(subInfos.get(0)).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testSetDefaultVoiceSubId() throws Exception {
+ clearInvocations(mContext);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.setDefaultVoiceSubId(1));
+
+ // Grant MODIFY_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDefaultVoiceSubId(1);
+ assertThat(mSubscriptionManagerServiceUT.getDefaultVoiceSubId()).isEqualTo(1);
+
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION)).isEqualTo(1);
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(2)).sendBroadcastAsUser(
+ captorIntent.capture(), eq(UserHandle.ALL));
+
+ Intent intent = captorIntent.getAllValues().get(0);
+ assertThat(intent.getAction()).isEqualTo(
+ TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+
+ Bundle b = intent.getExtras();
+
+ assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
+ assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+
+ intent = captorIntent.getAllValues().get(1);
+ assertThat(intent.getAction()).isEqualTo(
+ SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+
+ b = intent.getExtras();
+
+ assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
+ assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+ }
+
+ @Test
+ public void testSetDefaultDataSubId() throws Exception {
+ clearInvocations(mContext);
+ doReturn(false).when(mTelephonyManager).isVoiceCapable();
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.setDefaultDataSubId(1));
+
+ // Grant MODIFY_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDefaultDataSubId(1);
+ assertThat(mSubscriptionManagerServiceUT.getDefaultDataSubId()).isEqualTo(1);
+
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION)).isEqualTo(1);
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(2)).sendBroadcastAsUser(
+ captorIntent.capture(), eq(UserHandle.ALL));
+
+ Intent intent = captorIntent.getAllValues().get(0);
+ assertThat(intent.getAction()).isEqualTo(
+ TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+
+ Bundle b = intent.getExtras();
+
+ assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
+ assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+
+ intent = captorIntent.getAllValues().get(1);
+ assertThat(intent.getAction()).isEqualTo(
+ SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+
+ b = intent.getExtras();
+
+ assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
+ assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+ }
+
+ @Test
+ public void testSetDefaultSmsSubId() throws Exception {
+ clearInvocations(mContext);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class,
+ () -> mSubscriptionManagerServiceUT.setDefaultSmsSubId(1));
+
+ // Grant MODIFY_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDefaultSmsSubId(1);
+ assertThat(mSubscriptionManagerServiceUT.getDefaultSmsSubId()).isEqualTo(1);
+
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION)).isEqualTo(1);
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcastAsUser(captorIntent.capture(), eq(UserHandle.ALL));
+
+ Intent intent = captorIntent.getValue();
+ assertThat(intent.getAction()).isEqualTo(
+ SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
+
+ Bundle b = intent.getExtras();
+
+ assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
+ assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+ }
+
+ @Test
+ public void testIsActiveSubId() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setSimSlotIndex(SubscriptionManager.INVALID_SIM_SLOT_INDEX).build());
+
+ // Should fail without READ_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .isActiveSubId(1, CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant READ_PRIVILEGED_PHONE_STATE permission for insertion.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.isActiveSubId(
+ 1, CALLING_PACKAGE, CALLING_FEATURE)).isTrue();
+ assertThat(mSubscriptionManagerServiceUT.isActiveSubId(
+ 2, CALLING_PACKAGE, CALLING_FEATURE)).isFalse();
+ }
+
+ @Test
+ public void testGetActiveSubscriptionInfoList() {
+ // Grant MODIFY_PHONE_STATE permission for insertion.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setSimSlotIndex(SubscriptionManager.INVALID_SIM_SLOT_INDEX).build());
+ // Remove MODIFY_PHONE_STATE
+ mContextFixture.removeCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // Should get an empty list without READ_PHONE_STATE.
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+
+ // Grant READ_PHONE_STATE permission for insertion.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+
+ List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(1);
+ assertThat(subInfos.get(0).getIccId()).isEmpty();
+ assertThat(subInfos.get(0).getCardString()).isEmpty();
+ assertThat(subInfos.get(0).getNumber()).isEmpty();
+ assertThat(subInfos.get(0).getGroupUuid()).isNull();
+
+ // Grant carrier privilege
+ setCarrierPrivilegesForSubId(true, 1);
+
+ subInfos = mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(1);
+ assertThat(subInfos.get(0)).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testGetActiveSubscriptionInfoForSimSlotIndex() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ // Should fail without READ_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoForSimSlotIndex(0, CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant READ_PHONE_STATE permission for insertion.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ SubscriptionInfo subInfo = mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoForSimSlotIndex(0, CALLING_PACKAGE,
+ CALLING_FEATURE);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getIccId()).isEmpty();
+ assertThat(subInfo.getNumber()).isEmpty();
+
+ // Grant carrier privilege for sub 1
+ setCarrierPrivilegesForSubId(true, 1);
+ subInfo = mSubscriptionManagerServiceUT.getActiveSubscriptionInfoForSimSlotIndex(
+ 0, CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfo).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testUpdateEmbeddedSubscriptions() {
+ EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
+ .setIccid(FAKE_ICCID1)
+ .setNickname(FAKE_CARRIER_NAME1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1, null, null, null,
+ null, FAKE_CARRIER_ID1, FAKE_CARRIER_ID1))
+ .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules(
+ FAKE_NATIVE_ACCESS_RULES1)))
+ .build();
+ EuiccProfileInfo profileInfo2 = new EuiccProfileInfo.Builder(FAKE_ICCID2)
+ .setIccid(FAKE_ICCID2)
+ .setNickname(FAKE_CARRIER_NAME2)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, null, null,
+ null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2))
+ .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules(
+ FAKE_NATIVE_ACCESS_RULES2)))
+ .build();
+
+ GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult(
+ EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+ 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());
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ assertThat(subInfo.getPortIndex()).isEqualTo(TelephonyManager.INVALID_PORT_INDEX);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_CARRIER);
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC1);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC1);
+ assertThat(subInfo.getProfileClass()).isEqualTo(
+ SubscriptionManager.PROFILE_CLASS_OPERATIONAL);
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.isRemovableEmbedded()).isFalse();
+ assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES1);
+
+ subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(2);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ assertThat(subInfo.getPortIndex()).isEqualTo(TelephonyManager.INVALID_PORT_INDEX);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID2);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME2);
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_CARRIER);
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC2);
+ assertThat(subInfo.getProfileClass()).isEqualTo(
+ SubscriptionManager.PROFILE_CLASS_OPERATIONAL);
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.isRemovableEmbedded()).isFalse();
+ assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES2);
+ }
+
+ @Test
+ public void testUpdateEmbeddedSubscriptionsNullResult() {
+ // Grant READ_PHONE_STATE permission.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+
+ doReturn(null).when(mEuiccController).blockingGetEuiccProfileInfoList(anyInt());
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null);
+ processAllMessages();
+
+ List<SubscriptionInfo> subInfoList = mSubscriptionManagerServiceUT
+ .getAllSubInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfoList).isEmpty();
+ }
+
+ @Test
+ public void testGetActiveSubscriptionInfo() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without READ_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfo(1, CALLING_PACKAGE, CALLING_FEATURE));
+
+ // Grant READ_PHONE_STATE permission for insertion.
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ SubscriptionInfo subInfo = mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoForSimSlotIndex(0, CALLING_PACKAGE,
+ CALLING_FEATURE);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getIccId()).isEmpty();
+ assertThat(subInfo.getNumber()).isEmpty();
+
+ // Grant carrier privilege for sub 1
+ setCarrierPrivilegesForSubId(true, 1);
+ subInfo = mSubscriptionManagerServiceUT.getActiveSubscriptionInfoForSimSlotIndex(
+ 0, CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfo).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testSetDisplayNameUsingSrc() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setDisplayNameUsingSrc(FAKE_CARRIER_NAME2, 1,
+ SubscriptionManager.NAME_SOURCE_CARRIER_ID));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // Carrier ID name source should have lower priority. Should not be able to update the
+ // display name.
+ assertThat(mSubscriptionManagerServiceUT.setDisplayNameUsingSrc(FAKE_CARRIER_NAME2,
+ 1, SubscriptionManager.NAME_SOURCE_CARRIER_ID)).isEqualTo(0);
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1).getDisplayName())
+ .isEqualTo(FAKE_CARRIER_NAME1);
+
+ // User input display name should have highest priority.
+ assertThat(mSubscriptionManagerServiceUT.setDisplayNameUsingSrc(FAKE_CARRIER_NAME2,
+ 1, SubscriptionManager.NAME_SOURCE_USER_INPUT)).isEqualTo(1);
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1).getDisplayName())
+ .isEqualTo(FAKE_CARRIER_NAME2);
+ }
+
+ @Test
+ public void testGetActiveSubInfoCount() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ // Should fail without READ_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getActiveSubInfoCount(CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(2);
+ }
+
+ @Test
+ public void testSetIconTint() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setIconTint(1, 12345));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.setIconTint(1, 12345);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getIconTint()).isEqualTo(12345);
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
+ public void testGetActiveSubscriptionInfoForIccId() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without READ_PRIVILEGED_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoForIccId(FAKE_ICCID1, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ SubscriptionInfo subInfo = mSubscriptionManagerServiceUT.getActiveSubscriptionInfoForIccId(
+ FAKE_ICCID1, CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfo).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testGetAccessibleSubscriptionInfoList() {
+ doReturn(true).when(mEuiccManager).isEnabled();
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ doReturn(true).when(mSubscriptionManager).canManageSubscription(
+ any(SubscriptionInfo.class), eq(CALLING_PACKAGE));
+ // FAKE_SUBSCRIPTION_INFO2 is a not eSIM. So the list should be empty.
+ assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList(
+ CALLING_PACKAGE)).isEmpty();
+
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ doReturn(false).when(mEuiccManager).isEnabled();
+ assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList(
+ CALLING_PACKAGE)).isNull();
+
+ doReturn(false).when(mSubscriptionManager).canManageSubscription(
+ any(SubscriptionInfo.class), eq(CALLING_PACKAGE));
+
+ doReturn(true).when(mEuiccManager).isEnabled();
+ assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList(
+ CALLING_PACKAGE)).isEmpty();
+
+ doReturn(true).when(mSubscriptionManager).canManageSubscription(
+ any(SubscriptionInfo.class), eq(CALLING_PACKAGE));
+ assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList(
+ CALLING_PACKAGE)).isEqualTo(List.of(new SubscriptionInfoInternal.Builder(
+ FAKE_SUBSCRIPTION_INFO1).setId(2).build().toSubscriptionInfo()));
+ }
+
+ @Test
+ public void testIsSubscriptionEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without READ_PRIVILEGED_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .isSubscriptionEnabled(1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.isSubscriptionEnabled(1)).isTrue();
+ assertThat(mSubscriptionManagerServiceUT.isSubscriptionEnabled(2)).isFalse();
+ }
+
+ @Test
+ public void testGetEnabledSubscriptionId() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without READ_PRIVILEGED_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getEnabledSubscriptionId(0));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThrows(IllegalArgumentException.class, () -> mSubscriptionManagerServiceUT
+ .getEnabledSubscriptionId(SubscriptionManager.INVALID_SIM_SLOT_INDEX));
+
+ assertThat(mSubscriptionManagerServiceUT.getEnabledSubscriptionId(0)).isEqualTo(1);
+ assertThat(mSubscriptionManagerServiceUT.getEnabledSubscriptionId(1)).isEqualTo(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ assertThrows(IllegalArgumentException.class, () -> mSubscriptionManagerServiceUT
+ .getEnabledSubscriptionId(2));
+ }
+
+ @Test
+ public void testGetActiveDataSubscriptionId() {
+ doReturn(12345).when(mPhoneSwitcher).getActiveDataSubId();
+ assertThat(mSubscriptionManagerServiceUT.getActiveDataSubscriptionId()).isEqualTo(12345);
+ }
+
+ @Test
+ public void testSetGetSubscriptionUserHandle() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MANAGE_SUBSCRIPTION_USER_ASSOCIATION
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionUserHandle(FAKE_USER_HANDLE, 1));
+
+ mContextFixture.addCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ mSubscriptionManagerServiceUT.setSubscriptionUserHandle(FAKE_USER_HANDLE, 1);
+
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getUserId()).isEqualTo(FAKE_USER_HANDLE.getIdentifier());
+
+ mContextFixture.removeCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ // Should fail without MANAGE_SUBSCRIPTION_USER_ASSOCIATION
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getSubscriptionUserHandle(1));
+
+ mContextFixture.addCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionUserHandle(1))
+ .isEqualTo(FAKE_USER_HANDLE);
+ }
+
+ @Test
+ public void testIsSubscriptionAssociatedWithUser() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MANAGE_SUBSCRIPTION_USER_ASSOCIATION
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .isSubscriptionAssociatedWithUser(1, FAKE_USER_HANDLE));
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getSubscriptionInfoListAssociatedWithUser(FAKE_USER_HANDLE));
+
+ mContextFixture.addCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setSubscriptionUserHandle(FAKE_USER_HANDLE, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ List<SubscriptionInfo> associatedSubInfoList = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoListAssociatedWithUser(FAKE_USER_HANDLE);
+ assertThat(associatedSubInfoList.size()).isEqualTo(1);
+ assertThat(associatedSubInfoList.get(0).getSubscriptionId()).isEqualTo(1);
+
+ assertThat(mSubscriptionManagerServiceUT.isSubscriptionAssociatedWithUser(1,
+ FAKE_USER_HANDLE)).isEqualTo(true);
+
+ // Work profile is not associated with any subscription
+ associatedSubInfoList = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoListAssociatedWithUser(FAKE_MANAGED_PROFILE_USER_HANDLE);
+ assertThat(associatedSubInfoList.size()).isEqualTo(0);
+ assertThat(mSubscriptionManagerServiceUT.isSubscriptionAssociatedWithUser(1,
+ FAKE_MANAGED_PROFILE_USER_HANDLE)).isEqualTo(false);
+ }
+
+ @Test
+ public void testSetUsageSetting() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setUsageSetting(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC, 1,
+ CALLING_PACKAGE));
+
+ // Grant carrier privilege
+ setCarrierPrivilegesForSubId(true, 1);
+ mSubscriptionManagerServiceUT.setUsageSetting(
+ SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC, 1, CALLING_PACKAGE);
+
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getUsageSetting()).isEqualTo(
+ SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+ }
+
+ @Test
+ public void testSetDisplayNumber() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setDisplayNumber(FAKE_PHONE_NUMBER2, 1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDisplayNumber(FAKE_PHONE_NUMBER2, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getNumber()).isEqualTo(FAKE_PHONE_NUMBER2);
+ }
+
+ @Test
+ public void testSetOpportunistic() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setOpportunistic(true, 1, CALLING_PACKAGE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.setOpportunistic(true, 1, CALLING_PACKAGE);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.isOpportunistic()).isTrue();
+ }
+
+ @Test
+ public void testGetOpportunisticSubscriptions() {
+ testSetOpportunistic();
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ // Should get an empty list without READ_PHONE_STATE.
+ assertThat(mSubscriptionManagerServiceUT.getOpportunisticSubscriptions(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+
+ setIdentifierAccess(true);
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
+
+ List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT
+ .getOpportunisticSubscriptions(CALLING_PACKAGE, CALLING_FEATURE);
+ assertThat(subInfos).hasSize(2);
+ assertThat(subInfos.get(0)).isEqualTo(new SubscriptionInfoInternal
+ .Builder(FAKE_SUBSCRIPTION_INFO1).setOpportunistic(1).build().toSubscriptionInfo());
+ assertThat(subInfos.get(1)).isEqualTo(FAKE_SUBSCRIPTION_INFO2.toSubscriptionInfo());
+ }
+
+ @Test
+ public void testSetPreferredDataSubscriptionId() {
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setPreferredDataSubscriptionId(1, false, null));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setPreferredDataSubscriptionId(1, false, null);
+ verify(mPhoneSwitcher).trySetOpportunisticDataSubscription(eq(1), eq(false), eq(null));
+ }
+
+ @Test
+ public void testGetPreferredDataSubscriptionId() {
+ // Should fail without READ_PRIVILEGED_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getPreferredDataSubscriptionId());
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ doReturn(12345).when(mPhoneSwitcher).getAutoSelectedDataSubId();
+ assertThat(mSubscriptionManagerServiceUT.getPreferredDataSubscriptionId()).isEqualTo(12345);
+ }
+
+ @Test
+ public void testAddSubscriptionsIntoGroup() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ ParcelUuid newUuid = ParcelUuid.fromString(GROUP_UUID);
+ String newOwner = "new owner";
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .addSubscriptionsIntoGroup(new int[]{1, 2}, newUuid, CALLING_PACKAGE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubscriptionsIntoGroup(
+ new int[]{1, 2}, newUuid, newOwner);
+
+ SubscriptionInfo subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfo(1);
+ assertThat(subInfo.getGroupUuid()).isEqualTo(newUuid);
+ assertThat(subInfo.getGroupOwner()).isEqualTo(newOwner);
+
+ subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfo(2);
+ assertThat(subInfo.getGroupUuid()).isEqualTo(newUuid);
+ assertThat(subInfo.getGroupOwner()).isEqualTo(newOwner);
+ }
+
+ @Test
+ public void testSetDeviceToDeviceStatusSharing() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setDeviceToDeviceStatusSharing(SubscriptionManager.D2D_SHARING_SELECTED_CONTACTS,
+ 1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDeviceToDeviceStatusSharing(
+ SubscriptionManager.D2D_SHARING_SELECTED_CONTACTS, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getDeviceToDeviceStatusSharingPreference()).isEqualTo(
+ SubscriptionManager.D2D_SHARING_SELECTED_CONTACTS);
+ }
+
+ @Test
+ public void testSetDeviceToDeviceStatusSharingContacts() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT2, 1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT2, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getDeviceToDeviceStatusSharingContacts()).isEqualTo(FAKE_CONTACT2);
+ }
+
+ @Test
+ public void testGetPhoneNumberFromFirstAvailableSource() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without phone number access
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .getPhoneNumberFromFirstAvailableSource(1, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+
+ assertThat(mSubscriptionManagerServiceUT.getPhoneNumberFromFirstAvailableSource(
+ 1, CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(FAKE_PHONE_NUMBER1);
+ }
+
+ @Test
+ public void testSetUiccApplicationsEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setUiccApplicationsEnabled(false, 1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(false, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ verify(mMockedSubscriptionManagerServiceCallback).onUiccApplicationsEnabledChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.areUiccApplicationsEnabled()).isFalse();
+
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(false, 1);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback, never()).onSubscriptionChanged(eq(1));
+ verify(mMockedSubscriptionManagerServiceCallback, never())
+ .onUiccApplicationsEnabledChanged(eq(1));
+ }
+
+ @Test
+ public void testCanDisablePhysicalSubscription() {
+ // Should fail without READ_PRIVILEGED_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .canDisablePhysicalSubscription());
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ doReturn(false).when(mPhone).canDisablePhysicalSubscription();
+ assertThat(mSubscriptionManagerServiceUT.canDisablePhysicalSubscription()).isFalse();
+
+ doReturn(true).when(mPhone).canDisablePhysicalSubscription();
+ assertThat(mSubscriptionManagerServiceUT.canDisablePhysicalSubscription()).isTrue();
+ }
+
+ @Test
+ public void testSetGetEnhanced4GModeEnabled() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_ENHANCED_4G_MODE_ENABLED
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getEnhanced4GModeEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetVideoTelephonyEnabled() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_VT_IMS_ENABLED, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_VT_IMS_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_VT_IMS_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_VT_IMS_ENABLED
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_VT_IMS_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getVideoTelephonyEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetWifiCallingEnabled() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ENABLED, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_WFC_IMS_ENABLED
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getWifiCallingEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetWifiCallingMode() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_MODE, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_MODE, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_MODE, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_WFC_IMS_MODE
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_MODE, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getWifiCallingMode()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetWifiCallingModeForRoaming() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("2");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_WFC_IMS_ROAMING_MODE
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getWifiCallingModeForRoaming()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetEnabledMobileDataPolicies() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES, CALLING_PACKAGE,
+ CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo(FAKE_MOBILE_DATA_POLICY1);
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_ENABLED_MOBILE_DATA_POLICIES
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES, FAKE_MOBILE_DATA_POLICY2);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getEnabledMobileDataPolicies()).isEqualTo(FAKE_MOBILE_DATA_POLICY2);
+ }
+
+ @Test
+ public void testSetGetRcsUceEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_IMS_RCS_UCE_ENABLED
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getRcsUceEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetCrossSimCallingEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED, CALLING_PACKAGE,
+ CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_CROSS_SIM_CALLING_ENABLED
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getCrossSimCallingEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetRcsConfig() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_RCS_CONFIG, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_RCS_CONFIG, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo(Base64.encodeToString(FAKE_RCS_CONFIG1, Base64.DEFAULT));
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_RCS_CONFIG, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_RCS_CONFIG
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_RCS_CONFIG,
+ Base64.encodeToString(FAKE_RCS_CONFIG2, Base64.DEFAULT));
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getRcsConfig()).isEqualTo(FAKE_RCS_CONFIG2);
+ }
+
+ @Test
+ public void testSetGetDeviceToDeviceStatusSharingPreference() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_D2D_STATUS_SHARING, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_D2D_STATUS_SHARING
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getDeviceToDeviceStatusSharingPreference()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetVoImsOptInEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, CALLING_PACKAGE, CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_VOIMS_OPT_IN_STATUS
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_VOIMS_OPT_IN_STATUS, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getVoImsOptInEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetGetDeviceToDeviceStatusSharingContacts() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, CALLING_PACKAGE,
+ CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, CALLING_PACKAGE,
+ CALLING_FEATURE)).isEqualTo(FAKE_CONTACT1);
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+ FAKE_CONTACT2));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, FAKE_CONTACT2);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getDeviceToDeviceStatusSharingContacts()).isEqualTo(FAKE_CONTACT2);
+ }
+
+ @Test
+ public void testSetGetNrAdvancedCallingEnabled() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, CALLING_PACKAGE,
+ CALLING_FEATURE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo("1");
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, "0"));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // COLUMN_NR_ADVANCED_CALLING_ENABLED
+ mSubscriptionManagerServiceUT.setSubscriptionProperty(1,
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, "0");
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getNrAdvancedCallingEnabled()).isEqualTo(0);
+ }
+
+ @Test
+ public void testSetSubscriptionPropertyInvalidField() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ assertThrows(IllegalArgumentException.class, () -> mSubscriptionManagerServiceUT
+ .setSubscriptionProperty(1, "hahahaha", "0"));
+ }
+
+ @Test
+ public void testGetNonAccessibleFields() throws Exception {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ Field field = SubscriptionManagerService.class.getDeclaredField(
+ "DIRECT_ACCESS_SUBSCRIPTION_COLUMNS");
+ field.setAccessible(true);
+ Set<String> accessibleColumns = (Set<String>) field.get(null);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ for (String column : SimInfo.getAllColumns()) {
+ if (accessibleColumns.contains(column)) {
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1, column,
+ CALLING_PACKAGE, CALLING_FEATURE);
+ } else {
+ assertThrows(SecurityException.class, () ->
+ mSubscriptionManagerServiceUT.getSubscriptionProperty(1, column,
+ CALLING_PACKAGE, CALLING_FEATURE));
+ }
+ }
+ }
+
+ @Test
+ public void testSyncToGroup() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.createSubscriptionGroup(new int[]{1, 2}, CALLING_PACKAGE);
+
+ mSubscriptionManagerServiceUT.syncGroupedSetting(1);
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2).getIconTint())
+ .isEqualTo(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getIconTint());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2).getDataRoaming())
+ .isEqualTo(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1)
+ .getDataRoaming());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getEnhanced4GModeEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getEnhanced4GModeEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getVideoTelephonyEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getVideoTelephonyEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getWifiCallingEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getWifiCallingEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getWifiCallingMode()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getWifiCallingMode());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getWifiCallingModeForRoaming()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getWifiCallingModeForRoaming());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getWifiCallingEnabledForRoaming()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getWifiCallingEnabledForRoaming());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getEnabledMobileDataPolicies()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getEnabledMobileDataPolicies());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getUiccApplicationsEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getUiccApplicationsEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getRcsUceEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getRcsUceEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getCrossSimCallingEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getCrossSimCallingEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getRcsConfig()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getRcsConfig());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getDeviceToDeviceStatusSharingPreference()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getDeviceToDeviceStatusSharingPreference());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getVoImsOptInEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getVoImsOptInEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getDeviceToDeviceStatusSharingContacts()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getDeviceToDeviceStatusSharingContacts());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getNrAdvancedCallingEnabled()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getNrAdvancedCallingEnabled());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2)
+ .getUserId()).isEqualTo(mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1).getUserId());
+ }
+
+ @Test
+ public void testRemoveSubInfo() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .removeSubInfo(FAKE_ICCID1, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID1,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(true);
+ assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID2,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(true);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE).isEmpty()).isTrue();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+ }
+
+ @Test
+ public void testUserUnlockUpdateEmbeddedSubscriptions() {
+ doReturn(true).when(mUiccSlot).isEuicc();
+ doReturn(1).when(mUiccController).convertToPublicCardId(FAKE_ICCID1);
+ doReturn(TelephonyManager.INVALID_PORT_INDEX).when(mUiccSlot)
+ .getPortIndexFromIccId(anyString());
+
+ EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
+ .setIccid(FAKE_ICCID1)
+ .setNickname(FAKE_CARRIER_NAME1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1, null, 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));
+
+ mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED));
+ processAllMessages();
+
+ verify(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ assertThat(subInfo.getPortIndex()).isEqualTo(TelephonyManager.INVALID_PORT_INDEX);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_CARRIER);
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC1);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC1);
+ assertThat(subInfo.getProfileClass()).isEqualTo(
+ SubscriptionManager.PROFILE_CLASS_OPERATIONAL);
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.isRemovableEmbedded()).isFalse();
+ assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES1);
+ }
+
+ @Test
+ public void testInsertNewSim() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ doReturn(FAKE_IMSI1).when(mTelephonyManager).getSubscriberId();
+ doReturn(FAKE_MCC1 + FAKE_MNC1).when(mTelephonyManager).getSimOperatorNumeric(anyInt());
+ doReturn(FAKE_PHONE_NUMBER1).when(mTelephonyManager).getLine1Number(anyInt());
+ doReturn(FAKE_EHPLMNS1.split(",")).when(mSimRecords).getEhplmns();
+ doReturn(FAKE_HPLMNS1.split(",")).when(mSimRecords).getPlmnsFromHplmnActRecord();
+ doReturn(0).when(mUiccSlot).getPortIndexFromIccId(anyString());
+ doReturn(false).when(mUiccSlot).isEuicc();
+ doReturn(1).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID1));
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_READY, null, null);
+ processAllMessages();
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_LOADED, null, null);
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getSubId(0)).isEqualTo(1);
+ assertThat(mSubscriptionManagerServiceUT.getSlotIndex(1)).isEqualTo(0);
+ assertThat(mSubscriptionManagerServiceUT.getPhoneId(1)).isEqualTo(0);
+
+ mSubscriptionManagerServiceUT.setCarrierId(1, FAKE_CARRIER_ID1);
+ mSubscriptionManagerServiceUT.setDisplayNameUsingSrc(FAKE_CARRIER_NAME1, 1,
+ SubscriptionManager.NAME_SOURCE_SIM_SPN);
+ mSubscriptionManagerServiceUT.setCarrierName(1, FAKE_CARRIER_NAME1);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getPortIndex()).isEqualTo(0);
+ assertThat(subInfo.isEmbedded()).isFalse();
+ assertThat(subInfo.getCarrierId()).isEqualTo(FAKE_CARRIER_ID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_SIM_SPN);
+ assertThat(subInfo.getCarrierName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.isOpportunistic()).isFalse();
+ assertThat(subInfo.getNumber()).isEqualTo(FAKE_PHONE_NUMBER1);
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC1);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC1);
+ assertThat(subInfo.getEhplmns()).isEqualTo(FAKE_EHPLMNS1);
+ assertThat(subInfo.getHplmns()).isEqualTo(FAKE_HPLMNS1);
+ assertThat(subInfo.getCardString()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getCardId()).isEqualTo(1);
+ assertThat(subInfo.getSubscriptionType()).isEqualTo(
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ assertThat(subInfo.areUiccApplicationsEnabled()).isTrue();
+ assertThat(subInfo.getAllowedNetworkTypesForReasons()).isEqualTo("user="
+ + RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE));
+ }
+
+ @Test
+ public void testGroupDisable() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setGroupUuid(FAKE_UUID1).build());
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).isGroupDisabled())
+ .isFalse();
+ }
+
+ @Test
+ public void testGetPhoneNumber() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ testSetPhoneNumber();
+ assertThat(mSubscriptionManagerServiceUT.getPhoneNumber(1,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEqualTo(FAKE_PHONE_NUMBER2);
+ assertThat(mSubscriptionManagerServiceUT.getPhoneNumber(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, CALLING_PACKAGE, CALLING_FEATURE))
+ .isEmpty();
+ }
+
+ @Test
+ public void testDeleteEsim() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ // pSIM with ICCID2
+ insertSubscription(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO2)
+ .setSimSlotIndex(0).build());
+
+ // eSIM with ICCID1
+ EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
+ .setIccid(FAKE_ICCID1)
+ .setNickname(FAKE_CARRIER_NAME1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1, null, 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));
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+ processAllMessages();
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 1, TelephonyManager.SIM_STATE_READY, null, null);
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 1, TelephonyManager.SIM_STATE_LOADED, null, null);
+ processAllMessages();
+
+ // Now we should have two subscriptions in the database. One for pSIM, one for eSIM.
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1).isEmbedded()).isFalse();
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).isEmbedded()).isTrue();
+
+ // Delete the eSIM. blockingGetEuiccProfileInfoList will return an empty list.
+ result = new GetEuiccProfileInfoListResult(
+ EuiccService.RESULT_OK, new EuiccProfileInfo[0], false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+ doReturn("").when(mUiccPort).getIccId();
+ doReturn(TelephonyManager.INVALID_PORT_INDEX)
+ .when(mUiccSlot).getPortIndexFromIccId(anyString());
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+ mSubscriptionManagerServiceUT.updateSimState(
+ 1, TelephonyManager.SIM_STATE_NOT_READY, null, null);
+
+ processAllMessages();
+
+ // The original pSIM is still pSIM
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1).isEmbedded()).isFalse();
+ // The original eSIM becomes removed pSIM ¯\_(ツ)_/¯
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).isEmbedded()).isFalse();
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).getPortIndex())
+ .isEqualTo(TelephonyManager.INVALID_PORT_INDEX);
+ }
+
+ @Test
+ public void testEsimSwitch() {
+ setIdentifierAccess(true);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID2)
+ .setIccid(FAKE_ICCID2)
+ .setNickname(FAKE_CARRIER_NAME2)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, null, null,
+ null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2))
+ .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules(
+ FAKE_NATIVE_ACCESS_RULES2)))
+ .build();
+
+ GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult(
+ EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+ doReturn(FAKE_ICCID2).when(mUiccCard).getCardId();
+ doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(1));
+ doReturn(FAKE_ICCID2).when(mUiccPort).getIccId();
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+ processAllMessages();
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_READY, null, null);
+ processAllMessages();
+
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_LOADED, null, null);
+ processAllMessages();
+
+ List<SubscriptionInfo> subInfoList = mSubscriptionManagerServiceUT
+ .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+
+ assertThat(subInfoList).hasSize(1);
+ assertThat(subInfoList.get(0).isActive()).isTrue();
+ assertThat(subInfoList.get(0).getSubscriptionId()).isEqualTo(2);
+ assertThat(subInfoList.get(0).getIccId()).isEqualTo(FAKE_ICCID2);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ assertThat(subInfo.getPortIndex()).isEqualTo(TelephonyManager.DEFAULT_PORT_INDEX);
+ }
+
+ @Test
+ public void testDump() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ final StringWriter stringWriter = new StringWriter();
+ assertThrows(SecurityException.class, ()
+ -> mSubscriptionManagerServiceUT.dump(new FileDescriptor(),
+ new PrintWriter(stringWriter), null));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.DUMP);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ mSubscriptionManagerServiceUT.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+ null);
+ assertThat(stringWriter.toString().length()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testOnSubscriptionChanged() {
+ CountDownLatch latch = new CountDownLatch(1);
+ SubscriptionManagerServiceCallback callback =
+ new SubscriptionManagerServiceCallback(Runnable::run) {
+ @Override
+ public void onSubscriptionChanged(int subId) {
+ latch.countDown();
+ logd("testOnSubscriptionChanged: onSubscriptionChanged");
+ }
+ };
+ mSubscriptionManagerServiceUT.registerCallback(callback);
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ processAllMessages();
+ assertThat(latch.getCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testOnUiccApplicationsEnabled() {
+ CountDownLatch latch = new CountDownLatch(1);
+ Executor executor = Runnable::run;
+ SubscriptionManagerServiceCallback callback =
+ new SubscriptionManagerServiceCallback(executor) {
+ @Override
+ public void onUiccApplicationsEnabledChanged(int subId) {
+ latch.countDown();
+ logd("testOnSubscriptionChanged: onUiccApplicationsEnabledChanged");
+ }
+ };
+ assertThat(callback.getExecutor()).isEqualTo(executor);
+ mSubscriptionManagerServiceUT.registerCallback(callback);
+ int subId = insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(false, subId);
+ processAllMessages();
+ assertThat(latch.getCount()).isEqualTo(0);
+
+ mSubscriptionManagerServiceUT.unregisterCallback(callback);
+ // without override. Nothing should happen.
+ callback = new SubscriptionManagerServiceCallback(Runnable::run);
+ mSubscriptionManagerServiceUT.registerCallback(callback);
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(true, subId);
+ processAllMessages();
+ }
+
+ @Test
+ public void testDeactivatePsim() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ testInsertNewSim();
+
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(false, 1);
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_NOT_READY, null, null);
+
+ processAllMessages();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.isActive()).isFalse();
+ assertThat(subInfo.areUiccApplicationsEnabled()).isFalse();
+ }
+
+ @Test
+ public void testRemoteSim() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_MAC_ADDRESS1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+ processAllMessages();
+
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_MAC_ADDRESS1);
+ assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
+ assertThat(subInfo.getSubscriptionType()).isEqualTo(
+ SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+
+ assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_MAC_ADDRESS1,
+ SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM)).isEqualTo(true);
+ assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+
+ setIdentifierAccess(true);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_MAC_ADDRESS2, FAKE_CARRIER_NAME2,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isNotEmpty();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isNotEmpty();
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE).get(0).getIccId()).isEqualTo(FAKE_MAC_ADDRESS2);
+ }
+
+ @Test
+ public void testRemoveSubscriptionsFromGroup() {
+ testAddSubscriptionsIntoGroup();
+
+ mContextFixture.removeCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ assertThrows(SecurityException.class, ()
+ -> mSubscriptionManagerServiceUT.removeSubscriptionsFromGroup(new int[]{2},
+ ParcelUuid.fromString(GROUP_UUID), CALLING_PACKAGE));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ assertThrows(IllegalArgumentException.class, ()
+ -> mSubscriptionManagerServiceUT.removeSubscriptionsFromGroup(new int[]{3},
+ ParcelUuid.fromString(GROUP_UUID), CALLING_PACKAGE));
+
+ assertThrows(IllegalArgumentException.class, ()
+ -> mSubscriptionManagerServiceUT.removeSubscriptionsFromGroup(new int[]{2},
+ ParcelUuid.fromString("55911c5b-83ed-419d-8f9b-4e027cf09305"), CALLING_PACKAGE));
+
+ mSubscriptionManagerServiceUT.removeSubscriptionsFromGroup(new int[]{2},
+ ParcelUuid.fromString(GROUP_UUID), CALLING_PACKAGE);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(2);
+ assertThat(subInfo.getGroupUuid()).isEmpty();
+ assertThat(subInfo.getGroupOwner()).isEmpty();
+ }
+
+ @Test
+ public void testUpdateSimStateForInactivePort() {
+ testSetUiccApplicationsEnabled();
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ mSubscriptionManagerServiceUT.updateSimStateForInactivePort(0, null);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.areUiccApplicationsEnabled()).isTrue();
+ }
+
+ @Test
+ public void testInactiveSimInserted() {
+ mContextFixture.putResource(com.android.internal.R.string.default_card_name,
+ FAKE_DEFAULT_CARD_NAME);
+
+ doReturn(0).when(mUiccSlot).getPortIndexFromIccId(eq(FAKE_ICCID1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ mSubscriptionManagerServiceUT.updateSimStateForInactivePort(-1, FAKE_ICCID1);
+ processAllMessages();
+
+ // Make sure the inactive SIM's information was inserted.
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo("CARD 1");
+ assertThat(subInfo.getPortIndex()).isEqualTo(0);
+ }
+
+ @Test
+ public void testRestoreAllSimSpecificSettingsFromBackup() {
+ assertThrows(SecurityException.class, ()
+ -> mSubscriptionManagerServiceUT.restoreAllSimSpecificSettingsFromBackup(
+ new byte[0]));
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // TODO: Briefly copy the logic from TelephonyProvider to
+ // SubscriptionDatabaseManagerTest.SubscriptionProvider
+ mSubscriptionManagerServiceUT.restoreAllSimSpecificSettingsFromBackup(
+ new byte[0]);
+ }
+
+ @Test
+ public void testSubscriptionMap() {
+ SubscriptionMap<Integer, Integer> map = new SubscriptionMap<>();
+ map.put(1, 1);
+ assertThat(map.get(1)).isEqualTo(1);
+ map.put(0, 2);
+ assertThat(map.get(0)).isEqualTo(2);
+ map.remove(1);
+ assertThat(map.get(1)).isNull();
+ map.clear();
+ assertThat(map).hasSize(0);
+ }
+
+ @Test
+ public void testSimNotReady() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_NOT_READY, null, null);
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+ }
+
+ @Test
+ public void testSimNotReadyBySimDeactivate() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ mSubscriptionManagerServiceUT.updateSimState(
+ 0, TelephonyManager.SIM_STATE_NOT_READY, null, null);
+ doReturn(true).when(mUiccProfile).isEmptyProfile();
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+ }
+
+ @Test
+ public void testInactiveSimRemoval() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO2);
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ doReturn(FAKE_ICCID2).when(mUiccSlot).getIccId(0);
+ doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState();
+
+ mSubscriptionManagerServiceUT.setUiccApplicationsEnabled(false, 1);
+ mSubscriptionManagerServiceUT.updateSimState(
+ 1, TelephonyManager.SIM_STATE_NOT_READY, null, null);
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1)
+ .areUiccApplicationsEnabled()).isFalse();
+ assertThat(mSubscriptionManagerServiceUT.getAvailableSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).hasSize(1);
+
+ // Now remove the SIM
+ doReturn(null).when(mUiccSlot).getIccId(0);
+ doReturn(IccCardStatus.CardState.CARDSTATE_ABSENT).when(mUiccSlot).getCardState();
+ mSubscriptionManagerServiceUT.updateSimState(
+ 1, TelephonyManager.SIM_STATE_ABSENT, null, null);
+ processAllMessages();
+
+ assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
+ // UICC should be re-enabled again for next re-insertion.
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1)
+ .areUiccApplicationsEnabled()).isTrue();
+ assertThat(mSubscriptionManagerServiceUT.getAvailableSubscriptionInfoList(
+ CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+ }
+
+ @Test
+ public void testEmbeddedProfilesUpdateFailed() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult(
+ EuiccService.RESULT_MUST_DEACTIVATE_SIM, null, false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+ processAllMessages();
+
+ // The existing subscription should not be altered if the previous update failed.
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1))
+ .isEqualTo(FAKE_SUBSCRIPTION_INFO1);
+
+ EuiccProfileInfo profileInfo = new EuiccProfileInfo.Builder(FAKE_ICCID2)
+ .setIccid(FAKE_ICCID2)
+ .setNickname(FAKE_CARRIER_NAME2)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, 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[]{profileInfo}, false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+
+ // Update for the 2nd time.
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1), null);
+ processAllMessages();
+
+ // The previous subscription should be marked as non-embedded.
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(1).isEmbedded())
+ .isEqualTo(false);
+
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).getIccId())
+ .isEqualTo(FAKE_ICCID2);
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionInfo(2).isEmbedded())
+ .isEqualTo(true);
+ }
+
+
+ @Test
+ public void testNonNullSubInfoBuilderFromEmbeddedProfile() {
+ EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
+ .setIccid(FAKE_ICCID1) //can't build profile with null iccid.
+ .setNickname(null) //nullable
+ .setServiceProviderName(null) //nullable
+ .setProfileName(null) //nullable
+ .setCarrierIdentifier(null) //nullable
+ .setUiccAccessRule(null) //nullable
+ .build();
+
+ EuiccProfileInfo profileInfo2 = new EuiccProfileInfo.Builder(FAKE_ICCID2)
+ .setIccid(FAKE_ICCID2) //impossible to build profile with null iccid.
+ .setNickname(null) //nullable
+ .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, null, null,
+ null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2)) //not allow null mcc/mnc.
+ .setUiccAccessRule(null) //nullable
+ .build();
+
+ GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult(
+ EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false);
+ doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
+ 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());
+
+ mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null);
+ processAllMessages();
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
+ assertThat(subInfo.getDisplayName()).isEqualTo("");
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_UNKNOWN);
+ assertThat(subInfo.getMcc()).isEqualTo("");
+ assertThat(subInfo.getMnc()).isEqualTo("");
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.isRemovableEmbedded()).isFalse();
+ assertThat(subInfo.getNativeAccessRules()).isEqualTo(new byte[]{});
+
+ subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2);
+ assertThat(subInfo.getSubscriptionId()).isEqualTo(2);
+ assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID2);
+ assertThat(subInfo.getDisplayName()).isEqualTo("");
+ assertThat(subInfo.getDisplayNameSource()).isEqualTo(
+ SubscriptionManager.NAME_SOURCE_UNKNOWN);
+ assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2);
+ assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC2);
+ assertThat(subInfo.isEmbedded()).isTrue();
+ assertThat(subInfo.isRemovableEmbedded()).isFalse();
+ assertThat(subInfo.getNativeAccessRules()).isEqualTo(new byte[]{});
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java
new file mode 100644
index 0000000..c040b9e
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static com.android.internal.telephony.uicc.IccConstants.EF_ADN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_MBDN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_PBR;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.gsm.UsimPhoneBookManager;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class AdnRecordCacheTest extends TelephonyTest {
+
+ private AdnRecordCacheUT mAdnRecordCache;
+ private TestLooper mTestLooper;
+ private Handler mTestHandler;
+ private IccFileHandler mFhMock;
+ private UsimPhoneBookManager mUsimPhoneBookManager;
+
+ @SuppressWarnings("ClassCanBeStatic")
+ private class AdnRecordCacheUT extends AdnRecordCache {
+ AdnRecordCacheUT(IccFileHandler fh, UsimPhoneBookManager usimPhoneBookManager) {
+ super(fh, usimPhoneBookManager);
+ }
+
+ protected void setAdnLikeWriters(int key, ArrayList<Message> waiters) {
+ super.setAdnLikeWriters(key, waiters);
+ }
+
+ protected void setAdnLikeFiles(int key, ArrayList<AdnRecord> adnRecordList) {
+ super.setAdnLikeFiles(key, adnRecordList);
+ }
+
+ protected void setUserWriteResponse(int key, Message message) {
+ super.setUserWriteResponse(key, message);
+ }
+
+ protected UsimPhoneBookManager getUsimPhoneBookManager() {
+ return super.getUsimPhoneBookManager();
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ // Mocked classes
+ mFhMock = mock(IccFileHandler.class);
+ mUsimPhoneBookManager = mock(UsimPhoneBookManager.class);
+ mTestLooper = new TestLooper();
+ mTestHandler = new Handler(mTestLooper.getLooper());
+ mTestHandler.post(
+ () -> mAdnRecordCache = new AdnRecordCacheUT(mFhMock, mUsimPhoneBookManager));
+ mTestLooper.dispatchAll();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestLooper != null) {
+ mTestLooper.dispatchAll();
+ mTestLooper = null;
+ }
+ mTestHandler.removeCallbacksAndMessages(null);
+ mTestHandler = null;
+ mAdnRecordCache = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void resetTest() {
+ Message message1 = Message.obtain(mTestHandler);
+ Message message2 = Message.obtain(mTestHandler);
+
+ // test data to create mAdnLikeWaiters
+ ArrayList<Message> waiters = new ArrayList<>();
+ waiters.add(message1);
+ mAdnRecordCache.setAdnLikeWriters(EF_MBDN, waiters);
+
+ // test data to create mAdnLikeFiles
+ setAdnLikeFiles(EF_MBDN);
+
+ // test data to create mUserWriteResponse
+ mAdnRecordCache.setUserWriteResponse(EF_MBDN, message2);
+
+ mAdnRecordCache.reset();
+
+ mTestLooper.dispatchAll();
+ AsyncResult ar1 = (AsyncResult) message1.obj;
+ AsyncResult ar2 = (AsyncResult) message2.obj;
+ Assert.assertTrue(ar1.exception.toString().contains("AdnCache reset"));
+ Assert.assertTrue(ar2.exception.toString().contains("AdnCace reset"));
+ }
+
+ @Test
+ public void updateAdnByIndexEfException() {
+ int efId = 0x6FC5;
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.updateAdnByIndex(efId, null, 0, null, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains("EF is not known ADN-like EF:0x6FC5")));
+ }
+
+ @Test
+ public void updateAdnByIndex_WriteResponseException() {
+ int efId = EF_MBDN;
+ Message message = Message.obtain(mTestHandler);
+ Message message2 = Message.obtain(mTestHandler);
+ AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+ // test data to create mUserWriteResponse
+ mAdnRecordCache.setUserWriteResponse(efId, message2);
+ mAdnRecordCache.updateAdnByIndex(efId, adnRecord, 0, null, message);
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains("Have pending update for EF:0x6FC7")));
+ }
+
+ @Test
+ public void updateAdnByIndex() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, "success2", null);
+ response.sendToTarget();
+ return response;
+ })
+ .when(mFhMock)
+ .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ Assert.assertNotNull(message);
+ AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+ // test data to create mUserWriteResponse
+ mAdnRecordCache.updateAdnByIndex(EF_MBDN, adnRecord, 0, null, message);
+ mTestLooper.startAutoDispatch();
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void updateAdnBySearch_EfException() {
+ int efId = 0x6FC5;
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.updateAdnBySearch(efId, null, null, null, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains("EF is not known ADN-like EF:0x6FC5")));
+ }
+
+ @Test
+ public void updateAdnBySearch_Exception() {
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.updateAdnBySearch(EF_MBDN, null, null, null, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains("Adn list not exist for EF:0x6FC7")));
+ }
+
+ @Test
+ public void updateAdnBySearch_AdnListError() {
+ int efId = EF_MBDN;
+ setAdnLikeFiles(efId);
+ Message message = Message.obtain(mTestHandler);
+ AdnRecord oldAdn = new AdnRecord("oldAlphaTag", "123456789");
+ mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains(
+ "Adn record don't exist for ADN Record 'oldAlphaTag'")));
+ }
+
+ @Test
+ public void updateAdnBySearch_PendingUpdate() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, "success2", null);
+ response.sendToTarget();
+ return response;
+ })
+ .when(mFhMock)
+ .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ int efId = EF_MBDN;
+ setAdnLikeFiles(efId);
+ Message message = Message.obtain(mTestHandler);
+ AdnRecord oldAdn = new AdnRecord("AlphaTag", "123456789");
+ mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+ mTestLooper.dispatchAll();
+
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void updateAdnBySearch() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, "success", null);
+ response.sendToTarget();
+ return response;
+ })
+ .when(mFhMock)
+ .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ int efId = EF_MBDN;
+ setAdnLikeFiles(efId);
+ Message message = Message.obtain(mTestHandler);
+ AdnRecord oldAdn = new AdnRecord("AlphaTag", "123456789");
+ mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+ mTestLooper.dispatchAll();
+
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ }
+
+
+ @Test
+ public void updateAdnBySearch_AdnException() {
+ doReturn(null).when(mUsimPhoneBookManager).loadEfFilesFromUsim();
+ Message message = Message.obtain(mTestHandler);
+ AdnRecord oldAdn = new AdnRecord("oldAlphaTag", "123456789");
+ mAdnRecordCache.updateAdnBySearch(EF_PBR, oldAdn, null, null, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNotNull(ar.exception);
+ assertTrue((ar.exception.toString().contains("Adn list not exist for EF:0x4F30")));
+ }
+
+ @Test
+ public void requestLoadAllAdnLike_AlreadyLoadedEf() {
+ int efId = EF_MBDN;
+ setAdnLikeFiles(efId);
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.requestLoadAllAdnLike(efId, 0, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNull(ar.exception);
+ Assert.assertNotNull(ar.result);
+ }
+
+ @Test
+ public void requestLoadAllAdnLike_AlreadyLoadingEf() {
+ int efId = EF_MBDN;
+ // test data to create mAdnLikeWaiters
+ Message message = Message.obtain(mTestHandler);
+ ArrayList<Message> waiters = new ArrayList<>();
+ waiters.add(message);
+ mAdnRecordCache.setAdnLikeWriters(efId, waiters);
+ mAdnRecordCache.requestLoadAllAdnLike(efId, 0, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertNull(ar);
+ }
+
+ @Test
+ public void requestLoadAllAdnLike_NotKnownEf() {
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.requestLoadAllAdnLike(EF_MBDN, -1, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ Assert.assertTrue(ar.exception.toString().contains("EF is not known ADN-like EF:0x"));
+ }
+
+ @Test
+ public void requestLoadAllAdnLike() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.REQUEST_NOT_SUPPORTED));
+ response.sendToTarget();
+ return response;
+ })
+ .when(mFhMock)
+ .loadEFLinearFixedAll(anyInt(), anyString(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mAdnRecordCache.requestLoadAllAdnLike(EF_ADN, 0x6FC8, message);
+ mTestLooper.dispatchAll();
+
+ verify(mFhMock, times(1)).loadEFLinearFixedAll(anyInt(), anyString(), any(Message.class));
+ }
+
+ private void setAdnLikeFiles(int ef) {
+ // test data to create mAdnLikeFiles
+ ArrayList<AdnRecord> adnRecordList = new ArrayList<>();
+ AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+ adnRecordList.add(adnRecord);
+ mAdnRecordCache.setAdnLikeFiles(ef, adnRecordList);
+ }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java
new file mode 100644
index 0000000..63e68e2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+import android.util.Log;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+
+public class IccFileHandlerTest {
+ CommandsInterface mCi;
+ IccFileHandler mIccFileHandler;
+ private TestLooper mTestLooper;
+ private Handler mTestHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ mCi = mock(CommandsInterface.class);
+ mTestLooper = new TestLooper();
+ mTestHandler = new Handler(mTestLooper.getLooper());
+ mTestHandler.post(
+ () -> mIccFileHandler = new IccFileHandler(mCi) {
+ @Override
+ protected String getEFPath(int efid) {
+ switch (efid) {
+ case 0x4f30:
+ case 0x4f3a:
+ return "3F007F105F3A";
+ }
+ return "";
+ }
+
+ @Override
+ protected void logd(String s) {
+ Log.d("IccFileHandlerTest", s);
+ }
+
+ @Override
+ protected void loge(String s) {
+ Log.d("IccFileHandlerTest", s);
+ }
+ });
+
+
+ mTestLooper.dispatchAll();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestLooper != null) {
+ mTestLooper.dispatchAll();
+ mTestLooper = null;
+ }
+ mTestHandler.removeCallbacksAndMessages(null);
+ mTestHandler = null;
+ mIccFileHandler = null;
+ mCi = null;
+ }
+
+ @Test
+ public void loadEFLinearFixed_WithNullPath() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixed(0, "", 0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFLinearFixed() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixed(0, 0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFImgLinearFixed() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFImgLinearFixed(0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void getEFLinearRecordSize_WithNullPath() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.getEFLinearRecordSize(0, "", message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void getEFLinearRecordSize() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.getEFLinearRecordSize(0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void getEFTransparentRecordSize() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.getEFTransparentRecordSize(0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_FileNotFoundAtGetRecord() throws InterruptedException {
+ int efId = 0x4f30;
+ final CountDownLatch latch = new CountDownLatch(1);
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ IccIoResult iir = new IccIoResult(0x94, 0x00,
+ IccUtils.hexStringToBytes(null));
+ AsyncResult.forMessage(response, iir, null);
+ mTestHandler.postDelayed(latch::countDown, 100);
+ response.sendToTarget();
+ return null;
+ }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+ anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(efId, null, message);
+ mTestLooper.startAutoDispatch();
+ latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNotNull(ar);
+ assertTrue(ar.exception instanceof IccFileNotFound);
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_FileNotFoundAtReadRecord() throws InterruptedException {
+ int efid = 0x4f30;
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ IccIoResult iir = null;
+ if (response.what == 6) {
+ iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(
+ "000000454F30040000FFFF01020145"));
+ latch.countDown();
+ } else if (response.what == 7) {
+ iir = new IccIoResult(0x94, 0x00, IccUtils.hexStringToBytes(null));
+ mTestHandler.postDelayed(latch::countDown, 100);
+ }
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+ anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+ mTestLooper.startAutoDispatch();
+ latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNotNull(ar);
+ assertTrue(ar.exception instanceof IccFileNotFound);
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_ExceptionAtGetRecord() throws InterruptedException {
+ int efid = 0x4f30;
+ final CountDownLatch latch = new CountDownLatch(1);
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ mTestHandler.postDelayed(latch::countDown, 100);
+ return null;
+ }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+ anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+ mTestLooper.startAutoDispatch();
+ latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertSame(CommandException.Error.OPERATION_NOT_ALLOWED,
+ ((CommandException) ar.exception).getCommandError());
+ assertNull(ar.result);
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_ExceptionAtReadRecord() throws InterruptedException {
+ int efid = 0x4f30;
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ String hexString = null;
+ IccIoResult iir = null;
+ if (response.what == 6) {
+ hexString = "000000454F30040000FFFF01020145";
+ iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+ AsyncResult.forMessage(response, iir, null);
+ latch.countDown();
+ } else if (response.what == 7) {
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mTestHandler.postDelayed(latch::countDown, 100);
+ }
+ response.sendToTarget();
+ mTestHandler.postDelayed(latch::countDown, 100);
+ return null;
+ }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+ anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+ mTestLooper.startAutoDispatch();
+ latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertSame(CommandException.Error.OPERATION_NOT_ALLOWED,
+ ((CommandException) ar.exception).getCommandError());
+ assertNull(ar.result);
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_FullLoop() throws InterruptedException {
+ int efid = 0x4f30;
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ String hexString = null;
+ if (response.what == 6) {
+ hexString = "000000454F30040000FFFF01020145";
+ latch.countDown();
+ } else if (response.what == 7) {
+ try {
+ IccFileHandler.LoadLinearFixedContext lc =
+ (IccFileHandler.LoadLinearFixedContext) response.obj;
+ if (mIccFileHandler.getEfid(lc) == efid) {
+ hexString =
+ "A814C0034F3A01C1034F3202C5034F0904C9034F2109A90FC3034F611"
+ + "5C4034F1108CA034F5017AA0FC2034F4A03C7034F4B06CB"
+ + "034F4F16FFFFFFFFFFFFFFFFFFFFFFFFFF";
+ }
+ mTestHandler.postDelayed(latch::countDown, 100);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e("UsimFH", e.getMessage());
+ }
+
+ }
+ IccIoResult iir = new IccIoResult(0x90, 0x00,
+ IccUtils.hexStringToBytes(hexString));
+ AsyncResult.forMessage(response, iir, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+ mTestLooper.startAutoDispatch();
+ latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNotNull(ar);
+ ArrayList<byte[]> results = (ArrayList<byte[]>) ar.result;
+ assertEquals(
+ "A814C0034F3A01C1034F3202C5034F0904C9034F2109A90FC3034F6115C4034F1108CA03"
+ + "4F5017AA0FC2034F4A03C7034F4B06CB034F4F16FFFFFFFFFFFFFFFFFFFFFFFFFF",
+ IccUtils.bytesToHexString(results.get(0)));
+ verify(mCi, times(2)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFLinearFixedAll_WithNullPath() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(0, "", message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFLinearFixedAll() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFLinearFixedAll(0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFTransparent() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFTransparent(0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFTransparent_WithZeroSize() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFTransparent(0, 0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void loadEFImgTransparent() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ isNull(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.loadEFImgTransparent(0, 0, 0, 0, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void updateEFLinearFixed_WithNullPath() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ anyString(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.updateEFLinearFixed(0, "", 0, new byte[10], null, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void updateEFLinearFixed() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ anyString(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.updateEFLinearFixed(0, 0, new byte[10], null, message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+ }
+
+ @Test
+ public void updateEFTransparent() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(9);
+ AsyncResult.forMessage(response, "Success", null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mCi)
+ .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+ anyString(), isNull(), isNull(), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIccFileHandler.updateEFTransparent(0, new byte[10], message);
+ verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+ anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java
new file mode 100644
index 0000000..b3e0a85
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IccIoResultTest {
+
+ @Test
+ public void check0x90_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x90, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+ }
+
+ @Test
+ public void check0x91_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x91, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+ }
+
+ @Test
+ public void check0x9E_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x9E, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+ }
+
+ @Test
+ public void check0x9F_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x9F, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+ }
+
+ @Test
+ public void check0x94_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x94, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(resultStr != null && (resultStr.contains("no EF selected")));
+
+ iccIoResult = new IccIoResult(0x94, 0x02, new byte[10]);
+ resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("out f range (invalid address)")));
+
+ iccIoResult = new IccIoResult(0x94, 0x04, new byte[10]);
+ resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("file ID not found/pattern not found")));
+
+ iccIoResult = new IccIoResult(0x94, 0x08, new byte[10]);
+ resultStr = iccIoResult.toString();
+
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("file is inconsistent with the command")));
+ }
+
+ @Test
+ public void check0x98_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x98, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x98, 0x02, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("no CHV initialized")));
+
+ iccIoResult = new IccIoResult(0x98, 0x04, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("access condition not fulfilled")));
+
+ iccIoResult = new IccIoResult(0x98, 0x08, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("in contradiction with CHV status")));
+
+ iccIoResult = new IccIoResult(0x98, 0x10, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "in contradiction with invalidation status")));
+
+ iccIoResult = new IccIoResult(0x98, 0x40, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "unsuccessful CHV verification, no attempt left")));
+
+ iccIoResult = new IccIoResult(0x98, 0x50, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "increase cannot be performed, Max value reached")));
+
+ iccIoResult = new IccIoResult(0x98, 0x62, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "authentication error, application specific")));
+
+ iccIoResult = new IccIoResult(0x98, 0x64, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "authentication error, security context not supported")));
+
+ iccIoResult = new IccIoResult(0x98, 0x65, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("key freshness failure")));
+
+ iccIoResult = new IccIoResult(0x98, 0x66, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "authentication error, no memory space available")));
+
+ iccIoResult = new IccIoResult(0x98, 0x67, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "authentication error, no memory space available in EF_MUK")));
+ }
+
+ @Test
+ public void check0x61_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x61, 0x20, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("more response bytes available")));
+ }
+
+ @Test
+ public void check0x62_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x62, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+ iccIoResult = new IccIoResult(0x62, 0x81, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains(
+ "part of returned data may be corrupted")));
+
+ iccIoResult = new IccIoResult(0x62, 0x82, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "end of file/record reached before reading Le bytes")));
+
+ iccIoResult = new IccIoResult(0x62, 0x83, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("selected file invalidated")));
+
+ iccIoResult = new IccIoResult(0x62, 0x84, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("selected file in termination state")));
+
+ iccIoResult = new IccIoResult(0x62, 0xF1, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("more data available")));
+
+ iccIoResult = new IccIoResult(0x62, 0xF2, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "more data available and proactive command pending")));
+
+ iccIoResult = new IccIoResult(0x62, 0xF3, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("response data available")));
+
+ iccIoResult = new IccIoResult(0x62, 0xF4, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+ }
+
+ @Test
+ public void check0x63_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x63, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains(
+ "command successful but after using an internal update retry routine 0 "
+ + "times")));
+
+ iccIoResult = new IccIoResult(0x63, 0xF1, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("more data expected")));
+
+ iccIoResult = new IccIoResult(0x63, 0xF2, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains(
+ "more data expected and proactive command pending")));
+
+ iccIoResult = new IccIoResult(0x63, 0xF3, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("unknown")));
+ }
+
+ @Test
+ public void check0x64_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x64, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x64, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("no information given")));
+ }
+
+ @Test
+ public void check0x65_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x65, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x65, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue((resultStr != null) && (resultStr.contains(
+ "no information given, state of non-volatile memory changed")));
+
+ iccIoResult = new IccIoResult(0x65, 0x81, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("memory problem")));
+ }
+
+ @Test
+ public void check0x67_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x67, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "the interpretation of this status word is command dependent")));
+
+ iccIoResult = new IccIoResult(0x67, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "incorrect parameter P3")));
+ }
+
+ @Test
+ public void check0x68_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x68, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x68, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+ iccIoResult = new IccIoResult(0x68, 0x81, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("logical channel not supported")));
+
+ iccIoResult = new IccIoResult(0x68, 0x82, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("secure messaging not supported")));
+ }
+
+ @Test
+ public void check0x69_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x69, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x69, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+ iccIoResult = new IccIoResult(0x69, 0x81, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains(
+ "command incompatible with file structure")));
+
+ iccIoResult = new IccIoResult(0x69, 0x82, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("security status not satisfied")));
+
+ iccIoResult = new IccIoResult(0x69, 0x83, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("authentication/PIN method blocked")));
+
+ iccIoResult = new IccIoResult(0x69, 0x84, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("referenced data invalidated")));
+
+ iccIoResult = new IccIoResult(0x69, 0x85, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("conditions of use not satisfied")));
+
+ iccIoResult = new IccIoResult(0x69, 0x86, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue((resultStr != null) && (resultStr.contains(
+ "command not allowed (no EF selected)")));
+
+ iccIoResult = new IccIoResult(0x69, 0x89, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue((resultStr != null) && (resultStr.contains(
+ "command not allowed - secure channel - security not satisfied")));
+ }
+
+ @Test
+ public void check0x6A_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6A, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x80, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "incorrect parameters in the data field")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x81, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("function not supported")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x82, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("file not found")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x83, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("record not found")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x84, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("not enough memory space")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x86, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ (resultStr != null) && (resultStr.contains("incorrect parameters P1 to P2")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x87, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue((resultStr != null) && (resultStr.contains(
+ "lc inconsistent with P1 to P2")));
+
+ iccIoResult = new IccIoResult(0x6A, 0x88, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue((resultStr != null) && (resultStr.contains(
+ "referenced data not found")));
+ }
+
+ @Test
+ public void check0x6B_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6B, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(
+ resultStr != null && (resultStr.contains("incorrect parameter P1 or P2")));
+ }
+
+ @Test
+ public void check0x6C_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6C, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains("wrong length, retry with ")));
+ }
+
+ @Test
+ public void check0x6D_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6D, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "unknown instruction code given in the command")));
+ }
+
+ @Test
+ public void check0x6E_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6E, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "wrong instruction class given in the command")));
+ }
+
+ @Test
+ public void check0x6F_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x6F, 0xC0, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "the interpretation of this status word is command dependent")));
+
+ iccIoResult = new IccIoResult(0x6F, 0x00, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "technical problem with no diagnostic given")));
+ }
+
+ @Test
+ public void check0x92_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x92, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "command successful but after using an internal update retry routine")));
+
+ iccIoResult = new IccIoResult(0x92, 0x40, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "memory problem")));
+
+ iccIoResult = new IccIoResult(0x92, 0x41, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "unknown")));
+ }
+
+ @Test
+ public void check0x93_ErrorCodeParsing() {
+ IccIoResult iccIoResult = new IccIoResult(0x93, 0x00, new byte[10]);
+ String resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "SIM Application Toolkit is busy. Command cannot be executed"
+ + " at present, further normal commands are allowed")));
+
+ iccIoResult = new IccIoResult(0x93, 0x41, new byte[10]);
+ resultStr = iccIoResult.toString();
+ Assert.assertTrue(resultStr != null && (resultStr.contains(
+ "unknown")));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
old mode 100755
new mode 100644
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
index b97ae1f..6f4666c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
@@ -30,13 +30,23 @@
package com.android.internal.telephony.uicc;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
-import android.os.HandlerThread;
+import android.os.Handler;
import android.os.Message;
+import android.os.test.TestLooper;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -46,31 +56,48 @@
public class IsimUiccRecordsTest extends TelephonyTest {
- private IsimUiccRecords mIsimUiccRecords;
+ // Mocked classes
+ private IccFileHandler mFhMock;
+ private TestLooper mTestLooper;
+ private Handler mTestHandler;
+ private IsimUiccRecordsUT mIsimUiccRecordsUT;
- private class IsimUiccRecordsTestHandler extends HandlerThread {
- private IsimUiccRecordsTestHandler(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext, mSimulatedCommands);
- setReady(true);
+ @SuppressWarnings("ClassCanBeStatic")
+ private class IsimUiccRecordsUT extends IsimUiccRecords {
+ IsimUiccRecordsUT(UiccCardApplication app, Context c,
+ CommandsInterface ci, IccFileHandler mFhMock) {
+ super(app, c, ci);
+ mFh = mFhMock;
}
}
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- new IsimUiccRecordsTestHandler(TAG).start();
- waitUntilReady();
+ mFhMock = mock(IccFileHandler.class);
+ mTestLooper = new TestLooper();
+ mTestHandler = new Handler(mTestLooper.getLooper());
+ mTestHandler.post(
+ () -> {
+ mIsimUiccRecordsUT =
+ new IsimUiccRecordsUT(
+ mUiccCardApplication3gpp,
+ mContext,
+ mSimulatedCommands,
+ mFhMock);
+ });
+ mTestLooper.dispatchAll();
}
@After
public void tearDown() throws Exception {
- mIsimUiccRecords.dispose();
- mIsimUiccRecords = null;
+ if (mTestLooper != null) {
+ mTestLooper.dispatchAll();
+ mTestLooper = null;
+ }
+ mTestHandler.removeCallbacksAndMessages(null);
+ mTestHandler = null;
+ mIsimUiccRecordsUT = null;
super.tearDown();
}
@@ -79,12 +106,274 @@
Message msg = new Message();
msg.what = IccRecords.EVENT_REFRESH;
msg.obj = new AsyncResult(null, null, null);
- mIsimUiccRecords.handleMessage(msg);
+ mIsimUiccRecordsUT.handleMessage(msg);
ArgumentCaptor<Intent> intentCapture = ArgumentCaptor.forClass(Intent.class);
verify(mContext).sendBroadcast(intentCapture.capture());
-
assertEquals(
- ((Intent) intentCapture.getValue()).getAction(), IsimUiccRecords.INTENT_ISIM_REFRESH);
+ ((Intent) intentCapture.getValue()).getAction(),
+ IsimUiccRecords.INTENT_ISIM_REFRESH);
}
-}
+ @Test
+ public void testPsiSmscTelValue() {
+ // Testing smsc successfully reading case
+ String smscTest = "tel:+13123149810";
+ String hexSmsc =
+ "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+ byte[] smscBytes = getStringToByte(hexSmsc);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+ AsyncResult.forMessage(message, smscBytes, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(smscTest, mIsimUiccRecordsUT.getSmscIdentity());
+ }
+
+ private byte[] getStringToByte(String hexSmsc) {
+ byte[] smscBytes = IccUtils.hexStringToBytes(hexSmsc);
+ return smscBytes;
+ }
+
+ @Test
+ public void testGetPsiSmscSipValue() {
+ // Testing smsc successfully reading case
+ String smscTest = "sip:+12063130004@msg.pc.t-mobile.com;user=phone";
+ byte[] smscBytes = getStringToByte(
+ "802F7369703A2B3132303633313330303034406D73672E70632E742D6D6F62696C6"
+ + "52E636F6D3B757365723D70686F6E65FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+ AsyncResult.forMessage(message, smscBytes, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(smscTest, mIsimUiccRecordsUT.getSmscIdentity());
+ }
+
+ @Test
+ public void testGetPsiSmscValueException() {
+ // Testing smsc exception handling case
+ String hexSmsc =
+ "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+ byte[] smscBytes = getStringToByte(hexSmsc);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+ AsyncResult.forMessage(message, smscBytes,
+ new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(null, mIsimUiccRecordsUT.getSmscIdentity());
+ }
+
+ @Test
+ public void testGetPsiSmscValueInvalidObject() {
+ // Testing smsc invalid data handling case
+ String smscTest = "tel:+13123149810";
+ byte[] smscBytes = GsmAlphabet.stringToGsm8BitPacked(smscTest);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+ AsyncResult.forMessage(message, smscBytes,
+ new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(null, mIsimUiccRecordsUT.getSmscIdentity());
+ }
+
+ @Test
+ public void testGetSmssTpmrValue() {
+ // Testing tpmr successfully reading case
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) (tpmr & 0xFF);
+ IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+ AsyncResult.forMessage(message, null, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(tpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testGetSmssTpmrValueException() {
+ // Testing tpmr reading fail case [ exception case ]
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) (tpmr & 0xFF);
+ IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+ AsyncResult.forMessage(message, null,
+ new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(-1, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testGetSmssTpmrValueExtreme() {
+ // Testing extreme tpmr value case [ fails ]
+ byte[] smss = new byte[2];
+ int tpmr = 400;
+ smss[0] = (byte) (tpmr & 0xFF);
+ IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+ AsyncResult.forMessage(message, null, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(144, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+ private void setValidSmssValue() {
+ // preset/ initialize smssvalue
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) (tpmr & 0xFF);
+ IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+ AsyncResult.forMessage(message, null, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ assertEquals(tpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrValue() {
+ // Testing tpmr successfully setting case
+ setValidSmssValue();
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ assertEquals(updateTpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+
+ @Test
+ public void testSetSmssTpmrValueException() {
+ // Testing exception while setting TPMR [ with out setting initial value]
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ Message message = Message.obtain(mTestHandler);
+ mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ assertEquals(-1, mIsimUiccRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrValueException2() {
+ // Testing exception while setting TPMR
+ setValidSmssValue();
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+
+ AsyncResult.forMessage(response, true, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ Message message = Message.obtain(mTestHandler);
+ mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertTrue(((CommandException) ar.exception).getCommandError() ==
+ CommandException.Error.OPERATION_NOT_ALLOWED);
+ }
+
+ @Test
+ public void testGetSimServiceTable() {
+ // reading sim service table successfully case
+ byte[] sst = new byte[9];
+ for (int i = 0; i < sst.length; i++) {
+ if (i % 2 == 0) {
+ sst[i] = 0;
+ } else {
+ sst[i] = 1;
+ }
+ }
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+ AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ String mockSst = IccUtils.bytesToHexString(sst);
+ String resultSst = mIsimUiccRecordsUT.getIsimIst();
+ assertEquals(mockSst, resultSst);
+ }
+
+ @Test
+ public void testGetSimServiceTableException() {
+ // sim service table exception handling case
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+ AsyncResult ar = AsyncResult.forMessage(message, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mIsimUiccRecordsUT.handleMessage(message);
+ String resultSst = mIsimUiccRecordsUT.getIsimIst();
+ assertEquals(null, resultSst);
+ }
+
+ @Test
+ public void testGetSsimServiceTableLessTableSize() {
+ // The less IST table size will not give any problem
+ byte[] sst = new byte[5];
+ for (int i = 0; i < sst.length; i++) {
+ if (i % 2 == 0) {
+ sst[i] = 0;
+ } else {
+ sst[i] = 1;
+ }
+ }
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+ AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ String mockSst = IccUtils.bytesToHexString(sst);
+ String resultSst = mIsimUiccRecordsUT.getIsimIst();
+ assertEquals(mockSst, resultSst);
+ }
+
+ @Test
+ public void testGetSsimServiceTableLargeTableSize() {
+ // The Big IST table size will not give any problem [ in feature the table may grows]
+ byte[] sst = new byte[99];
+ for (int i = 0; i < sst.length; i++) {
+ if (i % 2 == 0) {
+ sst[i] = 0;
+ } else {
+ sst[i] = 1;
+ }
+ }
+ Message message = mIsimUiccRecordsUT.obtainMessage(
+ IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+ AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+ mIsimUiccRecordsUT.handleMessage(message);
+ String mockSst = IccUtils.bytesToHexString(sst);
+ String resultSst = mIsimUiccRecordsUT.getIsimIst();
+ assertEquals(mockSst, resultSst);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java
index a8c74d5..ea6c778 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java
@@ -19,8 +19,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
@@ -29,6 +32,7 @@
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -44,6 +48,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -58,19 +64,38 @@
private int mBootCount;
private int mSimulatedRebootsCount;
private PinStorage mPinStorage;
+ private PersistableBundle mBundle;
+
+ // mocks
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private void simulateReboot() {
mSimulatedRebootsCount++;
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.BOOT_COUNT, mBootCount + mSimulatedRebootsCount);
+ createPinStorageAndCaptureListener();
+ }
+
+ private void createPinStorageAndCaptureListener() {
+ // Capture listener to emulate the carrier config change notification used later
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mPinStorage = new PinStorage(mContext);
mPinStorage.mShortTermSecretKeyDurationMinutes = 0;
+ verify(mCarrierConfigManager, atLeastOnce()).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
}
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
+ mCarrierConfigChangeListener = Mockito.mock(
+ CarrierConfigManager.CarrierConfigChangeListener.class);
+
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
// Store boot count, so that correct value can be restored at the end.
mBootCount = Settings.Global.getInt(
@@ -89,8 +114,7 @@
when(mKeyguardManager.isDeviceSecure()).thenReturn(false);
when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
- mPinStorage = new PinStorage(mContext);
- mPinStorage.mShortTermSecretKeyDurationMinutes = 0;
+ createPinStorageAndCaptureListener();
}
@After
@@ -341,7 +365,7 @@
PersistableBundle carrierConfigs = new PersistableBundle();
carrierConfigs.putBoolean(
CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, false);
- when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(carrierConfigs);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(carrierConfigs);
mPinStorage.storePin("1234", 0);
@@ -362,10 +386,10 @@
PersistableBundle carrierConfigs = new PersistableBundle();
carrierConfigs.putBoolean(
CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, false);
- when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(carrierConfigs);
- final Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
- mContext.sendBroadcast(intent);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(carrierConfigs);
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
int result = mPinStorage.prepareUnattendedReboot(sWorkSource);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/PortUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/PortUtilsTest.java
new file mode 100644
index 0000000..69d9a7d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/PortUtilsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PortUtilsTest extends TelephonyTest {
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ doReturn(IccSlotStatus.MultipleEnabledProfilesMode.NONE)
+ .when(mUiccController).getSupportedMepMode(anyInt());
+ processAllMessages();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testConvertToHalPortIndex() {
+ assertEquals(0, PortUtils.convertToHalPortIndex(0, 0));
+ doReturn(IccSlotStatus.MultipleEnabledProfilesMode.MEP_A1)
+ .when(mUiccController).getSupportedMepMode(anyInt());
+ assertEquals(1, PortUtils.convertToHalPortIndex(0, 0));
+ }
+
+ @Test
+ public void testConvertFromHalPortIndex() {
+ assertEquals(0, PortUtils.convertFromHalPortIndex(0, 1,
+ IccCardStatus.CardState.CARDSTATE_PRESENT,
+ IccSlotStatus.MultipleEnabledProfilesMode.MEP_A1));
+ assertEquals(1, PortUtils.convertFromHalPortIndex(0, 1,
+ IccCardStatus.CardState.CARDSTATE_PRESENT,
+ IccSlotStatus.MultipleEnabledProfilesMode.MEP_B));
+ assertEquals(1, PortUtils.convertFromHalPortIndex(0, 1,
+ IccCardStatus.CardState.CARDSTATE_ABSENT,
+ IccSlotStatus.MultipleEnabledProfilesMode.MEP_A1));
+ doReturn(IccSlotStatus.MultipleEnabledProfilesMode.MEP_A1)
+ .when(mUiccController).getSupportedMepMode(anyInt());
+ assertEquals(0, PortUtils.convertFromHalPortIndex(0, 1,
+ IccCardStatus.CardState.CARDSTATE_ABSENT,
+ IccSlotStatus.MultipleEnabledProfilesMode.NONE));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java
old mode 100755
new mode 100644
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
index 9183235..e109ebb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
@@ -22,9 +22,11 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
@@ -32,6 +34,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.test.TestLooper;
+import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -41,6 +44,7 @@
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo;
import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName;
+import com.android.telephony.Rlog;
import org.junit.After;
import org.junit.Before;
@@ -50,6 +54,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class SIMRecordsTest extends TelephonyTest {
@@ -59,6 +66,7 @@
private static final List<String> EMPTY_FPLMN_LIST = new ArrayList<>();
private static final int EF_SIZE = 12;
private static final int MAX_NUM_FPLMN = 4;
+ private static final int SET_VOICE_MAIL_TIMEOUT = 1000;
// Mocked classes
private IccFileHandler mFhMock;
@@ -169,37 +177,37 @@
private void setUpSetForbiddenPlmnsTests() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- AsyncResult.forMessage(response, EF_SIZE, null);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .getEFTransparentRecordSize(anyInt(), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, EF_SIZE, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .getEFTransparentRecordSize(anyInt(), any(Message.class));
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(2);
- AsyncResult.forMessage(response, true, null);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
}
@Test
public void testGetForbiddenPlmns() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- byte[] encodedFplmn = IccUtils.encodeFplmns(SHORT_FPLMNS_LIST, EF_SIZE);
- AsyncResult.forMessage(response, encodedFplmn, null);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ byte[] encodedFplmn = IccUtils.encodeFplmns(SHORT_FPLMNS_LIST, EF_SIZE);
+ AsyncResult.forMessage(response, encodedFplmn, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
Message message = Message.obtain(mTestHandler);
mSIMRecordsUT.getForbiddenPlmns(message);
@@ -214,15 +222,15 @@
@Test
public void testGetForbiddenPlmnsException() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- AsyncResult.forMessage(response, null, new CommandException(
- CommandException.Error.OPERATION_NOT_ALLOWED));
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
Message message = Message.obtain(mTestHandler);
mSIMRecordsUT.getForbiddenPlmns(message);
@@ -238,14 +246,14 @@
@Test
public void testGetForbiddenPlmnsNull() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- AsyncResult.forMessage(response);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
Message message = Message.obtain(mTestHandler);
mSIMRecordsUT.getForbiddenPlmns(message);
@@ -259,15 +267,15 @@
@Test
public void testGetForbiddenPlmnsEmptyList() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- byte[] encodedFplmn = IccUtils.encodeFplmns(EMPTY_FPLMN_LIST, EF_SIZE);
- AsyncResult.forMessage(response, encodedFplmn, null);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ byte[] encodedFplmn = IccUtils.encodeFplmns(EMPTY_FPLMN_LIST, EF_SIZE);
+ AsyncResult.forMessage(response, encodedFplmn, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
Message message = Message.obtain(mTestHandler);
mSIMRecordsUT.getForbiddenPlmns(message);
@@ -282,14 +290,14 @@
@Test
public void testGetForbiddenPlmnsInvalidLength() {
doAnswer(
- invocation -> {
- Message response = invocation.getArgument(1);
- AsyncResult.forMessage(response, new byte[] { (byte) 0xFF, (byte) 0xFF }, null);
- response.sendToTarget();
- return null;
- })
- .when(mFhMock)
- .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, new byte[]{(byte) 0xFF, (byte) 0xFF}, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
Message message = Message.obtain(mTestHandler);
mSIMRecordsUT.getForbiddenPlmns(message);
@@ -313,7 +321,7 @@
targetPnns.add(new PlmnNetworkName(name, null));
Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_PNN_DONE);
- AsyncResult ar = AsyncResult.forMessage(message, rawPnns, null);
+ AsyncResult.forMessage(message, rawPnns, null);
mSIMRecordsUT.handleMessage(message);
List<PlmnNetworkName> parsedPnns = Arrays.asList(mSIMRecordsUT.getPnns());
@@ -321,23 +329,215 @@
}
private static byte[] encodePnn(String name) {
- byte[] gsm7BitName = new byte[] {};
+ byte[] gsm7BitName = new byte[]{};
try {
gsm7BitName = GsmAlphabet.stringToGsm7BitPacked(name);
gsm7BitName[0] = (byte) (name.length() % 8 | 0x80);
} catch (Exception ex) {
fail("SimRecordsTest: GsmAlphabet.stringToGsm7BitPacked() exception:" + ex);
}
-
byte[] encodedName = new byte[gsm7BitName.length + 2];
encodedName[0] = 0x43;
encodedName[1] = (byte) gsm7BitName.length;
System.arraycopy(gsm7BitName, 0, encodedName, 2, gsm7BitName.length);
-
return encodedName;
}
@Test
+ public void testGetSmssTpmrValue() {
+ // Testing tpmr successfully reading case
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) tpmr;
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_SMSS_RECORD_DONE, smss);
+ AsyncResult.forMessage(message, smss, null);
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(tpmr, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testGetSmssTpmrValueException() {
+ // Testing tpmr exception case
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) tpmr;
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_SMSS_RECORD_DONE, smss);
+ AsyncResult.forMessage(message, smss, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(-1, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrValue() {
+ // Testing tpmr successfully updating case
+ setValidSmssValue();
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ assertEquals(updateTpmr, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrNegativevalue() {
+ // Testing tpmr successfully updating case
+ setValidSmssValue();
+ int updateTpmr = -2;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ // 10 is previous set value
+ assertEquals(10, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrHighvalue() {
+ // Testing tpmr successfully updating case
+ setValidSmssValue();
+ int updateTpmr = 256;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ // 10 is previous set value
+ assertEquals(10, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrMaxValue() {
+ // Testing tpmr successfully updating case
+ setValidSmssValue();
+ int updateTpmr = 255;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ assertEquals(updateTpmr, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrValueException() {
+ // Testing exception while setting TPMR [ with out setting initial value]
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+ assertEquals(-1, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
+ public void testSetSmssTpmrValueException2() {
+ // Testing exception while setting TPMR
+ setValidSmssValue();
+ int updateTpmr = 30;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+
+ AsyncResult.forMessage(response, true, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertTrue(((CommandException) ar.exception).getCommandError() ==
+ CommandException.Error.OPERATION_NOT_ALLOWED);
+ }
+
+ @Test
+ public void testSetSmssTpmrLargeValue() {
+ // Testing Large TPMR value setting
+ setValidSmssValue();
+ int updateTpmr = 300;
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, true, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof IllegalArgumentException);
+ }
+
+ private void setValidSmssValue() {
+ // preset/ initialize smssvalue
+ byte[] smss = new byte[2];
+ int tpmr = 10;
+ smss[0] = (byte) (tpmr & 0xFF);
+ IccRecords.SmssRecord smssRecord = mSIMRecordsUT.createSmssRecord(null, smss);
+ Message message = mSIMRecordsUT.obtainMessage(
+ IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+ AsyncResult.forMessage(message, null, null);
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(tpmr, mSIMRecordsUT.getSmssTpmrValue());
+ }
+
+ @Test
public void testGetEfOpl() {
ArrayList<byte[]> rawOpl = new ArrayList<byte[]>();
List<OperatorPlmnInfo> targetOpl = new ArrayList<OperatorPlmnInfo>();
@@ -352,7 +552,7 @@
targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
- AsyncResult ar = AsyncResult.forMessage(message, rawOpl, null);
+ AsyncResult.forMessage(message, rawOpl, null);
mSIMRecordsUT.handleMessage(message);
List<OperatorPlmnInfo> parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
@@ -368,7 +568,7 @@
targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
- ar = AsyncResult.forMessage(message, rawOpl, null);
+ AsyncResult.forMessage(message, rawOpl, null);
mSIMRecordsUT.handleMessage(message);
parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
@@ -383,7 +583,7 @@
rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex));
message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
- ar = AsyncResult.forMessage(message, rawOpl, null);
+ AsyncResult.forMessage(message, rawOpl, null);
mSIMRecordsUT.handleMessage(message);
parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
@@ -405,7 +605,370 @@
data[5] = (byte) (lacTacEnd >>> 8);
data[6] = (byte) lacTacEnd;
data[7] = (byte) pnnIndex;
-
return data;
}
+
+ @Test
+ public void testGetPsiSmscTelValue() {
+ // Testing smsc successfully reading case
+ String smscTest = "tel:+13123149810";
+ String hexSmsc =
+ "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+ byte[] smscBytes = getStringToByte(hexSmsc);
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+ AsyncResult.forMessage(message, smscBytes, null);
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(smscTest, mSIMRecordsUT.getSmscIdentity());
+ }
+
+ private byte[] getStringToByte(String hexSmsc) {
+ byte[] smscBytes = IccUtils.hexStringToBytes(hexSmsc);
+ return smscBytes;
+ }
+
+ @Test
+ public void testGetPsiSmscSipValue() {
+ // Testing smsc successfully reading case
+ String smscTest = "sip:+12063130004@msg.pc.t-mobile.com;user=phone";
+ byte[] smscBytes = getStringToByte(
+ "802F7369703A2B3132303633313330303034406D73672E70632E742D6D6F62696C6"
+ + "52E636F6D3B757365723D70686F6E65FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+ AsyncResult.forMessage(message, smscBytes, null);
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(smscTest, mSIMRecordsUT.getSmscIdentity());
+ }
+
+ @Test
+ public void testGetPsiSmscException() {
+ // Testing smsc exception handling case
+ String hexSmsc =
+ "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+ byte[] smscBytes = getStringToByte(hexSmsc);
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+ AsyncResult ar = AsyncResult.forMessage(message, smscBytes,
+ new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mSIMRecordsUT.handleMessage(message);
+ assertTrue(ar.exception instanceof CommandException);
+ assertEquals(null, mSIMRecordsUT.getSmscIdentity());
+ }
+
+ @Test
+ public void testGetPsiSmscValueInvalidObject() {
+ // Testing smsc invalid data handling case
+ String smscTest = "tel:+13123149810";
+ byte[] smscBytes = GsmAlphabet.stringToGsm8BitPacked(smscTest);
+ Message message = mSIMRecordsUT.obtainMessage(
+ SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+ AsyncResult ar = AsyncResult.forMessage(message, smscBytes,
+ new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mSIMRecordsUT.handleMessage(message);
+ assertEquals(null, mSIMRecordsUT.getSmscIdentity());
+ assertTrue(ar.exception instanceof CommandException);
+ }
+
+ @Test
+ public void testGetSimServiceTable() {
+ // reading sim service table successfully case
+ byte[] sst = new byte[111];
+ for (int i = 0; i < sst.length; i++) {
+ if (i % 2 == 0) {
+ sst[i] = 0;
+ } else {
+ sst[i] = 1;
+ }
+ }
+ Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+ AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+ mSIMRecordsUT.handleMessage(message);
+ String mockSst = IccUtils.bytesToHexString(sst);
+ String resultSst = mSIMRecordsUT.getSimServiceTable();
+ assertEquals(mockSst, resultSst);
+ }
+
+ @Test
+ public void testGetSimServiceTableException() {
+ // sim service table exception handling case
+ Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+ AsyncResult ar = AsyncResult.forMessage(message, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ mSIMRecordsUT.handleMessage(message);
+ String resultSst = mSIMRecordsUT.getSimServiceTable();
+ assertEquals(null, resultSst);
+ }
+
+ @Test
+ public void testGetSsimServiceTableLessTableSize() {
+ // sim service table reading case
+ byte[] sst = new byte[12];
+ for (int i = 0; i < sst.length; i++) {
+ if (i % 2 == 0) {
+ sst[i] = 0;
+ } else {
+ sst[i] = 1;
+ }
+ }
+ Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+ AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+ mSIMRecordsUT.handleMessage(message);
+ String mockSst = IccUtils.bytesToHexString(sst);
+ String resultSst = mSIMRecordsUT.getSimServiceTable();
+ assertEquals(mockSst, resultSst);
+ }
+
+ @Test
+ public void testSetVoiceMailNumber() throws InterruptedException {
+ String voiceMailNumber = "1234567890";
+ String alphaTag = "Voicemail";
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the first invocation");
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the second invocation");
+ Message response = invocation.getArgument(5);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+ eq(null), any(Message.class));
+
+ mSIMRecordsUT.setMailboxIndex(1);
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+ latch.await(5, TimeUnit.SECONDS);
+ mTestLooper.startAutoDispatch();
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null), anyInt(),
+ any(byte[].class), eq(null), any(Message.class));
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return mSIMRecordsUT.getVoiceMailNumber() != null;
+ }
+ });
+ assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+ assertEquals(alphaTag, mSIMRecordsUT.getVoiceMailAlphaTag());
+ }
+
+ @Test
+ public void testSetVoiceMailNumberBigAlphatag() throws InterruptedException {
+ String voiceMailNumber = "1234567890";
+ String alphaTag = "VoicemailAlphaTag-VoicemailAlphaTag";
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the first invocation");
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the second invocation");
+ Message response = invocation.getArgument(5);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+ eq(null), any(Message.class));
+
+ mSIMRecordsUT.setMailboxIndex(1);
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+ latch.await(8, TimeUnit.SECONDS);
+ mTestLooper.startAutoDispatch();
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null), anyInt(),
+ any(byte[].class), eq(null), any(Message.class));
+ //if attempt to save bugAlphatag which sim don't support so we will make it null
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return mSIMRecordsUT.getVoiceMailNumber() != null;
+ }
+ });
+ assertEquals(null, mSIMRecordsUT.getVoiceMailAlphaTag());
+ assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+ }
+
+ @Test
+ public void testSetVoiceMailNumberUtf16Alphatag() throws InterruptedException {
+ String voiceMailNumber = "1234567890";
+ String alphaTag = "หมายเลขข้à¸à¸„วามเสียง"; // Messagerie vocale
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the first invocation");
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the second invocation");
+ Message response = invocation.getArgument(5);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+ eq(null), any(Message.class));
+
+ mSIMRecordsUT.setMailboxIndex(1);
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+ latch.await(5, TimeUnit.SECONDS);
+
+ mTestLooper.startAutoDispatch();
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null), anyInt(),
+ any(byte[].class), eq(null), any(Message.class));
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return mSIMRecordsUT.getVoiceMailNumber() != null;
+ }
+ });
+ assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+ //if attempt to save bugAlphatag which sim don't support so we will make it null
+ assertEquals(null, mSIMRecordsUT.getVoiceMailAlphaTag());
+ }
+
+ @Test
+ public void testSetVoiceMailNullNumber() throws InterruptedException {
+ String voiceMailNumber = null;
+ String alphaTag = "VoicemailAlphaTag"; // Messagerie vocale
+ final CountDownLatch latch = new CountDownLatch(2);
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the first invocation");
+ Message response = invocation.getArgument(2);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+ doAnswer(invocation -> {
+ int[] result = new int[3];
+ result[0] = 32;
+ result[1] = 32;
+ result[2] = 1;
+ Rlog.d("SIMRecordsTest", "Executing the second invocation");
+ Message response = invocation.getArgument(5);
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ latch.countDown();
+ return null;
+ }).when(mFhMock).updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+ eq(null), any(Message.class));
+
+ mSIMRecordsUT.setMailboxIndex(1);
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+ latch.await(5, TimeUnit.SECONDS);
+ mTestLooper.startAutoDispatch();
+ verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+ verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null), anyInt(),
+ any(byte[].class), eq(null), any(Message.class));
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return mSIMRecordsUT.getVoiceMailAlphaTag() != null;
+ }
+ });
+ assertEquals(null, mSIMRecordsUT.getVoiceMailNumber());
+ assertEquals(alphaTag, mSIMRecordsUT.getVoiceMailAlphaTag());
+ }
+
+ public interface Condition {
+ Object expected();
+
+ Object actual();
+ }
+
+ protected void sleep(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (Exception e) {
+ Log.d(TAG, "InterruptedException");
+ }
+ }
+
+ protected void waitUntilConditionIsTrueOrTimeout(Condition condition) {
+ final long start = System.currentTimeMillis();
+ while (!Objects.equals(condition.expected(), condition.actual())
+ && System.currentTimeMillis() - start
+ < (long) SIMRecordsTest.SET_VOICE_MAIL_TIMEOUT) {
+ sleep(50);
+ }
+ assertEquals("Service Unbound", condition.expected(), condition.actual());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index b30c3a7..e4fabc5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -61,7 +61,7 @@
mIccIoResult = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes("FF40"));
mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
mUiccCard = new UiccCard(mContext, mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
- new Object(), false);
+ new Object(), IccSlotStatus.MultipleEnabledProfilesMode.NONE);
processAllMessages();
logd("create UiccCard");
}
@@ -97,4 +97,10 @@
assertNull(mUiccCard.getUiccPort(INVALID_PORT_ID));
assertNotNull(mUiccCard.getUiccPort(TelephonyManager.DEFAULT_PORT_INDEX));
}
+
+ @Test
+ @SmallTest
+ public void testGetCardId() {
+ assertNull(mUiccCard.getCardId());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
index c885f5a..143d7c9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -42,7 +42,6 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@@ -86,7 +85,7 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
AsyncResult ar = new AsyncResult(null, iir, null);
message.obj = ar;
@@ -94,16 +93,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- anyInt(), anyInt(), anyString(), any(Message.class));
+ anyInt(), anyInt(), anyString(), eq(false) /*isEs10Command*/, any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -328,7 +327,7 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
AsyncResult ar = new AsyncResult(null, iir, null);
message.obj = ar;
@@ -336,16 +335,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- anyInt(), anyInt(), anyString(), any(Message.class));
+ anyInt(), anyInt(), anyString(), eq(false) /*isEs10Command*/, any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
@@ -389,7 +388,7 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
AsyncResult ar = new AsyncResult(null, iir, null);
message.obj = ar;
@@ -397,16 +396,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- anyInt(), anyInt(), anyString(), any(Message.class));
+ anyInt(), anyInt(), anyString(), eq(false) /*isEs10Command*/, any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -446,7 +445,7 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
AsyncResult ar = new AsyncResult(null, iir, null);
message.obj = ar;
@@ -454,16 +453,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- anyInt(), anyInt(), anyString(), any(Message.class));
+ anyInt(), anyInt(), anyString(), eq(false /*isEs10Command*/), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -510,11 +509,11 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -570,43 +569,35 @@
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
currentFileId.set((String) invocation.getArguments()[6]);
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
message.obj = ar;
message.sendToTarget();
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
- eq(0x04), eq(0x02), anyString(), any(Message.class));
+ eq(0x04), eq(0x02), anyString(), eq(false /*isEs10Command*/), any(Message.class));
// Read binary - since params are identical across files, we need to keep track of which
// file was selected most recently and give back that content.
- Map<String, String> binaryContent =
- new HashMap<>() {
- {
- // ODF
- put("5031", "A706300404025207");
- // DODF
- put(
- "5207",
- "A1293000300F0C0D4750205345204163632043746CA1143012060A2A864886FC6B"
- + "81480101300404024200");
- // ACMF
- put("4200", "301004080102030405060708300404024300");
- // ACRF
- put("4300", "3010A0080406FFFFFFFFFFFF300404024310");
- // ACCF
- put(
- "4310",
- "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51"
- + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA"
- + "FE8226079EF6F676FD1859");
- }
- };
+ Map<String, String> binaryContent = Map.of(
+ // ODF
+ "5031", "A706300404025207",
+ // DODF
+ "5207", "A1293000300F0C0D4750205345204163632043746CA1143012060A2A864886FC6B"
+ + "81480101300404024200",
+ // ACMF
+ "4200", "301004080102030405060708300404024300",
+ // ACRF
+ "4300", "3010A0080406FFFFFFFFFFFF300404024310",
+ // ACCF
+ "4310", "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51"
+ + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA"
+ + "FE8226079EF6F676FD1859");
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir =
new IccIoResult(0x90, 0x00,
IccUtils.hexStringToBytes(binaryContent.get(currentFileId.get())));
@@ -616,16 +607,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
- eq(0x00), eq(0x00), eq(""), any(Message.class));
+ eq(0x00), eq(0x00), eq(""), eq(false /*isEs10Command*/), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -676,36 +667,30 @@
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
currentFileId.set((String) invocation.getArguments()[6]);
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
message.obj = ar;
message.sendToTarget();
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
- eq(0x04), eq(0x02), anyString(), any(Message.class));
+ eq(0x04), eq(0x02), anyString(), eq(false /*isEs10Command*/), any(Message.class));
// Read binary - since params are identical across files, we need to keep track of which
// file was selected most recently and give back that content.
- Map<String, String> binaryContent =
- new HashMap<>() {
- {
- // ODF fails
- put("5031", "");
- // ACRF
- put("4300", "3010A0080406FFFFFFFFFFFF300404024310");
- // ACCF
- put(
- "4310",
- "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51"
- + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA"
- + "FE8226079EF6F676FD1859");
- }
- };
+ Map<String, String> binaryContent = Map.of(
+ // ODF fails
+ "5031", "",
+ // ACRF
+ "4300", "3010A0080406FFFFFFFFFFFF300404024310",
+ // ACCF
+ "4310", "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51"
+ + "465350302204204C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EA"
+ + "FE8226079EF6F676FD1859");
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir =
new IccIoResult(0x90, 0x00,
IccUtils.hexStringToBytes(binaryContent.get(currentFileId.get())));
@@ -715,16 +700,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
- eq(0x00), eq(0x00), eq(""), any(Message.class));
+ eq(0x00), eq(0x00), eq(""), eq(false /*isEs10Command*/), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
@@ -781,7 +766,7 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00,
IccUtils.hexStringToBytes(hexString1));
AsyncResult ar = new AsyncResult(null, iir, null);
@@ -790,12 +775,12 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- eq(P2), anyInt(), anyString(), any(Message.class));
+ eq(P2), anyInt(), anyString(), eq(false), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[7];
+ Message message = (Message) invocation.getArguments()[8];
IccIoResult iir = new IccIoResult(0x90, 0x00,
IccUtils.hexStringToBytes(hexString2));
AsyncResult ar = new AsyncResult(null, iir, null);
@@ -804,16 +789,16 @@
return null;
}
}).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
- eq(P2_EXTENDED_DATA), anyInt(), anyString(), any(Message.class));
+ eq(P2_EXTENDED_DATA), anyInt(), anyString(), eq(false), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Message message = (Message) invocation.getArguments()[1];
+ Message message = (Message) invocation.getArguments()[2];
message.sendToTarget();
return null;
}
- }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), eq(false), any(Message.class));
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 0344e57..2ab23f3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -104,6 +104,8 @@
doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
+ doReturn(IccSlotStatus.MultipleEnabledProfilesMode.NONE)
+ .when(mMockSlot).getSupportedMepMode();
// set number of slots to 1
mContextFixture.putIntResource(com.android.internal.R.integer.config_num_physical_slots, 1);
@@ -114,6 +116,8 @@
mIccCardStatus.mCdmaSubscriptionAppIndex =
mIccCardStatus.mImsSubscriptionAppIndex =
mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ mIccCardStatus.mSupportedMepMode = IccSlotStatus.MultipleEnabledProfilesMode.NONE;
mSimulatedCommands.setIccCardStatus(mIccCardStatus);
// for testing we pretend slotIndex is set. In reality it would be invalid on older versions
// (before 1.2) of hal
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 bddb044..14e95f1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
@@ -140,7 +140,7 @@
record = mUiccPort.getOpenLogicalChannelRecord(CHANNEL_ID);
assertThat(record).isNull();
- verify(mUiccProfile).iccCloseLogicalChannel(eq(CHANNEL_ID), eq(null));
+ verify(mUiccProfile).iccCloseLogicalChannel(eq(CHANNEL_ID), eq(false), eq(null));
}
private IccLogicalChannelRequest getIccLogicalChannelRequest() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index 856639a..a9034eb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -23,7 +23,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeast;
@@ -32,8 +32,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Message;
@@ -41,14 +41,17 @@
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.telephony.ISub;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import org.junit.After;
@@ -69,6 +72,8 @@
}
private IccIoResult mIccIoResult;
+ private PersistableBundle mBundle;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
private static final int UICCPROFILE_CARRIER_PRIVILEGE_LOADED_EVENT = 3;
@@ -78,6 +83,7 @@
private Handler mMockedHandler;
private UiccCard mUiccCard;
private SubscriptionInfo mSubscriptionInfo;
+ private ISub mMockedIsub;
private IccCardApplicationStatus composeUiccApplicationStatus(
IccCardApplicationStatus.AppType appType,
@@ -99,6 +105,13 @@
mMockedHandler = mock(Handler.class);
mUiccCard = mock(UiccCard.class);
mSubscriptionInfo = mock(SubscriptionInfo.class);
+ mMockedIsub = mock(ISub.class);
+
+ doReturn(mMockedIsub).when(mIBinder).queryLocalInterface(anyString());
+ mServiceManagerMockedServices.put("isub", mIBinder);
+
+ doReturn(1).when(mMockedIsub).getSubId(0);
+
/* initially there are no application available, but the array should not be empty. */
IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
IccCardApplicationStatus.AppType.APPTYPE_USIM,
@@ -109,8 +122,18 @@
mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
mIccIoResult = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes("FF40"));
mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mCarrierConfigManager.getConfigForSubId(anyInt(), any())).thenReturn(mBundle);
+
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mUiccProfile = new UiccProfile(mContext, mSimulatedCommands, mIccCardStatus,
0 /* phoneId */, mUiccCard, new Object());
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
+
processAllMessages();
logd("Create UiccProfile");
@@ -121,6 +144,7 @@
public void tearDown() throws Exception {
mUiccProfile = null;
mIccIoResult = null;
+ mBundle = null;
super.tearDown();
}
@@ -203,7 +227,7 @@
anyInt(), isA(Message.class));
verify(mSimulatedCommandsVerifier, times(2)).iccTransmitApduLogicalChannel(
anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyString(),
- isA(Message.class)
+ anyBoolean(), isA(Message.class)
);
}
@@ -526,8 +550,9 @@
carrierConfigBundle.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING,
fakeCarrierName);
- // broadcast CARRIER_CONFIG_CHANGED
- mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ // send carrier config change
+ mCarrierConfigChangeListener.onCarrierConfigChanged(mPhone.getPhoneId(), mPhone.getSubId(),
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
processAllMessages();
// verify that setSimOperatorNameForPhone() is called with fakeCarrierName
@@ -552,15 +577,17 @@
mUiccProfile.getApplicationIndex(0).getIccRecords().mIccId = fakeIccId;
- doReturn(false).when(mSubscriptionController)
- .checkPhoneIdAndIccIdMatch(anyInt(), anyString());
+ doReturn(new SubscriptionInfoInternal.Builder().setSimSlotIndex(0).setId(1)
+ .setIccId("98765").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(anyInt());
mUiccProfile.setOperatorBrandOverride(fakeBrand);
String brandInSharedPreference = mContext.getSharedPreferences("file name", 0)
.getString("operator_branding_" + fakeIccId, null);
assertNotEquals(fakeBrand, brandInSharedPreference);
- doReturn(true).when(mSubscriptionController)
- .checkPhoneIdAndIccIdMatch(anyInt(), anyString());
+ doReturn(new SubscriptionInfoInternal.Builder().setSimSlotIndex(0).setId(1)
+ .setIccId(fakeIccId).build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(anyInt());
mUiccProfile.setOperatorBrandOverride(fakeBrand);
brandInSharedPreference = mContext.getSharedPreferences("file name", 0)
.getString("operator_branding_" + fakeIccId, null);
@@ -576,8 +603,6 @@
mUiccProfile.getApplicationIndex(0).getIccRecords().mIccId = fakeIccId1;
doReturn(fakeIccId2).when(mSubscriptionInfo).getIccId();
- doReturn(mSubscriptionInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfoForSimSlotIndex(eq(0), any(), any());
mUiccProfile.setOperatorBrandOverride(fakeBrand);
String brandInSharedPreference = mContext.getSharedPreferences("file name", 0)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index 7bc737b..230f147 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -158,8 +158,7 @@
assertTrue(mUiccSlot.isActive());
assertNull(mUiccSlot.getUiccCard());
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
- verify(mSubInfoRecordUpdater).updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccController).updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
}
@Test
@@ -380,8 +379,7 @@
// Make sure when received CARDSTATE_ABSENT state in the first time,
mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
- verify(mSubInfoRecordUpdater).updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccController).updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
assertNull(mUiccSlot.getUiccCard());
}
@@ -414,8 +412,9 @@
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
// assert that we tried to update subscriptions
- verify(mSubInfoRecordUpdater).updateInternalIccStateForInactivePort(
- activeIss.mSimPortInfos[0].mLogicalSlotIndex, inactiveIss.mSimPortInfos[0].mIccId);
+ verify(mUiccController).updateSimStateForInactivePort(
+ activeIss.mSimPortInfos[0].mLogicalSlotIndex,
+ inactiveIss.mSimPortInfos[0].mIccId);
}
@Test
@@ -434,12 +433,10 @@
assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
assertNotNull(mUiccSlot.getUiccCard());
- // Simulate when SIM is removed, UiccCard and UiccProfile should be disposed and ABSENT
- // state is sent to SubscriptionInfoUpdater.
+ // Simulate when SIM is removed
mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
- verify(mSubInfoRecordUpdater).updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccController).updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
verify(mUiccProfile).dispose();
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
assertNull(mUiccSlot.getUiccCard());
@@ -462,9 +459,7 @@
// radio state unavailable
mUiccSlot.onRadioStateUnavailable(phoneId);
- // Verify that UNKNOWN state is sent to SubscriptionInfoUpdater in this case.
- verify(mSubInfoRecordUpdater).updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, phoneId);
+ verify(mUiccController).updateSimState(phoneId, IccCardConstants.State.UNKNOWN, null);
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
assertNull(mUiccSlot.getUiccCard());
@@ -472,9 +467,7 @@
mIccCardStatus.mCardState = CardState.CARDSTATE_ABSENT;
mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
- // Verify that ABSENT state is sent to SubscriptionInfoUpdater in this case.
- verify(mSubInfoRecordUpdater).updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccController).updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
assertNull(mUiccSlot.getUiccCard());
}
@@ -511,4 +504,79 @@
assertTrue("EuiccCard should be removable", mUiccSlot.isRemovable());
}
+ @Test
+ @SmallTest
+ public void testMultipleEnabledProfilesData() {
+ IccSlotStatus iss = new IccSlotStatus();
+ IccSimPortInfo simPortInfo1 = new IccSimPortInfo();
+ simPortInfo1.mPortActive = false;
+ simPortInfo1.mLogicalSlotIndex = -1;
+ simPortInfo1.mIccId = "fake-iccid";
+
+ IccSimPortInfo simPortInfo2 = new IccSimPortInfo();
+ simPortInfo2.mPortActive = true;
+ simPortInfo2.mLogicalSlotIndex = 0;
+ simPortInfo2.mIccId = "fake-iccid";
+
+ iss.mSimPortInfos = new IccSimPortInfo[] {simPortInfo1, simPortInfo2};
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ iss.atr = "3B9F97C00AB1FE453FC6838031E073FE211F65D002341569810F21";
+ iss.setMultipleEnabledProfilesMode(3);
+
+
+ // initial state
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertEquals(IccSlotStatus.MultipleEnabledProfilesMode.NONE,
+ mUiccSlot.getSupportedMepMode());
+ assertFalse(mUiccSlot.isMultipleEnabledProfileSupported());
+
+ // update slot to inactive
+ mUiccSlot.update(null, iss, 0 /* slotIndex */);
+
+ // assert on updated values
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
+ assertTrue(mUiccSlot.isMultipleEnabledProfileSupported());
+ assertEquals(IccSlotStatus.MultipleEnabledProfilesMode.MEP_B,
+ mUiccSlot.getSupportedMepMode());
+
+ iss.mSimPortInfos = new IccSimPortInfo[] {simPortInfo1};
+ iss.setMultipleEnabledProfilesMode(1); // Set MEP mode to MEP-A1
+
+ // update port info and MEP mode
+ mUiccSlot.update(null, iss, 0 /* slotIndex */);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isMultipleEnabledProfileSupported());
+ assertEquals(IccSlotStatus.MultipleEnabledProfilesMode.MEP_A1,
+ mUiccSlot.getSupportedMepMode());
+
+ //update port info and MEP mode to test HAL version 2.0
+ iss.mSimPortInfos = new IccSimPortInfo[] {simPortInfo1, simPortInfo2};
+ iss.setMultipleEnabledProfilesMode(0); // Set MEP mode to NONE(assume modem sends)
+
+ // update port info and MEP mode
+ mUiccSlot.update(null, iss, 0 /* slotIndex */);
+ assertTrue(mUiccSlot.isMultipleEnabledProfileSupported());
+ assertEquals(IccSlotStatus.MultipleEnabledProfilesMode.MEP_B,
+ mUiccSlot.getSupportedMepMode());
+ }
+
+ @Test
+ @SmallTest
+ public void testSimStateUnknown() {
+ int phoneId = 0;
+ int slotIndex = 0;
+ // Initially state is unknown
+ assertTrue(mUiccSlot.isStateUnknown());
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
+ assertNull(mUiccSlot.getUiccCard());
+ // As CardState is absent, state should not be unknown
+ assertFalse(mUiccSlot.isStateUnknown());
+ // radio state unavailable
+ mUiccSlot.onRadioStateUnavailable(phoneId);
+ // When radio is not available, state is unknown
+ assertTrue(mUiccSlot.isStateUnknown());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index 18247d3..7e51bad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -99,7 +99,8 @@
// The first broadcast should be sent after initialization.
UiccCard card = new UiccCard(mContext, mSimulatedCommands,
- makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object(), false);
+ makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object(),
+ IccSlotStatus.MultipleEnabledProfilesMode.NONE);
when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
uiccLauncher.handleMessage(msg);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index 79c4af4..b6dd7bd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -34,6 +35,7 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccSlotPortMapping;
+import com.android.internal.telephony.uicc.IccSlotStatus;
import com.android.internal.telephony.uicc.euicc.apdu.LogicalChannelMocker;
import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
@@ -94,7 +96,7 @@
mEuiccCard =
new EuiccCard(mContext, mMockCi, mMockIccCardStatus,
- 0 /* phoneId */, new Object(), false) {
+ 0 /* phoneId */, new Object(), IccSlotStatus.MultipleEnabledProfilesMode.NONE) {
@Override
protected void loadEidAndNotifyRegistrants() {}
@@ -133,7 +135,8 @@
public void testPassEidInConstructor() {
mMockIccCardStatus.eid = "1A2B3C4D";
mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
- mMockIccCardStatus, 0 /* phoneId */, new Object(), false);
+ mMockIccCardStatus, 0 /* phoneId */, new Object(),
+ IccSlotStatus.MultipleEnabledProfilesMode.NONE);
final int eventEidReady = 0;
Handler handler = new Handler(Looper.myLooper()) {
@@ -154,7 +157,8 @@
int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");
mHandler.post(() -> {
mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
- mMockIccCardStatus, 0 /* phoneId */, new Object(), false);
+ mMockIccCardStatus, 0 /* phoneId */, new Object(),
+ IccSlotStatus.MultipleEnabledProfilesMode.NONE);
});
processAllMessages();
@@ -188,7 +192,7 @@
private void verifyStoreData(int channel, String command) {
verify(mMockCi, times(1))
.iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91),
- eq(0), eq(command.length() / 2), eq(command), any());
+ eq(0), eq(command.length() / 2), eq(command), anyBoolean(), any());
}
private int mockLogicalChannelResponses(Object... responses) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
index 8caa248..d140ca8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.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.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -49,6 +50,8 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccSlotPortMapping;
+import com.android.internal.telephony.uicc.IccSlotStatus;
+import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.asn1.Asn1Node;
import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
@@ -118,7 +121,8 @@
mMockIccCardStatus.mSlotPortMapping = new IccSlotPortMapping();
mEuiccPort =
new EuiccPort(mContext, mMockCi, mMockIccCardStatus,
- 0 /* phoneId */, new Object(), mEuiccCard, false) {
+ 0 /* phoneId */, new Object(), mEuiccCard,
+ IccSlotStatus.MultipleEnabledProfilesMode.NONE) {
@Override
protected byte[] getDeviceId() {
return IccUtils.bcdToBytes("987654321012345");
@@ -171,7 +175,7 @@
"BF2D14A012E3105A0A896700000000004523019F7001019000");
ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
- mEuiccPort.mIsSupportsMultipleEnabledProfiles = true; // MEP capable
+ mEuiccPort.mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B; // MEP capable
mEuiccPort.getAllProfiles(resultCaptor, mHandler);
processAllMessages();
@@ -206,7 +210,7 @@
"BF2D18A016E3145A0A896700000000004523019F7001009F2401019000");
ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
- mEuiccPort.mIsSupportsMultipleEnabledProfiles = true; // MEP capable
+ mEuiccPort.mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B; // MEP capable
mEuiccPort.getAllProfiles(resultCaptor, mHandler);
processAllMessages();
@@ -228,7 +232,7 @@
"BF2D14A012E3105A0A896700000000004523FF9F7001009000");
ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
- mEuiccPort.mIsSupportsMultipleEnabledProfiles = true; // MEP capable
+ mEuiccPort.mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B; // MEP capable
mEuiccPort.getAllProfiles(resultCaptor, mHandler);
processAllMessages();
@@ -375,6 +379,22 @@
}
@Test
+ public void testSwitchToProfile_MepA1() {
+ int channel = mockLogicalChannelResponses("BF31038001039000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mMockIccCardStatus.mSlotPortMapping.mPortIndex = 1;
+ mEuiccPort.updateSupportedMepMode(MultipleEnabledProfilesMode.MEP_A1);
+ mEuiccPort.update(mContext, mMockCi, mMockIccCardStatus, mEuiccCard);
+ mEuiccPort.switchToProfile("98760000000000543210", true, resultCaptor, mHandler);
+ processAllMessages();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ // In case of MEP-A1, verify portIndex is shifted or not.
+ verifyStoreData(channel, "BF3114A00C5A0A896700000000004523018101FF820102");
+ }
+
+ @Test
public void testGetEid() {
int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");
@@ -884,7 +904,7 @@
verify(mMockCi, never())
.iccTransmitApduLogicalChannel(
eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
- any());
+ anyBoolean(), any());
}
@Test
@@ -917,7 +937,7 @@
verify(mMockCi, never())
.iccTransmitApduLogicalChannel(
eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
- any());
+ anyBoolean(), any());
}
@Test
@@ -1175,7 +1195,7 @@
private void verifyStoreData(int channel, String command) {
verify(mMockCi, times(1))
.iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91),
- eq(0), eq(command.length() / 2), eq(command), any());
+ eq(0), eq(command.length() / 2), eq(command), anyBoolean(), any());
}
private int mockLogicalChannelResponses(Object... responses) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index 2b9e767..b073c6a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -119,7 +120,7 @@
assertNull(mResponseCaptor.response);
assertNull(mResponseCaptor.exception);
verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any());
- verify(mMockCi).iccCloseLogicalChannel(eq(channel), any());
+ verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
}
@Test
@@ -149,7 +150,7 @@
assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
}
@Test
@@ -169,13 +170,13 @@
assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(1), eq("ab"), any());
+ eq(3), eq(1), eq("ab"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq(""), any());
+ eq(3), eq(0), eq(""), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
- eq(0), eq(2), eq("abcd"), any());
+ eq(0), eq(2), eq("abcd"), anyBoolean(), any());
}
@Test
@@ -196,11 +197,11 @@
assertEquals("A3", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(1), eq("ab"), any());
+ eq(3), eq(1), eq("ab"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq(""), any());
+ eq(3), eq(0), eq(""), anyBoolean(), any());
}
@Test
@@ -216,11 +217,11 @@
assertEquals("A1A1A1B2B2B2B2C3C3", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0),
- eq(0), eq(4), eq(""), any());
+ eq(0), eq(4), eq(""), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0),
- eq(0), eq(2), eq(""), any());
+ eq(0), eq(2), eq(""), anyBoolean(), any());
}
@Test
@@ -244,15 +245,15 @@
assertEquals("C3", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("b"), any());
+ eq(3), eq(0), eq("b"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
- eq(0), eq(0xFF), eq(s1), any());
+ eq(0), eq(0xFF), eq(s1), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
- eq(1), eq(0xFF), eq(s2), any());
+ eq(1), eq(0xFF), eq(s2), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
- eq(2), eq(16), eq(s3), any());
+ eq(2), eq(16), eq(s3), anyBoolean(), any());
}
@Test
@@ -272,9 +273,9 @@
assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
- eq(0), eq(0xFF), eq(s1), any());
+ eq(0), eq(0xFF), eq(s1), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
- eq(1), eq(0xFF), eq(s2), any());
+ eq(1), eq(0xFF), eq(s2), anyBoolean(), any());
}
@Test
@@ -290,7 +291,7 @@
assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response));
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
- eq(0), eq(0), eq(""), any());
+ eq(0), eq(0), eq(""), anyBoolean(), any());
}
@Test
@@ -313,13 +314,13 @@
assertEquals(0x6985, ((ApduException) mResponseCaptor.exception).getApduStatus());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
- eq(3), eq(0), eq("a"), any());
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
- eq(0), eq(0xFF), eq(s1), any());
+ eq(0), eq(0xFF), eq(s1), anyBoolean(), any());
verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
- eq(1), eq(0xFF), eq(s2), any());
+ eq(1), eq(0xFF), eq(s2), anyBoolean(), any());
verify(mMockCi, never()).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2),
- eq(0x91), eq(2), eq(16), eq(s3), any());
+ eq(0x91), eq(2), eq(16), eq(s3), anyBoolean(), any());
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
index e9796a1..27f743f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc.euicc.apdu;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -64,32 +65,39 @@
public static void mockSendToLogicalChannel(CommandsInterface mockCi, int channel,
Object... responseObjects) {
ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
+
doAnswer(new Answer() {
private int mIndex = 0;
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object responseObject = responseObjects[mIndex++];
- boolean isException = responseObject instanceof Throwable;
- int sw1 = 0;
- int sw2 = 0;
- String hex = responseObject.toString();
- if (!isException) {
- int l = hex.length();
- sw1 = Integer.parseInt(hex.substring(l - 4, l - 2), 16);
- sw2 = Integer.parseInt(hex.substring(l - 2), 16);
- hex = hex.substring(0, l - 4);
- }
- IccIoResult result = isException ? null : new IccIoResult(sw1, sw2, hex);
- Throwable exception = isException ? (Throwable) responseObject : null;
-
- Message msg = response.getValue();
- AsyncResult.forMessage(msg, result, exception);
- msg.sendToTarget();
+ mockIccTransmitApduLogicalChannelResponse(response, responseObject);
return null;
}
}).when(mockCi).iccTransmitApduLogicalChannel(eq(channel), anyInt(), anyInt(), anyInt(),
- anyInt(), anyInt(), anyString(), response.capture());
+ anyInt(), anyInt(), anyString(), anyBoolean(), response.capture());
+ }
+
+ private static void mockIccTransmitApduLogicalChannelResponse(ArgumentCaptor<Message> response,
+ Object responseObject) throws Throwable {
+
+ boolean isException = responseObject instanceof Throwable;
+ int sw1 = 0;
+ int sw2 = 0;
+ String hex = responseObject.toString();
+ if (!isException) {
+ int l = hex.length();
+ sw1 = Integer.parseInt(hex.substring(l - 4, l - 2), 16);
+ sw2 = Integer.parseInt(hex.substring(l - 2), 16);
+ hex = hex.substring(0, l - 4);
+ }
+ IccIoResult result = isException ? null : new IccIoResult(sw1, sw2, hex);
+ Throwable exception = isException ? (Throwable) responseObject : null;
+
+ Message msg = response.getValue();
+ AsyncResult.forMessage(msg, result, exception);
+ msg.sendToTarget();
}
public static void mockCloseLogicalChannel(CommandsInterface mockCi, int channel) {
@@ -99,7 +107,8 @@
AsyncResult.forMessage(msg);
msg.sendToTarget();
return null;
- }).when(mockCi).iccCloseLogicalChannel(eq(channel), response.capture());
+ }).when(mockCi).iccCloseLogicalChannel(eq(channel),
+ eq(true /*isEs10*/), response.capture());
}
private static int[] getSelectResponse(String responseHex) {
diff --git a/tools/tdi b/tools/tdi
deleted file mode 100755
index 940a83b..0000000
--- a/tools/tdi
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash
-# Telephony Debug Intents
-#set -x
-
-file_name='tdi'
-
-# Get the command as the first parameter
-cmd=$1
-shift
-
-function dc_errors()
-{
- if [ "$1" == "" ]; then
- echo "Usage: $file_name $cmd <dc> <count> <cause> <retry-time>"
- echo " <dc> must specifiy the DataConnection such as DC or GsmDC-1"
- echo " <count> := number of times to retry"
- echo " <cause> := From DataConnection.FailCause; such as -3 for SIGNAL_LOST"
- echo " <retry-time> := suggested retry time in milli-seconds"
- exit
- fi
- the_DC=$1
- echo "the_DC=$the_DC"
-
- if [ "$2" != "" ]; then
- counter="--ei counter $2";
- fi
- echo "counter=$counter"
-
- if [ "$3" != "" ]; then
- fail_cause="--ei fail_cause $3";
- fi
- echo "fail_cause=$fail_cause"
-
- if [ "$4" != "" ]; then
- suggested_retry_time="--ei suggested_retry_time $4";
- fi
- echo "suggested_retry_time=$suggested_retry_time"
-
-
- adb shell am broadcast -a com.android.internal.telephony.$the_DC.action_fail_bringup $counter $fail_cause $suggested_retry_time
-}
-
-
-case ${cmd} in
- dce) dc_errors "$@";;
- # Add more commands in the future
- *) echo 'Broadcast telephony debug intents'; echo 'usage: tdi [dce]'; echo ' dce=DC errors';;
-esac