Merge "IMS: Clear 'Merge Triggered By Conference' Flag"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..9d6bc5b
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// 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.
+
+
+filegroup {
+ name: "opt-telephony-srcs",
+ srcs: [
+ "src/java/android/telephony/**/*.java",
+ ],
+}
+
+filegroup {
+ name: "opt-telephony-htmls",
+ srcs: [
+ "src/java/android/telephony/**/*.html",
+ ],
+}
diff --git a/Android.mk b/Android.mk
index 6e4e43e..561aed0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,24 +22,22 @@
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src/java
LOCAL_SRC_FILES := $(call all-java-files-under, src/java) \
$(call all-Iaidl-files-under, src/java) \
- $(call all-logtags-files-under, src/java) \
- $(call all-proto-files-under, proto)
+ $(call all-logtags-files-under, src/java)
-LOCAL_JAVA_LIBRARIES := voip-common ims-common services
+LOCAL_JAVA_LIBRARIES := voip-common ims-common services bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := \
+ telephony-protos \
android.hardware.radio-V1.0-java \
android.hardware.radio-V1.1-java \
android.hardware.radio-V1.2-java \
+ android.hardware.radio.config-V1.0-java \
android.hardware.radio.deprecated-V1.0-java \
android.hidl.base-V1.0-java
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := telephony-common
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := store_unknown_fields=true,enum_style=java
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
LOCAL_EMMA_INSTRUMENT := true
diff --git a/OWNERS b/OWNERS
index 02f7976..7276a1a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,6 +7,5 @@
tgunn@google.com
jminjie@google.com
mpq@google.com
-sanketpadawe@google.com
shuoq@google.com
refuhoo@google.com
diff --git a/proto/Android.bp b/proto/Android.bp
new file mode 100644
index 0000000..a36f7a5
--- /dev/null
+++ b/proto/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+java_library_static {
+ name: "telephony-protos",
+ proto: {
+ type: "nano",
+ output_params: [
+ "store_unknown_fields=true",
+ "enum_style=java",
+ ],
+ },
+ srcs: ["src/**/*.proto"],
+ no_framework_libs: true,
+ jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
+}
\ No newline at end of file
diff --git a/jarjar-rules.txt b/proto/jarjar-rules.txt
similarity index 100%
rename from jarjar-rules.txt
rename to proto/jarjar-rules.txt
diff --git a/proto/src/carrierId.proto b/proto/src/carrierId.proto
new file mode 100644
index 0000000..f67e9f6
--- /dev/null
+++ b/proto/src/carrierId.proto
@@ -0,0 +1,83 @@
+//
+// 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.
+//
+
+syntax = "proto2";
+
+package carrierIdentification;
+
+option java_package = "com.android.internal.telephony";
+option java_outer_classname = "CarrierIdProto";
+
+// A complete list of carriers
+message CarrierList {
+ // A collection of carriers. one entry for one carrier.
+ repeated CarrierId carrier_id = 1;
+ // Version number of current carrier list
+ optional int32 version = 2;
+};
+
+// CarrierId is the unique representation of a carrier in CID table.
+message CarrierId {
+ // [Optional] A unique canonical number designated to a carrier.
+ optional int32 canonical_id = 1;
+
+ // [Optional] A user-friendly carrier name (not localized).
+ optional string carrier_name = 2;
+
+ // [Optional] Carrier attributes to match a carrier. At least one value is required.
+ repeated CarrierAttribute carrier_attribute = 3;
+};
+
+// Attributes used to match a carrier.
+// For each field within this message:
+// - if not set, the attribute is ignored;
+// - if set, the device must have one of the specified values to match.
+// Match is based on AND between any field that is set and OR for values within a repeated field.
+message CarrierAttribute {
+ // [Optional] The MCC and MNC that map to this carrier. At least one value is required.
+ repeated string mccmnc_tuple = 1;
+
+ // [Optional] Prefix of IMSI (International Mobile Subscriber Identity) in
+ // decimal format. Some digits can be replaced with "x" symbols matching any digit.
+ // Sample values: 20404794, 21670xx2xxx.
+ repeated string imsi_prefix_xpattern = 2;
+
+ // [Optional] The Service Provider Name. Read from subscription EF_SPN.
+ // Sample values: C Spire, LeclercMobile
+ repeated string spn = 3;
+
+ // [Optional] PLMN network name. Read from subscription EF_PNN.
+ // Sample values:
+ repeated string plmn = 4;
+
+ // [Optional] Group Identifier Level1 for a GSM phone. Read from subscription EF_GID1.
+ // Sample values: 6D, BAE0000000000000
+ repeated string gid1 = 5;
+
+ // [Optional] Group Identifier Level2 for a GSM phone. Read from subscription EF_GID2.
+ // Sample values: 6D, BAE0000000000000
+ repeated string gid2 = 6;
+
+ // [Optional] The Access Point Name, corresponding to "apn" field returned by
+ // "content://telephony/carriers/preferapn" on device.
+ // Sample values: fast.t-mobile.com, internet
+ repeated string preferred_apn = 7;
+
+ // [Optional] Prefix of Integrated Circuit Card Identifier. Read from subscription EF_ICCID.
+ // Sample values: 894430, 894410
+ repeated string iccid_prefix = 8;
+};
+
diff --git a/proto/telephony.proto b/proto/src/telephony.proto
similarity index 90%
rename from proto/telephony.proto
rename to proto/src/telephony.proto
index 1f1a2bb..215d14e 100644
--- a/proto/telephony.proto
+++ b/proto/src/telephony.proto
@@ -44,6 +44,9 @@
// The end time of this log
optional Time end_time = 7;
+
+ // Modem power stats
+ optional ModemPowerStats modem_power_stats = 8;
}
// The time information
@@ -547,6 +550,9 @@
// System time overwritten by NITZ (Network time)
NITZ_TIME = 12;
+
+ // Carrier Identification Matching Event
+ CARRIER_ID_MATCHING = 13;
}
// Setup a packet data connection
@@ -757,6 +763,8 @@
DEACTIVATE_REASON_RADIO_OFF = 2;
DEACTIVATE_REASON_PDP_RESET = 3;
+
+ DEACTIVATE_REASON_HANDOVER = 4;
}
}
@@ -772,6 +780,29 @@
optional string reason = 2;
}
+ message CarrierIdMatching {
+
+ // Carrier id table version number
+ optional int32 cid_table_version = 1;
+
+ // Carrier id matching result object
+ optional CarrierIdMatchingResult result = 2;
+ }
+
+ message CarrierIdMatchingResult {
+
+ // A unique carrier id
+ optional int32 carrier_id = 1;
+
+ // Group id level 1. Logged only if gid1 is configured from subscription
+ // but its matching rule is unknown
+ optional string gid1 = 2;
+
+ // MCC and MNC that map to this carrier. Logged only if mccmnc is configured
+ // from subscription but its matching rule is unknown
+ optional string mccmnc = 3;
+ }
+
// Time when event happened on device, in milliseconds since epoch
optional int64 timestamp_millis = 1;
@@ -816,6 +847,9 @@
// NITZ time in milliseconds
optional int64 nitz_timestamp_millis = 15;
+
+ // Carrier id matching event
+ optional CarrierIdMatching carrier_id_matching = 16;
}
enum TimeInterval {
@@ -1060,7 +1094,7 @@
// Time since previous event
optional TimeInterval delay = 2;
- // Settings at the begining of the session or when changed
+ // Settings at the beginning of the session or when changed
optional TelephonySettings settings = 3;
// State at the beginning of the session or when changed
@@ -1165,6 +1199,9 @@
// Notification about received SMS
SMS_RECEIVED = 8;
+
+ // CB message received
+ CB_SMS_RECEIVED = 9;
}
// Formats used to encode SMS messages
@@ -1190,13 +1227,58 @@
SMS_IMS = 3;
}
+ message CBMessage {
+ // CB message format
+ optional Format msgFormat = 1;
+
+ // CB message priority
+ optional CBPriority msgPriority = 2;
+
+ // Type of CB msg
+ optional CBMessageType msgType = 3;
+
+ // Service category of CB message
+ optional int32 serviceCategory = 4;
+ }
+
+ enum CBMessageType {
+ // Unknown type
+ TYPE_UNKNOWN = 0;
+
+ // ETWS CB msg
+ ETWS = 1;
+
+ // CMAS CB msg
+ CMAS = 2;
+
+ // CB msg other than ETWS and CMAS
+ OTHER = 3;
+ }
+
+ enum CBPriority {
+ // Unknown priority
+ PRIORITY_UNKNOWN = 0;
+
+ // NORMAL priority
+ NORMAL = 1;
+
+ // Interactive priority
+ INTERACTIVE = 2;
+
+ // Urgent priority
+ URGENT = 3;
+
+ // Emergency priority
+ EMERGENCY = 4;
+ }
+
// Event type
optional Type type = 1;
// Time since previous event
optional TimeInterval delay = 2;
- // Settings at the begining of the session or when changed
+ // Settings at the beginning of the session or when changed
optional TelephonySettings settings = 3;
// State at the beginning of the session or when changed
@@ -1227,6 +1309,9 @@
// Numeric ID
optional int32 ril_request_id = 12;
+
+ // Cellbroadcast message content
+ optional CBMessage cell_broadcast_message = 13;
}
// Time when session has started, in minutes since epoch,
@@ -1242,3 +1327,35 @@
// Indicating some sms session events are dropped
optional bool events_dropped = 4;
}
+
+// Power stats for modem
+message ModemPowerStats {
+
+ // Duration of log (ms). This is the duration of time device is
+ // on battery and modem power stats are collected.
+ optional int64 logging_duration_ms = 1;
+
+ // Energy consumed by modem (mAh)
+ optional double energy_consumed_mah = 2;
+
+ // Number of packets sent (tx)
+ optional int64 num_packets_tx = 3;
+
+ // Amount of time kernel is active because of cellular data (ms)
+ optional int64 cellular_kernel_active_time_ms = 4;
+
+ // Amount of time spent in very poor rx signal level (ms)
+ optional int64 time_in_very_poor_rx_signal_level_ms = 5;
+
+ // Amount of time modem is in sleep (ms)
+ optional int64 sleep_time_ms = 6;
+
+ // Amount of time modem is in idle (ms)
+ optional int64 idle_time_ms = 7;
+
+ // Amount of time modem is in rx (ms)
+ optional int64 rx_time_ms = 8;
+
+ // Amount of time modem is in tx (ms)
+ repeated int64 tx_time_ms = 9;
+}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 137b2a7..c8cea5a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -45,6 +45,7 @@
protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
protected RegistrantList mImsNetworkStateChangedRegistrants = new RegistrantList();
protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList();
+ protected RegistrantList mIccSlotStatusChangedRegistrants = new RegistrantList();
protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList();
protected Registrant mUnsolOemHookRawRegistrant;
@@ -74,7 +75,9 @@
protected RegistrantList mCarrierInfoForImsiEncryptionRegistrants = new RegistrantList();
protected RegistrantList mRilNetworkScanResultRegistrants = new RegistrantList();
protected RegistrantList mModemResetRegistrants = new RegistrantList();
-
+ protected RegistrantList mNattKeepaliveStatusRegistrants = new RegistrantList();
+ protected RegistrantList mPhysicalChannelConfigurationRegistrants = new RegistrantList();
+ protected RegistrantList mLceInfoRegistrants = new RegistrantList();
protected Registrant mGsmSmsRegistrant;
protected Registrant mCdmaSmsRegistrant;
@@ -95,7 +98,6 @@
protected Registrant mGsmBroadcastSmsRegistrant;
protected Registrant mCatCcAlphaRegistrant;
protected Registrant mSsRegistrant;
- protected Registrant mLceInfoRegistrant;
// Preferred network type received from PhoneFactory.
// This is used when establishing a connection to the
@@ -283,6 +285,17 @@
}
@Override
+ public void registerForIccSlotStatusChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mIccSlotStatusChangedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForIccSlotStatusChanged(Handler h) {
+ mIccSlotStatusChangedRegistrants.remove(h);
+ }
+
+ @Override
public void setOnNewGsmSms(Handler h, int what, Object obj) {
mGsmSmsRegistrant = new Registrant (h, what, obj);
}
@@ -836,6 +849,17 @@
}
@Override
+ public void registerForPhysicalChannelConfiguration(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mPhysicalChannelConfigurationRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForPhysicalChannelConfiguration(Handler h) {
+ mPhysicalChannelConfigurationRegistrants.remove(h);
+ }
+
+ @Override
public void registerForSrvccStateChanged(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
@@ -899,15 +923,18 @@
@Override
public void registerForLceInfo(Handler h, int what, Object obj) {
- mLceInfoRegistrant = new Registrant(h, what, obj);
+ Registrant r = new Registrant(h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mLceInfoRegistrants.add(r);
+ }
}
@Override
public void unregisterForLceInfo(Handler h) {
- if (mLceInfoRegistrant != null && mLceInfoRegistrant.getHandler() == h) {
- mLceInfoRegistrant.clear();
- mLceInfoRegistrant = null;
- }
+ synchronized (mStateMonitor) {
+ mLceInfoRegistrants.remove(h);
+ }
}
@Override
@@ -939,4 +966,20 @@
public void unregisterForCarrierInfoForImsiEncryption(Handler h) {
mCarrierInfoForImsiEncryptionRegistrants.remove(h);
}
+
+ @Override
+ public void registerForNattKeepaliveStatus(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mNattKeepaliveStatusRegistrants.add(r);
+ }
+ }
+
+ @Override
+ public void unregisterForNattKeepaliveStatus(Handler h) {
+ synchronized (mStateMonitor) {
+ mNattKeepaliveStatusRegistrants.remove(h);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/BlockChecker.java b/src/java/com/android/internal/telephony/BlockChecker.java
index dcbeea0..456be56 100644
--- a/src/java/com/android/internal/telephony/BlockChecker.java
+++ b/src/java/com/android/internal/telephony/BlockChecker.java
@@ -1,6 +1,7 @@
package com.android.internal.telephony;
import android.content.Context;
+import android.os.Bundle;
import android.provider.BlockedNumberContract;
import android.telephony.Rlog;
@@ -12,18 +13,40 @@
private static final boolean VDBG = false; // STOPSHIP if true.
/**
- * Returns {@code true} if {@code phoneNumber} is blocked.
+ * Returns {@code true} if {@code phoneNumber} is blocked according to {@code extras}.
* <p>
* This method catches all underlying exceptions to ensure that this method never throws any
* exception.
+ * <p>
+ * @deprecated use {@link #isBlocked(Context, String, Bundle)} instead.
+ *
+ * @param context the context of the caller.
+ * @param phoneNumber the number to check.
+ * @return {@code true} if the number is blocked. {@code false} otherwise.
*/
+ @Deprecated
public static boolean isBlocked(Context context, String phoneNumber) {
+ return isBlocked(context, phoneNumber, null /* extras */);
+ }
+
+ /**
+ * Returns {@code true} if {@code phoneNumber} is blocked according to {@code extras}.
+ * <p>
+ * This method catches all underlying exceptions to ensure that this method never throws any
+ * exception.
+ *
+ * @param context the context of the caller.
+ * @param phoneNumber the number to check.
+ * @param extras the extra attribute of the number.
+ * @return {@code true} if the number is blocked. {@code false} otherwise.
+ */
+ public static boolean isBlocked(Context context, String phoneNumber, Bundle extras) {
boolean isBlocked = false;
long startTimeNano = System.nanoTime();
try {
if (BlockedNumberContract.SystemContract.shouldSystemBlockNumber(
- context, phoneNumber)) {
+ context, phoneNumber, extras)) {
Rlog.d(TAG, phoneNumber + " is blocked.");
isBlocked = true;
}
diff --git a/src/java/com/android/internal/telephony/CallFailCause.java b/src/java/com/android/internal/telephony/CallFailCause.java
index ed39b4d..acc1432 100644
--- a/src/java/com/android/internal/telephony/CallFailCause.java
+++ b/src/java/com/android/internal/telephony/CallFailCause.java
@@ -24,33 +24,122 @@
* CDMA call failure reasons are derived from the possible call failure scenarios described
* in "CDMA IS2000 - Release A (C.S0005-A v6.0)" standard.
*
+ * The detailed fail causes are defined in ITU Recommendation Q.850.
+ *
* {@hide}
*
*/
public interface CallFailCause {
+ // The disconnect cause is not valid (Not received a disconnect cause)
+ int NOT_VALID = -1;
+
// Unassigned/Unobtainable number
int UNOBTAINABLE_NUMBER = 1;
+ int NO_ROUTE_TO_DEST = 3;
+ int CHANNEL_UNACCEPTABLE = 6;
int OPERATOR_DETERMINED_BARRING = 8;
int NORMAL_CLEARING = 16;
- // Busy Tone
int USER_BUSY = 17;
+ int NO_USER_RESPONDING = 18;
- // No Tone
+ /**
+ * This cause is used when the called party has been alerted but does not respond with a connect
+ * indication within a prescribed period of time. Note - This cause is not necessarily generated
+ * by Q.931 procedures but may be generated by internal network timers.
+ */
+ int USER_ALERTING_NO_ANSWER = 19;
+
+ /**
+ * The equipment sending this cause does not wish to accept this call, although it could have
+ * accepted the call because the equipment sending this cause is neither busy nor incompatible.
+ * The network may also generate this cause, indicating that the call was cleared due to a
+ * supplementary service constraint. The diagnostic field may contain additional information
+ * about the supplementary service and reason for rejection.
+ */
+ int CALL_REJECTED = 21;
+
int NUMBER_CHANGED = 22;
+ int PRE_EMPTION = 25;
+
+ // The user has not been awarded the incoming call.
+ int NON_SELECTED_USER_CLEARING = 26;
+
+ int DESTINATION_OUT_OF_ORDER = 27;
+
+ // Incomplete number
+ int INVALID_NUMBER_FORMAT = 28;
+
+ // Supplementary service requested by the user cannot be provide by the network.
+ int FACILITY_REJECTED = 29;
+
int STATUS_ENQUIRY = 30;
int NORMAL_UNSPECIFIED = 31;
-
- // Congestion Tone
int NO_CIRCUIT_AVAIL = 34;
+
+ // Resource unavailable
+ int NETWORK_OUT_OF_ORDER = 38;
int TEMPORARY_FAILURE = 41;
int SWITCHING_CONGESTION = 42;
+ int ACCESS_INFORMATION_DISCARDED = 43;
int CHANNEL_NOT_AVAIL = 44;
+ int RESOURCES_UNAVAILABLE_UNSPECIFIED = 47;
int QOS_NOT_AVAIL = 49;
- int BEARER_NOT_AVAIL = 58;
- // others
+ // Service or option unavailable
+ /**
+ * The user has requested a supplementary service, which is available, but the user is not
+ * authorized to use.
+ */
+ int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
+ /**
+ * Although the called party is a member of the CUG (Closed User Group) for the incoming CUG
+ * call, incoming calls are not allowed to this member of the CUG.
+ */
+ int INCOMING_CALL_BARRED_WITHIN_CUG = 55;
+ int BEARER_CAPABILITY_NOT_AUTHORISED = 57;
+ int BEARER_NOT_AVAIL = 58;
+ /**
+ * This cause is used to report a service or option not available event only when no other cause
+ * between 49-62 (where a service or option is unavailable) applies.
+ */
+ int SERVICE_OR_OPTION_NOT_AVAILABLE = 63;
+ int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
+
+ // Service or option not implemented
int ACM_LIMIT_EXCEEDED = 68;
+ int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
+ /**
+ * The calling party has requested an unrestricted bearer service but that the equipment sending
+ * this cause only supports the restricted version of the requested bearer capability.
+ */
+ int ONLY_RESTRICTED_DIGITAL_INFO_BC_AVAILABLE = 70;
+ int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79;
+ int INVALID_TRANSACTION_ID_VALUE = 81;
+
+ // Invalid message
+ int USER_NOT_MEMBER_OF_CUG = 87;
+ int INCOMPATIBLE_DESTINATION = 88;
+ int INVALID_TRANSIT_NETWORK_SELECTION = 91;
+ int SEMANTICALLY_INCORRECT_MESSAGE = 95;
+ int INVALID_MANDATORY_INFORMATION = 96;
+
+ // Protocol error
+ int MESSAGE_TYPE_NON_EXISTENT = 97;
+ int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROT_STATE = 98;
+ int IE_NON_EXISTENT_OR_NOT_IMPLEMENTED = 99;
+ /**
+ * The equipment sending this cause has received an information element which it has
+ * implemented; however, one or more fields in the information element are coded in such a way
+ * which has not been implemented by the equipment sending this cause.
+ */
+ int CONDITIONAL_IE_ERROR = 100;
+ int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101;
+ int RECOVERY_ON_TIMER_EXPIRY = 102;
+ int PROTOCOL_ERROR_UNSPECIFIED = 111;
+ int INTERWORKING_UNSPECIFIED = 127;
+
+ // Others
int CALL_BARRED = 240;
int FDN_BLOCKED = 241;
int IMEI_NOT_ACCEPTED = 243;
diff --git a/src/java/com/android/internal/telephony/CallForwardInfo.java b/src/java/com/android/internal/telephony/CallForwardInfo.java
index dccf306..e40028f 100644
--- a/src/java/com/android/internal/telephony/CallForwardInfo.java
+++ b/src/java/com/android/internal/telephony/CallForwardInfo.java
@@ -16,12 +16,16 @@
package com.android.internal.telephony;
+import android.telecom.Log;
+
/**
* See also RIL_CallForwardInfo in include/telephony/ril.h
*
* {@hide}
*/
public class CallForwardInfo {
+ private static final String TAG = "CallForwardInfo";
+
public int status; /*1 = active, 0 = not active */
public int reason; /* from TS 27.007 7.11 "reason" */
public int serviceClass; /* Saum of CommandsInterface.SERVICE_CLASS */
@@ -31,9 +35,9 @@
@Override
public String toString() {
- return super.toString() + (status == 0 ? " not active " : " active ")
- + " reason: " + reason
- + " serviceClass: " + serviceClass + " " + timeSeconds + " seconds";
-
+ return "[CallForwardInfo: status=" + (status == 0 ? " not active " : " active ")
+ + ", reason= " + reason
+ + ", serviceClass= " + serviceClass + ", timeSec= " + timeSeconds + " seconds"
+ + ", number=" + Log.pii(number) + "]";
}
}
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 2775fe6..b90629d 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -964,7 +964,8 @@
// FIXME Taken from klp-sprout-dev but setAudioMode was removed in L.
//mIsEccDialing = PhoneNumberUtils.isEmergencyNumber(dialString);
- result = phone.dial(dialString, videoState);
+ result = phone.dial(dialString, new PhoneInternalInterface.DialArgs.Builder<>()
+ .setVideoState(videoState).build());
if (VDBG) {
Rlog.d(LOG_TAG, "End dial(" + phone + ", "+ dialString + ")");
@@ -986,7 +987,10 @@
*/
public Connection dial(Phone phone, String dialString, UUSInfo uusInfo, int videoState)
throws CallStateException {
- return phone.dial(dialString, uusInfo, videoState, null);
+ return phone.dial(dialString,
+ new PhoneInternalInterface.DialArgs.Builder<>()
+ .setUusInfo(uusInfo)
+ .setVideoState(videoState).build());
}
/**
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 6b9a70a..4582404 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -26,6 +26,7 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
@@ -33,6 +34,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,6 +64,7 @@
public static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED = 5;
public static final int EVENT_DATA_ROAMING_OFF = 6;
public static final int EVENT_SIM_STATE_CHANGED = 7;
+ public static final int EVENT_APN_SETTINGS_CHANGED = 8;
/** Member variables */
private final Phone mPhone;
@@ -147,7 +150,7 @@
break;
case EVENT_MOBILE_DATA_SETTINGS_CHANGED:
log("EVENT_MOBILE_DATA_SETTINGS_CHANGED");
- if (!mPhone.getDataEnabled()) carrierActionReset();
+ if (!mPhone.isUserDataEnabled()) carrierActionReset();
break;
case EVENT_DATA_ROAMING_OFF:
log("EVENT_DATA_ROAMING_OFF");
@@ -168,6 +171,8 @@
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
EVENT_APM_SETTINGS_CHANGED);
+ mSettingsObserver.observe(
+ Telephony.Carriers.CONTENT_URI, EVENT_APN_SETTINGS_CHANGED);
if (mPhone.getServiceStateTracker() != null) {
mPhone.getServiceStateTracker().registerForDataRoamingOff(
this, EVENT_DATA_ROAMING_OFF, null, false);
@@ -181,6 +186,11 @@
}
}
break;
+ case EVENT_APN_SETTINGS_CHANGED:
+ log("EVENT_APN_SETTINGS_CHANGED");
+ // Reset carrier actions when APN change.
+ carrierActionReset();
+ break;
default:
loge("Unknown carrier action: " + msg.what);
}
diff --git a/src/java/com/android/internal/telephony/CarrierIdentifier.java b/src/java/com/android/internal/telephony/CarrierIdentifier.java
new file mode 100644
index 0000000..69131ca
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierIdentifier.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright 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;
+
+import static android.provider.Telephony.CarrierId;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Telephony;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * CarrierIdentifier identifies the subscription carrier and returns a canonical carrier Id
+ * and a user friendly carrier name. CarrierIdentifier reads subscription info and check against
+ * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a
+ * dedicated CarrierIdentifier.
+ */
+public class CarrierIdentifier extends Handler {
+ private static final String LOG_TAG = CarrierIdentifier.class.getSimpleName();
+ private static final boolean DBG = true;
+ private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+ // events to trigger carrier identification
+ private static final int SIM_LOAD_EVENT = 1;
+ private static final int SIM_ABSENT_EVENT = 2;
+ private static final int SPN_OVERRIDE_EVENT = 3;
+ private static final int ICC_CHANGED_EVENT = 4;
+ private static final int PREFER_APN_UPDATE_EVENT = 5;
+ private static final int CARRIER_ID_DB_UPDATE_EVENT = 6;
+
+ private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
+ Telephony.Carriers.CONTENT_URI, "preferapn");
+ private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
+
+ // cached matching rules based mccmnc to speed up resolution
+ private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
+ // cached carrier Id
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+ // cached carrier name
+ private String mCarrierName;
+ // cached preferapn name
+ private String mPreferApn;
+ // cached service provider name. telephonyManager API returns empty string as default value.
+ // some carriers need to target devices with Empty SPN. In that case, carrier matching rule
+ // should specify "" spn explicitly.
+ private String mSpn = "";
+
+ private Context mContext;
+ private Phone mPhone;
+ private IccRecords mIccRecords;
+ private UiccProfile mUiccProfile;
+ private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
+ private final TelephonyManager mTelephonyMgr;
+ private final SubscriptionsChangedListener mOnSubscriptionsChangedListener =
+ new SubscriptionsChangedListener();
+
+ private final ContentObserver mContentObserver = new ContentObserver(this) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (CONTENT_URL_PREFER_APN.equals(uri.getLastPathSegment())) {
+ logd("onChange URI: " + uri);
+ sendEmptyMessage(PREFER_APN_UPDATE_EVENT);
+ } else if (CarrierId.All.CONTENT_URI.equals(uri)) {
+ logd("onChange URI: " + uri);
+ sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT);
+ }
+ }
+ };
+
+ private class SubscriptionsChangedListener
+ extends SubscriptionManager.OnSubscriptionsChangedListener {
+ final AtomicInteger mPreviousSubId =
+ new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ /**
+ * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList}
+ */
+ @Override
+ public void onSubscriptionsChanged() {
+ int subId = mPhone.getSubId();
+ if (mPreviousSubId.getAndSet(subId) != subId) {
+ if (DBG) {
+ logd("SubscriptionListener.onSubscriptionInfoChanged subId: "
+ + mPreviousSubId);
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ sendEmptyMessage(SIM_LOAD_EVENT);
+ } else {
+ sendEmptyMessage(SIM_ABSENT_EVENT);
+ }
+ }
+ }
+ }
+
+ public CarrierIdentifier(Phone phone) {
+ logd("Creating CarrierIdentifier[" + phone.getPhoneId() + "]");
+ mContext = phone.getContext();
+ mPhone = phone;
+ mTelephonyMgr = TelephonyManager.from(mContext);
+
+ // register events
+ mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false,
+ mContentObserver);
+ mContext.getContentResolver().registerContentObserver(
+ CarrierId.All.CONTENT_URI, false, mContentObserver);
+ SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
+ }
+
+ /**
+ * Entry point for the carrier identification.
+ *
+ * 1. SIM_LOAD_EVENT
+ * This indicates that all SIM records has been loaded and its first entry point for the
+ * carrier identification. Note, there are other attributes could be changed on the fly
+ * like APN and SPN. We cached all carrier matching rules based on MCCMNC to speed
+ * up carrier resolution on following trigger events.
+ *
+ * 2. PREFER_APN_UPDATE_EVENT
+ * This indicates prefer apn has been changed. It could be triggered when user modified
+ * APN settings or when default data connection first establishes on the current carrier.
+ * We follow up on this by querying prefer apn sqlite and re-issue carrier identification
+ * with the updated prefer apn name.
+ *
+ * 3. SPN_OVERRIDE_EVENT
+ * This indicates that SPN value as been changed. It could be triggered from EF_SPN
+ * record loading, carrier config override
+ * {@link android.telephony.CarrierConfigManager#KEY_CARRIER_NAME_STRING}
+ * or carrier app override {@link TelephonyManager#setOperatorBrandOverride(String)}.
+ * we follow up this by checking the cached mSPN against the latest value and issue
+ * carrier identification only if spn changes.
+ *
+ * 4. CARRIER_ID_DB_UPDATE_EVENT
+ * This indicates that carrierIdentification database which stores all matching rules
+ * has been updated. It could be triggered from OTA or assets update.
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ if (VDBG) logd("handleMessage: " + msg.what);
+ switch (msg.what) {
+ case SIM_LOAD_EVENT:
+ case CARRIER_ID_DB_UPDATE_EVENT:
+ mSpn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
+ mPreferApn = getPreferApn();
+ loadCarrierMatchingRulesOnMccMnc();
+ break;
+ case SIM_ABSENT_EVENT:
+ mCarrierMatchingRulesOnMccMnc.clear();
+ mSpn = null;
+ mPreferApn = null;
+ updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
+ break;
+ case PREFER_APN_UPDATE_EVENT:
+ String preferApn = getPreferApn();
+ if (!equals(mPreferApn, preferApn, true)) {
+ logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
+ mPreferApn = preferApn;
+ matchCarrier();
+ }
+ break;
+ case SPN_OVERRIDE_EVENT:
+ String spn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
+ if (!equals(mSpn, spn, true)) {
+ logd("[updateSpn] from:" + mSpn + " to:" + spn);
+ mSpn = spn;
+ matchCarrier();
+ }
+ break;
+ case ICC_CHANGED_EVENT:
+ // all records used for carrier identification are from SimRecord
+ final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
+ mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
+ if (mIccRecords != newIccRecords) {
+ if (mIccRecords != null) {
+ logd("Removing stale icc objects.");
+ mIccRecords.unregisterForRecordsLoaded(this);
+ mIccRecords = null;
+ }
+ if (newIccRecords != null) {
+ logd("new Icc object");
+ newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
+ mIccRecords = newIccRecords;
+ }
+ }
+ // check UICC profile
+ final UiccProfile uiccProfile = UiccController.getInstance()
+ .getUiccProfileForPhone(mPhone.getPhoneId());
+ if (mUiccProfile != uiccProfile) {
+ if (mUiccProfile != null) {
+ logd("unregister operatorBrandOverride");
+ mUiccProfile.unregisterForOperatorBrandOverride(this);
+ mUiccProfile = null;
+ }
+ if (uiccProfile != null) {
+ logd("register operatorBrandOverride");
+ uiccProfile.registerForOpertorBrandOverride(this, SPN_OVERRIDE_EVENT, null);
+ mUiccProfile = uiccProfile;
+ }
+ }
+ break;
+ default:
+ loge("invalid msg: " + msg.what);
+ break;
+ }
+ }
+
+ private void loadCarrierMatchingRulesOnMccMnc() {
+ try {
+ String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
+ Cursor cursor = mContext.getContentResolver().query(
+ CarrierId.All.CONTENT_URI,
+ /* projection */ null,
+ /* selection */ CarrierId.All.MCCMNC + "=?",
+ /* selectionArgs */ new String[]{mccmnc}, null);
+ try {
+ if (cursor != null) {
+ if (VDBG) {
+ logd("[loadCarrierMatchingRules]- " + cursor.getCount()
+ + " Records(s) in DB" + " mccmnc: " + mccmnc);
+ }
+ mCarrierMatchingRulesOnMccMnc.clear();
+ while (cursor.moveToNext()) {
+ mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
+ }
+ matchCarrier();
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ } catch (Exception ex) {
+ loge("[loadCarrierMatchingRules]- ex: " + ex);
+ }
+ }
+
+ private String getPreferApn() {
+ Cursor cursor = mContext.getContentResolver().query(
+ Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
+ + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN},
+ /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
+ try {
+ if (cursor != null) {
+ if (VDBG) {
+ logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB");
+ }
+ while (cursor.moveToNext()) {
+ String apn = cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.APN));
+ logd("[getPreferApn]- " + apn);
+ return apn;
+ }
+ }
+ } catch (Exception ex) {
+ loge("[getPreferApn]- exception: " + ex);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+ private void updateCarrierIdAndName(int cid, String name) {
+ boolean update = false;
+ if (!equals(name, mCarrierName, true)) {
+ logd("[updateCarrierName] from:" + mCarrierName + " to:" + name);
+ mCarrierName = name;
+ update = true;
+ }
+ if (cid != mCarrierId) {
+ logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid);
+ mCarrierId = cid;
+ update = true;
+ }
+ if (update) {
+ mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:"
+ + mCarrierName);
+ final Intent intent = new Intent(TelephonyManager
+ .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
+ intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
+ intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
+ mContext.sendBroadcast(intent);
+
+ // update current subscriptions
+ ContentValues cv = new ContentValues();
+ cv.put(CarrierId.CARRIER_ID, mCarrierId);
+ cv.put(CarrierId.CARRIER_NAME, mCarrierName);
+ mContext.getContentResolver().update(
+ Uri.withAppendedPath(CarrierId.CONTENT_URI,
+ Integer.toString(mPhone.getSubId())), cv, null, null);
+ }
+ }
+
+ private CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
+ return new CarrierMatchingRule(
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ CarrierId.All.IMSI_PREFIX_XPATTERN)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ CarrierId.All.ICCID_PREFIX)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)));
+ }
+
+ /**
+ * carrier matching attributes with corresponding cid
+ */
+ private static class CarrierMatchingRule {
+ /**
+ * These scores provide the hierarchical relationship between the attributes, intended to
+ * resolve conflicts in a deterministic way. The scores are constructed such that a match
+ * from a higher tier will beat any subsequent match which does not match at that tier,
+ * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule
+ * matches as the score helps to find the best match uniquely. e.g.,
+ * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all
+ * matches with subscription data. rule 2 wins with the highest matching score.
+ */
+ private static final int SCORE_MCCMNC = 1 << 7;
+ private static final int SCORE_IMSI_PREFIX = 1 << 6;
+ private static final int SCORE_ICCID_PREFIX = 1 << 5;
+ private static final int SCORE_GID1 = 1 << 4;
+ private static final int SCORE_GID2 = 1 << 3;
+ private static final int SCORE_PLMN = 1 << 2;
+ private static final int SCORE_SPN = 1 << 1;
+ private static final int SCORE_APN = 1 << 0;
+
+ private static final int SCORE_INVALID = -1;
+
+ // carrier matching attributes
+ private String mMccMnc;
+ private String mImsiPrefixPattern;
+ private String mIccidPrefix;
+ private String mGid1;
+ private String mGid2;
+ private String mPlmn;
+ private String mSpn;
+ private String mApn;
+
+ // user-facing carrier name
+ private String mName;
+ // unique carrier id
+ private int mCid;
+
+ private int mScore = 0;
+
+ CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
+ String gid1, String gid2, String plmn, String spn, String apn, int cid,
+ String name) {
+ mMccMnc = mccmnc;
+ mImsiPrefixPattern = imsiPrefixPattern;
+ mIccidPrefix = iccidPrefix;
+ mGid1 = gid1;
+ mGid2 = gid2;
+ mPlmn = plmn;
+ mSpn = spn;
+ mApn = apn;
+ mCid = cid;
+ mName = name;
+ }
+
+ // Calculate matching score. Values which aren't set in the rule are considered "wild".
+ // All values in the rule must match in order for the subscription to be considered part of
+ // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier
+ // will beat any subsequent match which does not match at that tier. When there are multiple
+ // matches at the same tier, the match with highest score will be used.
+ public void match(CarrierMatchingRule subscriptionRule) {
+ mScore = 0;
+ if (mMccMnc != null) {
+ if (!CarrierIdentifier.equals(subscriptionRule.mMccMnc, mMccMnc, false)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_MCCMNC;
+ }
+ if (mImsiPrefixPattern != null) {
+ if (!imsiPrefixMatch(subscriptionRule.mImsiPrefixPattern, mImsiPrefixPattern)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_IMSI_PREFIX;
+ }
+ if (mIccidPrefix != null) {
+ if (!iccidPrefixMatch(subscriptionRule.mIccidPrefix, mIccidPrefix)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_ICCID_PREFIX;
+ }
+ if (mGid1 != null) {
+ // full string match. carrier matching should cover the corner case that gid1
+ // with garbage tail due to SIM manufacture issues.
+ if (!CarrierIdentifier.equals(subscriptionRule.mGid1, mGid1, true)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_GID1;
+ }
+ if (mGid2 != null) {
+ // full string match. carrier matching should cover the corner case that gid2
+ // with garbage tail due to SIM manufacture issues.
+ if (!CarrierIdentifier.equals(subscriptionRule.mGid2, mGid2, true)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_GID2;
+ }
+ if (mPlmn != null) {
+ if (!CarrierIdentifier.equals(subscriptionRule.mPlmn, mPlmn, true)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_PLMN;
+ }
+ if (mSpn != null) {
+ if (!CarrierIdentifier.equals(subscriptionRule.mSpn, mSpn, true)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_SPN;
+ }
+ if (mApn != null) {
+ if (!CarrierIdentifier.equals(subscriptionRule.mApn, mApn, true)) {
+ mScore = SCORE_INVALID;
+ return;
+ }
+ mScore += SCORE_APN;
+ }
+ }
+
+ private boolean imsiPrefixMatch(String imsi, String prefixXPattern) {
+ if (TextUtils.isEmpty(prefixXPattern)) return true;
+ if (TextUtils.isEmpty(imsi)) return false;
+ if (imsi.length() < prefixXPattern.length()) {
+ return false;
+ }
+ for (int i = 0; i < prefixXPattern.length(); i++) {
+ if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X')
+ && (prefixXPattern.charAt(i) != imsi.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean iccidPrefixMatch(String iccid, String prefix) {
+ if (iccid == null || prefix == null) {
+ return false;
+ }
+ return iccid.startsWith(prefix);
+ }
+
+ public String toString() {
+ return "[CarrierMatchingRule] -"
+ + " mccmnc: " + mMccMnc
+ + " gid1: " + mGid1
+ + " gid2: " + mGid2
+ + " plmn: " + mPlmn
+ + " imsi_prefix: " + mImsiPrefixPattern
+ + " iccid_prefix" + mIccidPrefix
+ + " spn: " + mSpn
+ + " apn: " + mApn
+ + " name: " + mName
+ + " cid: " + mCid
+ + " score: " + mScore;
+ }
+ }
+
+ /**
+ * find the best matching carrier from candidates with matched MCCMNC and notify
+ * all interested parties on carrier id change.
+ */
+ private void matchCarrier() {
+ if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
+ logd("[matchCarrier]" + "skip before sim records loaded");
+ return;
+ }
+ final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
+ final String iccid = mPhone.getIccSerialNumber();
+ final String gid1 = mPhone.getGroupIdLevel1();
+ final String gid2 = mPhone.getGroupIdLevel2();
+ final String imsi = mPhone.getSubscriberId();
+ final String plmn = mPhone.getPlmn();
+ final String spn = mSpn;
+ final String apn = mPreferApn;
+
+ if (VDBG) {
+ logd("[matchCarrier]"
+ + " mnnmnc:" + mccmnc
+ + " gid1: " + gid1
+ + " gid2: " + gid2
+ + " imsi: " + Rlog.pii(LOG_TAG, imsi)
+ + " iccid: " + Rlog.pii(LOG_TAG, iccid)
+ + " plmn: " + plmn
+ + " spn: " + spn
+ + " apn: " + apn);
+ }
+
+ CarrierMatchingRule subscriptionRule = new CarrierMatchingRule(
+ mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn,
+ TelephonyManager.UNKNOWN_CARRIER_ID, null);
+
+ int maxScore = CarrierMatchingRule.SCORE_INVALID;
+ CarrierMatchingRule maxRule = null;
+
+ for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
+ rule.match(subscriptionRule);
+ if (rule.mScore > maxScore) {
+ maxScore = rule.mScore;
+ maxRule = rule;
+ }
+ }
+
+ if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
+ logd("[matchCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
+ + " name: " + null);
+ updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
+ } else {
+ logd("[matchCarrier] cid: " + maxRule.mCid + " name: " + maxRule.mName);
+ updateCarrierIdAndName(maxRule.mCid, maxRule.mName);
+ }
+
+ /*
+ * Write Carrier Identification Matching event, logging with the
+ * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
+ * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
+ * read mccmnc.
+ * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
+ * but the read gid1 is not matched within the highest-scored rule.
+ * 3) successfully found a matched carrier id in the provider.
+ * 4) use carrier list version to compare the unknown carrier ratio between each version.
+ */
+ String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
+ && !TextUtils.isEmpty(subscriptionRule.mGid1)) ? subscriptionRule.mGid1 : null;
+ String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
+ || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
+ && !TextUtils.isEmpty(subscriptionRule.mMccMnc)) ? subscriptionRule.mMccMnc : null;
+ TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
+ mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
+ unknownMccmncToLog, unknownGid1ToLog);
+ }
+
+ public int getCarrierListVersion() {
+ final Cursor cursor = mContext.getContentResolver().query(
+ Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
+ "get_version"), null, null, null);
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ }
+
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ public String getCarrierName() {
+ return mCarrierName;
+ }
+
+ private static boolean equals(String a, String b, boolean ignoreCase) {
+ if (a == null && b == null) return true;
+ if (a != null && b != null) {
+ return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b);
+ }
+ return false;
+ }
+
+ private static void logd(String str) {
+ Rlog.d(LOG_TAG, str);
+ }
+ private static void loge(String str) {
+ Rlog.e(LOG_TAG, str);
+ }
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("mCarrierIdLocalLogs:");
+ ipw.increaseIndent();
+ mCarrierIdLocalLog.dump(fd, pw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("mCarrierId: " + mCarrierId);
+ ipw.println("mCarrierName: " + mCarrierName);
+ ipw.println("version: " + getCarrierListVersion());
+
+ ipw.println("mCarrierMatchingRules on mccmnc: "
+ + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()));
+ ipw.increaseIndent();
+ for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
+ ipw.println(rule.toString());
+ }
+ ipw.decreaseIndent();
+
+ ipw.println("mSpn: " + mSpn);
+ ipw.println("mPreferApn: " + mPreferApn);
+ ipw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 96dd8c4..6a6a065 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -16,26 +16,145 @@
package com.android.internal.telephony;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.os.UserHandle;
+import android.provider.Telephony;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
- /**
+import java.util.Date;
+
+/**
* This class provides methods to retreive information from the CarrierKeyProvider.
*/
public class CarrierInfoManager {
- private static final String TAG = "CarrierInfoManager";
+ private static final String LOG_TAG = "CarrierInfoManager";
+ private static final String KEY_TYPE = "KEY_TYPE";
+
+ /*
+ * Rate limit (in milliseconds) the number of times the Carrier keys can be reset.
+ * Do it at most once every 12 hours.
+ */
+ private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000;
+
+ // Last time the resetCarrierKeysForImsiEncryption API was called successfully.
+ private long mLastAccessResetCarrierKey = 0;
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
- * @return ImsiEncryptionInfo which contains the information including the public key to be
+ * @param mContext
+ * @return ImsiEncryptionInfo which contains the information, including the public key, to be
* used for encryption.
*/
- public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
- //TODO implementation will be done in subsequent CL.
+ public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
+ Context mContext) {
+ String mcc = "";
+ String mnc = "";
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ String networkOperator = telephonyManager.getNetworkOperator();
+ if (!TextUtils.isEmpty(networkOperator)) {
+ mcc = networkOperator.substring(0, 3);
+ mnc = networkOperator.substring(3);
+ Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc);
+ } else {
+ Log.e(LOG_TAG, "Invalid networkOperator: " + networkOperator);
+ return null;
+ }
+ Cursor findCursor = null;
+ try {
+ // In the current design, MVNOs are not supported. If we decide to support them,
+ // we'll need to add to this CL.
+ ContentResolver mContentResolver = mContext.getContentResolver();
+ String[] columns = {Telephony.CarrierColumns.PUBLIC_KEY,
+ Telephony.CarrierColumns.EXPIRATION_TIME,
+ Telephony.CarrierColumns.KEY_IDENTIFIER};
+ findCursor = mContentResolver.query(Telephony.CarrierColumns.CONTENT_URI, columns,
+ "mcc=? and mnc=? and key_type=?",
+ new String[]{mcc, mnc, String.valueOf(keyType)}, null);
+ if (findCursor == null || !findCursor.moveToFirst()) {
+ Log.d(LOG_TAG, "No rows found for keyType: " + keyType);
+ return null;
+ }
+ if (findCursor.getCount() > 1) {
+ Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType);
+ }
+ byte[] carrier_key = findCursor.getBlob(0);
+ Date expirationTime = new Date(findCursor.getLong(1));
+ String keyIdentifier = findCursor.getString(2);
+ return new ImsiEncryptionInfo(mcc, mnc, keyType, keyIdentifier, carrier_key,
+ expirationTime);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Bad arguments:" + e);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Query failed:" + e);
+ } finally {
+ if (findCursor != null) {
+ findCursor.close();
+ }
+ }
return null;
}
/**
+ * Inserts or update the Carrier Key in the database
+ * @param imsiEncryptionInfo ImsiEncryptionInfo object.
+ * @param mContext Context.
+ */
+ public static void updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo,
+ Context mContext) {
+ byte[] keyBytes = imsiEncryptionInfo.getPublicKey().getEncoded();
+ ContentResolver mContentResolver = mContext.getContentResolver();
+ // In the current design, MVNOs are not supported. If we decide to support them,
+ // we'll need to add to this CL.
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Telephony.CarrierColumns.MCC, imsiEncryptionInfo.getMcc());
+ contentValues.put(Telephony.CarrierColumns.MNC, imsiEncryptionInfo.getMnc());
+ contentValues.put(Telephony.CarrierColumns.KEY_TYPE,
+ imsiEncryptionInfo.getKeyType());
+ contentValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
+ imsiEncryptionInfo.getKeyIdentifier());
+ contentValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
+ contentValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
+ imsiEncryptionInfo.getExpirationTime().getTime());
+ try {
+ Log.i(LOG_TAG, "Inserting imsiEncryptionInfo into db");
+ mContentResolver.insert(Telephony.CarrierColumns.CONTENT_URI, contentValues);
+ } catch (SQLiteConstraintException e) {
+ Log.i(LOG_TAG, "Insert failed, updating imsiEncryptionInfo into db");
+ ContentValues updatedValues = new ContentValues();
+ updatedValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
+ updatedValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
+ imsiEncryptionInfo.getExpirationTime().getTime());
+ updatedValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
+ imsiEncryptionInfo.getKeyIdentifier());
+ try {
+ int nRows = mContentResolver.update(Telephony.CarrierColumns.CONTENT_URI,
+ updatedValues,
+ "mcc=? and mnc=? and key_type=?", new String[]{
+ imsiEncryptionInfo.getMcc(),
+ imsiEncryptionInfo.getMnc(),
+ String.valueOf(imsiEncryptionInfo.getKeyType())});
+ if (nRows == 0) {
+ Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo);
+ }
+ } catch (Exception ex) {
+ Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo + ex);
+ }
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Error inserting/updating values:" + imsiEncryptionInfo + e);
+ }
+ }
+
+ /**
* Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
* This includes the public key and the key identifier. This information will be stored in the
* device keystore.
@@ -43,10 +162,77 @@
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier Attribute value pair that helps a server locate
* the private key to decrypt the permanent identity.
+ * @param mContext Context.
*/
- public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
- //TODO implementation will be done in subsequent CL.
- return;
+ public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+ Context mContext) {
+ Log.i(LOG_TAG, "inserting carrier key: " + imsiEncryptionInfo);
+ updateOrInsertCarrierKey(imsiEncryptionInfo, mContext);
+ //todo send key to modem. Will be done in a subsequent CL.
+ }
+
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * @param context Context
+ * @param mPhoneId phoneId
+ *
+ */
+ public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) {
+ Log.i(LOG_TAG, "resetting carrier key");
+ // Check rate limit.
+ long now = System.currentTimeMillis();
+ if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) {
+ Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded");
+ return;
+ }
+ mLastAccessResetCarrierKey = now;
+ deleteCarrierInfoForImsiEncryption(context);
+ Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
+ resetIntent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId);
+ context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
+ }
+
+ /**
+ * Deletes all the keys for a given Carrier from the device keystore.
+ * @param context Context
+ */
+ public static void deleteCarrierInfoForImsiEncryption(Context context) {
+ Log.i(LOG_TAG, "deleting carrier key from db");
+ String mcc = "";
+ String mnc = "";
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String simOperator = telephonyManager.getSimOperator();
+ if (!TextUtils.isEmpty(simOperator)) {
+ mcc = simOperator.substring(0, 3);
+ mnc = simOperator.substring(3);
+ } else {
+ Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator);
+ return;
+ }
+ ContentResolver mContentResolver = context.getContentResolver();
+ try {
+ String whereClause = "mcc=? and mnc=?";
+ String[] whereArgs = new String[] { mcc, mnc };
+ mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, whereClause, whereArgs);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Delete failed" + e);
+ }
+ }
+
+ /**
+ * Deletes all the keys from the device keystore.
+ * @param context Context
+ */
+ public static void deleteAllCarrierKeysForImsiEncryption(Context context) {
+ Log.i(LOG_TAG, "deleting ALL carrier keys from db");
+ ContentResolver mContentResolver = context.getContentResolver();
+ try {
+ mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, null, null);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Delete failed" + e);
+ }
}
}
-
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
new file mode 100644
index 0000000..1513a33
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -0,0 +1,573 @@
+/*
+ * 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;
+
+import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.app.AlarmManager;
+import android.app.DownloadManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.org.bouncycastle.util.io.pem.PemReader;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * This class contains logic to get Certificates and keep them current.
+ * The class will be instantiated by various Phone implementations.
+ */
+public class CarrierKeyDownloadManager {
+ private static final String LOG_TAG = "CarrierKeyDownloadManager";
+
+ private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC";
+
+ private static final int DAY_IN_MILLIS = 24 * 3600 * 1000;
+
+ // Create a window prior to the key expiration, during which the cert will be
+ // downloaded. Defines the start date of that window. So if the key expires on
+ // Dec 21st, the start of the renewal window will be Dec 1st.
+ private static final int START_RENEWAL_WINDOW_DAYS = 21;
+
+ // This will define the end date of the window.
+ private static final int END_RENEWAL_WINDOW_DAYS = 7;
+
+
+
+ /* Intent for downloading the public key */
+ private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX =
+ "com.android.internal.telephony.carrier_key_download_alarm";
+
+ @VisibleForTesting
+ public int mKeyAvailability = 0;
+
+ public static final String MNC = "MNC";
+ public static final String MCC = "MCC";
+ private static final String SEPARATOR = ":";
+
+ private static final String JSON_CERTIFICATE = "certificate";
+ // This is a hack to accommodate certain Carriers who insists on using the public-key
+ // field to store the certificate. We'll just use which-ever is not null.
+ private static final String JSON_CERTIFICATE_ALTERNATE = "public-key";
+ private static final String JSON_TYPE = "key-type";
+ private static final String JSON_IDENTIFIER = "key-identifier";
+ private static final String JSON_CARRIER_KEYS = "carrier-keys";
+ private static final String JSON_TYPE_VALUE_WLAN = "WLAN";
+ private static final String JSON_TYPE_VALUE_EPDG = "EPDG";
+
+
+ private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG,
+ TelephonyManager.KEY_TYPE_WLAN};
+ private static final int UNINITIALIZED_KEY_TYPE = -1;
+
+ private final Phone mPhone;
+ private final Context mContext;
+ public final DownloadManager mDownloadManager;
+ private String mURL;
+
+ public CarrierKeyDownloadManager(Phone phone) {
+ mPhone = phone;
+ mContext = phone.getContext();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId());
+ filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
+ mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
+ mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ int slotId = mPhone.getPhoneId();
+ if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) {
+ Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
+ handleAlarmOrConfigChange();
+ } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) {
+ if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ Log.d(LOG_TAG, "Handling reset intent: " + action);
+ handleAlarmOrConfigChange();
+ }
+ } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+ if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ Log.d(LOG_TAG, "Carrier Config changed: " + action);
+ handleAlarmOrConfigChange();
+ }
+ } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
+ Log.d(LOG_TAG, "Download Complete");
+ long carrierKeyDownloadIdentifier =
+ intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
+ String mccMnc = getMccMncSetFromPref();
+ if (isValidDownload(mccMnc)) {
+ onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc);
+ onPostDownloadProcessing(carrierKeyDownloadIdentifier);
+ }
+ }
+ }
+ };
+
+ private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) {
+ resetRenewalAlarm();
+ cleanupDownloadPreferences(carrierKeyDownloadIdentifier);
+ }
+
+ private void handleAlarmOrConfigChange() {
+ if (carrierUsesKeys()) {
+ if (areCarrierKeysAbsentOrExpiring()) {
+ boolean downloadStartedSuccessfully = downloadKey();
+ // if the download was attemped, but not started successfully, and if carriers uses
+ // keys, we'll still want to renew the alarms, and try downloading the key a day
+ // later.
+ if (!downloadStartedSuccessfully) {
+ resetRenewalAlarm();
+ }
+ } else {
+ return;
+ }
+ } else {
+ // delete any existing alarms.
+ cleanupRenewalAlarms();
+ }
+ }
+
+ private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) {
+ Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier);
+ SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
+ editor.remove(String.valueOf(carrierKeyDownloadIdentifier));
+ editor.commit();
+ }
+
+ private void cleanupRenewalAlarms() {
+ Log.d(LOG_TAG, "Cleaning up existing renewal alarms");
+ int slotId = mPhone.getPhoneId();
+ Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
+ PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ AlarmManager alarmManager =
+ (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE);
+ alarmManager.cancel(carrierKeyDownloadIntent);
+ }
+
+ /**
+ * this method returns the date to be used to decide on when to start downloading the key.
+ * from the carrier.
+ **/
+ @VisibleForTesting
+ public long getExpirationDate() {
+ long minExpirationDate = Long.MAX_VALUE;
+ for (int key_type : CARRIER_KEY_TYPES) {
+ if (!isKeyEnabled(key_type)) {
+ continue;
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo =
+ mPhone.getCarrierInfoForImsiEncryption(key_type);
+ if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) {
+ if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) {
+ minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime();
+ }
+ }
+ }
+
+ // if there are no keys, or expiration date is in the past, or within 7 days, then we
+ // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to
+ // expiration.
+ if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate
+ < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) {
+ minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS;
+ } else {
+ // We don't want all the phones to download the certs simultaneously, so
+ // we pick a random time during the download window to avoid this situation.
+ Random random = new Random();
+ int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
+ int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
+ int randomTime = random.nextInt(max - min) + min;
+ minExpirationDate = minExpirationDate - randomTime;
+ }
+ return minExpirationDate;
+ }
+
+ /**
+ * this method resets the alarm. Starts by cleaning up the existing alarms.
+ * We look at the earliest expiration date, and setup an alarms X days prior.
+ * If the expiration date is in the past, we'll setup an alarm to run the next day. This
+ * could happen if the download has failed.
+ **/
+ @VisibleForTesting
+ public void resetRenewalAlarm() {
+ cleanupRenewalAlarms();
+ int slotId = mPhone.getPhoneId();
+ long minExpirationDate = getExpirationDate();
+ Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate));
+ final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
+ Context.ALARM_SERVICE);
+ Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
+ PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, minExpirationDate,
+ carrierKeyDownloadIntent);
+ Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time="
+ + new Date(minExpirationDate));
+ }
+
+ private String getMccMncSetFromPref() {
+ // check if this is a download that we had created. We do this by checking if the
+ // downloadId is stored in the shared prefs.
+ int slotId = mPhone.getPhoneId();
+ SharedPreferences preferences = getDefaultSharedPreferences(mContext);
+ return preferences.getString(MCC_MNC_PREF_TAG + slotId, null);
+ }
+
+ /**
+ * Returns the sim operator.
+ **/
+ @VisibleForTesting
+ public String getSimOperator() {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.getSimOperator(mPhone.getSubId());
+ }
+
+ /**
+ * checks if the download was sent by this particular instance. We do this by including the
+ * slot id in the key. If no value is found, we know that the download was not for this
+ * instance of the phone.
+ **/
+ @VisibleForTesting
+ public boolean isValidDownload(String mccMnc) {
+ String mccCurrent = "";
+ String mncCurrent = "";
+ String mccSource = "";
+ String mncSource = "";
+
+ String simOperator = getSimOperator();
+ if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) {
+ Log.e(LOG_TAG, "simOperator or mcc/mnc is empty");
+ return false;
+ }
+
+ String[] splitValue = mccMnc.split(SEPARATOR);
+ mccSource = splitValue[0];
+ mncSource = splitValue[1];
+ Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource);
+
+ mccCurrent = simOperator.substring(0, 3);
+ mncCurrent = simOperator.substring(3);
+ Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent);
+
+ if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method will try to parse the downloaded information, and persist it in the database.
+ **/
+ private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) {
+ Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier);
+ String jsonStr;
+ DownloadManager.Query query = new DownloadManager.Query();
+ query.setFilterById(carrierKeyDownloadIdentifier);
+ Cursor cursor = mDownloadManager.query(query);
+ InputStream source = null;
+
+ if (cursor == null) {
+ return;
+ }
+ if (cursor.moveToFirst()) {
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
+ try {
+ source = new FileInputStream(
+ mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier)
+ .getFileDescriptor());
+ jsonStr = convertToString(source);
+ parseJsonAndPersistKey(jsonStr, mccMnc);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier
+ + ". " + e);
+ } finally {
+ mDownloadManager.remove(carrierKeyDownloadIdentifier);
+ try {
+ source.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ Log.d(LOG_TAG, "Completed downloading keys");
+ }
+ cursor.close();
+ return;
+ }
+
+ /**
+ * This method checks if the carrier requires key. We'll read the carrier config to make that
+ * determination.
+ * @return boolean returns true if carrier requires keys, else false.
+ **/
+ private boolean carrierUsesKeys() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager == null) {
+ return false;
+ }
+ int subId = mPhone.getSubId();
+ PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
+ if (b == null) {
+ return false;
+ }
+ mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
+ mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
+ if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) {
+ Log.d(LOG_TAG, "Carrier not enabled or invalid values");
+ return false;
+ }
+ for (int key_type : CARRIER_KEY_TYPES) {
+ if (isKeyEnabled(key_type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String convertToString(InputStream is) {
+ try {
+ // The current implementation at certain Carriers has the data gzipped, which requires
+ // us to unzip the contents. Longer term, we want to add a flag in carrier config which
+ // determines if the data needs to be zipped or not.
+ GZIPInputStream gunzip = new GZIPInputStream(is);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8));
+ StringBuilder sb = new StringBuilder();
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append('\n');
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes,
+ * including the Carrier public key, the key type and the key identifier. Once the nodes have
+ * been extracted, they get persisted to the database. Sample:
+ * "carrier-keys": [ { "certificate": "",
+ * "key-type": "WLAN",
+ * "key-identifier": ""
+ * } ]
+ * @param jsonStr the json string.
+ * @param mccMnc contains the mcc, mnc.
+ */
+ @VisibleForTesting
+ public void parseJsonAndPersistKey(String jsonStr, String mccMnc) {
+ if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) {
+ Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty");
+ return;
+ }
+ PemReader reader = null;
+ try {
+ String mcc = "";
+ String mnc = "";
+ String[] splitValue = mccMnc.split(SEPARATOR);
+ mcc = splitValue[0];
+ mnc = splitValue[1];
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS);
+ for (int i = 0; i < keys.length(); i++) {
+ JSONObject key = keys.getJSONObject(i);
+ // This is a hack to accommodate certain carriers who insist on using the public-key
+ // field to store the certificate. We'll just use which-ever is not null.
+ String cert = null;
+ if (key.has(JSON_CERTIFICATE)) {
+ cert = key.getString(JSON_CERTIFICATE);
+ } else {
+ cert = key.getString(JSON_CERTIFICATE_ALTERNATE);
+ }
+ String typeString = key.getString(JSON_TYPE);
+ int type = UNINITIALIZED_KEY_TYPE;
+ if (typeString.equals(JSON_TYPE_VALUE_WLAN)) {
+ type = TelephonyManager.KEY_TYPE_WLAN;
+ } else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) {
+ type = TelephonyManager.KEY_TYPE_EPDG;
+ }
+ String identifier = key.getString(JSON_IDENTIFIER);
+ ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes());
+ Reader fReader = new BufferedReader(new InputStreamReader(inStream));
+ reader = new PemReader(fReader);
+ Pair<PublicKey, Long> keyInfo =
+ getKeyInformation(reader.readPemObject().getContent());
+ reader.close();
+ savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc);
+ }
+ } catch (final JSONException e) {
+ Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ }
+ }
+ }
+
+ /**
+ * introspects the mKeyAvailability bitmask
+ * @return true if the digit at position k is 1, else false.
+ */
+ @VisibleForTesting
+ public boolean isKeyEnabled(int keyType) {
+ //since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
+ int returnValue = (mKeyAvailability >> (keyType - 1)) & 1;
+ return (returnValue == 1) ? true : false;
+ }
+
+ /**
+ * Checks whether is the keys are absent or close to expiration. Returns true, if either of
+ * those conditions are true.
+ * @return boolean returns true when keys are absent or close to expiration, else false.
+ */
+ @VisibleForTesting
+ public boolean areCarrierKeysAbsentOrExpiring() {
+ for (int key_type : CARRIER_KEY_TYPES) {
+ if (!isKeyEnabled(key_type)) {
+ continue;
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo =
+ mPhone.getCarrierInfoForImsiEncryption(key_type);
+ if (imsiEncryptionInfo == null) {
+ Log.d(LOG_TAG, "Key not found for: " + key_type);
+ return true;
+ }
+ Date imsiDate = imsiEncryptionInfo.getExpirationTime();
+ long timeToExpire = imsiDate.getTime() - System.currentTimeMillis();
+ return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false;
+ }
+ return false;
+ }
+
+ private boolean downloadKey() {
+ Log.d(LOG_TAG, "starting download from: " + mURL);
+ String mcc = "";
+ String mnc = "";
+ String simOperator = getSimOperator();
+
+ if (!TextUtils.isEmpty(simOperator)) {
+ mcc = simOperator.substring(0, 3);
+ mnc = simOperator.substring(3);
+ Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
+ } else {
+ Log.e(LOG_TAG, "mcc, mnc: is empty");
+ return false;
+ }
+ try {
+ DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
+ request.setAllowedOverMetered(false);
+ request.setVisibleInDownloadsUi(false);
+ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
+ Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
+ SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
+
+ String mccMnc = mcc + SEPARATOR + mnc;
+ int slotId = mPhone.getPhoneId();
+ Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc
+ + "," + carrierKeyDownloadRequestId);
+ editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc);
+ editor.commit();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Save the public key
+ * @param certificate certificate that contains the public key.
+ * @return Pair containing the Public Key and the expiration date.
+ **/
+ @VisibleForTesting
+ public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception {
+ InputStream inStream = new ByteArrayInputStream(certificate);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
+ Pair<PublicKey, Long> keyInformation =
+ new Pair(cert.getPublicKey(), cert.getNotAfter().getTime());
+ return keyInformation;
+ }
+
+ /**
+ * Save the public key
+ * @param publicKey public key.
+ * @param type key-type.
+ * @param identifier which is an opaque string.
+ * @param expirationDate expiration date of the key.
+ * @param mcc
+ * @param mnc
+ **/
+ @VisibleForTesting
+ public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate,
+ String mcc, String mnc) {
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier,
+ publicKey, new Date(expirationDate));
+ mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index ab010cc..d2f0a16 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -123,7 +123,7 @@
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return;
}
- if (TextUtils.isEmpty(simState)) return;
+ if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.length) return;
if (simState.equals(mLastSimState[phoneId])) {
// ignore consecutive duplicated events
return;
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 700dfe0..2f57838 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -16,13 +16,14 @@
package com.android.internal.telephony;
-import android.app.PendingIntent;
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.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -30,9 +31,17 @@
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.NotificationChannelController;
+import java.util.HashMap;
+import java.util.Map;
+
+
+
/**
* This contains Carrier specific logic based on the states/events
* managed in ServiceStateTracker.
@@ -45,19 +54,82 @@
protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2;
protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3;
protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4;
- private static final int SHOW_NOTIFICATION = 200;
- private static final int NOTIFICATION_ID = 1000;
+ protected static final int CARRIER_EVENT_IMS_CAPABILITIES_CHANGED = CARRIER_EVENT_BASE + 5;
+
private static final int UNINITIALIZED_DELAY_VALUE = -1;
- private int mDelay = UNINITIALIZED_DELAY_VALUE;
private Phone mPhone;
- private boolean mIsPhoneRegistered = false;
private ServiceStateTracker mSST;
+ private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
+ private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ public static final int NOTIFICATION_PREF_NETWORK = 1000;
+ public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;
public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
this.mPhone = phone;
this.mSST = sst;
phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ // Listen for subscriber changes
+ SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
+ new OnSubscriptionsChangedListener(this.getLooper()) {
+ @Override
+ public void onSubscriptionsChanged() {
+ int subId = mPhone.getSubId();
+ if (mPreviousSubId != subId) {
+ mPreviousSubId = subId;
+ registerPrefNetworkModeObserver();
+ }
+ }
+ });
+
+ registerNotificationTypes();
+ registerPrefNetworkModeObserver();
+ }
+
+ private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) {
+ @Override
+ public void onChange(boolean selfChange) {
+ handlePrefNetworkModeChanged();
+ }
+ };
+
+ /**
+ * Return preferred network mode observer
+ */
+ @VisibleForTesting
+ public ContentObserver getContentObserver() {
+ return mPrefNetworkModeObserver;
+ }
+
+ private void registerPrefNetworkModeObserver() {
+ int subId = mPhone.getSubId();
+ unregisterPrefNetworkModeObserver();
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mPhone.getContext().getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId),
+ true,
+ mPrefNetworkModeObserver);
+ }
+ }
+
+ private void unregisterPrefNetworkModeObserver() {
+ mPhone.getContext().getContentResolver().unregisterContentObserver(
+ mPrefNetworkModeObserver);
+ }
+
+ /**
+ * Returns mNotificationTypeMap
+ */
+ @VisibleForTesting
+ public Map<Integer, NotificationType> getNotificationTypeMap() {
+ return mNotificationTypeMap;
+ }
+
+ private void registerNotificationTypes() {
+ mNotificationTypeMap.put(NOTIFICATION_PREF_NETWORK,
+ new PrefNetworkNotification(NOTIFICATION_PREF_NETWORK));
+ mNotificationTypeMap.put(NOTIFICATION_EMERGENCY_NETWORK,
+ new EmergencyNetworkNotification(NOTIFICATION_EMERGENCY_NETWORK));
}
@Override
@@ -65,19 +137,20 @@
switch (msg.what) {
case CARRIER_EVENT_VOICE_REGISTRATION:
case CARRIER_EVENT_DATA_REGISTRATION:
- mIsPhoneRegistered = true;
- handleConfigChanges();
- break;
case CARRIER_EVENT_VOICE_DEREGISTRATION:
case CARRIER_EVENT_DATA_DEREGISTRATION:
- if (isGlobalModeOrRadioOffOrAirplaneMode() || isPhoneStillRegistered()) {
- break;
- }
- mIsPhoneRegistered = false;
handleConfigChanges();
break;
- case SHOW_NOTIFICATION:
- sendNotification();
+ case CARRIER_EVENT_IMS_CAPABILITIES_CHANGED:
+ handleImsCapabilitiesChanged();
+ break;
+ case NOTIFICATION_EMERGENCY_NETWORK:
+ case NOTIFICATION_PREF_NETWORK:
+ Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what);
+ NotificationType notificationType = mNotificationTypeMap.get(msg.what);
+ if (notificationType != null) {
+ sendNotification(notificationType);
+ }
break;
}
}
@@ -90,48 +163,116 @@
|| mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE);
}
+ private boolean isPhoneVoiceRegistered() {
+ if (mSST.mSS == null) {
+ return true; //something has gone wrong, return true and not show the notification.
+ }
+ return (mSST.mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
+ }
+
+ private boolean isPhoneRegisteredForWifiCalling() {
+ Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled());
+ return mPhone.isWifiCallingEnabled();
+ }
+
/**
- * Returns true if the preferred network is set to 'Global' or the radio is off or in
- * Airplane Mode else returns false.
+ * Returns true if the radio is off or in Airplane Mode else returns false.
*/
- private boolean isGlobalModeOrRadioOffOrAirplaneMode() {
+ @VisibleForTesting
+ public boolean isRadioOffOrAirplaneMode() {
+ Context context = mPhone.getContext();
+ int airplaneMode = -1;
+ try {
+ airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
+ } catch (Exception e) {
+ Rlog.e(LOG_TAG, "Unable to get AIRPLACE_MODE_ON.");
+ return true;
+ }
+ return (!mSST.isRadioOn() || (airplaneMode != 0));
+ }
+
+ /**
+ * Returns true if the preferred network is set to 'Global'.
+ */
+ private boolean isGlobalMode() {
Context context = mPhone.getContext();
int preferredNetworkSetting = -1;
- int airplaneMode = -1;
- int subId = mPhone.getSubId();
try {
preferredNetworkSetting =
android.provider.Settings.Global.getInt(context.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
- Phone.PREFERRED_NT_MODE);
- airplaneMode = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0);
+ android.provider.Settings.Global.PREFERRED_NETWORK_MODE
+ + mPhone.getSubId(), Phone.PREFERRED_NT_MODE);
} catch (Exception e) {
Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE.");
return true;
}
- return ((preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) ||
- !mSST.isRadioOn() || (airplaneMode != 0));
+ return (preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ }
+
+ private void handleConfigChanges() {
+ for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
+ NotificationType notificationType = entry.getValue();
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void handlePrefNetworkModeChanged() {
+ NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
+ if (notificationType != null) {
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void handleImsCapabilitiesChanged() {
+ NotificationType notificationType = mNotificationTypeMap
+ .get(NOTIFICATION_EMERGENCY_NETWORK);
+ if (notificationType != null) {
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) {
+ if (evaluateSendingMessage(notificationType)) {
+ Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
+ Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
+ sendMessageDelayed(notificationMsg, getDelay(notificationType));
+ } else {
+ cancelNotification(notificationType.getTypeId());
+ Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
+ }
}
/**
- * Contains logic to decide when to create/cancel notifications.
- */
- private void handleConfigChanges() {
- if (mDelay == UNINITIALIZED_DELAY_VALUE) {
- cancelNotification();
- return;
- }
- // send a notification if the device is registerd to a network.
- if (mIsPhoneRegistered) {
- cancelNotification();
- Rlog.i(LOG_TAG, "canceling all notifications. ");
- } else {
- Message notificationMsg;
- notificationMsg = obtainMessage(SHOW_NOTIFICATION, null);
- Rlog.i(LOG_TAG, "starting timer for notifications. ");
- sendMessageDelayed(notificationMsg, mDelay);
- }
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public boolean evaluateSendingMessage(NotificationType notificationType) {
+ return notificationType.sendMessage();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public int getDelay(NotificationType notificationType) {
+ return notificationType.getDelay();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public Notification.Builder getNotificationBuilder(NotificationType notificationType) {
+ return notificationType.getNotificationBuilder();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public NotificationManager getNotificationManager(Context context) {
+ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -140,8 +281,10 @@
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
- mDelay = b.getInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
- Rlog.i(LOG_TAG, "reading time to delay notification: " + mDelay);
+ for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
+ NotificationType notificationType = entry.getValue();
+ notificationType.setDelay(b);
+ }
handleConfigChanges();
}
};
@@ -149,56 +292,203 @@
/**
* Post a notification to the NotificationManager for changing network type.
*/
- private void sendNotification() {
- Context context = mPhone.getContext();
-
- Rlog.i(LOG_TAG, "w/values: " + "," + mIsPhoneRegistered + "," + mDelay
- + "," + isGlobalModeOrRadioOffOrAirplaneMode() + "," + mSST.isRadioOn());
-
- // exit if the network preference is set to Global or if the phone is registered.
- if (isGlobalModeOrRadioOffOrAirplaneMode() || mIsPhoneRegistered) {
+ @VisibleForTesting
+ public void sendNotification(NotificationType notificationType) {
+ if (!evaluateSendingMessage(notificationType)) {
return;
}
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
-
-
- Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
- PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
- PendingIntent.FLAG_ONE_SHOT);
-
- CharSequence title =
- context.getText(com.android.internal.R.string.NetworkPreferenceSwitchTitle);
- CharSequence details =
- context.getText(com.android.internal.R.string.NetworkPreferenceSwitchSummary);
-
-
- Notification mNotification = new Notification.Builder(context)
- .setWhen(System.currentTimeMillis())
+ Context context = mPhone.getContext();
+ Notification.Builder builder = getNotificationBuilder(notificationType);
+ // set some common attributes
+ builder.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
- .setContentTitle(title)
.setColor(context.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(new Notification.BigTextStyle().bigText(details))
- .setContentText(details)
- .setContentIntent(settingsIntent)
- .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
- .build();
+ com.android.internal.R.color.system_notification_accent_color));
- notificationManager.notify(NOTIFICATION_ID, mNotification);
+ getNotificationManager(context).notify(notificationType.getTypeId(), builder.build());
}
/**
* Cancel notifications if a registration is pending or has been sent.
- */
- private void cancelNotification() {
+ **/
+ public void cancelNotification(int notificationId) {
Context context = mPhone.getContext();
- mIsPhoneRegistered = true;
- removeMessages(SHOW_NOTIFICATION);
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_ID);
+ removeMessages(notificationId);
+ getNotificationManager(context).cancel(notificationId);
+ }
+
+ /**
+ * Dispose the CarrierServiceStateTracker.
+ */
+ public void dispose() {
+ unregisterPrefNetworkModeObserver();
+ }
+
+ /**
+ * Class that defines the different types of notifications.
+ */
+ public interface NotificationType {
+
+ /**
+ * decides if the message should be sent, Returns boolean
+ **/
+ boolean sendMessage();
+
+ /**
+ * returns the interval by which the message is delayed.
+ **/
+ int getDelay();
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ void setDelay(PersistableBundle bundle);
+
+ /**
+ * returns notification type id.
+ **/
+ int getTypeId();
+
+ /**
+ * returns the notification builder, for the notification to be displayed.
+ **/
+ Notification.Builder getNotificationBuilder();
+ }
+
+ /**
+ * Class that defines the network notification, which is shown when the phone cannot camp on
+ * a network, and has 'preferred mode' set to global.
+ */
+ public class PrefNetworkNotification implements NotificationType {
+
+ private final int mTypeId;
+ private int mDelay = UNINITIALIZED_DELAY_VALUE;
+
+ PrefNetworkNotification(int typeId) {
+ this.mTypeId = typeId;
+ }
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ public void setDelay(PersistableBundle bundle) {
+ if (bundle == null) {
+ Rlog.e(LOG_TAG, "bundle is null");
+ return;
+ }
+ this.mDelay = bundle.getInt(
+ CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+ Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay);
+ }
+
+ public int getDelay() {
+ return mDelay;
+ }
+
+ public int getTypeId() {
+ return mTypeId;
+ }
+
+ /**
+ * Contains logic on sending notifications.
+ */
+ public boolean sendMessage() {
+ Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
+ + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
+ + "," + mSST.isRadioOn());
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
+ || isRadioOffOrAirplaneMode()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Builds a partial notificaiton builder, and returns it.
+ */
+ public Notification.Builder getNotificationBuilder() {
+ Context context = mPhone.getContext();
+ Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
+ notificationIntent.putExtra("expandable", true);
+ PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
+ PendingIntent.FLAG_ONE_SHOT);
+ CharSequence title = context.getText(
+ com.android.internal.R.string.NetworkPreferenceSwitchTitle);
+ CharSequence details = context.getText(
+ com.android.internal.R.string.NetworkPreferenceSwitchSummary);
+ return new Notification.Builder(context)
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setContentText(details)
+ .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
+ .setContentIntent(settingsIntent);
+ }
+ }
+
+ /**
+ * Class that defines the emergency notification, which is shown when the user is out of cell
+ * connectivity, but has wifi enabled.
+ */
+ public class EmergencyNetworkNotification implements NotificationType {
+
+ private final int mTypeId;
+ private int mDelay = UNINITIALIZED_DELAY_VALUE;
+
+ EmergencyNetworkNotification(int typeId) {
+ this.mTypeId = typeId;
+ }
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ public void setDelay(PersistableBundle bundle) {
+ if (bundle == null) {
+ Rlog.e(LOG_TAG, "bundle is null");
+ return;
+ }
+ this.mDelay = bundle.getInt(
+ CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT);
+ Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+ }
+
+ public int getDelay() {
+ return mDelay;
+ }
+
+ public int getTypeId() {
+ return mTypeId;
+ }
+
+ /**
+ * Contains logic on sending notifications,
+ */
+ public boolean sendMessage() {
+ Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: "
+ + "," + isPhoneVoiceRegistered() + "," + mDelay + ","
+ + isPhoneRegisteredForWifiCalling() + "," + mSST.isRadioOn());
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneVoiceRegistered()
+ || !isPhoneRegisteredForWifiCalling()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Builds a partial notificaiton builder, and returns it.
+ */
+ public Notification.Builder getNotificationBuilder() {
+ Context context = mPhone.getContext();
+ CharSequence title = context.getText(
+ com.android.internal.R.string.EmergencyCallWarningTitle);
+ CharSequence details = context.getText(
+ com.android.internal.R.string.EmergencyCallWarningSummary);
+ return new Notification.Builder(context)
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setContentText(details)
+ .setChannel(NotificationChannelController.CHANNEL_ID_WFC);
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 4227148..8fc4642 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -30,6 +30,12 @@
import android.provider.Telephony;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
@@ -37,6 +43,8 @@
*/
public class CellBroadcastHandler extends WakeLockStateMachine {
+ private final LocalLog mLocalLog = new LocalLog(100);
+
private CellBroadcastHandler(Context context, Phone phone) {
this("CellBroadcastHandler", context, phone);
}
@@ -82,9 +90,18 @@
String receiverPermission;
int appOp;
+ // Log Cellbroadcast msg received event
+ TelephonyMetrics metrics = TelephonyMetrics.getInstance();
+ metrics.writeNewCBSms(mPhone.getPhoneId(), message.getMessageFormat(),
+ message.getMessagePriority(), message.isCmasMessage(), message.isEtwsMessage(),
+ message.getServiceCategory());
+
+ String msg;
Intent intent;
if (message.isEmergencyMessage()) {
- log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
+ msg = "Dispatching emergency SMS CB, SmsCbMessage is: " + message;
+ log(msg);
+ mLocalLog.log(msg);
intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
// Explicitly send the intent to the default cell broadcast receiver.
intent.setPackage(mContext.getResources().getString(
@@ -92,7 +109,9 @@
receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
} else {
- log("Dispatching SMS CB, SmsCbMessage is: " + message);
+ msg = "Dispatching SMS CB, SmsCbMessage is: " + message;
+ log(msg);
+ mLocalLog.log(msg);
intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
// Send implicit intent since there are various 3rd party carrier apps listen to
// this intent.
@@ -121,4 +140,11 @@
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
mReceiver, getHandler(), Activity.RESULT_OK, null, null);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CellBroadcastHandler:");
+ mLocalLog.dump(fd, pw, args);
+ pw.flush();
+ }
}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
new file mode 100644
index 0000000..8b840de
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright 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;
+
+import android.annotation.CallSuper;
+import android.hardware.radio.V1_0.CellInfoType;
+import android.hardware.radio.V1_0.RegState;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityTdscdma;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.NetworkServiceCallback;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of network services for Cellular. It's a service that handles network requests
+ * for Cellular. It passes the requests to inner CellularNetworkServiceProvider which has a
+ * handler thread for each slot.
+ */
+public class CellularNetworkService extends NetworkService {
+ private static final boolean DBG = false;
+
+ private static final String TAG = CellularNetworkService.class.getSimpleName();
+
+ private static final int GET_CS_REGISTRATION_STATE_DONE = 1;
+ private static final int GET_PS_REGISTRATION_STATE_DONE = 2;
+ private static final int NETWORK_REGISTRATION_STATE_CHANGED = 3;
+
+ private class CellularNetworkServiceProvider extends NetworkServiceProvider {
+
+ private final ConcurrentHashMap<Message, NetworkServiceCallback> mCallbackMap =
+ new ConcurrentHashMap<>();
+
+ private final Looper mLooper;
+
+ private final HandlerThread mHandlerThread;
+
+ private final Handler mHandler;
+
+ private final Phone mPhone;
+
+ CellularNetworkServiceProvider(int slotId) {
+ super(slotId);
+
+ mPhone = PhoneFactory.getPhone(getSlotId());
+
+ mHandlerThread = new HandlerThread(CellularNetworkService.class.getSimpleName());
+ mHandlerThread.start();
+ mLooper = mHandlerThread.getLooper();
+ mHandler = new Handler(mLooper) {
+ @Override
+ public void handleMessage(Message message) {
+ NetworkServiceCallback callback = mCallbackMap.remove(message);
+
+ AsyncResult ar;
+ switch (message.what) {
+ case GET_CS_REGISTRATION_STATE_DONE:
+ case GET_PS_REGISTRATION_STATE_DONE:
+ if (callback == null) return;
+ ar = (AsyncResult) message.obj;
+ int domain = (message.what == GET_CS_REGISTRATION_STATE_DONE)
+ ? NetworkRegistrationState.DOMAIN_CS
+ : NetworkRegistrationState.DOMAIN_PS;
+ NetworkRegistrationState netState =
+ getRegistrationStateFromResult(ar.result, domain);
+
+ int resultCode;
+ if (ar.exception != null || netState == null) {
+ resultCode = NetworkServiceCallback.RESULT_ERROR_FAILED;
+ } else {
+ resultCode = NetworkServiceCallback.RESULT_SUCCESS;
+ }
+
+ try {
+ if (DBG) {
+ log("Calling callback.onGetNetworkRegistrationStateComplete."
+ + "resultCode = " + resultCode
+ + ", netState = " + netState);
+ }
+ callback.onGetNetworkRegistrationStateComplete(
+ resultCode, netState);
+ } catch (Exception e) {
+ loge("Exception: " + e);
+ }
+ break;
+ case NETWORK_REGISTRATION_STATE_CHANGED:
+ notifyNetworkRegistrationStateChanged();
+ break;
+ default:
+ return;
+ }
+ }
+ };
+
+ mPhone.mCi.registerForNetworkStateChanged(
+ mHandler, NETWORK_REGISTRATION_STATE_CHANGED, null);
+ }
+
+ private int getRegStateFromHalRegState(int halRegState) {
+ switch (halRegState) {
+ case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+ case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
+ return NetworkRegistrationState.REG_STATE_NOT_REG_NOT_SEARCHING;
+ case RegState.REG_HOME:
+ return NetworkRegistrationState.REG_STATE_HOME;
+ case RegState.NOT_REG_MT_SEARCHING_OP:
+ case RegState.NOT_REG_MT_SEARCHING_OP_EM:
+ return NetworkRegistrationState.REG_STATE_NOT_REG_SEARCHING;
+ case RegState.REG_DENIED:
+ case RegState.REG_DENIED_EM:
+ return NetworkRegistrationState.REG_STATE_DENIED;
+ case RegState.UNKNOWN:
+ case RegState.UNKNOWN_EM:
+ return NetworkRegistrationState.REG_STATE_UNKNOWN;
+ case RegState.REG_ROAMING:
+ return NetworkRegistrationState.REG_STATE_ROAMING;
+ default:
+ return NetworkRegistrationState.REG_STATE_NOT_REG_NOT_SEARCHING;
+ }
+ }
+
+ private boolean isEmergencyOnly(int halRegState) {
+ switch (halRegState) {
+ case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
+ case RegState.NOT_REG_MT_SEARCHING_OP_EM:
+ case RegState.REG_DENIED_EM:
+ case RegState.UNKNOWN_EM:
+ return true;
+ case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+ case RegState.REG_HOME:
+ case RegState.NOT_REG_MT_SEARCHING_OP:
+ case RegState.REG_DENIED:
+ case RegState.UNKNOWN:
+ case RegState.REG_ROAMING:
+ default:
+ return false;
+ }
+ }
+
+ private int[] getAvailableServices(int regState, int domain, boolean emergencyOnly) {
+ int[] availableServices = null;
+
+ // In emergency only states, only SERVICE_TYPE_EMERGENCY is available.
+ // Otherwise, certain services are available only if it's registered on home or roaming
+ // network.
+ if (emergencyOnly) {
+ availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_EMERGENCY};
+ } else if (regState == NetworkRegistrationState.REG_STATE_ROAMING
+ || regState == NetworkRegistrationState.REG_STATE_HOME) {
+ if (domain == NetworkRegistrationState.DOMAIN_PS) {
+ availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_DATA};
+ } else if (domain == NetworkRegistrationState.DOMAIN_CS) {
+ availableServices = new int[] {
+ NetworkRegistrationState.SERVICE_TYPE_VOICE,
+ NetworkRegistrationState.SERVICE_TYPE_SMS,
+ NetworkRegistrationState.SERVICE_TYPE_VIDEO
+ };
+ }
+ }
+
+ return availableServices;
+ }
+
+ private int getAccessNetworkTechnologyFromRat(int rilRat) {
+ return ServiceState.rilRadioTechnologyToNetworkType(rilRat);
+ }
+
+ private NetworkRegistrationState getRegistrationStateFromResult(Object result, int domain) {
+ if (result == null) {
+ return null;
+ }
+
+ // TODO: unify when voiceRegStateResult and DataRegStateResult are unified.
+ if (domain == NetworkRegistrationState.DOMAIN_CS) {
+ return createRegistrationStateFromVoiceRegState(result);
+ } else if (domain == NetworkRegistrationState.DOMAIN_PS) {
+ return createRegistrationStateFromDataRegState(result);
+ } else {
+ return null;
+ }
+ }
+
+ private NetworkRegistrationState createRegistrationStateFromVoiceRegState(Object result) {
+ int transportType = TransportType.WWAN;
+ int domain = NetworkRegistrationState.DOMAIN_CS;
+
+ if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
+ android.hardware.radio.V1_0.VoiceRegStateResult voiceRegState =
+ (android.hardware.radio.V1_0.VoiceRegStateResult) result;
+ int regState = getRegStateFromHalRegState(voiceRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(voiceRegState.rat);
+ int reasonForDenial = voiceRegState.reasonForDenial;
+ boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
+ boolean cssSupported = voiceRegState.cssSupported;
+ int roamingIndicator = voiceRegState.roamingIndicator;
+ int systemIsInPrl = voiceRegState.systemIsInPrl;
+ int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
+ int[] availableServices = getAvailableServices(
+ regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ } else if (result instanceof android.hardware.radio.V1_2.VoiceRegStateResult) {
+ android.hardware.radio.V1_2.VoiceRegStateResult voiceRegState =
+ (android.hardware.radio.V1_2.VoiceRegStateResult) result;
+ int regState = getRegStateFromHalRegState(voiceRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(voiceRegState.rat);
+ int reasonForDenial = voiceRegState.reasonForDenial;
+ boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
+ boolean cssSupported = voiceRegState.cssSupported;
+ int roamingIndicator = voiceRegState.roamingIndicator;
+ int systemIsInPrl = voiceRegState.systemIsInPrl;
+ int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
+ int[] availableServices = getAvailableServices(
+ regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ }
+
+ return null;
+ }
+
+ private NetworkRegistrationState createRegistrationStateFromDataRegState(Object result) {
+ int domain = NetworkRegistrationState.DOMAIN_PS;
+ int transportType = TransportType.WWAN;
+
+ if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
+ android.hardware.radio.V1_0.DataRegStateResult dataRegState =
+ (android.hardware.radio.V1_0.DataRegStateResult) result;
+ int regState = getRegStateFromHalRegState(dataRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(dataRegState.rat);
+ int reasonForDenial = dataRegState.reasonDataDenied;
+ boolean emergencyOnly = isEmergencyOnly(dataRegState.regState);
+ int maxDataCalls = dataRegState.maxDataCalls;
+ int[] availableServices = getAvailableServices(regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, maxDataCalls);
+ } else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
+ android.hardware.radio.V1_2.DataRegStateResult dataRegState =
+ (android.hardware.radio.V1_2.DataRegStateResult) result;
+ int regState = getRegStateFromHalRegState(dataRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(dataRegState.rat);
+ int reasonForDenial = dataRegState.reasonDataDenied;
+ boolean emergencyOnly = isEmergencyOnly(dataRegState.regState);
+ int maxDataCalls = dataRegState.maxDataCalls;
+ int[] availableServices = getAvailableServices(regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, maxDataCalls);
+ }
+
+ return null;
+ }
+
+ private CellIdentity convertHalCellIdentityToCellIdentity(
+ android.hardware.radio.V1_0.CellIdentity cellIdentity) {
+ if (cellIdentity == null) {
+ return null;
+ }
+
+ CellIdentity result = null;
+ switch(cellIdentity.cellInfoType) {
+ case CellInfoType.GSM: {
+ if (cellIdentity.cellIdentityGsm.size() == 1) {
+ android.hardware.radio.V1_0.CellIdentityGsm cellIdentityGsm =
+ cellIdentity.cellIdentityGsm.get(0);
+ result = new CellIdentityGsm(cellIdentityGsm.lac, cellIdentityGsm.cid,
+ cellIdentityGsm.arfcn, cellIdentityGsm.bsic, cellIdentityGsm.mcc,
+ cellIdentityGsm.mnc, null, null);
+ }
+ break;
+ }
+ case CellInfoType.WCDMA: {
+ if (cellIdentity.cellIdentityWcdma.size() == 1) {
+ android.hardware.radio.V1_0.CellIdentityWcdma cellIdentityWcdma =
+ cellIdentity.cellIdentityWcdma.get(0);
+ result = new CellIdentityWcdma(cellIdentityWcdma.lac, cellIdentityWcdma.cid,
+ cellIdentityWcdma.psc, cellIdentityWcdma.uarfcn,
+ cellIdentityWcdma.mcc, cellIdentityWcdma.mnc, null, null);
+ }
+ break;
+ }
+ case CellInfoType.TD_SCDMA: {
+ if (cellIdentity.cellIdentityTdscdma.size() == 1) {
+ android.hardware.radio.V1_0.CellIdentityTdscdma cellIdentityTdscdma =
+ cellIdentity.cellIdentityTdscdma.get(0);
+ result = new CellIdentityTdscdma(cellIdentityTdscdma.mcc,
+ cellIdentityTdscdma.mnc, cellIdentityTdscdma.lac,
+ cellIdentityTdscdma.cid, cellIdentityTdscdma.cpid,
+ Integer.MAX_VALUE, null, null);
+ }
+ break;
+ }
+ case CellInfoType.LTE: {
+ if (cellIdentity.cellIdentityLte.size() == 1) {
+ android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
+ cellIdentity.cellIdentityLte.get(0);
+
+ result = new CellIdentityLte(cellIdentityLte.ci, cellIdentityLte.pci,
+ cellIdentityLte.tac, cellIdentityLte.earfcn, Integer.MAX_VALUE,
+ cellIdentityLte.mcc, cellIdentityLte.mnc, null, null);
+ }
+ break;
+ }
+ case CellInfoType.CDMA: {
+ if (cellIdentity.cellIdentityCdma.size() == 1) {
+ android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
+ cellIdentity.cellIdentityCdma.get(0);
+
+ result = new CellIdentityCdma(cellIdentityCdma.networkId,
+ cellIdentityCdma.systemId, cellIdentityCdma.baseStationId,
+ cellIdentityCdma.longitude, cellIdentityCdma.latitude);
+ }
+ break;
+ }
+ case CellInfoType.NONE:
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ private CellIdentity convertHalCellIdentityToCellIdentity(
+ android.hardware.radio.V1_2.CellIdentity cellIdentity) {
+ if (cellIdentity == null) {
+ return null;
+ }
+
+ CellIdentity result = null;
+ switch(cellIdentity.cellInfoType) {
+ case CellInfoType.GSM: {
+ if (cellIdentity.cellIdentityGsm.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityGsm cellIdentityGsm =
+ cellIdentity.cellIdentityGsm.get(0);
+
+ result = new CellIdentityGsm(
+ cellIdentityGsm.base.lac,
+ cellIdentityGsm.base.cid,
+ cellIdentityGsm.base.arfcn,
+ cellIdentityGsm.base.bsic,
+ cellIdentityGsm.base.mcc,
+ cellIdentityGsm.base.mnc,
+ cellIdentityGsm.operatorNames.alphaLong,
+ cellIdentityGsm.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.WCDMA: {
+ if (cellIdentity.cellIdentityWcdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityWcdma cellIdentityWcdma =
+ cellIdentity.cellIdentityWcdma.get(0);
+
+ result = new CellIdentityWcdma(
+ cellIdentityWcdma.base.lac,
+ cellIdentityWcdma.base.cid,
+ cellIdentityWcdma.base.psc,
+ cellIdentityWcdma.base.uarfcn,
+ cellIdentityWcdma.base.mcc,
+ cellIdentityWcdma.base.mnc,
+ cellIdentityWcdma.operatorNames.alphaLong,
+ cellIdentityWcdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.TD_SCDMA: {
+ if (cellIdentity.cellIdentityTdscdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityTdscdma cellIdentityTdscdma =
+ cellIdentity.cellIdentityTdscdma.get(0);
+
+ result = new CellIdentityTdscdma(
+ cellIdentityTdscdma.base.mcc,
+ cellIdentityTdscdma.base.mnc,
+ cellIdentityTdscdma.base.lac,
+ cellIdentityTdscdma.base.cid,
+ cellIdentityTdscdma.base.cpid,
+ cellIdentityTdscdma.uarfcn,
+ cellIdentityTdscdma.operatorNames.alphaLong,
+ cellIdentityTdscdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.LTE: {
+ if (cellIdentity.cellIdentityLte.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityLte cellIdentityLte =
+ cellIdentity.cellIdentityLte.get(0);
+
+ result = new CellIdentityLte(
+ cellIdentityLte.base.ci,
+ cellIdentityLte.base.pci,
+ cellIdentityLte.base.tac,
+ cellIdentityLte.base.earfcn,
+ cellIdentityLte.bandwidth,
+ cellIdentityLte.base.mcc,
+ cellIdentityLte.base.mnc,
+ cellIdentityLte.operatorNames.alphaLong,
+ cellIdentityLte.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.CDMA: {
+ if (cellIdentity.cellIdentityCdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityCdma cellIdentityCdma =
+ cellIdentity.cellIdentityCdma.get(0);
+
+ result = new CellIdentityCdma(
+ cellIdentityCdma.base.networkId,
+ cellIdentityCdma.base.systemId,
+ cellIdentityCdma.base.baseStationId,
+ cellIdentityCdma.base.longitude,
+ cellIdentityCdma.base.latitude,
+ cellIdentityCdma.operatorNames.alphaLong,
+ cellIdentityCdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.NONE:
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ @Override
+ public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
+ if (DBG) log("getNetworkRegistrationState for domain " + domain);
+ Message message = null;
+
+ if (domain == NetworkRegistrationState.DOMAIN_CS) {
+ message = Message.obtain(mHandler, GET_CS_REGISTRATION_STATE_DONE);
+ mCallbackMap.put(message, callback);
+ mPhone.mCi.getVoiceRegistrationState(message);
+ } else if (domain == NetworkRegistrationState.DOMAIN_PS) {
+ message = Message.obtain(mHandler, GET_PS_REGISTRATION_STATE_DONE);
+ mCallbackMap.put(message, callback);
+ mPhone.mCi.getDataRegistrationState(message);
+ } else {
+ loge("getNetworkRegistrationState invalid domain " + domain);
+ callback.onGetNetworkRegistrationStateComplete(
+ NetworkServiceCallback.RESULT_ERROR_INVALID_ARG, null);
+ }
+ }
+
+ @CallSuper
+ protected void onDestroy() {
+ super.onDestroy();
+
+ mCallbackMap.clear();
+ mHandlerThread.quit();
+ mPhone.mCi.unregisterForNetworkStateChanged(mHandler);
+ }
+ }
+
+ @Override
+ protected NetworkServiceProvider createNetworkServiceProvider(int slotId) {
+ if (DBG) log("Cellular network service created for slot " + slotId);
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ loge("Tried to Cellular network service with invalid slotId " + slotId);
+ return null;
+ }
+ return new CellularNetworkServiceProvider(slotId);
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ClientWakelockTracker.java b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
index 5bec60b..fa71e76 100644
--- a/src/java/com/android/internal/telephony/ClientWakelockTracker.java
+++ b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
@@ -18,10 +18,10 @@
import android.os.SystemClock;
import android.telephony.ClientRequestStats;
-import android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -119,13 +119,13 @@
return false;
}
- void dumpClientRequestTracker() {
- Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------");
+ void dumpClientRequestTracker(PrintWriter pw) {
+ pw.println("-------mClients---------------");
synchronized (mClients) {
for (String key : mClients.keySet()) {
- Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key);
- Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString());
+ pw.println("Client : " + key);
+ pw.println(mClients.get(key).toString());
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index 6cba8f2..fe9fa72 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony;
-import com.android.internal.telephony.RILConstants;
-
import android.telephony.Rlog;
/**
@@ -52,6 +50,7 @@
USSD_MODIFIED_TO_SS,
USSD_MODIFIED_TO_USSD,
SS_MODIFIED_TO_DIAL,
+ SS_MODIFIED_TO_DIAL_VIDEO,
SS_MODIFIED_TO_USSD,
SS_MODIFIED_TO_SS,
SIM_ALREADY_POWERED_OFF,
@@ -260,6 +259,8 @@
return new CommandException(Error.DEVICE_IN_USE);
case RILConstants.ABORTED:
return new CommandException(Error.ABORTED);
+ case RILConstants.INVALID_RESPONSE:
+ return new CommandException(Error.INVALID_RESPONSE);
case RILConstants.OEM_ERROR_1:
return new CommandException(Error.OEM_ERROR_1);
case RILConstants.OEM_ERROR_2:
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index f339693..9f6ce5e 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
import android.os.WorkSource;
@@ -23,9 +25,9 @@
import android.telephony.ClientRequestStats;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.data.DataProfile;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccCardStatus;
@@ -189,6 +191,10 @@
*/
void registerForIccStatusChanged(Handler h, int what, Object obj);
void unregisterForIccStatusChanged(Handler h);
+ /** Register for ICC slot status changed event */
+ void registerForIccSlotStatusChanged(Handler h, int what, Object obj);
+ /** Unregister for ICC slot status changed event */
+ void unregisterForIccSlotStatusChanged(Handler h);
void registerForCallStateChanged(Handler h, int what, Object obj);
void unregisterForCallStateChanged(Handler h);
@@ -1630,22 +1636,27 @@
/**
* Setup a packet data connection On successful completion, the result
- * message will return a {@link com.android.internal.telephony.dataconnection.DataCallResponse}
- * object containing the connection information.
+ * message will return a SetupDataResult object containing the connection information.
*
- * @param radioTechnology
- * Radio technology to use. Values is one of RIL_RADIO_TECHNOLOGY_*
+ * @param accessNetworkType
+ * Access network to use. Values is one of AccessNetworkConstants.AccessNetworkType.
* @param dataProfile
* Data profile for data call setup
* @param isRoaming
* Device is roaming or not
* @param allowRoaming
* Flag indicating data roaming is enabled or not
+ * @param reason
+ * The reason for data setup
+ * @param linkProperties
+ * If the reason is for handover, this indicates the link properties of the existing
+ * data connection
* @param result
* Callback message
*/
- void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result);
+ void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result);
/**
* Deactivate packet data connection
@@ -1702,6 +1713,22 @@
public void getIccCardStatus(Message result);
/**
+ * Request the status of all the physical UICC slots.
+ *
+ * @param result Callback message containing a {@link java.util.ArrayList} of
+ * {@link com.android.internal.telephony.uicc.IccSlotStatus} instances for all the slots.
+ */
+ void getIccSlotsStatus(Message result);
+
+ /**
+ * Set the mapping from logical slots to physical slots.
+ *
+ * @param physicalSlots Mapping from logical slots to physical slots.
+ * @param result Callback message is empty on completion.
+ */
+ void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result);
+
+ /**
* Return if the current radio is LTE on CDMA. This
* is a tri-state return value as for a period of time
* the mode may be unknown.
@@ -1712,18 +1739,6 @@
public int getLteOnCdmaMode();
/**
- * Request the ISIM application on the UICC to perform the AKA
- * challenge/response algorithm for IMS authentication. The nonce string
- * and challenge response are Base64 encoded Strings.
- *
- * @param nonce the nonce string to pass with the ISIM authentication request
- * @param response a callback message with the String response in the obj field
- * @deprecated
- * @see requestIccSimAuthentication
- */
- public void requestIsimAuthentication(String nonce, Message response);
-
- /**
* Request the SIM application on the UICC to perform authentication
* challenge/response algorithm. The data string and challenge response are
* Base64 encoded Strings.
@@ -1783,6 +1798,17 @@
void unregisterForCellInfoList(Handler h);
/**
+ * Fires when a new {@link android.telephony.PhysicalChannelConfig} list is received from the
+ * RIL.
+ */
+ void registerForPhysicalChannelConfiguration(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters the handler for {@link android.telephony.PhysicalChannelConfig} updates.
+ */
+ void unregisterForPhysicalChannelConfiguration(Handler h);
+
+ /**
* Set Initial Attach Apn
*
* @param dataProfile
@@ -2063,7 +2089,7 @@
* Register for unsolicited PCO data. This information is carrier-specific,
* opaque binary blobs destined for carrier apps for interpretation.
*
- * @param h Handler for notificaiton message.
+ * @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
@@ -2110,6 +2136,38 @@
void setUnsolResponseFilter(int filter, Message result);
/**
+ * Send the signal strength reporting criteria to the modem.
+ *
+ * @param hysteresisMs A hysteresis time in milliseconds. A value of 0 disables hysteresis.
+ * @param hysteresisDb An interval in dB defining the required magnitude change between reports.
+ * A value of 0 disables hysteresis.
+ * @param thresholdsDbm An array of trigger thresholds in dBm. A size of 0 disables thresholds.
+ * @param ran RadioAccessNetwork for which to apply criteria.
+ * @param result callback message contains the information of SUCCESS/FAILURE
+ */
+ void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb, int[] thresholdsDbm,
+ int ran, Message result);
+
+ /**
+ * Send the link capacity reporting criteria to the modem
+ *
+ * @param hysteresisMs A hysteresis time in milliseconds. A value of 0 disables hysteresis.
+ * @param hysteresisDlKbps An interval in kbps defining the required magnitude change between DL
+ * reports. A value of 0 disables hysteresis.
+ * @param hysteresisUlKbps An interval in kbps defining the required magnitude change between UL
+ * reports. A value of 0 disables hysteresis.
+ * @param thresholdsDlKbps An array of trigger thresholds in kbps for downlink reports. A size
+ * of 0 disables thresholds.
+ * @param thresholdsUlKbps An array of trigger thresholds in kbps for uplink reports. A size
+ * of 0 disables thresholds.
+ * @param ran RadioAccessNetwork for which to apply criteria.
+ * @param result callback message contains the information of SUCCESS/FAILURE
+ */
+ void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result);
+
+ /**
* Set SIM card power up or down
*
* @param state State of SIM (power down, power up, pass through)
@@ -2123,7 +2181,7 @@
/**
* Register for unsolicited Carrier Public Key.
*
- * @param h Handler for notificaiton message.
+ * @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
@@ -2132,14 +2190,14 @@
/**
* DeRegister for unsolicited Carrier Public Key.
*
- * @param h Handler for notificaiton message.
+ * @param h Handler for notification message.
*/
void unregisterForCarrierInfoForImsiEncryption(Handler h);
/**
* Register for unsolicited Network Scan result.
*
- * @param h Handler for notificaiton message.
+ * @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
@@ -2148,10 +2206,45 @@
/**
* DeRegister for unsolicited Network Scan result.
*
- * @param h Handler for notificaiton message.
+ * @param h Handler for notification message.
*/
void unregisterForNetworkScanResult(Handler h);
+ /**
+ * Register for unsolicited NATT Keepalive Status Indications
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForNattKeepaliveStatus(Handler h, int what, Object obj);
+
+ /**
+ * Deregister for unsolicited NATT Keepalive Status Indications.
+ *
+ * @param h Handler for notification message.
+ */
+ void unregisterForNattKeepaliveStatus(Handler h);
+
+ /**
+ * Start sending NATT Keepalive packets on a specified data connection
+ *
+ * @param contextId cid that identifies the data connection for this keepalive
+ * @param packetData the keepalive packet data description
+ * @param intervalMillis a time interval in ms between keepalive packet transmissions
+ * @param result a Message to return to the requester
+ */
+ void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result);
+
+ /**
+ * Stop sending NATT Keepalive packets on a specified data connection
+ *
+ * @param sessionHandle the keepalive session handle (from the modem) to stop
+ * @param result a Message to return to the requester
+ */
+ void stopNattKeepalive(int sessionHandle, Message result);
+
default public List<ClientRequestStats> getClientRequestStats() {
return null;
}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 8c54a31..b37ad04 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -104,6 +104,9 @@
public void onConnectionEvent(String event, Bundle extras);
public void onRttModifyRequestReceived();
public void onRttModifyResponseReceived(int status);
+ public void onDisconnect(int cause);
+ public void onRttInitiated();
+ public void onRttTerminated();
}
/**
@@ -143,6 +146,12 @@
public void onRttModifyRequestReceived() {}
@Override
public void onRttModifyResponseReceived(int status) {}
+ @Override
+ public void onDisconnect(int cause) {}
+ @Override
+ public void onRttInitiated() {}
+ @Override
+ public void onRttTerminated() {}
}
public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -485,6 +494,11 @@
}
/**
+ * Deflect individual Connection
+ */
+ public abstract void deflect(String number) throws CallStateException;
+
+ /**
* Hangup individual Connection
*/
public abstract void hangup() throws CallStateException;
@@ -516,7 +530,7 @@
mUserData = null;
}
- public final void addPostDialListener(PostDialListener listener) {
+ public void addPostDialListener(PostDialListener listener) {
if (!mPostDialListeners.contains(listener)) {
mPostDialListeners.add(listener);
}
@@ -660,7 +674,7 @@
*
* @param listener A listener.
*/
- public final void addListener(Listener listener) {
+ public void addListener(Listener listener) {
mListeners.add(listener);
}
@@ -1054,6 +1068,29 @@
}
}
+ public void onRttInitiated() {
+ for (Listener l : mListeners) {
+ l.onRttInitiated();
+ }
+ }
+
+ public void onRttTerminated() {
+ for (Listener l : mListeners) {
+ l.onRttTerminated();
+ }
+ }
+ /**
+ * Notify interested parties that this connection disconnected.
+ * {@code TelephonyConnection}, for example, uses this.
+ * @param reason the disconnect code, per {@link DisconnectCause}.
+ */
+ protected void notifyDisconnect(int reason) {
+ Rlog.i(TAG, "notifyDisconnect: callId=" + getTelecomCallId() + ", reason=" + reason);
+ for (Listener l : mListeners) {
+ l.onDisconnect(reason);
+ }
+ }
+
/**
*
*/
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index c13e540..8f1f078 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -22,6 +22,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.CellInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -125,6 +126,9 @@
int subId = sender.getSubId();
try {
if (mRegistry != null) {
+ Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive="
+ + sender.getCallForwardingIndicator());
+
mRegistry.notifyCallForwardingChangedForSubscriber(subId,
sender.getCallForwardingIndicator());
}
@@ -229,6 +233,19 @@
}
@Override
+ public void notifyPhysicalChannelConfiguration(Phone sender,
+ List<PhysicalChannelConfig> configs) {
+ int subId = sender.getSubId();
+ try {
+ if (mRegistry != null) {
+ mRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs);
+ }
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ @Override
public void notifyOtaspChanged(Phone sender, int otaspMode) {
// FIXME: subId?
try {
@@ -307,6 +324,16 @@
}
@Override
+ public void notifyUserMobileDataStateChanged(Phone sender, boolean state) {
+ try {
+ mRegistry.notifyUserMobileDataStateChangedForPhoneId(
+ sender.getPhoneId(), sender.getSubId(), state);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ @Override
public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
try {
mRegistry.notifyOemHookRawEventForSubscriber(subId, rawData);
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 73ab075..04e96ca 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -25,14 +25,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManager;
-import android.hardware.radio.V1_0.IndicationFilter;
+import android.hardware.radio.V1_2.IndicationFilter;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
import android.util.LocalLog;
+import android.util.SparseIntArray;
import android.view.Display;
import com.android.internal.util.IndentingPrintWriter;
@@ -55,10 +59,14 @@
protected static final String TAG = DeviceStateMonitor.class.getSimpleName();
private static final int EVENT_RIL_CONNECTED = 0;
- private static final int EVENT_SCREEN_STATE_CHANGED = 1;
- private static final int EVENT_POWER_SAVE_MODE_CHANGED = 2;
- private static final int EVENT_CHARGING_STATE_CHANGED = 3;
- private static final int EVENT_TETHERING_STATE_CHANGED = 4;
+ private static final int EVENT_UPDATE_MODE_CHANGED = 1;
+ private static final int EVENT_SCREEN_STATE_CHANGED = 2;
+ private static final int EVENT_POWER_SAVE_MODE_CHANGED = 3;
+ private static final int EVENT_CHARGING_STATE_CHANGED = 4;
+ private static final int EVENT_TETHERING_STATE_CHANGED = 5;
+
+ // TODO(b/74006656) load hysteresis values from a property when DeviceStateMonitor starts
+ private static final int HYSTERESIS_KBPS = 50;
private final Phone mPhone;
@@ -96,6 +104,8 @@
*/
private boolean mIsLowDataExpected;
+ private SparseIntArray mUpdateModes = new SparseIntArray();
+
/**
* The unsolicited response filter. See IndicationFilter defined in types.hal for the definition
* of each bit.
@@ -196,14 +206,27 @@
* @return True if low data is expected
*/
private boolean isLowDataExpected() {
- return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
+ return !mIsCharging && !mIsTetheringOn && !mIsScreenOn;
}
/**
* @return True if signal strength update should be turned off.
*/
private boolean shouldTurnOffSignalStrength() {
- return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn);
+ // We should not turn off signal strength update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When the update mode is IGNORE_SCREEN_OFF. This mode is used in some corner cases like
+ // when Bluetooth carkit is connected, we still want to update signal strength even
+ // when screen is off.
+ if (mIsCharging || mIsScreenOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_SIGNAL_STRENGTH)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off signal strength update.
+ return true;
}
/**
@@ -211,14 +234,104 @@
* trigger the network update unsolicited response.
*/
private boolean shouldTurnOffFullNetworkUpdate() {
- return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn && !mIsTetheringOn);
+ // We should not turn off full network update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_FULL_NETWORK_STATE)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off full network state update.
+ return true;
}
/**
* @return True if data dormancy status update should be turned off.
*/
private boolean shouldTurnOffDormancyUpdate() {
- return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
+ // We should not turn off data dormancy update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off data dormancy update.
+ return true;
+ }
+
+ /**
+ * @return True if link capacity estimate update should be turned off.
+ */
+ private boolean shouldTurnOffLinkCapacityEstimate() {
+ // We should not turn off link capacity update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off link capacity update.
+ return true;
+ }
+
+ /**
+ * @return True if physical channel config update should be turned off.
+ */
+ private boolean shouldTurnOffPhysicalChannelConfig() {
+ // We should not turn off physical channel update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off physical channel config update.
+ return true;
+ }
+
+ /**
+ * Set indication update mode
+ *
+ * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
+ * @param mode The voice activation state
+ */
+ public void setIndicationUpdateMode(int filters, int mode) {
+ sendMessage(obtainMessage(EVENT_UPDATE_MODE_CHANGED, filters, mode));
+ }
+
+ private void onSetIndicationUpdateMode(int filters, int mode) {
+ if ((filters & TelephonyManager.INDICATION_FILTER_SIGNAL_STRENGTH) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_SIGNAL_STRENGTH, mode);
+ }
+ if ((filters & TelephonyManager.INDICATION_FILTER_FULL_NETWORK_STATE) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_FULL_NETWORK_STATE, mode);
+ }
+ if ((filters & TelephonyManager.INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED, mode);
+ }
+ if ((filters & TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE, mode);
+ }
+ if ((filters & TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG, mode);
+ }
}
/**
@@ -233,8 +346,17 @@
case EVENT_RIL_CONNECTED:
onRilConnected();
break;
+ case EVENT_UPDATE_MODE_CHANGED:
+ onSetIndicationUpdateMode(msg.arg1, msg.arg2);
+ break;
+ case EVENT_SCREEN_STATE_CHANGED:
+ case EVENT_POWER_SAVE_MODE_CHANGED:
+ case EVENT_CHARGING_STATE_CHANGED:
+ case EVENT_TETHERING_STATE_CHANGED:
+ onUpdateDeviceState(msg.what, msg.arg1 != 0);
+ break;
default:
- updateDeviceState(msg.what, msg.arg1 != 0);
+ throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
}
}
@@ -244,7 +366,7 @@
* @param eventType Device state event type
* @param state True if enabled/on, otherwise disabled/off.
*/
- private void updateDeviceState(int eventType, boolean state) {
+ private void onUpdateDeviceState(int eventType, boolean state) {
switch (eventType) {
case EVENT_SCREEN_STATE_CHANGED:
if (mIsScreenOn == state) return;
@@ -286,6 +408,14 @@
newFilter |= IndicationFilter.DATA_CALL_DORMANCY_CHANGED;
}
+ if (!shouldTurnOffLinkCapacityEstimate()) {
+ newFilter |= IndicationFilter.LINK_CAPACITY_ESTIMATE;
+ }
+
+ if (!shouldTurnOffPhysicalChannelConfig()) {
+ newFilter |= IndicationFilter.PHYSICAL_CHANNEL_CONFIG;
+ }
+
setUnsolResponseFilter(newFilter, false);
}
@@ -302,6 +432,8 @@
sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
setUnsolResponseFilter(mUnsolicitedResponseFilter, true);
+ setSignalStrengthReportingCriteria();
+ setLinkCapacityReportingCriteria();
}
/**
@@ -344,6 +476,28 @@
}
}
+ private void setSignalStrengthReportingCriteria() {
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.EUTRAN, AccessNetworkType.EUTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000);
+ }
+
+ private void setLinkCapacityReportingCriteria() {
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.GERAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.UTRAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000);
+ }
+
/**
* @return True if the device is currently in power save mode.
* See {@link android.os.BatteryManager#isPowerSaveMode BatteryManager.isPowerSaveMode()}.
@@ -429,4 +583,85 @@
ipw.decreaseIndent();
ipw.flush();
}
+
+ /**
+ * dBm thresholds that correspond to changes in signal strength indications.
+ */
+ private static final class AccessNetworkThresholds {
+
+ /**
+ * List of dBm thresholds for GERAN {@link AccessNetworkType}.
+ *
+ * Calculated from GSM asu level thresholds - TS 27.007 Sec 8.5
+ */
+ public static final int[] GERAN = new int[] {
+ -109,
+ -103,
+ -97,
+ -89,
+ };
+
+ /**
+ * List of default dBm thresholds for UTRAN {@link AccessNetworkType}.
+ *
+ * These thresholds are taken from the WCDMA RSCP defaults in {@link CarrierConfigManager}.
+ * See TS 27.007 Sec 8.69.
+ */
+ public static final int[] UTRAN = new int[] {
+ -114, /* SIGNAL_STRENGTH_POOR */
+ -104, /* SIGNAL_STRENGTH_MODERATE */
+ -94, /* SIGNAL_STRENGTH_GOOD */
+ -84 /* SIGNAL_STRENGTH_GREAT */
+ };
+
+ /**
+ * List of default dBm thresholds for EUTRAN {@link AccessNetworkType}.
+ *
+ * These thresholds are taken from the LTE RSRP defaults in {@link CarrierConfigManager}.
+ */
+ public static final int[] EUTRAN = new int[] {
+ -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ -128, /* SIGNAL_STRENGTH_POOR */
+ -118, /* SIGNAL_STRENGTH_MODERATE */
+ -108, /* SIGNAL_STRENGTH_GOOD */
+ -98, /* SIGNAL_STRENGTH_GREAT */
+ -44 /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ };
+
+ /**
+ * List of dBm thresholds for CDMA2000 {@link AccessNetworkType}.
+ *
+ * These correspond to EVDO level thresholds.
+ */
+ public static final int[] CDMA2000 = new int[] {
+ -105,
+ -90,
+ -75,
+ -65
+ };
+ }
+
+ /**
+ * Downlink reporting thresholds in kbps
+ *
+ * <p>Threshold values taken from FCC Speed Guide
+ * (https://www.fcc.gov/reports-research/guides/broadband-speed-guide) and Android WiFi speed
+ * labels (https://support.google.com/pixelphone/answer/2819519#strength_speed).
+ */
+ private static final int[] LINK_CAPACITY_DOWNLINK_THRESHOLDS = new int[] {
+ 500, // Web browsing
+ 1000, // SD video streaming
+ 5000, // HD video streaming
+ 10000, // file downloading
+ 20000, // 4K video streaming
+ };
+
+ /** Uplink reporting thresholds in kbps */
+ private static final int[] LINK_CAPACITY_UPLINK_THRESHOLDS = new int[] {
+ 100, // VoIP calls
+ 500,
+ 1000,
+ 5000,
+ 10000,
+ };
}
diff --git a/src/java/com/android/internal/telephony/DriverCall.java b/src/java/com/android/internal/telephony/DriverCall.java
index a923d8f..96e1556 100644
--- a/src/java/com/android/internal/telephony/DriverCall.java
+++ b/src/java/com/android/internal/telephony/DriverCall.java
@@ -37,6 +37,30 @@
// statements that use this enum
}
+ /**
+ * Audio information
+ */
+ /** Unspecified audio codec */
+ public static final int AUDIO_QUALITY_UNSPECIFIED = 0;
+ /** AMR (Narrowband) audio codec */
+ public static final int AUDIO_QUALITY_AMR = 1;
+ /** AMR (Wideband) audio codec */
+ public static final int AUDIO_QUALITY_AMR_WB = 2;
+ /** GSM Enhanced Full-Rate audio codec */
+ public static final int AUDIO_QUALITY_GSM_EFR = 3;
+ /** GSM Full-Rate audio codec */
+ public static final int AUDIO_QUALITY_GSM_FR = 4;
+ /** GSM Half-Rate audio codec */
+ public static final int AUDIO_QUALITY_GSM_HR = 5;
+ /** Enhanced Variable rate codec */
+ public static final int AUDIO_QUALITY_EVRC = 6;
+ /** Enhanced Variable rate codec revision B */
+ public static final int AUDIO_QUALITY_EVRC_B = 7;
+ /** Enhanced Variable rate codec (Wideband) */
+ public static final int AUDIO_QUALITY_EVRC_WB = 8;
+ /** Enhanced Variable rate codec (Narrowband) */
+ public static final int AUDIO_QUALITY_EVRC_NW = 9;
+
public int index;
public boolean isMT;
public State state; // May be null if unavail
@@ -50,6 +74,7 @@
public String name;
public int namePresentation;
public UUSInfo uusInfo;
+ public int audioQuality = AUDIO_QUALITY_UNSPECIFIED;
/** returns null on error */
static DriverCall
@@ -113,7 +138,8 @@
+ (isVoice ? "voc" : "nonvoc") + ","
+ (isVoicePrivacy ? "evp" : "noevp") + ","
/*+ "number=" + number */ + ",cli=" + numberPresentation + ","
- /*+ "name="+ name */ + "," + namePresentation;
+ /*+ "name="+ name */ + "," + namePresentation + ","
+ + "audioQuality=" + audioQuality;
}
public static State
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 5960051..f4c561c 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -1634,7 +1634,7 @@
@Override
protected void log(String msg) {
- Rlog.d(LOG_TAG, "[GsmCdmaCallTracker] " + msg);
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
@Override
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index afef78f..c0f67e2 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -141,6 +141,8 @@
mParent.attach(this, dc);
fetchDtmfToneDelay(phone);
+
+ setAudioQuality(getAudioQualityFromDC(dc.audioQuality));
}
/** This is an MO call, created when dialing */
@@ -355,6 +357,12 @@
}
@Override
+ public void deflect(String number) throws CallStateException {
+ // Deflect is not supported.
+ throw new CallStateException ("deflect is not supported for CS");
+ }
+
+ @Override
public void separate() throws CallStateException {
if (!mDisconnected) {
mOwner.separate(this);
@@ -504,6 +512,9 @@
case CallFailCause.NORMAL_UNSPECIFIED:
return DisconnectCause.NORMAL_UNSPECIFIED;
+ case CallFailCause.USER_ALERTING_NO_ANSWER:
+ return DisconnectCause.TIMED_OUT;
+
case CallFailCause.ERROR_UNSPECIFIED:
case CallFailCause.NORMAL_CLEARING:
default:
@@ -580,6 +591,7 @@
if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
mOwner.getPhone().notifyDisconnect(this);
+ notifyDisconnect(cause);
if (mParent != null) {
changed = mParent.connectionDisconnected(this);
@@ -631,6 +643,17 @@
}
}
+ int newAudioQuality = getAudioQualityFromDC(dc.audioQuality);
+ if (getAudioQuality() != newAudioQuality) {
+ if (Phone.DEBUG_PHONE) {
+ log("update: audioQuality # changed!: "
+ + (newAudioQuality == Connection.AUDIO_QUALITY_HIGH_DEFINITION
+ ? "high" : "standard"));
+ }
+ setAudioQuality(newAudioQuality);
+ changed = true;
+ }
+
// A null cnapName should be the same as ""
if (TextUtils.isEmpty(dc.name)) {
if (!TextUtils.isEmpty(mCnapName)) {
@@ -923,6 +946,16 @@
}
}
+ private int getAudioQualityFromDC(int audioQuality) {
+ switch (audioQuality) {
+ case DriverCall.AUDIO_QUALITY_AMR_WB:
+ case DriverCall.AUDIO_QUALITY_EVRC_NW:
+ return Connection.AUDIO_QUALITY_HIGH_DEFINITION;
+ default:
+ return Connection.AUDIO_QUALITY_STANDARD;
+ }
+ }
+
/**
* Set post dial state and acquire wake lock while switching to "started" or "pause"
* state, the wake lock will be released if state switches out of "started" or "pause"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 903aacb..5dd36ad 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -28,6 +28,7 @@
import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -76,7 +77,7 @@
import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.test.SimulatedRadioControl;
-import com.android.internal.telephony.uicc.IccCardProxy;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccException;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccVmNotSupportedException;
@@ -87,6 +88,8 @@
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -106,6 +109,13 @@
private static final boolean DBG = true;
private static final boolean VDBG = false; /* STOPSHIP if true */
+ /** Required magnitude change between unsolicited SignalStrength reports. */
+ private static final int REPORTING_HYSTERESIS_DB = 2;
+ /** Required throughput change between unsolicited LinkCapacityEstimate reports. */
+ private static final int REPORTING_HYSTERESIS_KBPS = 50;
+ /** Minimum time between unsolicited SignalStrength and LinkCapacityEstimate reports. */
+ private static final int REPORTING_HYSTERESIS_MILLIS = 3000;
+
//GSM
// Key used to read/write voice mail number
private static final String VM_NUMBER = "vm_number_key";
@@ -156,7 +166,8 @@
public ServiceStateTracker mSST;
private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>();
private IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
- private DeviceStateMonitor mDeviceStateMonitor;
+ // Used for identify the carrier of current subscription
+ private CarrierIdentifier mCarrerIdentifier;
private int mPrecisePhoneType;
@@ -181,12 +192,13 @@
}
private IccSmsInterfaceManager mIccSmsInterfaceManager;
- private IccCardProxy mIccCardProxy;
private boolean mResetModemOnRadioTechnologyChange = false;
private int mRilVersion;
private boolean mBroadcastEmergencyCallStateChanges = false;
+ private CarrierKeyDownloadManager mCDM;
+ private CarrierInfoManager mCIM;
// Constructors
@@ -212,6 +224,8 @@
mSST = mTelephonyComponentFactory.makeServiceStateTracker(this, this.mCi);
// DcTracker uses SST so needs to be created after it is instantiated
mDcTracker = mTelephonyComponentFactory.makeDcTracker(this);
+ mCarrerIdentifier = mTelephonyComponentFactory.makeCarrierIdentifier(this);
+
mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
mDeviceStateMonitor = mTelephonyComponentFactory.makeDeviceStateMonitor(this);
logd("GsmCdmaPhone: constructor: sub = " + mPhoneId);
@@ -238,7 +252,6 @@
= (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
mIccSmsInterfaceManager = mTelephonyComponentFactory.makeIccSmsInterfaceManager(this);
- mIccCardProxy = mTelephonyComponentFactory.makeIccCardProxy(mContext, mCi, mPhoneId);
mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
@@ -269,6 +282,8 @@
mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mCDM = new CarrierKeyDownloadManager(this);
+ mCIM = new CarrierInfoManager();
}
private void initRatSpecific(int precisePhoneType) {
@@ -278,12 +293,16 @@
mMeid = null;
mPrecisePhoneType = precisePhoneType;
+ logd("Precise phone type " + mPrecisePhoneType);
TelephonyManager tm = TelephonyManager.from(mContext);
+ UiccProfile uiccProfile = getUiccProfile();
if (isPhoneTypeGsm()) {
mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM);
- mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
+ if (uiccProfile != null) {
+ uiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
+ }
} else {
mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
// This is needed to handle phone process crashes
@@ -296,31 +315,30 @@
mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_CDMA);
- mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+ if (uiccProfile != null) {
+ uiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+ }
// Sets operator properties by retrieving from build-time system property
String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC);
logd("init: operatorAlpha='" + operatorAlpha
+ "' operatorNumeric='" + operatorNumeric + "'");
- if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) ==
- null || isPhoneTypeCdmaLte()) {
- if (!TextUtils.isEmpty(operatorAlpha)) {
- logd("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'");
- tm.setSimOperatorNameForPhone(mPhoneId, operatorAlpha);
- }
- if (!TextUtils.isEmpty(operatorNumeric)) {
- logd("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric +
- "'");
- logd("update icc_operator_numeric=" + operatorNumeric);
- tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric);
+ if (!TextUtils.isEmpty(operatorAlpha)) {
+ logd("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'");
+ tm.setSimOperatorNameForPhone(mPhoneId, operatorAlpha);
+ }
+ if (!TextUtils.isEmpty(operatorNumeric)) {
+ logd("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric +
+ "'");
+ logd("update icc_operator_numeric=" + operatorNumeric);
+ tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric);
- SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
- // Sets iso country property by retrieving from build-time system property
- setIsoCountryProperty(operatorNumeric);
- // Updates MCC MNC device configuration information
- logd("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
- }
+ SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
+ // Sets iso country property by retrieving from build-time system property
+ setIsoCountryProperty(operatorNumeric);
+ // Updates MCC MNC device configuration information
+ logd("update mccmnc=" + operatorNumeric);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
}
// Sets current entry in the telephony carrier table
@@ -341,10 +359,7 @@
} else {
String iso = "";
try {
- iso = MccTable.countryCodeForMcc(Integer.parseInt(
- operatorNumeric.substring(0,3)));
- } catch (NumberFormatException ex) {
- Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex);
+ iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3));
} catch (StringIndexOutOfBoundsException ex) {
Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex);
}
@@ -515,12 +530,6 @@
ret = PhoneConstants.DataState.DISCONNECTED;
} else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
switch (mDcTracker.getState(apnType)) {
- case RETRYING:
- case FAILED:
- case IDLE:
- ret = PhoneConstants.DataState.DISCONNECTED;
- break;
-
case CONNECTED:
case DISCONNECTING:
if ( mCT.mState != PhoneConstants.State.IDLE
@@ -529,12 +538,12 @@
} else {
ret = PhoneConstants.DataState.CONNECTED;
}
- break;
-
+ break;
case CONNECTING:
- case SCANNING:
ret = PhoneConstants.DataState.CONNECTING;
- break;
+ break;
+ default:
+ ret = PhoneConstants.DataState.DISCONNECTED;
}
}
@@ -664,22 +673,6 @@
mNotifier.notifyCallForwardingChanged(this);
}
- // override for allowing access from other classes of this package
- /**
- * {@inheritDoc}
- */
- @Override
- public void setSystemProperty(String property, String value) {
- if (getUnitTestMode()) {
- return;
- }
- if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
- TelephonyManager.setTelephonyProperty(mPhoneId, property, value);
- } else {
- super.setSystemProperty(property, value);
- }
- }
-
@Override
public void registerForSuppServiceNotification(
Handler h, int what, Object obj) {
@@ -1043,14 +1036,9 @@
}
@Override
- public Connection dial(String dialString, int videoState) throws CallStateException {
- return dial(dialString, null, videoState, null);
- }
-
- @Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ public Connection dial(String dialString, @NonNull DialArgs dialArgs)
throws CallStateException {
- if (!isPhoneTypeGsm() && uusInfo != null) {
+ if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
throw new CallStateException("Sending UUS information NOT supported in CDMA!");
}
@@ -1065,13 +1053,13 @@
boolean useImsForCall = isImsUseEnabled()
&& imsPhone != null
&& (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
- (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState)))
+ (imsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
&& (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
boolean useImsForEmergency = imsPhone != null
&& isEmergency
&& alwaysTryImsForEmergencyCarrierConfig
- && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
+ && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
&& imsPhone.isImsAvailable();
String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
@@ -1097,12 +1085,12 @@
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
- Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
+ Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);
if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
try {
if (DBG) logd("Trying IMS PS call");
- return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
+ return imsPhone.dial(dialString, dialArgs);
} catch (CallStateException e) {
if (DBG) logd("IMS PS call exception " + e +
"useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
@@ -1125,8 +1113,9 @@
}
// Check non-emergency voice CS call - shouldn't dial when POWER_OFF
if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
- && !VideoProfile.isVideo(videoState) /* voice call */
- && !isEmergency /* non-emergency call */) {
+ && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
+ && !isEmergency /* non-emergency call */
+ && !(isUt && useImsForUt) /* not UT */) {
throw new CallStateException(
CallStateException.ERROR_POWER_OFF,
"cannot dial voice call in airplane mode");
@@ -1137,7 +1126,7 @@
&& mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */
&& !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
&& ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */
- && !VideoProfile.isVideo(videoState) /* voice call */
+ && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
&& !isEmergency /* non-emergency call */) {
throw new CallStateException(
CallStateException.ERROR_OUT_OF_SERVICE,
@@ -1146,9 +1135,11 @@
if (DBG) logd("Trying (non-IMS) CS call");
if (isPhoneTypeGsm()) {
- return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
+ return dialInternal(dialString, new DialArgs.Builder<>()
+ .setIntentExtras(dialArgs.intentExtras)
+ .build());
} else {
- return dialInternal(dialString, null, videoState, intentExtras);
+ return dialInternal(dialString, dialArgs);
}
}
@@ -1189,14 +1180,13 @@
}
@Override
- protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
- Bundle intentExtras)
+ protected Connection dialInternal(String dialString, DialArgs dialArgs)
throws CallStateException {
- return dialInternal(dialString, uusInfo, videoState, intentExtras, null);
+ return dialInternal(dialString, dialArgs, null);
}
- protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
- Bundle intentExtras, ResultReceiver wrappedCallback)
+ protected Connection dialInternal(String dialString, DialArgs dialArgs,
+ ResultReceiver wrappedCallback)
throws CallStateException {
// Need to make sure dialString gets parsed properly
@@ -1215,9 +1205,10 @@
if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
- return mCT.dial(newDialString, uusInfo, intentExtras);
+ return mCT.dial(newDialString, dialArgs.uusInfo, dialArgs.intentExtras);
} else if (mmi.isTemporaryModeCLIR()) {
- return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
+ return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,
+ dialArgs.intentExtras);
} else {
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
@@ -1290,8 +1281,7 @@
// Try USSD over GSM.
try {
- dialInternal(ussdRequest, null, VideoProfile.STATE_AUDIO_ONLY, null,
- wrappedCallback);
+ dialInternal(ussdRequest, new DialArgs.Builder<>().build(), wrappedCallback);
} catch (Exception e) {
logd("handleUssdRequest: exception" + e);
return false;
@@ -1405,8 +1395,11 @@
if (!isPhoneTypeGsm() && TextUtils.isEmpty(number)) {
// Read platform settings for dynamic voicemail number
- if (getContext().getResources().getBoolean(com.android.internal
- .R.bool.config_telephony_use_own_number_for_voicemail)) {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configManager.getConfig();
+ if (b != null && b.getBoolean(
+ CarrierConfigManager.KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL)) {
number = getLine1Number();
} else {
number = "*86";
@@ -1524,12 +1517,48 @@
@Override
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
- return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType);
+ return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType, mContext);
}
@Override
public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
- CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+ CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext);
+ }
+
+ @Override
+ public int getCarrierId() {
+ return mCarrerIdentifier.getCarrierId();
+ }
+
+ @Override
+ public String getCarrierName() {
+ return mCarrerIdentifier.getCarrierName();
+ }
+
+ @Override
+ public void resetCarrierKeysForImsiEncryption() {
+ mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId);
+ }
+
+ @Override
+ public int getCarrierIdListVersion() {
+ return mCarrerIdentifier.getCarrierListVersion();
+ }
+
+ @Override
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ IccRecords r = null;
+ if (isPhoneTypeGsm()) {
+ r = mIccRecords.get();
+ } else if (isPhoneTypeCdmaLte()) {
+ r = mSimRecords;
+ } else {
+ loge("setCarrierTestOverride fails in CDMA only");
+ }
+ if (r != null) {
+ r.setCarrierTestOverride(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
+ }
}
@Override
@@ -1569,6 +1598,19 @@
}
@Override
+ public String getPlmn() {
+ if (isPhoneTypeGsm()) {
+ IccRecords r = mIccRecords.get();
+ return (r != null) ? r.getPnnHomeName() : null;
+ } else if (isPhoneTypeCdma()) {
+ loge("Plmn is not available in CDMA");
+ return null;
+ } else { //isPhoneTypeCdmaLte()
+ return (mSimRecords != null) ? mSimRecords.getPnnHomeName() : null;
+ }
+ }
+
+ @Override
public String getCdmaPrlVersion() {
return mSST.getPrlVersion();
}
@@ -1628,7 +1670,13 @@
Message resp;
mVmNumber = voiceMailNumber;
resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+
IccRecords r = mIccRecords.get();
+
+ if (!isPhoneTypeGsm() && mSimRecords != null) {
+ r = mSimRecords;
+ }
+
if (r != null) {
r.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
@@ -1650,14 +1698,10 @@
@Override
public String getSystemProperty(String property, String defValue) {
- if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
- if (getUnitTestMode()) {
- return null;
- }
- return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue);
- } else {
- return super.getSystemProperty(property, defValue);
+ if (getUnitTestMode()) {
+ return null;
}
+ return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue);
}
private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
@@ -1695,7 +1739,8 @@
} else {
resp = onComplete;
}
- mCi.queryCallForwardStatus(commandInterfaceCFReason, 0, null, resp);
+ mCi.queryCallForwardStatus(commandInterfaceCFReason,
+ CommandsInterface.SERVICE_CLASS_VOICE, null, resp);
}
} else {
loge("getCallForwardingOption: not possible in CDMA");
@@ -1742,11 +1787,63 @@
}
@Override
+ public void getCallBarring(String facility, String password, Message onComplete,
+ int serviceClass) {
+ if (isPhoneTypeGsm()) {
+ Phone imsPhone = mImsPhone;
+ if ((imsPhone != null)
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled())) {
+ imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
+ return;
+ }
+ mCi.queryFacilityLock(facility, password, serviceClass, onComplete);
+ } else {
+ loge("getCallBarringOption: not possible in CDMA");
+ }
+ }
+
+ @Override
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete, int serviceClass) {
+ if (isPhoneTypeGsm()) {
+ Phone imsPhone = mImsPhone;
+ if ((imsPhone != null)
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled())) {
+ imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
+ return;
+ }
+ mCi.setFacilityLock(facility, lockState, password, serviceClass, onComplete);
+ } else {
+ loge("setCallBarringOption: not possible in CDMA");
+ }
+ }
+
+ /**
+ * Changes access code used for call barring
+ *
+ * @param facility is one of CB_FACILTY_*
+ * @param oldPwd is old password
+ * @param newPwd is new password
+ * @param onComplete is callback message when the action is completed.
+ */
+ public void changeCallBarringPassword(String facility, String oldPwd, String newPwd,
+ Message onComplete) {
+ if (isPhoneTypeGsm()) {
+ mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete);
+ } else {
+ loge("changeCallBarringPassword: not possible in CDMA");
+ }
+ }
+
+ @Override
public void getOutgoingCallerIdDisplay(Message onComplete) {
if (isPhoneTypeGsm()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
- && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled())) {
imsPhone.getOutgoingCallerIdDisplay(onComplete);
return;
}
@@ -1761,7 +1858,8 @@
if (isPhoneTypeGsm()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
- && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+ || imsPhone.isUtEnabled())) {
imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
return;
}
@@ -1879,11 +1977,6 @@
}
@Override
- public void getDataCallList(Message response) {
- mCi.getDataCallList(response);
- }
-
- @Override
public void updateServiceLocation() {
mSST.enableSingleLocationUpdate();
}
@@ -1949,13 +2042,18 @@
}
@Override
- public boolean getDataEnabled() {
- return mDcTracker.getDataEnabled();
+ public boolean isUserDataEnabled() {
+ return mDcTracker.isUserDataEnabled();
}
@Override
- public void setDataEnabled(boolean enable) {
- mDcTracker.setDataEnabled(enable);
+ public boolean isDataEnabled() {
+ return mDcTracker.isDataEnabled();
+ }
+
+ @Override
+ public void setUserDataEnabled(boolean enable) {
+ mDcTracker.setUserDataEnabled(enable);
}
/**
@@ -2211,8 +2309,14 @@
config_switch_phone_on_voice_reg_state_change)) {
mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE));
}
- // Force update IMS service
- ImsManager.updateImsServiceConfig(mContext, mPhoneId, true);
+ // Force update IMS service if it is available, if it isn't the config will be
+ // updated when ImsPhoneCallTracker opens a connection.
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ if (imsManager.isServiceAvailable()) {
+ imsManager.updateImsServiceConfig(true);
+ } else {
+ logd("ImsManager is not available to update CarrierConfig.");
+ }
// Update broadcastEmergencyCallStateChanges
CarrierConfigManager configMgr = (CarrierConfigManager)
@@ -2269,9 +2373,7 @@
// Load the ERI based on carrier config. Carrier might have their specific ERI.
prepareEri();
- if (!isPhoneTypeGsm()) {
- mSST.pollState();
- }
+ mSST.pollState();
break;
@@ -2474,6 +2576,9 @@
}
}
+ // todo: check if ICC availability needs to be handled here. mSimRecords should not be needed
+ // now because APIs can be called directly on UiccProfile, and that should handle the requests
+ // correctly based on supported apps, voice RAT, etc.
@Override
protected void onUpdateIccAvailability() {
if (mUiccController == null ) {
@@ -2499,7 +2604,7 @@
if (mSimRecords != null) {
mSimRecords.unregisterForRecordsLoaded(this);
}
- if (isPhoneTypeCdmaLte()) {
+ if (isPhoneTypeCdmaLte() || isPhoneTypeCdma()) {
newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId,
UiccController.APP_FAM_3GPP);
SIMRecords newSimRecords = null;
@@ -2548,6 +2653,7 @@
private void processIccRecordEvents(int eventCode) {
switch (eventCode) {
case IccRecords.EVENT_CFI:
+ logi("processIccRecordEvents: EVENT_CFI");
notifyCallForwardingIndicator();
break;
}
@@ -2560,28 +2666,24 @@
*/
@Override
public boolean updateCurrentCarrierInProvider() {
- if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
- long currentDds = SubscriptionManager.getDefaultDataSubscriptionId();
- String operatorNumeric = getOperatorNumeric();
+ long currentDds = SubscriptionManager.getDefaultDataSubscriptionId();
+ String operatorNumeric = getOperatorNumeric();
- logd("updateCurrentCarrierInProvider: mSubId = " + getSubId()
- + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric);
+ logd("updateCurrentCarrierInProvider: mSubId = " + getSubId()
+ + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric);
- if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) {
- try {
- Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
- ContentValues map = new ContentValues();
- map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
- mContext.getContentResolver().insert(uri, map);
- return true;
- } catch (SQLException e) {
- Rlog.e(LOG_TAG, "Can't store current operator", e);
- }
+ if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) {
+ try {
+ Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
+ ContentValues map = new ContentValues();
+ map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
+ mContext.getContentResolver().insert(uri, map);
+ return true;
+ } catch (SQLException e) {
+ Rlog.e(LOG_TAG, "Can't store current operator", e);
}
- return false;
- } else {
- return true;
}
+ return false;
}
//CDMA
@@ -2606,7 +2708,7 @@
// Updates MCC MNC device configuration information
logd("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
return true;
} catch (SQLException e) {
@@ -3243,8 +3345,11 @@
mCi.setRadioPower(oldPowerState, null);
}
- // update voice radio tech in icc card proxy
- mIccCardProxy.setVoiceRadioTech(newVoiceRadioTech);
+ // update voice radio tech in UiccProfile
+ UiccProfile uiccProfile = getUiccProfile();
+ if (uiccProfile != null) {
+ uiccProfile.setVoiceRadioTech(newVoiceRadioTech);
+ }
// Send an Intent to the PhoneApp that we had a radio technology change
Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
@@ -3261,7 +3366,13 @@
+ (ServiceState.isGsm(newVoiceRadioTech) ? "GSM" : "CDMA"));
if (ServiceState.isCdma(newVoiceRadioTech)) {
- switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA_LTE);
+ UiccCardApplication cdmaApplication =
+ mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2);
+ if (cdmaApplication != null && cdmaApplication.getType() == AppType.APPTYPE_RUIM) {
+ switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
+ } else {
+ switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA_LTE);
+ }
} else if (ServiceState.isGsm(newVoiceRadioTech)) {
switchPhoneType(PhoneConstants.PHONE_TYPE_GSM);
} else {
@@ -3272,6 +3383,18 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
+ mCi.setSignalStrengthReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB,
+ thresholds, ran, null);
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) {
+ mCi.setLinkCapacityReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_KBPS,
+ REPORTING_HYSTERESIS_KBPS, dlThresholds, ulThresholds, ran, null);
+ }
+
+ @Override
public IccSmsInterfaceManager getIccSmsInterfaceManager(){
return mIccSmsInterfaceManager;
}
@@ -3289,12 +3412,30 @@
@Override
public boolean getIccRecordsLoaded() {
- return mIccCardProxy.getIccRecordsLoaded();
+ UiccProfile uiccProfile = getUiccProfile();
+ return uiccProfile != null && uiccProfile.getIccRecordsLoaded();
}
@Override
public IccCard getIccCard() {
- return mIccCardProxy;
+ // This function doesn't return null for backwards compatability purposes.
+ // To differentiate between cases where SIM is absent vs. unknown we return a dummy
+ // IccCard with the sim state set.
+ IccCard card = getUiccProfile();
+ if (card != null) {
+ return card;
+ } else {
+ UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId);
+ if (slot == null || slot.isStateUnknown()) {
+ return new IccCard(IccCardConstants.State.UNKNOWN);
+ } else {
+ return new IccCard(IccCardConstants.State.ABSENT);
+ }
+ }
+ }
+
+ private UiccProfile getUiccProfile() {
+ return UiccController.getInstance().getUiccProfileForPhone(mPhoneId);
}
@Override
@@ -3325,18 +3466,6 @@
}
pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled());
pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
-
- try {
- mIccCardProxy.dump(fd, pw, args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
- pw.println("DeviceStateMonitor:");
- mDeviceStateMonitor.dump(fd, pw, args);
- pw.println("++++++++++++++++++++++++++++++++");
}
@Override
@@ -3381,8 +3510,16 @@
if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_NV) {
operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric");
} else if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_RUIM_SIM) {
- curIccRecords = mSimRecords;
- if (curIccRecords != null) {
+ UiccCardApplication uiccCardApplication = mUiccApplication.get();
+ if (uiccCardApplication != null
+ && uiccCardApplication.getType() == AppType.APPTYPE_RUIM) {
+ logd("Legacy RUIM app present");
+ curIccRecords = mIccRecords.get();
+ } else {
+ // Use sim-records for SimApp, USimApp, CSimApp and ISimApp.
+ curIccRecords = mSimRecords;
+ }
+ if (curIccRecords != null && curIccRecords == mSimRecords) {
operatorNumeric = curIccRecords.getOperatorNumeric();
} else {
curIccRecords = mIccRecords.get();
@@ -3462,15 +3599,15 @@
}
private void logd(String s) {
- Rlog.d(LOG_TAG, "[GsmCdmaPhone] " + s);
+ Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
}
private void logi(String s) {
- Rlog.i(LOG_TAG, "[GsmCdmaPhone] " + s);
+ Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
}
private void loge(String s) {
- Rlog.e(LOG_TAG, "[GsmCdmaPhone] " + s);
+ Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
}
@Override
@@ -3495,4 +3632,22 @@
return mWakeLock;
}
+ @Override
+ public int getLteOnCdmaMode() {
+ int currentConfig = super.getLteOnCdmaMode();
+ int lteOnCdmaModeDynamicValue = currentConfig;
+
+ UiccCardApplication cdmaApplication =
+ mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2);
+ if (cdmaApplication != null && cdmaApplication.getType() == AppType.APPTYPE_RUIM) {
+ //Legacy RUIM cards don't support LTE.
+ lteOnCdmaModeDynamicValue = RILConstants.LTE_ON_CDMA_FALSE;
+
+ //Override only if static configuration is TRUE.
+ if (currentConfig == RILConstants.LTE_ON_CDMA_TRUE) {
+ return lteOnCdmaModeDynamicValue;
+ }
+ }
+ return currentConfig;
+ }
}
diff --git a/src/java/com/android/internal/telephony/IccCard.java b/src/java/com/android/internal/telephony/IccCard.java
index e5b34e2..7bab408 100644
--- a/src/java/com/android/internal/telephony/IccCard.java
+++ b/src/java/com/android/internal/telephony/IccCard.java
@@ -16,12 +16,12 @@
package com.android.internal.telephony;
+import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
-import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
/**
@@ -35,42 +35,51 @@
* Apps (those that have access to Phone object) can retrieve this object
* by calling phone.getIccCard()
*
- * This interface is implemented by IccCardProxy and the object PhoneApp
- * gets when it calls getIccCard is IccCardProxy.
+ * This interface is implemented by UiccProfile and the object PhoneApp
+ * gets when it calls getIccCard is UiccProfile.
*/
-public interface IccCard {
+public class IccCard {
+ private State mIccCardState = State.UNKNOWN;
+
+ /**
+ * Empty constructor.
+ */
+ public IccCard() {}
+
+ /**
+ * Set the state of the IccCard to be returned in {@link getState}.
+ */
+ public IccCard(State state) {
+ mIccCardState = state;
+ }
+
/**
* @return combined Card and current App state
*/
- public State getState();
+ public State getState() {
+ return mIccCardState;
+ }
+ // todo: delete
/**
* @return IccRecords object belonging to current UiccCardApplication
*/
- public IccRecords getIccRecords();
-
- /**
- * @return IccFileHandler object belonging to current UiccCardApplication
- */
- public IccFileHandler getIccFileHandler();
-
- /**
- * Notifies handler of any transition into IccCardConstants.State.ABSENT
- */
- public void registerForAbsent(Handler h, int what, Object obj);
- public void unregisterForAbsent(Handler h);
+ public IccRecords getIccRecords() {
+ return null;
+ }
/**
* Notifies handler of any transition into IccCardConstants.State.NETWORK_LOCKED
*/
- public void registerForNetworkLocked(Handler h, int what, Object obj);
- public void unregisterForNetworkLocked(Handler h);
-
+ public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ return;
+ }
/**
- * Notifies handler of any transition into IccCardConstants.State.isPinLocked()
+ * Unregister for networkLocked state change.
*/
- public void registerForLocked(Handler h, int what, Object obj);
- public void unregisterForLocked(Handler h);
+ public void unregisterForNetworkLocked(Handler h) {
+ return;
+ }
/**
* Supply the ICC PIN to the ICC
@@ -90,34 +99,37 @@
* && ((CommandException)(((AsyncResult)onComplete.obj).exception))
* .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
*/
- public void supplyPin (String pin, Message onComplete);
+ public void supplyPin(String pin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PUK to the ICC
*/
- public void supplyPuk (String puk, String newPin, Message onComplete);
+ public void supplyPuk(String puk, String newPin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PIN2 to the ICC
*/
- public void supplyPin2 (String pin2, Message onComplete);
+ public void supplyPin2(String pin2, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PUK2 to the ICC
*/
- public void supplyPuk2 (String puk2, String newPin2, Message onComplete);
-
- /**
- * Check whether fdn (fixed dialing number) service is available.
- * @return true if ICC fdn service available
- * false if ICC fdn service not available
- */
- public boolean getIccFdnAvailable();
+ public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply Network depersonalization code to the RIL
*/
- public void supplyNetworkDepersonalization (String pin, Message onComplete);
+ public void supplyNetworkDepersonalization(String pin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Check whether ICC pin lock is enabled
@@ -126,7 +138,18 @@
* @return true for ICC locked enabled
* false for ICC locked disabled
*/
- public boolean getIccLockEnabled();
+ public boolean getIccLockEnabled() {
+ return false;
+ }
+
+ /**
+ * Check whether fdn (fixed dialing number) service is available.
+ * @return true if ICC fdn service available
+ * false if ICC fdn service not available
+ */
+ public boolean getIccFdnAvailable() {
+ return false;
+ }
/**
* Check whether ICC fdn (fixed dialing number) is enabled
@@ -135,7 +158,9 @@
* @return true for ICC fdn enabled
* false for ICC fdn disabled
*/
- public boolean getIccFdnEnabled();
+ public boolean getIccFdnEnabled() {
+ return false;
+ }
/**
* Set the ICC pin lock enabled or disabled
@@ -148,8 +173,10 @@
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
- public void setIccLockEnabled (boolean enabled,
- String password, Message onComplete);
+ public void setIccLockEnabled(boolean enabled,
+ String password, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Set the ICC fdn enabled or disabled
@@ -162,8 +189,10 @@
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
- public void setIccFdnEnabled (boolean enabled,
- String password, Message onComplete);
+ public void setIccFdnEnabled(boolean enabled,
+ String password, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Change the ICC password used in ICC pin lock
@@ -177,7 +206,9 @@
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccLockPassword(String oldPassword, String newPassword,
- Message onComplete);
+ Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Change the ICC password used in ICC fdn enable
@@ -191,7 +222,9 @@
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccFdnPassword(String oldPassword, String newPassword,
- Message onComplete);
+ Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Returns service provider name stored in ICC card.
@@ -209,26 +242,42 @@
* yet available
*
*/
- public String getServiceProviderName ();
+ public String getServiceProviderName() {
+ return null;
+ }
/**
* Checks if an Application of specified type present on the card
* @param type is AppType to look for
*/
- public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type);
+ public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
+ return false;
+ }
/**
* @return true if a ICC card is present
*/
- public boolean hasIccCard();
+ public boolean hasIccCard() {
+ return false;
+ }
/**
* @return true if ICC card is PIN2 blocked
*/
- public boolean getIccPin2Blocked();
+ public boolean getIccPin2Blocked() {
+ return false;
+ }
/**
* @return true if ICC card is PUK2 blocked
*/
- public boolean getIccPuk2Blocked();
+ public boolean getIccPuk2Blocked() {
+ return false;
+ }
+
+ private void sendMessageWithCardAbsentException(Message onComplete) {
+ AsyncResult ret = AsyncResult.forMessage(onComplete);
+ ret.exception = new RuntimeException("No valid IccCard");
+ onComplete.sendToTarget();
+ }
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 0fc08c6..d7a45d8 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -40,17 +40,19 @@
import android.telephony.Rlog;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
-import android.telephony.TelephonyManager;
+import android.util.LocalLog;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.HexDump;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -78,12 +80,16 @@
protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
private static final int SMS_CB_CODE_SCHEME_MIN = 0;
private static final int SMS_CB_CODE_SCHEME_MAX = 255;
+ public static final int SMS_MESSAGE_PRIORITY_NOT_SPECIFIED = -1;
+ public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
protected Phone mPhone;
final protected Context mContext;
final protected AppOpsManager mAppOps;
final private UserManager mUserManager;
- protected SMSDispatcher mDispatcher;
+ protected SmsDispatchersController mDispatchersController;
+
+ private final LocalLog mCellBroadcastLocalLog = new LocalLog(100);
protected Handler mHandler = new Handler() {
@Override
@@ -127,12 +133,22 @@
};
protected IccSmsInterfaceManager(Phone phone) {
+ this(phone, phone.getContext(),
+ (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE),
+ (UserManager) phone.getContext().getSystemService(Context.USER_SERVICE),
+ new SmsDispatchersController(
+ phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor));
+ }
+
+ @VisibleForTesting
+ public IccSmsInterfaceManager(
+ Phone phone, Context context, AppOpsManager appOps, UserManager userManager,
+ SmsDispatchersController dispatchersController) {
mPhone = phone;
- mContext = phone.getContext();
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mDispatcher = new ImsSMSDispatcher(phone,
- phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
+ mContext = context;
+ mAppOps = appOps;
+ mUserManager = userManager;
+ mDispatchersController = dispatchersController;
}
protected void markMessagesAsRead(ArrayList<byte[]> messages) {
@@ -170,7 +186,7 @@
protected void updatePhoneObject(Phone phone) {
mPhone = phone;
- mDispatcher.updatePhoneObject(phone);
+ mDispatchersController.updatePhoneObject(phone);
}
protected void enforceReceiveAndSend(String message) {
@@ -321,11 +337,10 @@
*/
public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
- deliveryIntent);
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
+ sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
}
/**
@@ -334,11 +349,10 @@
*/
public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
- deliveryIntent);
+ if (!checkCallingSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
+ sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
}
/**
@@ -367,19 +381,16 @@
* raw pdu of the status report is in the extended data ("pdu").
*/
- private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
+ private void sendDataInternal(String destAddr, String scAddr,
int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
destAddr = filterDestAddress(destAddr);
- mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+ mDispatchersController.sendData(destAddr, scAddr, destPort, data, sentIntent,
+ deliveryIntent);
}
/**
@@ -389,11 +400,13 @@
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingSendTextPermissions(
+ persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) {
+ return;
+ }
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
- persistMessageForNonDefaultSmsApp);
+ persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
}
/**
@@ -403,11 +416,12 @@
public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessage) {
- mPhone.getContext().enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
- persistMessage);
+ persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
+ SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
}
/**
@@ -433,26 +447,101 @@
* @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 persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @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 including 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 including negative considered as Invalid Validity Period of the message.
*/
private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
- boolean persistMessageForNonDefaultSmsApp) {
+ boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+ int validityPeriod) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
- sentIntent + " deliveryIntent=" + deliveryIntent);
- }
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- if (!persistMessageForNonDefaultSmsApp) {
- enforcePrivilegedAppPermissions();
+ sentIntent + " deliveryIntent=" + deliveryIntent
+ + " priority=" + priority + " expectMore=" + expectMore
+ + " validityPeriod=" + validityPeriod);
}
destAddr = filterDestAddress(destAddr);
- mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
- null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
+ mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+ null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
+ priority, expectMore, validityPeriod);
+ }
+
+ /**
+ * Send a text based SMS with Messaging Options.
+ *
+ * @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>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> 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
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @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 including 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 including negative considered as Invalid Validity Period of the message.
+ */
+
+ public void sendTextWithOptions(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+ int validityPeriod) {
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
+ sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+ persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod);
}
/**
@@ -466,13 +555,27 @@
* the same time an SMS received from radio is acknowledged back.
*/
public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- enforcePrivilegedAppPermissions();
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCallerIsImsAppOrCarrierApp("injectSmsPdu");
+ }
+
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("pdu: " + pdu +
"\n format=" + format +
"\n receivedIntent=" + receivedIntent);
}
- mDispatcher.injectSmsPdu(pdu, format, receivedIntent);
+ mDispatchersController.injectSmsPdu(pdu, format,
+ result -> {
+ if (receivedIntent != null) {
+ try {
+ receivedIntent.send(result);
+ } catch (PendingIntent.CanceledException e) {
+ Rlog.d(LOG_TAG, "receivedIntent cancelled.");
+ }
+ }
+ }
+ );
}
/**
@@ -504,24 +607,74 @@
public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- if (!persistMessageForNonDefaultSmsApp) {
- // Only allow carrier app or carrier ims to skip auto message persistence.
- enforcePrivilegedAppPermissions();
+ sendMultipartTextWithOptions(callingPackage, destAddr, scAddr, parts, sentIntents,
+ deliveryIntents, persistMessageForNonDefaultSmsApp,
+ SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
+ SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
+ }
+
+ /**
+ * Send a multi-part text based SMS with Messaging Options.
+ *
+ * @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 parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * 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 deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @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 including 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 including negative considered as Invalid Validity Period of the message.
+ */
+
+ public void sendMultipartTextWithOptions(String callingPackage, String destAddr,
+ String scAddr, List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
+ int priority, boolean expectMore, int validityPeriod) {
+ if (!checkCallingSendTextPermissions(
+ persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) {
+ return;
}
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
int i = 0;
for (String part : parts) {
- log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
+ log("sendMultipartTextWithOptions: destAddr=" + destAddr + ", srAddr=" + scAddr +
", part[" + (i++) + "]=" + part);
}
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
destAddr = filterDestAddress(destAddr);
@@ -546,27 +699,31 @@
singleDeliveryIntent = deliveryIntents.get(i);
}
- mDispatcher.sendText(destAddr, scAddr, singlePart,
+ mDispatchersController.sendText(destAddr, scAddr, singlePart,
singleSentIntent, singleDeliveryIntent,
null/*messageUri*/, callingPackage,
- persistMessageForNonDefaultSmsApp);
+ persistMessageForNonDefaultSmsApp,
+ priority, expectMore, validityPeriod);
}
return;
}
- mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
- (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
- null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
+ mDispatchersController.sendMultipartText(destAddr,
+ scAddr,
+ (ArrayList<String>) parts,
+ (ArrayList<PendingIntent>) sentIntents,
+ (ArrayList<PendingIntent>) deliveryIntents,
+ null, callingPackage, persistMessageForNonDefaultSmsApp,
+ priority, expectMore, validityPeriod);
}
-
public int getPremiumSmsPermission(String packageName) {
- return mDispatcher.getPremiumSmsPermission(packageName);
+ return mDispatchersController.getPremiumSmsPermission(packageName);
}
public void setPremiumSmsPermission(String packageName, int permission) {
- mDispatcher.setPremiumSmsPermission(packageName, permission);
+ mDispatchersController.setPremiumSmsPermission(packageName, permission);
}
/**
@@ -636,7 +793,7 @@
} else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
return enableCdmaBroadcastRange(startMessageId, endMessageId);
} else {
- throw new IllegalArgumentException("Not a supportted RAN Type");
+ throw new IllegalArgumentException("Not a supported RAN Type");
}
}
@@ -646,30 +803,34 @@
} else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
return disableCdmaBroadcastRange(startMessageId, endMessageId);
} else {
- throw new IllegalArgumentException("Not a supportted RAN Type");
+ throw new IllegalArgumentException("Not a supported RAN Type");
}
}
synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Enabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
- log("Failed to add GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to add GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Added GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Added GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
@@ -678,24 +839,28 @@
synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Disabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
- log("Failed to remove GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to remove GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Removed GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Removed GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
@@ -704,24 +869,27 @@
synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Enabling cdma broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
- log("Failed to add cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to add cdma broadcast channels range " + startMessageId + " to "
+ + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Added cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Added cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
@@ -730,24 +898,27 @@
synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Disabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
- log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to remove cdma broadcast channels range " + startMessageId + " to "
+ + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Removed cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Removed cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
@@ -919,26 +1090,23 @@
}
public boolean isImsSmsSupported() {
- return mDispatcher.isIms();
+ return mDispatchersController.isIms();
}
public String getImsSmsFormat() {
- return mDispatcher.getImsSmsFormat();
+ return mDispatchersController.getImsSmsFormat();
}
public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) {
+ return;
+ }
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri
+ " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent);
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
- != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
if (!isFailedOrDraft(resolver, messageUri)) {
Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message");
returnUnspecifiedFailure(sentIntent);
@@ -951,20 +1119,18 @@
return;
}
textAndAddress[1] = filterDestAddress(textAndAddress[1]);
- mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
+ mDispatchersController.sendText(textAndAddress[1], scAddress, textAndAddress[0],
sentIntent, deliveryIntent, messageUri, callingPkg,
- true /* persistMessageForNonDefaultSmsApp */);
+ true /* persistMessageForNonDefaultSmsApp */, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
}
public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
- mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
- "Sending SMS message");
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) {
return;
}
- final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
if (!isFailedOrDraft(resolver, messageUri)) {
Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: "
+ "not FAILED or DRAFT message");
@@ -1007,14 +1173,16 @@
singleDeliveryIntent = deliveryIntents.get(i);
}
- mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
+ mDispatchersController.sendText(textAndAddress[1], scAddress, singlePart,
singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
- true /* persistMessageForNonDefaultSmsApp */);
+ true /* persistMessageForNonDefaultSmsApp */,
+ SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
}
return;
}
- mDispatcher.sendMultipartText(
+ mDispatchersController.sendMultipartText(
textAndAddress[1], // destAddress
scAddress,
parts,
@@ -1022,7 +1190,10 @@
(ArrayList<PendingIntent>) deliveryIntents,
messageUri,
callingPkg,
- true /* persistMessageForNonDefaultSmsApp */);
+ true /* persistMessageForNonDefaultSmsApp */,
+ SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /* expectMore */,
+ SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
}
private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) {
@@ -1104,32 +1275,69 @@
}
}
- private void enforceCarrierPrivilege() {
- UiccController controller = UiccController.getInstance();
- if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) {
- throw new SecurityException("No Carrier Privilege: No UICC");
+ /**
+ * Check that the caller can send text messages.
+ *
+ * For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted
+ * messages, the caller must either be the IMS app or a carrier-privileged app, or they must
+ * have both the MODIFY_PHONE_STATE and SEND_SMS permissions.
+ *
+ * @throws SecurityException if the caller is missing all necessary permission declaration or
+ * has had a necessary runtime permission revoked.
+ * @return true unless the caller has all necessary permissions but has a revoked AppOps bit.
+ */
+ @VisibleForTesting
+ public boolean checkCallingSendTextPermissions(
+ boolean persistMessageForNonDefaultSmsApp, String callingPackage, String message) {
+ // TODO(b/75978989): Should we allow IMS/carrier apps for persisted messages as well?
+ if (!persistMessageForNonDefaultSmsApp) {
+ try {
+ enforceCallerIsImsAppOrCarrierApp(message);
+ // No need to also check SEND_SMS.
+ return true;
+ } catch (SecurityException e) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE, message);
+ }
}
- if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction(
- mContext.getPackageManager()) !=
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- throw new SecurityException("No Carrier Privilege.");
- }
+ return checkCallingSendSmsPermission(callingPackage, message);
}
/**
- * Enforces that the caller has {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or is one of the following apps:
+ * Check that the caller (or self, if this is not an IPC) has SEND_SMS permissions.
+ *
+ * @throws SecurityException if the caller is missing the permission declaration or has had the
+ * permission revoked at runtime.
+ * @return whether the caller has the OP_SEND_SMS AppOps bit.
+ */
+ private boolean checkCallingOrSelfSendSmsPermission(String callingPackage, String message) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, message);
+ return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Check that the caller has SEND_SMS permissions. Can only be called during an IPC.
+ *
+ * @throws SecurityException if the caller is missing the permission declaration or has had the
+ * permission revoked at runtime.
+ * @return whether the caller has the OP_SEND_SMS AppOps bit.
+ */
+ private boolean checkCallingSendSmsPermission(String callingPackage, String message) {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, message);
+ return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Enforces that the caller is one of the following apps:
* <ul>
* <li> IMS App
* <li> Carrier App
* </ul>
*/
- private void enforcePrivilegedAppPermissions() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
+ @VisibleForTesting
+ public void enforceCallerIsImsAppOrCarrierApp(String message) {
int callingUid = Binder.getCallingUid();
String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
@@ -1145,7 +1353,7 @@
}
}
- enforceCarrierPrivilege();
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mPhone.getSubId(), message);
}
private String filterDestAddress(String destAddr) {
@@ -1154,4 +1362,11 @@
return result != null ? result : destAddr;
}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CellBroadcast log:");
+ mCellBroadcastLocalLog.dump(fd, pw, args);
+ pw.println("SMS dispatcher controller log:");
+ mDispatchersController.dump(fd, pw, args);
+ pw.flush();
+ }
}
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
deleted file mode 100644
index 4d8f62c..0000000
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ /dev/null
@@ -1,408 +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 static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.Rlog;
-
-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.gsm.GsmInboundSmsHandler;
-import com.android.internal.telephony.gsm.GsmSMSDispatcher;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class ImsSMSDispatcher extends SMSDispatcher {
- private static final String TAG = "RIL_ImsSms";
-
- private SMSDispatcher mCdmaDispatcher;
- private SMSDispatcher mGsmDispatcher;
-
- private GsmInboundSmsHandler mGsmInboundSmsHandler;
- private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
-
-
- /** true if IMS is registered and sms is supported, false otherwise.*/
- private boolean mIms = false;
- private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
-
- public ImsSMSDispatcher(Phone phone, SmsStorageMonitor storageMonitor,
- SmsUsageMonitor usageMonitor) {
- super(phone, usageMonitor, null);
- Rlog.d(TAG, "ImsSMSDispatcher created");
-
- // Create dispatchers, inbound SMS handlers and
- // broadcast undelivered messages in raw table.
- mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this);
- mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
- storageMonitor, phone);
- mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
- storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
- mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler);
- SmsBroadcastUndelivered.initialize(phone.getContext(),
- mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
- InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
-
- mCi.registerForOn(this, EVENT_RADIO_ON, null);
- mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
- }
-
- /* Updates the phone object when there is a change */
- @Override
- protected void updatePhoneObject(Phone phone) {
- Rlog.d(TAG, "In IMS updatePhoneObject ");
- super.updatePhoneObject(phone);
- mCdmaDispatcher.updatePhoneObject(phone);
- mGsmDispatcher.updatePhoneObject(phone);
- mGsmInboundSmsHandler.updatePhoneObject(phone);
- mCdmaInboundSmsHandler.updatePhoneObject(phone);
- }
-
- public void dispose() {
- mCi.unregisterForOn(this);
- mCi.unregisterForImsNetworkStateChanged(this);
- mGsmDispatcher.dispose();
- mCdmaDispatcher.dispose();
- mGsmInboundSmsHandler.dispose();
- mCdmaInboundSmsHandler.dispose();
- }
-
- /**
- * Handles events coming from the phone stack. Overridden from handler.
- *
- * @param msg the message to handle
- */
- @Override
- public void handleMessage(Message msg) {
- AsyncResult ar;
-
- switch (msg.what) {
- case EVENT_RADIO_ON:
- case EVENT_IMS_STATE_CHANGED: // received unsol
- mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
- break;
-
- case EVENT_IMS_STATE_DONE:
- ar = (AsyncResult) msg.obj;
-
- if (ar.exception == null) {
- updateImsInfo(ar);
- } else {
- Rlog.e(TAG, "IMS State query failed with exp "
- + ar.exception);
- }
- break;
-
- default:
- super.handleMessage(msg);
- }
- }
-
- private void setImsSmsFormat(int format) {
- // valid format?
- switch (format) {
- case PhoneConstants.PHONE_TYPE_GSM:
- mImsSmsFormat = "3gpp";
- break;
- case PhoneConstants.PHONE_TYPE_CDMA:
- mImsSmsFormat = "3gpp2";
- break;
- default:
- mImsSmsFormat = "unknown";
- break;
- }
- }
-
- private void updateImsInfo(AsyncResult ar) {
- int[] responseArray = (int[])ar.result;
-
- mIms = false;
- if (responseArray[0] == 1) { // IMS is registered
- Rlog.d(TAG, "IMS is registered!");
- mIms = true;
- } else {
- Rlog.d(TAG, "IMS is NOT registered!");
- }
-
- setImsSmsFormat(responseArray[1]);
-
- if (("unknown".equals(mImsSmsFormat))) {
- Rlog.e(TAG, "IMS format was unknown!");
- // failed to retrieve valid IMS SMS format info, set IMS to unregistered
- mIms = false;
- }
- }
-
- @Override
- public void sendData(String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- if (isCdmaMo()) {
- mCdmaDispatcher.sendData(destAddr, scAddr, destPort,
- data, sentIntent, deliveryIntent);
- } else {
- mGsmDispatcher.sendData(destAddr, scAddr, destPort,
- data, sentIntent, deliveryIntent);
- }
- }
-
- @Override
- public void sendMultipartText(String destAddr, String scAddr,
- ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
- boolean persistMessage) {
- if (isCdmaMo()) {
- mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
- parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
- } else {
- mGsmDispatcher.sendMultipartText(destAddr, scAddr,
- parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
- }
- }
-
- @Override
- protected void sendSms(SmsTracker tracker) {
- // sendSms is a helper function to other send functions, sendText/Data...
- // it is not part of ISms.stub
- Rlog.e(TAG, "sendSms should never be called from here!");
- }
-
- @Override
- protected void sendSmsByPstn(SmsTracker tracker) {
- // This function should be defined in Gsm/CdmaDispatcher.
- Rlog.e(TAG, "sendSmsByPstn should never be called from here!");
- }
-
- @Override
- public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
- boolean persistMessage) {
- Rlog.d(TAG, "sendText");
- if (isCdmaMo()) {
- mCdmaDispatcher.sendText(destAddr, scAddr,
- text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
- } else {
- mGsmDispatcher.sendText(destAddr, scAddr,
- text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
- }
- }
-
- @VisibleForTesting
- @Override
- public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- Rlog.d(TAG, "ImsSMSDispatcher:injectSmsPdu");
- try {
- // TODO We need to decide whether we should allow injecting GSM(3gpp)
- // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
- android.telephony.SmsMessage msg =
- android.telephony.SmsMessage.createFromPdu(pdu, format);
-
- // Only class 1 SMS are allowed to be injected.
- if (msg == null ||
- msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
- if (msg == null) {
- Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
- }
- if (receivedIntent != null) {
- receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
- }
- return;
- }
-
- AsyncResult ar = new AsyncResult(receivedIntent, msg, null);
-
- if (format.equals(SmsConstants.FORMAT_3GPP)) {
- Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg +
- ", format=" + format + "to mGsmInboundSmsHandler");
- mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
- } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
- Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg +
- ", format=" + format + "to mCdmaInboundSmsHandler");
- mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
- } else {
- // Invalid pdu format.
- Rlog.e(TAG, "Invalid pdu format: " + format);
- if (receivedIntent != null)
- receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
- }
- } catch (Exception e) {
- Rlog.e(TAG, "injectSmsPdu failed: ", e);
- try {
- if (receivedIntent != null)
- receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
- } catch (CanceledException ex) {}
- }
- }
-
- @Override
- public void sendRetrySms(SmsTracker tracker) {
- String oldFormat = tracker.mFormat;
-
- // newFormat will be based on voice technology
- String newFormat =
- (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ?
- mCdmaDispatcher.getFormat() :
- mGsmDispatcher.getFormat();
-
- // was previously sent sms format match with voice tech?
- if (oldFormat.equals(newFormat)) {
- if (isCdmaFormat(newFormat)) {
- Rlog.d(TAG, "old format matched new format (cdma)");
- mCdmaDispatcher.sendSms(tracker);
- return;
- } else {
- Rlog.d(TAG, "old format matched new format (gsm)");
- mGsmDispatcher.sendSms(tracker);
- return;
- }
- }
-
- // format didn't match, need to re-encode.
- HashMap map = tracker.getData();
-
- // to re-encode, fields needed are: scAddr, destAddr, and
- // text if originally sent as sendText or
- // data and destPort if originally sent as sendData.
- if (!( map.containsKey("scAddr") && map.containsKey("destAddr") &&
- ( map.containsKey("text") ||
- (map.containsKey("data") && map.containsKey("destPort"))))) {
- // should never come here...
- Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
- tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
- return;
- }
- String scAddr = (String)map.get("scAddr");
- String destAddr = (String)map.get("destAddr");
-
- SmsMessageBase.SubmitPduBase pdu = null;
- // figure out from tracker if this was sendText/Data
- if (map.containsKey("text")) {
- Rlog.d(TAG, "sms failed was text");
- String text = (String)map.get("text");
-
- if (isCdmaFormat(newFormat)) {
- Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
- pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
- scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
- } else {
- Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
- pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
- scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
- }
- } else if (map.containsKey("data")) {
- Rlog.d(TAG, "sms failed was data");
- byte[] data = (byte[])map.get("data");
- Integer destPort = (Integer)map.get("destPort");
-
- if (isCdmaFormat(newFormat)) {
- Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
- pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
- scAddr, destAddr, destPort.intValue(), data,
- (tracker.mDeliveryIntent != null));
- } else {
- Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
- pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
- scAddr, destAddr, destPort.intValue(), data,
- (tracker.mDeliveryIntent != null));
- }
- }
-
- // replace old smsc and pdu with newly encoded ones
- map.put("smsc", pdu.encodedScAddress);
- map.put("pdu", pdu.encodedMessage);
-
- SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ?
- mCdmaDispatcher : mGsmDispatcher;
-
- tracker.mFormat = dispatcher.getFormat();
- dispatcher.sendSms(tracker);
- }
-
- @Override
- protected void sendSubmitPdu(SmsTracker tracker) {
- sendRawPdu(tracker);
- }
-
- @Override
- protected String getFormat() {
- // this function should be defined in Gsm/CdmaDispatcher.
- Rlog.e(TAG, "getFormat should never be called from here!");
- return "unknown";
- }
-
- @Override
- protected GsmAlphabet.TextEncodingDetails calculateLength(
- CharSequence messageBody, boolean use7bitOnly) {
- Rlog.e(TAG, "Error! Not implemented for IMS.");
- return null;
- }
-
- @Override
- protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
- String message, SmsHeader smsHeader, int format, PendingIntent sentIntent,
- PendingIntent deliveryIntent, boolean lastPart,
- AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
- String fullMessageText) {
- Rlog.e(TAG, "Error! Not implemented for IMS.");
- return null;
- }
-
- @Override
- public boolean isIms() {
- return mIms;
- }
-
- @Override
- public String getImsSmsFormat() {
- return mImsSmsFormat;
- }
-
- /**
- * Determines whether or not to use CDMA format for MO SMS.
- * If SMS over IMS is supported, then format is based on IMS SMS format,
- * otherwise format is based on current phone type.
- *
- * @return true if Cdma format should be used for MO SMS, false otherwise.
- */
- private boolean isCdmaMo() {
- if (!isIms()) {
- // 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(mImsSmsFormat);
- }
-
- /**
- * Determines whether or not format given is CDMA format.
- *
- * @param format
- * @return true if format given is CDMA format, false otherwise.
- */
- private boolean isCdmaFormat(String format) {
- return (mCdmaDispatcher.getFormat().equals(format));
- }
-}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
new file mode 100644
index 0000000..5bc62d5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -0,0 +1,382 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+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.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.util.SMSDispatcherUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Responsible for communications with {@link com.android.ims.ImsManager} to send/receive messages
+ * over IMS.
+ * @hide
+ */
+public class ImsSmsDispatcher extends SMSDispatcher {
+
+ private static final String TAG = "ImsSmsDispacher";
+
+ @VisibleForTesting
+ public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
+ @VisibleForTesting
+ public AtomicInteger mNextToken = new AtomicInteger();
+ private final Object mLock = new Object();
+ private volatile boolean mIsSmsCapable;
+ private volatile boolean mIsImsServiceUp;
+ private volatile boolean mIsRegistered;
+ private final ImsManager.Connector mImsManagerConnector;
+ /**
+ * Listen to the IMS service state change
+ *
+ */
+ private ImsRegistrationImplBase.Callback mRegistrationCallback =
+ new ImsRegistrationImplBase.Callback() {
+ @Override
+ public void onRegistered(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ Rlog.d(TAG, "onImsConnected imsRadioTech=" + imsRadioTech);
+ synchronized (mLock) {
+ mIsRegistered = true;
+ }
+ }
+
+ @Override
+ public void onRegistering(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ Rlog.d(TAG, "onImsProgressing imsRadioTech=" + imsRadioTech);
+ synchronized (mLock) {
+ mIsRegistered = false;
+ }
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ Rlog.d(TAG, "onImsDisconnected imsReasonInfo=" + info);
+ synchronized (mLock) {
+ mIsRegistered = false;
+ }
+ }
+ };
+
+ private ImsFeature.CapabilityCallback mCapabilityCallback =
+ new ImsFeature.CapabilityCallback() {
+ @Override
+ public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+ synchronized (mLock) {
+ mIsSmsCapable = config.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS);
+ }
+ }
+ };
+
+ private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
+ @Override
+ public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
+ int reason) throws RemoteException {
+ Rlog.d(TAG, "onSendSmsResult token=" + token + " messageRef=" + messageRef
+ + " status=" + status + " reason=" + reason);
+ SmsTracker tracker = mTrackers.get(token);
+ if (tracker == null) {
+ throw new IllegalArgumentException("Invalid token.");
+ }
+ tracker.mMessageRef = messageRef;
+ switch(status) {
+ case ImsSmsImplBase.SEND_STATUS_OK:
+ tracker.onSent(mContext);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR:
+ tracker.onFailed(mContext, reason, 0 /* errorCode */);
+ mTrackers.remove(token);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
+ tracker.mRetryCount += 1;
+ sendSms(tracker);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+ fallbackToPstn(token, tracker);
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void onSmsStatusReportReceived(int token, int messageRef, String format, byte[] pdu)
+ throws RemoteException {
+ Rlog.d(TAG, "Status report received.");
+ SmsTracker tracker = mTrackers.get(token);
+ if (tracker == null) {
+ throw new RemoteException("Invalid token.");
+ }
+ Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
+ tracker, format, pdu);
+ Rlog.d(TAG, "Status report handle result, success: " + result.first +
+ "complete: " + result.second);
+ try {
+ getImsManager().acknowledgeSmsReport(
+ token,
+ messageRef,
+ result.first ? ImsSmsImplBase.STATUS_REPORT_STATUS_OK
+ : ImsSmsImplBase.STATUS_REPORT_STATUS_ERROR);
+ } catch (ImsException e) {
+ Rlog.e(TAG, "Failed to acknowledgeSmsReport(). Error: "
+ + e.getMessage());
+ }
+ if (result.second) {
+ mTrackers.remove(token);
+ }
+ }
+
+ @Override
+ public void onSmsReceived(int token, String format, byte[] pdu)
+ throws RemoteException {
+ Rlog.d(TAG, "SMS received.");
+ android.telephony.SmsMessage message =
+ android.telephony.SmsMessage.createFromPdu(pdu, format);
+ mSmsDispatchersController.injectSmsPdu(message, format, result -> {
+ Rlog.d(TAG, "SMS handled result: " + result);
+ int mappedResult;
+ switch (result) {
+ case Intents.RESULT_SMS_HANDLED:
+ mappedResult = ImsSmsImplBase.STATUS_REPORT_STATUS_OK;
+ break;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_NO_MEMORY;
+ break;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED;
+ break;
+ default:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
+ break;
+ }
+ try {
+ if (message != null && message.mWrappedSmsMessage != null) {
+ getImsManager().acknowledgeSms(token,
+ message.mWrappedSmsMessage.mMessageRef, mappedResult);
+ } else {
+ Rlog.w(TAG, "SMS Received with a PDU that could not be parsed.");
+ getImsManager().acknowledgeSms(token, 0, mappedResult);
+ }
+ } catch (ImsException e) {
+ Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
+ }
+ }, true);
+ }
+ };
+
+ public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+ super(phone, smsDispatchersController);
+
+ mImsManagerConnector = new ImsManager.Connector(mContext, mPhone.getPhoneId(),
+ new ImsManager.Connector.Listener() {
+ @Override
+ public void connectionReady(ImsManager manager) throws ImsException {
+ Rlog.d(TAG, "ImsManager: connection ready.");
+ synchronized (mLock) {
+ setListeners();
+ mIsImsServiceUp = true;
+ }
+ }
+
+ @Override
+ public void connectionUnavailable() {
+ Rlog.d(TAG, "ImsManager: connection unavailable.");
+ synchronized (mLock) {
+ mIsImsServiceUp = false;
+ }
+ }
+ });
+ mImsManagerConnector.connect();
+ }
+
+ private void setListeners() throws ImsException {
+ getImsManager().addRegistrationCallback(mRegistrationCallback);
+ getImsManager().addCapabilitiesCallback(mCapabilityCallback);
+ getImsManager().setSmsListener(mImsSmsListener);
+ getImsManager().onSmsReady();
+ }
+
+ private boolean isLteService() {
+ return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && (mPhone.getServiceState().
+ getState() == ServiceState.STATE_IN_SERVICE));
+ }
+
+ private boolean isLimitedLteService() {
+ return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && mPhone.getServiceState().isEmergencyOnly());
+ }
+
+ private boolean isEmergencySmsPossible() {
+ return isLteService() || isLimitedLteService();
+ }
+
+ public boolean isEmergencySmsSupport(String destAddr) {
+ PersistableBundle b;
+ boolean eSmsCarrierSupport = false;
+ if (!PhoneNumberUtils.isLocalEmergencyNumber(mContext, mPhone.getSubId(), destAddr)) {
+ Rlog.e(TAG, "Emergency Sms is not supported for: " + destAddr);
+ return false;
+ }
+ CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ Rlog.e(TAG, "configManager is null");
+ return false;
+ }
+ b = configManager.getConfigForSubId(getSubId());
+ if (b == null) {
+ Rlog.e(TAG, "PersistableBundle is null");
+ return false;
+ }
+ eSmsCarrierSupport = b.getBoolean(CarrierConfigManager.
+ KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
+ boolean lteOrLimitedLte = isEmergencySmsPossible();
+ Rlog.i(TAG, "isEmergencySmsSupport emergencySmsCarrierSupport: "
+ + eSmsCarrierSupport + " destAddr: " + destAddr + " mIsImsServiceUp: "
+ + mIsImsServiceUp + " lteOrLimitedLte: " + lteOrLimitedLte);
+
+ return eSmsCarrierSupport && mIsImsServiceUp && lteOrLimitedLte;
+ }
+
+
+ public boolean isAvailable() {
+ synchronized (mLock) {
+ Rlog.d(TAG, "isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
+ + ", cap= " + mIsSmsCapable);
+ return mIsImsServiceUp && mIsRegistered && mIsSmsCapable;
+ }
+ }
+
+ @Override
+ protected String getFormat() {
+ try {
+ return getImsManager().getSmsFormat();
+ } catch (ImsException e) {
+ Rlog.e(TAG, "Failed to get sms format. Error: " + e.getMessage());
+ return SmsConstants.FORMAT_UNKNOWN;
+ }
+ }
+
+ @Override
+ protected boolean shouldBlockSmsForEcbm() {
+ // We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA
+ // SMS.
+ return false;
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod) {
+ return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message,
+ statusReportRequested, smsHeader, priority, validityPeriod);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested) {
+ return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message,
+ statusReportRequested);
+ }
+
+ @Override
+ protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+ return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly);
+ }
+
+ @Override
+ public void sendSms(SmsTracker tracker) {
+ Rlog.d(TAG, "sendSms: "
+ + " mRetryCount=" + tracker.mRetryCount
+ + " mMessageRef=" + tracker.mMessageRef
+ + " SS=" + mPhone.getServiceState().getState());
+
+ // Flag that this Tracker is using the ImsService implementation of SMS over IMS for sending
+ // this message. Any fallbacks will happen over CS only.
+ tracker.mUsesImsServiceForIms = true;
+
+ HashMap<String, Object> map = tracker.getData();
+
+ byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
+ byte smsc[] = (byte[]) map.get(MAP_KEY_SMSC);
+ boolean isRetry = tracker.mRetryCount > 0;
+
+ if (SmsConstants.FORMAT_3GPP.equals(getFormat()) && tracker.mRetryCount > 0) {
+ // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
+ // TP-RD (bit 2) is 1 for retry
+ // and TP-MR is set to previously failed sms TP-MR
+ if (((0x01 & pdu[0]) == 0x01)) {
+ pdu[0] |= 0x04; // TP-RD
+ pdu[1] = (byte) tracker.mMessageRef; // TP-MR
+ }
+ }
+
+ int token = mNextToken.incrementAndGet();
+ mTrackers.put(token, tracker);
+ try {
+ getImsManager().sendSms(
+ token,
+ tracker.mMessageRef,
+ getFormat(),
+ smsc != null ? new String(smsc) : null,
+ isRetry,
+ pdu);
+ } catch (ImsException e) {
+ Rlog.e(TAG, "sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
+ fallbackToPstn(token, tracker);
+ }
+ }
+
+ private ImsManager getImsManager() {
+ return ImsManager.getInstance(mContext, mPhone.getPhoneId());
+ }
+
+ @VisibleForTesting
+ public void fallbackToPstn(int token, SmsTracker tracker) {
+ mSmsDispatchersController.sendRetrySms(tracker);
+ mTrackers.remove(token);
+ }
+
+ @Override
+ protected boolean isCdmaMo() {
+ return mSmsDispatchersController.isCdmaFormat(getFormat());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 391de50..b51498e 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -26,7 +26,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -61,6 +60,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.LocalLog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +70,8 @@
import com.android.internal.util.StateMachine;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -146,7 +148,7 @@
/** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
private static final int EVENT_RETURN_TO_IDLE = 4;
- /** Release wakelock after {@link mWakeLockTimeout} when returning to idle state. */
+ /** Release wakelock after {@link #mWakeLockTimeout} when returning to idle state. */
private static final int EVENT_RELEASE_WAKELOCK = 5;
/** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
@@ -158,16 +160,8 @@
/** New SMS received as an AsyncResult. */
public static final int EVENT_INJECT_SMS = 8;
- /** Update tracker object; used only in waiting state */
- private static final int EVENT_UPDATE_TRACKER = 9;
-
- /** Timeout in case state machine is stuck in a state for too long; used only in waiting
- * state */
- private static final int EVENT_STATE_TIMEOUT = 10;
-
- /** Timeout duration for EVENT_STATE_TIMEOUT */
- @VisibleForTesting
- public static final int STATE_TIMEOUT = 30000;
+ /** Update the sms tracker */
+ public static final int EVENT_UPDATE_TRACKER = 9;
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
@@ -217,6 +211,8 @@
private UserManager mUserManager;
+ private LocalLog mLocalLog = new LocalLog(64);
+
IDeviceIdleController mDeviceIdleController;
// Delete permanently from raw table
@@ -461,7 +457,7 @@
// if any broadcasts were sent, transition to waiting state
InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
if (processMessagePart(inboundSmsTracker)) {
- sendMessage(EVENT_UPDATE_TRACKER, inboundSmsTracker);
+ sendMessage(obtainMessage(EVENT_UPDATE_TRACKER, msg.obj));
transitionTo(mWaitingState);
} else {
// if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -487,10 +483,17 @@
}
return HANDLED;
+ case EVENT_UPDATE_TRACKER:
+ logd("process tracker message in DeliveringState " + msg.arg1);
+ return HANDLED;
+
// we shouldn't get this message type in this state, log error and halt.
case EVENT_BROADCAST_COMPLETE:
case EVENT_START_ACCEPTING_SMS:
default:
+ String errorMsg = "Unhandled msg in delivering state, msg.what = " + msg.what;
+ loge(errorMsg);
+ mLocalLog.log(errorMsg);
// let DefaultState handle these unexpected message types
return NOT_HANDLED;
}
@@ -505,13 +508,12 @@
* {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
*/
private class WaitingState extends State {
- private InboundSmsTracker mTracker;
+
+ private InboundSmsTracker mLastDeliveredSmsTracker;
@Override
public void enter() {
if (DBG) log("entering Waiting state");
- mTracker = null;
- sendMessageDelayed(EVENT_STATE_TIMEOUT, STATE_TIMEOUT);
}
@Override
@@ -520,32 +522,28 @@
// Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
// to give any receivers time to take their own wake locks
setWakeLockTimeout(WAKELOCK_TIMEOUT);
- if (VDBG) {
- if (hasMessages(EVENT_STATE_TIMEOUT)) {
- log("exiting Waiting state: removing EVENT_STATE_TIMEOUT from message queue");
- }
- if (hasMessages(EVENT_UPDATE_TRACKER)) {
- log("exiting Waiting state: removing EVENT_UPDATE_TRACKER from message queue");
- }
- }
- removeMessages(EVENT_STATE_TIMEOUT);
- removeMessages(EVENT_UPDATE_TRACKER);
}
@Override
public boolean processMessage(Message msg) {
log("WaitingState.processMessage:" + msg.what);
switch (msg.what) {
- case EVENT_UPDATE_TRACKER:
- mTracker = (InboundSmsTracker) msg.obj;
- return HANDLED;
-
case EVENT_BROADCAST_SMS:
// defer until the current broadcast completes
+ if (mLastDeliveredSmsTracker != null) {
+ String str = "Defer sms broadcast due to undelivered sms, "
+ + " messageCount = " + mLastDeliveredSmsTracker.getMessageCount()
+ + " destPort = " + mLastDeliveredSmsTracker.getDestPort()
+ + " timestamp = " + mLastDeliveredSmsTracker.getTimestamp()
+ + " currentTimestamp = " + System.currentTimeMillis();
+ logd(str);
+ mLocalLog.log(str);
+ }
deferMessage(msg);
return HANDLED;
case EVENT_BROADCAST_COMPLETE:
+ mLastDeliveredSmsTracker = null;
// return to idle after handling all deferred messages
sendMessage(EVENT_RETURN_TO_IDLE);
transitionTo(mDeliveringState);
@@ -555,16 +553,11 @@
// not ready to return to idle; ignore
return HANDLED;
- case EVENT_STATE_TIMEOUT:
- // stuck in WaitingState for too long; drop the message and exit this state
- if (mTracker != null) {
- log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; dropping message");
- dropSms(new SmsBroadcastReceiver(mTracker));
- } else {
- log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; mTracker is null "
- + "- sending EVENT_BROADCAST_COMPLETE");
- sendMessage(EVENT_BROADCAST_COMPLETE);
+ case EVENT_UPDATE_TRACKER:
+ for (int i = 1; i < 10; i++) {
+ deferMessage(obtainMessage(EVENT_UPDATE_TRACKER, i, i, msg.obj));
}
+ mLastDeliveredSmsTracker = (InboundSmsTracker) msg.obj;
return HANDLED;
default:
@@ -603,9 +596,9 @@
*/
private void handleInjectSms(AsyncResult ar) {
int result;
- PendingIntent receivedIntent = null;
+ SmsDispatchersController.SmsInjectionCallback callback = null;
try {
- receivedIntent = (PendingIntent) ar.userObj;
+ callback = (SmsDispatchersController.SmsInjectionCallback) ar.userObj;
SmsMessage sms = (SmsMessage) ar.result;
if (sms == null) {
result = Intents.RESULT_SMS_GENERIC_ERROR;
@@ -617,10 +610,8 @@
result = Intents.RESULT_SMS_GENERIC_ERROR;
}
- if (receivedIntent != null) {
- try {
- receivedIntent.send(result);
- } catch (CanceledException e) { }
+ if (callback != null) {
+ callback.onSmsInjectedResult(result);
}
}
@@ -806,7 +797,7 @@
if (messageCount == 1) {
// single-part message
pdus = new byte[][]{tracker.getPdu()};
- block = BlockChecker.isBlocked(mContext, tracker.getDisplayAddress());
+ block = BlockChecker.isBlocked(mContext, tracker.getDisplayAddress(), null);
} else {
// multi-part message
Cursor cursor = null;
@@ -863,7 +854,7 @@
// could be used for block checking purpose.
block = BlockChecker.isBlocked(mContext,
cursor.getString(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
- .get(DISPLAY_ADDRESS_COLUMN)));
+ .get(DISPLAY_ADDRESS_COLUMN)), null);
}
}
} catch (SQLException e) {
@@ -879,8 +870,10 @@
// Do not process null pdu(s). Check for that and return false in that case.
List<byte[]> pduList = Arrays.asList(pdus);
if (pduList.size() == 0 || pduList.contains(null)) {
- loge("processMessagePart: returning false due to " +
- (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)"));
+ String errorMsg = "processMessagePart: returning false due to "
+ + (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
+ loge(errorMsg);
+ mLocalLog.log(errorMsg);
return false;
}
@@ -1573,6 +1566,15 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ if (mCellBroadcastHandler != null) {
+ mCellBroadcastHandler.dump(fd, pw, args);
+ }
+ mLocalLog.dump(fd, pw, args);
+ }
+
// Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
private static String replaceFormFeeds(String s) {
return s == null ? "" : s.replace('\f', '\n');
diff --git a/src/java/com/android/internal/telephony/LinkCapacityEstimate.java b/src/java/com/android/internal/telephony/LinkCapacityEstimate.java
new file mode 100644
index 0000000..07fa373
--- /dev/null
+++ b/src/java/com/android/internal/telephony/LinkCapacityEstimate.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+/**
+ * Link Bandwidth Information from the Radio
+ */
+public class LinkCapacityEstimate {
+ /** Any field that is not reported shall be set to INVALID */
+ public static final int INVALID = -1;
+
+ /** LCE is active; Deprecated in HAL 1.2 */
+ public static final int STATUS_ACTIVE = 0;
+
+ /** LCE is suspended; Deprecated in HAL 1.2 */
+ public static final int STATUS_SUSPENDED = 1;
+
+ /** Downlink radio link capacity in kbps */
+ public final int downlinkCapacityKbps;
+
+ /** Uplink radio link capacity; added in HAL 1.2 */
+ public final int uplinkCapacityKbps;
+
+ /** Confidence of the downlink estimate as a percentage [1, 100]; deprecated in HAL 1.2 */
+ public final int confidence;
+
+ /** Status of the LCE; deprecated in HAL 1.2 */
+ public final int status; // either STATUS_ACTIVE, STATUS_SUSPENDED, or INVALID
+
+ /** Constructor matching the estimate in Radio HAL v1.0 */
+ public LinkCapacityEstimate(int downlinkCapacityKbps, int confidence, int status) {
+ this.downlinkCapacityKbps = downlinkCapacityKbps;
+ this.confidence = confidence;
+ this.status = status;
+ this.uplinkCapacityKbps = INVALID;
+ }
+
+ /** Constructor matching the estimate in Radio HAL v1.2 */
+ public LinkCapacityEstimate(int downlinkCapacityKbps, int uplinkCapacityKbps) {
+ this.downlinkCapacityKbps = downlinkCapacityKbps;
+ this.uplinkCapacityKbps = uplinkCapacityKbps;
+ this.confidence = INVALID;
+ this.status = INVALID;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{downlinkCapacityKbps=")
+ .append(downlinkCapacityKbps)
+ .append(", uplinkCapacityKbps=")
+ .append(uplinkCapacityKbps)
+ .append(", confidence=")
+ .append(confidence)
+ .append(", status=")
+ .append(status)
+ .toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
new file mode 100644
index 0000000..8977b03
--- /dev/null
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -0,0 +1,439 @@
+/*
+ * 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;
+
+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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The locale tracker keeps tracking the current locale of the phone.
+ */
+public class LocaleTracker extends Handler {
+ private static final boolean DBG = true;
+ private static final String TAG = LocaleTracker.class.getSimpleName();
+
+ /** Event for getting cell info from the modem */
+ private static final int EVENT_GET_CELL_INFO = 1;
+
+ /** Event for operator numeric update */
+ private static final int EVENT_UPDATE_OPERATOR_NUMERIC = 2;
+
+ /** Event for service state changed */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 3;
+
+ // Todo: Read this from Settings.
+ /** The minimum delay to get cell info from the modem */
+ private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS;
+
+ // Todo: Read this from Settings.
+ /** The maximum delay to get cell info from the modem */
+ private static final long CELL_INFO_MAX_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+ // Todo: Read this from Settings.
+ /** The delay for periodically getting cell info from the modem */
+ private static final long CELL_INFO_PERIODIC_POLLING_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+ private final Phone mPhone;
+
+ /** SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX */
+ private int mSimState;
+
+ /** Current serving PLMN's MCC/MNC */
+ @Nullable
+ private String mOperatorNumeric;
+
+ /** Current cell tower information */
+ @Nullable
+ private List<CellInfo> mCellInfo;
+
+ /** Count of invalid cell info we've got so far. Will reset once we get a successful one */
+ private int mFailCellInfoCount;
+
+ /** The ISO-3166 code of device's current country */
+ @Nullable
+ private String mCurrentCountryIso;
+
+ /** Current service state. Must be one of ServiceState.STATE_XXX. */
+ private int mLastServiceState = -1;
+
+ private final LocalLog mLocalLog = new LocalLog(50);
+
+ /** Broadcast receiver to get SIM card state changed event */
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(intent.getAction())) {
+ int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
+ if (phoneId == mPhone.getPhoneId()) {
+ onSimCardStateChanged(intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_UNKNOWN));
+ }
+ }
+ }
+ };
+
+ /**
+ * Message handler
+ *
+ * @param msg The message
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_GET_CELL_INFO:
+ synchronized (this) {
+ getCellInfo();
+ updateLocale();
+ }
+ break;
+ case EVENT_UPDATE_OPERATOR_NUMERIC:
+ updateOperatorNumericSync((String) msg.obj);
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ onServiceStateChanged((ServiceState) ar.result);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param phone The phone object
+ * @param looper The looper message handler
+ */
+ public LocaleTracker(Phone phone, Looper looper) {
+ super(looper);
+ mPhone = phone;
+ mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
+
+ mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+ }
+
+ /**
+ * Get the device's current country.
+ *
+ * @return The device's current country. Empty string if the information is not available.
+ */
+ @NonNull
+ public synchronized String getCurrentCountry() {
+ return (mCurrentCountryIso != null) ? mCurrentCountryIso : "";
+ }
+
+ /**
+ * Get the MCC from cell tower information.
+ *
+ * @return MCC in string format. Null if the information is not available.
+ */
+ @Nullable
+ private String getMccFromCellInfo() {
+ String selectedMcc = null;
+ if (mCellInfo != null) {
+ Map<String, Integer> countryCodeMap = new HashMap<>();
+ int maxCount = 0;
+ for (CellInfo cellInfo : mCellInfo) {
+ String mcc = null;
+ if (cellInfo instanceof CellInfoGsm) {
+ mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoLte) {
+ mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString();
+ }
+ if (mcc != null) {
+ int count = 1;
+ if (countryCodeMap.containsKey(mcc)) {
+ count = countryCodeMap.get(mcc) + 1;
+ }
+ countryCodeMap.put(mcc, count);
+ // This is unlikely, but if MCC from cell info looks different, we choose the
+ // MCC that occurs most.
+ if (count > maxCount) {
+ maxCount = count;
+ selectedMcc = mcc;
+ }
+ }
+ }
+ }
+ return selectedMcc;
+ }
+
+ /**
+ * Called when SIM card state changed. Only when we absolutely know the SIM is absent, we get
+ * cell info from the network. Other SIM states like NOT_READY might be just a transitioning
+ * state.
+ *
+ * @param state SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX.
+ */
+ private synchronized void onSimCardStateChanged(int state) {
+ if (mSimState != state && state == TelephonyManager.SIM_STATE_ABSENT) {
+ if (DBG) log("Sim absent. Get latest cell info from the modem.");
+ getCellInfo();
+ updateLocale();
+ }
+ mSimState = state;
+ }
+
+ /**
+ * Called when service state changed.
+ *
+ * @param serviceState Service state
+ */
+ private void onServiceStateChanged(ServiceState serviceState) {
+ int state = serviceState.getState();
+ if (state != mLastServiceState) {
+ if (state != ServiceState.STATE_POWER_OFF && TextUtils.isEmpty(mOperatorNumeric)) {
+ // When the device is out of airplane mode or powered on, and network's MCC/MNC is
+ // not available, we get cell info from the modem.
+ String msg = "Service state " + ServiceState.rilServiceStateToString(state)
+ + ". Get cell info now.";
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ getCellInfo();
+ } else if (state == ServiceState.STATE_POWER_OFF) {
+ // Clear the cell info when the device is in airplane mode.
+ if (mCellInfo != null) mCellInfo.clear();
+ stopCellInfoRetry();
+ }
+ updateLocale();
+ mLastServiceState = state;
+ }
+ }
+
+ /**
+ * Update MCC/MNC from network service state synchronously. Note if this is called from phone
+ * process's main thread and if the update operation requires getting cell info from the modem,
+ * the cached cell info will be used to determine the locale. If the cached cell info is not
+ * acceptable, use {@link #updateOperatorNumericAsync(String)} instead.
+ *
+ * @param operatorNumeric MCC/MNC of the operator
+ */
+ public synchronized void updateOperatorNumericSync(String operatorNumeric) {
+ // Check if the operator numeric changes.
+ if (DBG) log("updateOperatorNumericSync. mcc/mnc=" + operatorNumeric);
+ if (!Objects.equals(mOperatorNumeric, operatorNumeric)) {
+ String msg = "Operator numeric changes to " + operatorNumeric;
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ mOperatorNumeric = operatorNumeric;
+
+ // If the operator numeric becomes unavailable, we need to get the latest cell info so
+ // that we can get MCC from it.
+ if (TextUtils.isEmpty(mOperatorNumeric)) {
+ if (DBG) {
+ log("Operator numeric unavailable. Get latest cell info from the modem.");
+ }
+ getCellInfo();
+ } else {
+ // If operator numeric is available, that means we camp on network. So we should
+ // clear the cell info and stop cell info retry.
+ if (mCellInfo != null) mCellInfo.clear();
+ stopCellInfoRetry();
+ }
+ updateLocale();
+ }
+ }
+
+ /**
+ * Update MCC/MNC from network service state asynchronously. The update operation will run
+ * in locale tracker's handler's thread, which can get cell info synchronously from service
+ * state tracker. Note that the country code will not be available immediately after calling
+ * this method.
+ *
+ * @param operatorNumeric MCC/MNC of the operator
+ */
+ public void updateOperatorNumericAsync(String operatorNumeric) {
+ if (DBG) log("updateOperatorNumericAsync. mcc/mnc=" + operatorNumeric);
+ sendMessage(obtainMessage(EVENT_UPDATE_OPERATOR_NUMERIC, operatorNumeric));
+ }
+
+ /**
+ * Get the delay time to get cell info from modem. The delay time grows exponentially to prevent
+ * battery draining.
+ *
+ * @param failCount Count of invalid cell info we've got so far.
+ * @return The delay time for next get cell info
+ */
+ private long getCellInfoDelayTime(int failCount) {
+ // Exponentially grow the delay time
+ long delay = CELL_INFO_MIN_DELAY_MS * (long) Math.pow(2, failCount - 1);
+ if (delay < CELL_INFO_MIN_DELAY_MS) {
+ delay = CELL_INFO_MIN_DELAY_MS;
+ } else if (delay > CELL_INFO_MAX_DELAY_MS) {
+ delay = CELL_INFO_MAX_DELAY_MS;
+ }
+ return delay;
+ }
+
+ /**
+ * Stop retrying getting cell info from the modem. It cancels any scheduled cell info retrieving
+ * request.
+ */
+ private void stopCellInfoRetry() {
+ mFailCellInfoCount = 0;
+ removeMessages(EVENT_GET_CELL_INFO);
+ }
+
+ /**
+ * Get cell info from the modem.
+ */
+ private void getCellInfo() {
+ String msg;
+ if (!mPhone.getServiceStateTracker().getDesiredPowerState()) {
+ msg = "Radio is off. Stopped cell info retry. Cleared the previous cached cell info.";
+ if (mCellInfo != null) mCellInfo.clear();
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ stopCellInfoRetry();
+ return;
+ }
+
+ // Get all cell info. Passing null to use default worksource, which indicates the original
+ // request is from telephony internally.
+ mCellInfo = mPhone.getAllCellInfo(null);
+ msg = "getCellInfo: cell info=" + mCellInfo;
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ if (mCellInfo == null || mCellInfo.size() == 0) {
+ // If we can't get a valid cell info. Try it again later.
+ long delay = getCellInfoDelayTime(++mFailCellInfoCount);
+ if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs.");
+ removeMessages(EVENT_GET_CELL_INFO);
+ sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay);
+ } else {
+ // We successfully got cell info from the modem. We should stop cell info retry.
+ stopCellInfoRetry();
+
+ // Now we need to get the cell info from the modem periodically even if we already got
+ // the cell info because the user can move.
+ sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO),
+ CELL_INFO_PERIODIC_POLLING_DELAY_MS);
+ }
+ }
+
+ /**
+ * Update the device's current locale
+ */
+ private void updateLocale() {
+ // If MCC is available from network service state, use it first.
+ String mcc = null;
+ String countryIso = "";
+ if (!TextUtils.isEmpty(mOperatorNumeric)) {
+ try {
+ mcc = mOperatorNumeric.substring(0, 3);
+ countryIso = MccTable.countryCodeForMcc(mcc);
+ } catch (StringIndexOutOfBoundsException ex) {
+ loge("updateLocale: Can't get country from operator numeric. mcc = "
+ + mcc + ". ex=" + ex);
+ }
+ }
+
+ // If for any reason we can't get country from operator numeric, try to get it from cell
+ // info.
+ if (TextUtils.isEmpty(countryIso)) {
+ mcc = getMccFromCellInfo();
+ countryIso = MccTable.countryCodeForMcc(mcc);
+ }
+
+ String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso;
+ log(msg);
+ mLocalLog.log(msg);
+ if (!Objects.equals(countryIso, mCurrentCountryIso)) {
+ msg = "updateLocale: Change the current country to " + countryIso;
+ log(msg);
+ mLocalLog.log(msg);
+ mCurrentCountryIso = countryIso;
+
+ TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(),
+ TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, mCurrentCountryIso);
+
+ // Set the country code for wifi. This sets allowed wifi channels based on the
+ // country of the carrier we see. If we can't see any, reset to 0 so we don't
+ // broadcast on forbidden channels.
+ ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE))
+ .setCountryCode(countryIso, false);
+ }
+ }
+
+ private void log(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
+
+ /**
+ * Print the DeviceStateMonitor into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param pw A PrintWriter to which the dump is to be set.
+ * @param args Additional arguments to the dump request.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ pw.println("LocaleTracker:");
+ ipw.increaseIndent();
+ ipw.println("mOperatorNumeric = " + mOperatorNumeric);
+ ipw.println("mSimState = " + mSimState);
+ ipw.println("mCellInfo = " + mCellInfo);
+ ipw.println("mCurrentCountryIso = " + mCurrentCountryIso);
+ ipw.println("mFailCellInfoCount = " + mFailCellInfoCount);
+ ipw.println("Local logs:");
+ ipw.increaseIndent();
+ mLocalLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ ipw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 5714b29..fb28194 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -17,14 +17,11 @@
package com.android.internal.telephony;
import android.app.ActivityManager;
-import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Configuration;
-import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -32,7 +29,7 @@
import com.android.internal.app.LocaleStore.LocaleInfo;
import libcore.icu.ICU;
-import libcore.icu.TimeZoneNames;
+import libcore.util.TimeZoneFinder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -94,24 +91,8 @@
if (entry == null) {
return null;
}
- Locale locale = new Locale("", entry.mIso);
- String[] tz = TimeZoneNames.forLocale(locale);
- if (tz.length == 0) return null;
-
- String zoneName = tz[0];
-
- /* Use Australia/Sydney instead of Australia/Lord_Howe for Australia.
- * http://b/33228250
- * Todo: remove the code, see b/62418027
- */
- if (mcc == 505 /* Australia / Norfolk Island */) {
- for (String zone : tz) {
- if (zone.contains("Sydney")) {
- zoneName = zone;
- }
- }
- }
- return zoneName;
+ final String lowerCaseCountryCode = entry.mIso;
+ return TimeZoneFinder.getInstance().lookupDefaultTimeZoneIdByCountry(lowerCaseCountryCode);
}
/**
@@ -131,6 +112,19 @@
/**
* Given a GSM Mobile Country Code, returns
+ * an ISO two-character country code if available.
+ * Returns empty string if unavailable.
+ */
+ public static String countryCodeForMcc(String mcc) {
+ try {
+ return countryCodeForMcc(Integer.parseInt(mcc));
+ } catch (NumberFormatException ex) {
+ return "";
+ }
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns
* an ISO 2-3 character language code if available.
* Returns null if unavailable.
*/
@@ -176,11 +170,9 @@
* correct version of resources. If MCC is 0, MCC and MNC will be ignored (not set).
* @param context Context to act on.
* @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end
- * @param fromServiceState true if coming from the radio service state, false if from SIM
*/
- public static void updateMccMncConfiguration(Context context, String mccmnc,
- boolean fromServiceState) {
- Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState);
+ public static void updateMccMncConfiguration(Context context, String mccmnc) {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc);
if (Build.IS_DEBUGGABLE) {
String overrideMcc = SystemProperties.get("persist.sys.override_mcc");
@@ -193,19 +185,11 @@
if (!TextUtils.isEmpty(mccmnc)) {
int mcc, mnc;
- String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric();
- Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc);
- //Update mccmnc only for default subscription in case of MultiSim.
-// if (!defaultMccMnc.equals(mccmnc)) {
-// Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update.");
-// return;
-// }
-
try {
- mcc = Integer.parseInt(mccmnc.substring(0,3));
+ mcc = Integer.parseInt(mccmnc.substring(0, 3));
mnc = Integer.parseInt(mccmnc.substring(3));
- } catch (NumberFormatException e) {
- Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc);
+ } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
+ Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc + ". ex=" + ex);
return;
}
@@ -213,33 +197,24 @@
if (mcc != 0) {
setTimezoneFromMccIfNeeded(context, mcc);
}
- if (fromServiceState) {
- setWifiCountryCodeFromMcc(context, mcc);
- } else {
- // from SIM
- try {
- Configuration config = new Configuration();
- boolean updateConfig = false;
- if (mcc != 0) {
- config.mcc = mcc;
- config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
- updateConfig = true;
- }
- if (updateConfig) {
- Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
- ActivityManager.getService().updateConfiguration(config);
- } else {
- Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
- }
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Can't update configuration", e);
+ try {
+ Configuration config = new Configuration();
+ boolean updateConfig = false;
+ if (mcc != 0) {
+ config.mcc = mcc;
+ config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
+ updateConfig = true;
}
- }
- } else {
- if (fromServiceState) {
- // an empty mccmnc means no signal - tell wifi we don't know
- setWifiCountryCodeFromMcc(context, 0);
+
+ if (updateConfig) {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
+ ActivityManager.getService().updateConfiguration(config);
+ } else {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
+ }
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Can't update configuration", e);
}
}
}
@@ -375,22 +350,25 @@
* @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
*/
private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
- String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
- // timezone.equals("GMT") will be true and only true if the timezone was
- // set to a default value by the system server (when starting, system server.
- // sets the persist.sys.timezone to "GMT" if it's not set)."GMT" is not used by
- // any code that sets it explicitly (in case where something sets GMT explicitly,
- // "Etc/GMT" Olsen ID would be used).
- // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
- // better way of telling if the value has been defaulted.
- if (timezone == null || timezone.length() == 0 || timezone.equals("GMT")) {
- String zoneId = defaultTimeZoneForMcc(mcc);
- if (zoneId != null && zoneId.length() > 0) {
- // Set time zone based on MCC
- AlarmManager alarm =
- (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- alarm.setTimeZone(zoneId);
- Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ // Switch to use the time service helper associated with the NitzStateMachine impl
+ // being used. This logic will be removed once the old implementation is removed.
+ if (TelephonyComponentFactory.USE_NEW_NITZ_STATE_MACHINE) {
+ if (!NewTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ // Set time zone based on MCC
+ NewTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+ Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ }
+ }
+ } else {
+ if (!OldTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ // Set time zone based on MCC
+ OldTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+ Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ }
}
}
}
@@ -423,20 +401,6 @@
return locale;
}
- /**
- * Set the country code for wifi. This sets allowed wifi channels based on the
- * country of the carrier we see. If we can't see any, reset to 0 so we don't
- * broadcast on forbidden channels.
- * @param context Context to act on.
- * @param mcc Mobile Country Code of the operator. 0 if not known
- */
- private static void setWifiCountryCodeFromMcc(Context context, int mcc) {
- String country = MccTable.countryCodeForMcc(mcc);
- Slog.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country);
- WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- wM.setCountryCode(country, false);
- }
-
static {
sTable = new ArrayList<MccEntry>(240);
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
new file mode 100644
index 0000000..ae7ede0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -0,0 +1,235 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CarrierConfigManager;
+import android.telephony.INetworkService;
+import android.telephony.INetworkServiceCallback;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.Rlog;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Class that serves as the layer between NetworkService and ServiceStateTracker. It helps binding,
+ * sending request and registering for state change to NetworkService.
+ */
+public class NetworkRegistrationManager {
+ private static final String TAG = NetworkRegistrationManager.class.getSimpleName();
+
+ 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();
+
+ private INetworkService.Stub mServiceBinder;
+
+ private RegManagerDeathRecipient mDeathRecipient;
+
+ public NetworkRegistrationManager(int transportType, Phone phone) {
+ mTransportType = transportType;
+ mPhone = phone;
+ mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+
+ bindService();
+ }
+
+ public boolean isServiceConnected() {
+ return (mServiceBinder != null) && (mServiceBinder.isBinderAlive());
+ }
+
+ public void unregisterForNetworkRegistrationStateChanged(Handler h) {
+ mRegStateChangeRegistrants.remove(h);
+ }
+
+ public void registerForNetworkRegistrationStateChanged(Handler h, int what, Object obj) {
+ logd("registerForNetworkRegistrationStateChanged");
+ Registrant r = new Registrant(h, what, obj);
+ mRegStateChangeRegistrants.addUnique(h, what, obj);
+ }
+
+ private final Map<NetworkRegStateCallback, Message> mCallbackTable = new Hashtable();
+
+ public void getNetworkRegistrationState(int domain, Message onCompleteMessage) {
+ if (onCompleteMessage == null) return;
+
+ logd("getNetworkRegistrationState domain " + domain);
+ if (!isServiceConnected()) {
+ logd("service not connected.");
+ onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null,
+ new IllegalStateException("Service not connected."));
+ onCompleteMessage.sendToTarget();
+ return;
+ }
+
+ NetworkRegStateCallback callback = new NetworkRegStateCallback();
+ try {
+ mCallbackTable.put(callback, onCompleteMessage);
+ mServiceBinder.getNetworkRegistrationState(mPhone.getPhoneId(), domain, callback);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getNetworkRegistrationState RemoteException " + e);
+ mCallbackTable.remove(callback);
+ onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null, e);
+ onCompleteMessage.sendToTarget();
+ }
+ }
+
+ private class RegManagerDeathRecipient implements IBinder.DeathRecipient {
+
+ private final ComponentName mComponentName;
+
+ RegManagerDeathRecipient(ComponentName name) {
+ mComponentName = name;
+ }
+
+ @Override
+ public void binderDied() {
+ // TODO: try to restart the service.
+ logd("NetworkService(" + mComponentName + " transport type "
+ + mTransportType + ") died.");
+ }
+ }
+
+ private class NetworkServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logd("service connected.");
+ mServiceBinder = (INetworkService.Stub) service;
+ mDeathRecipient = new RegManagerDeathRecipient(name);
+ try {
+ mServiceBinder.linkToDeath(mDeathRecipient, 0);
+ mServiceBinder.createNetworkServiceProvider(mPhone.getPhoneId());
+ mServiceBinder.registerForNetworkRegistrationStateChanged(mPhone.getPhoneId(),
+ new NetworkRegStateCallback());
+ } catch (RemoteException exception) {
+ // Remote exception means that the binder already died.
+ mDeathRecipient.binderDied();
+ logd("RemoteException " + exception);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ logd("onServiceDisconnected " + name);
+ if (mServiceBinder != null) {
+ mServiceBinder.unlinkToDeath(mDeathRecipient, 0);
+ }
+ }
+ }
+
+ private class NetworkRegStateCallback extends INetworkServiceCallback.Stub {
+ @Override
+ public void onGetNetworkRegistrationStateComplete(
+ int result, NetworkRegistrationState state) {
+ logd("onGetNetworkRegistrationStateComplete result "
+ + result + " state " + state);
+ Message onCompleteMessage = mCallbackTable.remove(this);
+ if (onCompleteMessage != null) {
+ onCompleteMessage.arg1 = result;
+ onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, state, null);
+ onCompleteMessage.sendToTarget();
+ } else {
+ loge("onCompleteMessage is null");
+ }
+ }
+
+ @Override
+ public void onNetworkStateChanged() {
+ logd("onNetworkStateChanged");
+ mRegStateChangeRegistrants.notifyRegistrants();
+ }
+ }
+
+ private boolean bindService() {
+ Intent intent = new Intent(NetworkService.NETWORK_SERVICE_INTERFACE);
+ intent.setPackage(getPackageName());
+ try {
+ // We bind this as a foreground service because it is operating directly on the SIM,
+ // and we do not want it subjected to power-savings restrictions while doing so.
+ return mPhone.getContext().bindService(intent, new NetworkServiceConnection(),
+ Context.BIND_AUTO_CREATE);
+ } catch (SecurityException e) {
+ loge("bindService failed " + e);
+ return false;
+ }
+ }
+
+ private String getPackageName() {
+ String packageName;
+ int resourceId;
+ String carrierConfig;
+
+ switch (mTransportType) {
+ case TransportType.WWAN:
+ resourceId = com.android.internal.R.string.config_wwan_network_service_package;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
+ break;
+ case TransportType.WLAN:
+ resourceId = com.android.internal.R.string.config_wlan_network_service_package;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
+ break;
+ default:
+ throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+ + mTransportType);
+ }
+
+ // Read package name from resource overlay
+ packageName = mPhone.getContext().getResources().getString(resourceId);
+
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ if (b != null) {
+ // If carrier config overrides it, use the one from carrier config
+ packageName = b.getString(carrierConfig, packageName);
+ }
+
+ logd("Binding to packageName " + packageName + " for transport type"
+ + mTransportType);
+
+ return packageName;
+ }
+
+ private static int logd(String msg) {
+ return Rlog.d(TAG, msg);
+ }
+
+ private static int loge(String msg) {
+ return Rlog.e(TAG, msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index 46b1eef..2f416cc 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -16,9 +16,9 @@
package com.android.internal.telephony;
-import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.EUTRAN;
-import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.GERAN;
-import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.UTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
import android.hardware.radio.V1_0.RadioError;
import android.os.AsyncResult;
@@ -107,50 +107,52 @@
}
private boolean isValidScan(NetworkScanRequestInfo nsri) {
- if (nsri.mRequest.specifiers == null) {
+ if (nsri.mRequest == null || nsri.mRequest.getSpecifiers() == null) {
return false;
}
- if (nsri.mRequest.specifiers.length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
+ if (nsri.mRequest.getSpecifiers().length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
return false;
}
- for (RadioAccessSpecifier ras : nsri.mRequest.specifiers) {
- if (ras.radioAccessNetwork != GERAN && ras.radioAccessNetwork != UTRAN
- && ras.radioAccessNetwork != EUTRAN) {
+ for (RadioAccessSpecifier ras : nsri.mRequest.getSpecifiers()) {
+ if (ras.getRadioAccessNetwork() != GERAN && ras.getRadioAccessNetwork() != UTRAN
+ && ras.getRadioAccessNetwork() != EUTRAN) {
return false;
}
- if (ras.bands != null && ras.bands.length > NetworkScanRequest.MAX_BANDS) {
+ if (ras.getBands() != null && ras.getBands().length > NetworkScanRequest.MAX_BANDS) {
return false;
}
- if (ras.channels != null && ras.channels.length > NetworkScanRequest.MAX_CHANNELS) {
+ if (ras.getChannels() != null
+ && ras.getChannels().length > NetworkScanRequest.MAX_CHANNELS) {
return false;
}
}
- if ((nsri.mRequest.searchPeriodicity < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
- || (nsri.mRequest.searchPeriodicity
+ if ((nsri.mRequest.getSearchPeriodicity() < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
+ || (nsri.mRequest.getSearchPeriodicity()
> NetworkScanRequest.MAX_SEARCH_PERIODICITY_SEC)) {
return false;
}
- if ((nsri.mRequest.maxSearchTime < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
- || (nsri.mRequest.maxSearchTime > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
+ if ((nsri.mRequest.getMaxSearchTime() < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
+ || (nsri.mRequest.getMaxSearchTime() > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
return false;
}
- if ((nsri.mRequest.incrementalResultsPeriodicity
+ if ((nsri.mRequest.getIncrementalResultsPeriodicity()
< NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC)
- || (nsri.mRequest.incrementalResultsPeriodicity
+ || (nsri.mRequest.getIncrementalResultsPeriodicity()
> NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC)) {
return false;
}
- if ((nsri.mRequest.searchPeriodicity > nsri.mRequest.maxSearchTime)
- || (nsri.mRequest.incrementalResultsPeriodicity > nsri.mRequest.maxSearchTime)) {
+ if ((nsri.mRequest.getSearchPeriodicity() > nsri.mRequest.getMaxSearchTime())
+ || (nsri.mRequest.getIncrementalResultsPeriodicity()
+ > nsri.mRequest.getMaxSearchTime())) {
return false;
}
- if ((nsri.mRequest.mccMncs != null)
- && (nsri.mRequest.mccMncs.size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
+ if ((nsri.mRequest.getPlmns() != null)
+ && (nsri.mRequest.getPlmns().size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
return false;
}
return true;
@@ -274,10 +276,10 @@
return NetworkScan.ERROR_INVALID_SCAN;
case RadioError.DEVICE_IN_USE:
Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
- return NetworkScan.ERROR_MODEM_BUSY;
+ return NetworkScan.ERROR_MODEM_UNAVAILABLE;
default:
Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " + rilError);
- return NetworkScan.ERROR_RIL_ERROR;
+ return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
}
}
@@ -306,11 +308,11 @@
return NetworkScan.ERROR_INVALID_SCAN;
case DEVICE_IN_USE:
Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
- return NetworkScan.ERROR_MODEM_BUSY;
+ return NetworkScan.ERROR_MODEM_UNAVAILABLE;
default:
Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
+ error);
- return NetworkScan.ERROR_RIL_ERROR;
+ return NetworkScan.ERROR_RADIO_INTERFACE_ERROR;
}
}
@@ -332,7 +334,7 @@
if (!interruptLiveScan(nsri)) {
if (!cacheScan(nsri)) {
notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
- NetworkScan.ERROR_MODEM_BUSY, null);
+ NetworkScan.ERROR_MODEM_UNAVAILABLE, null);
}
}
}
@@ -389,7 +391,7 @@
}
} else {
logEmptyResultOrException(ar);
- deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true);
+ deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
}
}
@@ -458,12 +460,12 @@
// stopped, a new scan will automatically start with nsri.
// The new scan can interrupt the live scan only when all the below requirements are met:
// 1. There is 1 live scan and no other pending scan
- // 2. The new scan is requested by system process
- // 3. The live scan is not requested by system process
+ // 2. The new scan is requested by mobile network setting menu (owned by PHONE process)
+ // 3. The live scan is not requested by mobile network setting menu
private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
if (mLiveRequestInfo != null && mPendingRequestInfo == null
- && nsri.mUid == Process.SYSTEM_UID
- && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
+ && nsri.mUid == Process.PHONE_UID
+ && mLiveRequestInfo.mUid != Process.PHONE_UID) {
doInterruptScan(mLiveRequestInfo.mScanId);
mPendingRequestInfo = nsri;
notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
diff --git a/src/java/com/android/internal/telephony/NewNitzStateMachine.java b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
new file mode 100644
index 0000000..20c729f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright 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;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public final class NewNitzStateMachine implements NitzStateMachine {
+
+ private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+ private static final boolean DBG = ServiceStateTracker.DBG;
+
+ // Time detection state.
+
+ /**
+ * The last NITZ-sourced time considered sent to the time detector service. Used to rate-limit
+ * calls to the time detector.
+ */
+ private TimestampedValue<Long> mSavedNitzTime;
+
+ // Time Zone detection state.
+
+ /** We always keep the last NITZ signal received in mLatestNitzSignal. */
+ private TimestampedValue<NitzData> mLatestNitzSignal;
+
+ /**
+ * Records whether the device should have a country code available via
+ * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+ * received is (almost always) not enough to determine time zone. On test networks the country
+ * code should be available but can still be an empty string but this flag indicates that the
+ * information available is unlikely to improve.
+ */
+ private boolean mGotCountryCode = false;
+
+ /**
+ * The last time zone ID that has been determined. It may not have been set as the device time
+ * zone if automatic time zone detection is disabled but may later be used to set the time zone
+ * if the user enables automatic time zone detection.
+ */
+ private String mSavedTimeZoneId;
+
+ /**
+ * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
+ * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
+ * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
+ * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
+ * less reliable algorithms than NITZ-based detection such as by just using network country
+ * code.
+ */
+ private boolean mNitzTimeZoneDetectionSuccessful = false;
+
+ // Miscellaneous dependencies and helpers not related to detection state.
+ private final LocalLog mTimeLog = new LocalLog(15);
+ private final LocalLog mTimeZoneLog = new LocalLog(15);
+ private final GsmCdmaPhone mPhone;
+ private final DeviceState mDeviceState;
+ private final NewTimeServiceHelper mTimeServiceHelper;
+ private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+ /** Wake lock used while setting time of day. */
+ private final PowerManager.WakeLock mWakeLock;
+ private static final String WAKELOCK_TAG = "NitzStateMachine";
+
+ public NewNitzStateMachine(GsmCdmaPhone phone) {
+ this(phone,
+ new NewTimeServiceHelper(phone.getContext()),
+ new DeviceState(phone),
+ new TimeZoneLookupHelper());
+ }
+
+ @VisibleForTesting
+ public NewNitzStateMachine(GsmCdmaPhone phone, NewTimeServiceHelper timeServiceHelper,
+ DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
+ mPhone = phone;
+
+ Context context = phone.getContext();
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+ mDeviceState = deviceState;
+ mTimeZoneLookupHelper = timeZoneLookupHelper;
+ mTimeServiceHelper = timeServiceHelper;
+ mTimeServiceHelper.setListener(new NewTimeServiceHelper.Listener() {
+ @Override
+ public void onTimeZoneDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeZoneEnabled();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void handleNetworkCountryCodeSet(boolean countryChanged) {
+ boolean hadCountryCode = mGotCountryCode;
+ mGotCountryCode = true;
+
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
+ updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+ }
+
+ if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+ updateTimeZoneFromCountryAndNitz();
+ }
+ }
+
+ private void updateTimeZoneFromCountryAndNitz() {
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+
+ // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
+ // been set which makes it difficult to tell if it's what the user / time zone detection
+ // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
+ // device has ever been explicit set by the user or code.
+ final boolean isTimeZoneSettingInitialized =
+ mTimeServiceHelper.isTimeZoneSettingInitialized();
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode);
+ }
+
+ try {
+ NitzData nitzData = nitzSignal.getValue();
+
+ String zoneId;
+ if (nitzData.getEmulatorHostTimeZone() != null) {
+ zoneId = nitzData.getEmulatorHostTimeZone().getID();
+ } else if (!mGotCountryCode) {
+ // We don't have a country code so we won't try to look up the time zone.
+ zoneId = null;
+ } else if (TextUtils.isEmpty(isoCountryCode)) {
+ // We have a country code but it's empty. This is most likely because we're on a
+ // test network that's using a bogus MCC (eg, "001"). Obtain a TimeZone based only
+ // on the NITZ parameters: it's only going to be correct in a few cases but it
+ // should at least have the correct offset.
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ String logMsg = "updateTimeZoneFromCountryAndNitz: lookupByNitz returned"
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ } else if (mLatestNitzSignal == null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: No cached NITZ data available,"
+ + " not setting zone");
+ }
+ zoneId = null;
+ } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) {
+ String logMsg = "updateTimeZoneFromCountryAndNitz: Received NITZ looks bogus, "
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = null;
+ } else {
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+ nitzData, isoCountryCode);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: using"
+ + " lookupByNitzCountry(nitzData, isoCountryCode),"
+ + " nitzData=" + nitzData
+ + " isoCountryCode=" + isoCountryCode
+ + " lookupResult=" + lookupResult);
+ }
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ }
+
+ // Log the action taken to the dedicated time zone log.
+ final String tmpLog = "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal
+ + " zoneId=" + zoneId
+ + " isTimeZoneDetectionEnabled()="
+ + mTimeServiceHelper.isTimeZoneDetectionEnabled();
+ mTimeZoneLog.log(tmpLog);
+
+ // Set state as needed.
+ if (zoneId != null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: zoneId=" + zoneId);
+ }
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: skip changing zone"
+ + " as isTimeZoneDetectionEnabled() is false");
+ }
+ }
+ mSavedTimeZoneId = zoneId;
+ mNitzTimeZoneDetectionSuccessful = true;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: zoneId == null, do nothing");
+ }
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeZoneFromCountryAndNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " ex=" + ex);
+ }
+ }
+
+ /**
+ * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+ */
+ private boolean isNitzSignalOffsetInfoBogus(
+ TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
+
+ if (TextUtils.isEmpty(isoCountryCode)) {
+ // We cannot say for sure.
+ return false;
+ }
+
+ NitzData newNitzData = nitzSignal.getValue();
+ boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
+ return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
+ }
+
+ private boolean countryUsesUtc(
+ String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
+ return mTimeZoneLookupHelper.countryUsesUtc(
+ isoCountryCode,
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ }
+
+ @Override
+ public void handleNetworkAvailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
+ + mNitzTimeZoneDetectionSuccessful
+ + ", Setting mNitzTimeZoneDetectionSuccessful=false");
+ }
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNetworkUnavailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+ }
+
+ mGotCountryCode = false;
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ // Always store the latest NITZ signal received.
+ mLatestNitzSignal = nitzSignal;
+
+ updateTimeZoneFromCountryAndNitz();
+ updateTimeFromNitz();
+ }
+
+ private void updateTimeFromNitz() {
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+ try {
+ boolean ignoreNitz = mDeviceState.getIgnoreNitz();
+ if (ignoreNitz) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: Not suggesting system clock because"
+ + " gsm.ignore-nitz is set");
+ }
+ return;
+ }
+
+ // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
+ try {
+ // Acquire the wake lock as we are reading the elapsed realtime clock below.
+ mWakeLock.acquire();
+
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ long millisSinceNitzReceived =
+ elapsedRealtime - nitzSignal.getReferenceTimeMillis();
+ if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " nitzSignal=" + nitzSignal);
+ }
+ return;
+ }
+ } finally {
+ mWakeLock.release();
+ }
+
+ TimestampedValue<Long> newNitzTime = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+
+ // Perform rate limiting: a NITZ signal received too close to a previous
+ // one will be disregarded unless there is a significant difference between the
+ // UTC times they represent.
+ if (mSavedNitzTime != null) {
+ int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
+ int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
+
+ // Calculate the elapsed time between the new signal and the last signal.
+ long elapsedRealtimeSinceLastSaved = newNitzTime.getReferenceTimeMillis()
+ - mSavedNitzTime.getReferenceTimeMillis();
+
+ // Calculate the UTC difference between the time the two signals hold.
+ long utcTimeDifferenceMillis =
+ newNitzTime.getValue() - mSavedNitzTime.getValue();
+
+ // Ideally the difference between elapsedRealtimeSinceLastSaved and
+ // utcTimeDifferenceMillis would be zero.
+ long millisGained = utcTimeDifferenceMillis - elapsedRealtimeSinceLastSaved;
+
+ if (elapsedRealtimeSinceLastSaved <= nitzUpdateSpacing
+ && Math.abs(millisGained) <= nitzUpdateDiff) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time. NITZ signal is"
+ + " too similar to previous value received "
+ + " mSavedNitzTime=" + mSavedNitzTime
+ + ", nitzSignal=" + nitzSignal
+ + ", nitzUpdateSpacing=" + nitzUpdateSpacing
+ + ", nitzUpdateDiff=" + nitzUpdateDiff);
+ }
+ return;
+ }
+ }
+
+ String logMsg = "updateTimeFromNitz: suggesting system clock update"
+ + " nitzSignal=" + nitzSignal
+ + ", newNitzTime=" + newNitzTime
+ + ", mSavedNitzTime= " + mSavedNitzTime;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeLog.log(logMsg);
+ mTimeServiceHelper.suggestDeviceTime(newNitzTime);
+ TelephonyMetrics.getInstance().writeNITZEvent(
+ mPhone.getPhoneId(), newNitzTime.getValue());
+
+ // Save the last NITZ time signal that was suggested to enable rate limiting.
+ mSavedNitzTime = newNitzTime;
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeFromNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " ex=" + ex);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
+ }
+ mTimeServiceHelper.setDeviceTimeZone(zoneId);
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
+ + " zoneId=" + zoneId);
+ }
+ }
+
+ private void handleAutoTimeZoneEnabled() {
+ String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+ + " mSavedTimeZoneId=" + mSavedTimeZoneId;
+ if (DBG) {
+ Rlog.d(LOG_TAG, tmpLog);
+ }
+ mTimeZoneLog.log(tmpLog);
+ if (mSavedTimeZoneId != null) {
+ setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
+ }
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // Time Detection State
+ pw.println(" mSavedTime=" + mSavedNitzTime);
+
+ // Time Zone Detection State
+ pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
+ pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
+
+ // Miscellaneous
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.flush();
+ }
+
+ @Override
+ public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ ipw.println(" Time Logs:");
+ ipw.increaseIndent();
+ mTimeLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println(" Time zone Logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Update time zone by network country code, works well on countries which only have one time
+ * zone or multiple zones with the same offset.
+ *
+ * @param iso Country code from network MCC
+ */
+ private void updateTimeZoneFromNetworkCountryCode(String iso) {
+ CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+ iso, mTimeServiceHelper.currentTimeMillis());
+ if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+ String logMsg = "updateTimeZoneFromNetworkCountryCode: tz result found"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeZoneLog.log(logMsg);
+ String zoneId = lookupResult.zoneId;
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ }
+ mSavedTimeZoneId = zoneId;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromNetworkCountryCode: no good zone for"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult);
+ }
+ }
+ }
+
+ public boolean getNitzTimeZoneDetectionSuccessful() {
+ return mNitzTimeZoneDetectionSuccessful;
+ }
+
+ @Override
+ public NitzData getCachedNitzData() {
+ return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+ }
+
+ @Override
+ public String getSavedTimeZoneId() {
+ return mSavedTimeZoneId;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/NewTimeServiceHelper.java b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
new file mode 100644
index 0000000..1346c5f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 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;
+
+import android.app.AlarmManager;
+import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeSignal;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.TimestampedValue;
+
+/**
+ * An interface to various time / time zone detection behaviors that should be centralized into a
+ * new service.
+ */
+// Non-final to allow mocking.
+public class NewTimeServiceHelper {
+
+ /**
+ * Callback interface for automatic detection enable/disable changes.
+ */
+ public interface Listener {
+ /**
+ * Automatic time zone detection has been enabled or disabled.
+ */
+ void onTimeZoneDetectionChange(boolean enabled);
+ }
+
+ private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+
+ private final Context mContext;
+ private final ContentResolver mCr;
+ private final TimeDetector mTimeDetector;
+
+ private Listener mListener;
+
+ /** Creates a TimeServiceHelper */
+ public NewTimeServiceHelper(Context context) {
+ mContext = context;
+ mCr = context.getContentResolver();
+ mTimeDetector = context.getSystemService(TimeDetector.class);
+ }
+
+ /**
+ * Sets a listener that will be called when the automatic time / time zone detection setting
+ * changes.
+ */
+ public void setListener(Listener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener==null");
+ }
+ if (mListener != null) {
+ throw new IllegalStateException("listener already set");
+ }
+ this.mListener = listener;
+ mCr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange) {
+ listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
+ }
+ });
+ }
+
+ /**
+ * Returns the same value as {@link System#currentTimeMillis()}.
+ */
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the same value as {@link SystemClock#elapsedRealtime()}.
+ */
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Returns true if the device has an explicit time zone set.
+ */
+ public boolean isTimeZoneSettingInitialized() {
+ return isTimeZoneSettingInitializedStatic();
+
+ }
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ public boolean isTimeZoneDetectionEnabled() {
+ try {
+ return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ /**
+ * Set the device time zone and send out a sticky broadcast so the system can
+ * determine if the timezone was set by the carrier.
+ *
+ * @param zoneId timezone set by carrier
+ */
+ public void setDeviceTimeZone(String zoneId) {
+ setDeviceTimeZoneStatic(mContext, zoneId);
+ }
+
+ /**
+ * Suggest the time to the {@link TimeDetector}.
+ *
+ * @param signalTimeMillis the signal time as received from the network
+ */
+ public void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis) {
+ TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, signalTimeMillis);
+ mTimeDetector.suggestTime(timeSignal);
+ }
+
+ /**
+ * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
+ * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
+ * situation real service events may come in while a TelephonyTest is running, leading to flakes
+ * as the real / fake instance of TimeServiceHelper is swapped in and out from
+ * {@link TelephonyComponentFactory}.
+ */
+ static boolean isTimeZoneSettingInitializedStatic() {
+ // timezone.equals("GMT") will be true and only true if the timezone was
+ // set to a default value by the system server (when starting, system server
+ // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
+ // any code that sets it explicitly (in case where something sets GMT explicitly,
+ // "Etc/GMT" Olsen ID would be used).
+ // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
+ // better way of telling if the value has been defaulted.
+
+ String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
+ return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
+ }
+
+ /**
+ * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
+ * explanation.
+ */
+ static void setDeviceTimeZoneStatic(Context context, String zoneId) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.setTimeZone(zoneId);
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", zoneId);
+ context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/NitzData.java b/src/java/com/android/internal/telephony/NitzData.java
new file mode 100644
index 0000000..80f1c4a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NitzData.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 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;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Represents NITZ data. Various static methods are provided to help with parsing and intepretation
+ * of NITZ data.
+ *
+ * {@hide}
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class NitzData {
+ private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+ private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000;
+
+ /* Time stamp after 19 January 2038 is not supported under 32 bit */
+ private static final int MAX_NITZ_YEAR = 2037;
+
+ // Stored For logging / debugging only.
+ private final String mOriginalString;
+
+ private final int mZoneOffset;
+
+ private final Integer mDstOffset;
+
+ private final long mCurrentTimeMillis;
+
+ private final TimeZone mEmulatorHostTimeZone;
+
+ private NitzData(String originalString, int zoneOffsetMillis, Integer dstOffsetMillis,
+ long utcTimeMillis, TimeZone emulatorHostTimeZone) {
+ if (originalString == null) {
+ throw new NullPointerException("originalString==null");
+ }
+ this.mOriginalString = originalString;
+ this.mZoneOffset = zoneOffsetMillis;
+ this.mDstOffset = dstOffsetMillis;
+ this.mCurrentTimeMillis = utcTimeMillis;
+ this.mEmulatorHostTimeZone = emulatorHostTimeZone;
+ }
+
+ /**
+ * Parses the supplied NITZ string, returning the encoded data.
+ */
+ public static NitzData parse(String nitz) {
+ // "yy/mm/dd,hh:mm:ss(+/-)tz[,dt[,tzid]]"
+ // tz, dt are in number of quarter-hours
+
+ try {
+ /* 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) */
+ Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ c.clear();
+ c.set(Calendar.DST_OFFSET, 0);
+
+ String[] nitzSubs = nitz.split("[/:,+-]");
+
+ 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;
+ }
+ c.set(Calendar.YEAR, year);
+
+ // month is 0 based!
+ int month = Integer.parseInt(nitzSubs[1]) - 1;
+ c.set(Calendar.MONTH, month);
+
+ int date = Integer.parseInt(nitzSubs[2]);
+ c.set(Calendar.DATE, date);
+
+ int hour = Integer.parseInt(nitzSubs[3]);
+ c.set(Calendar.HOUR, hour);
+
+ int minute = Integer.parseInt(nitzSubs[4]);
+ c.set(Calendar.MINUTE, minute);
+
+ int second = Integer.parseInt(nitzSubs[5]);
+ c.set(Calendar.SECOND, second);
+
+ // The offset received from NITZ is the offset to add to get current local time.
+ boolean sign = (nitz.indexOf('-') == -1);
+ int totalUtcOffsetQuarterHours = Integer.parseInt(nitzSubs[6]);
+ int totalUtcOffsetMillis =
+ (sign ? 1 : -1) * totalUtcOffsetQuarterHours * MS_PER_QUARTER_HOUR;
+
+ // DST correction is already applied to the UTC offset. We could subtract it if we
+ // wanted the raw offset.
+ Integer dstAdjustmentQuarterHours =
+ (nitzSubs.length >= 8) ? Integer.parseInt(nitzSubs[7]) : null;
+ Integer dstAdjustmentMillis = null;
+ if (dstAdjustmentQuarterHours != null) {
+ dstAdjustmentMillis = dstAdjustmentQuarterHours * MS_PER_QUARTER_HOUR;
+ }
+
+ // As a special extension, the Android emulator appends the name of
+ // the host computer's timezone to the nitz string. this is zoneinfo
+ // timezone name of the form Area!Location or Area!Location!SubLocation
+ // so we need to convert the ! into /
+ TimeZone zone = null;
+ if (nitzSubs.length >= 9) {
+ String tzname = nitzSubs[8].replace('!', '/');
+ zone = TimeZone.getTimeZone(tzname);
+ }
+ return new NitzData(nitz, totalUtcOffsetMillis, dstAdjustmentMillis,
+ c.getTimeInMillis(), zone);
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
+ return null;
+ }
+ }
+
+ /** A method for use in tests to create NitzData instances. */
+ public static NitzData createForTests(int zoneOffsetMillis, Integer dstOffsetMillis,
+ long utcTimeMillis, TimeZone emulatorHostTimeZone) {
+ return new NitzData("Test data", zoneOffsetMillis, dstOffsetMillis, utcTimeMillis,
+ emulatorHostTimeZone);
+ }
+
+ /**
+ * Returns the current time as the number of milliseconds since the beginning of the Unix epoch
+ * (1/1/1970 00:00:00 UTC).
+ */
+ public long getCurrentTimeInMillis() {
+ return mCurrentTimeMillis;
+ }
+
+ /**
+ * Returns the total offset to apply to the {@link #getCurrentTimeInMillis()} to arrive at a
+ * local time.
+ */
+ public int getLocalOffsetMillis() {
+ return mZoneOffset;
+ }
+
+ /**
+ * Returns the offset (already included in {@link #getLocalOffsetMillis()}) associated with
+ * Daylight Savings Time (DST). This field is optional: {@code null} means the DST offset is
+ * unknown.
+ */
+ public Integer getDstAdjustmentMillis() {
+ return mDstOffset;
+ }
+
+ /**
+ * Returns {@link true} if the time is in Daylight Savings Time (DST), {@link false} if it is
+ * unknown or not in DST. See {@link #getDstAdjustmentMillis()}.
+ */
+ public boolean isDst() {
+ return mDstOffset != null && mDstOffset != 0;
+ }
+
+
+ /**
+ * Returns the time zone of the host computer when Android is running in an emulator. It is
+ * {@code null} for real devices. This information is communicated via a non-standard Android
+ * extension to NITZ.
+ */
+ public TimeZone getEmulatorHostTimeZone() {
+ return mEmulatorHostTimeZone;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ NitzData nitzData = (NitzData) o;
+
+ if (mZoneOffset != nitzData.mZoneOffset) {
+ return false;
+ }
+ if (mCurrentTimeMillis != nitzData.mCurrentTimeMillis) {
+ return false;
+ }
+ if (!mOriginalString.equals(nitzData.mOriginalString)) {
+ return false;
+ }
+ if (mDstOffset != null ? !mDstOffset.equals(nitzData.mDstOffset)
+ : nitzData.mDstOffset != null) {
+ return false;
+ }
+ return mEmulatorHostTimeZone != null ? mEmulatorHostTimeZone
+ .equals(nitzData.mEmulatorHostTimeZone) : nitzData.mEmulatorHostTimeZone == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mOriginalString.hashCode();
+ result = 31 * result + mZoneOffset;
+ result = 31 * result + (mDstOffset != null ? mDstOffset.hashCode() : 0);
+ result = 31 * result + (int) (mCurrentTimeMillis ^ (mCurrentTimeMillis >>> 32));
+ result = 31 * result + (mEmulatorHostTimeZone != null ? mEmulatorHostTimeZone.hashCode()
+ : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "NitzData{"
+ + "mOriginalString=" + mOriginalString
+ + ", mZoneOffset=" + mZoneOffset
+ + ", mDstOffset=" + mDstOffset
+ + ", mCurrentTimeMillis=" + mCurrentTimeMillis
+ + ", mEmulatorHostTimeZone=" + mEmulatorHostTimeZone
+ + '}';
+ }
+}
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
new file mode 100644
index 0000000..6a5e47a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -0,0 +1,139 @@
+/*
+ * 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;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.TimestampedValue;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public interface NitzStateMachine {
+
+ /**
+ * Called when the network country is set on the Phone. Although set, the network country code
+ * may be invalid.
+ *
+ * @param countryChanged true when the country code is known to have changed, false if it
+ * probably hasn't
+ */
+ void handleNetworkCountryCodeSet(boolean countryChanged);
+
+ /**
+ * Informs the {@link NitzStateMachine} that the network has become available.
+ */
+ void handleNetworkAvailable();
+
+ /**
+ * Informs the {@link NitzStateMachine} that the network has become unavailable.
+ */
+ void handleNetworkUnavailable();
+
+ /**
+ * Handle a new NITZ signal being received.
+ */
+ void handleNitzReceived(TimestampedValue<NitzData> nitzSignal);
+
+ /**
+ * Dumps the current in-memory state to the supplied PrintWriter.
+ */
+ void dumpState(PrintWriter pw);
+
+ /**
+ * Dumps the time / time zone logs to the supplied IndentingPrintWriter.
+ */
+ void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args);
+
+ /**
+ * Returns the last NITZ data that was cached.
+ */
+ NitzData getCachedNitzData();
+
+ /**
+ * Returns the time zone ID from the most recent time that a time zone could be determined by
+ * this state machine.
+ */
+ String getSavedTimeZoneId();
+
+ /**
+ * A proxy over device state that allows things like system properties, system clock
+ * to be faked for tests.
+ */
+ // Non-final to allow mocking.
+ class DeviceState {
+ private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
+ private final int mNitzUpdateSpacing;
+
+ private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
+ private final int mNitzUpdateDiff;
+
+ private final GsmCdmaPhone mPhone;
+ private final TelephonyManager mTelephonyManager;
+ private final ContentResolver mCr;
+
+ public DeviceState(GsmCdmaPhone phone) {
+ mPhone = phone;
+
+ Context context = phone.getContext();
+ mTelephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mCr = context.getContentResolver();
+ mNitzUpdateSpacing =
+ SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT);
+ mNitzUpdateDiff =
+ SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_DEFAULT);
+ }
+
+ /**
+ * If time between NITZ updates is less than {@link #getNitzUpdateSpacingMillis()} the
+ * update may be ignored.
+ */
+ public int getNitzUpdateSpacingMillis() {
+ return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_SPACING,
+ mNitzUpdateSpacing);
+ }
+
+ /**
+ * If {@link #getNitzUpdateSpacingMillis()} hasn't been exceeded but update is >
+ * {@link #getNitzUpdateDiffMillis()} do the update
+ */
+ public int getNitzUpdateDiffMillis() {
+ return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
+ }
+
+ /**
+ * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes".
+ */
+ public boolean getIgnoreNitz() {
+ String ignoreNitz = SystemProperties.get("gsm.ignore-nitz");
+ return ignoreNitz != null && ignoreNitz.equals("yes");
+ }
+
+ public String getNetworkCountryIsoForPhone() {
+ return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/OldNitzStateMachine.java b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
new file mode 100644
index 0000000..bb43f1e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 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;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public final class OldNitzStateMachine implements NitzStateMachine {
+
+ private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+ private static final boolean DBG = ServiceStateTracker.DBG;
+
+ // Time detection state.
+
+ /**
+ * The last NITZ-sourced time considered. If auto time detection was off at the time this may
+ * not have been used to set the device time, but it can be used if auto time detection is
+ * re-enabled.
+ */
+ private TimestampedValue<Long> mSavedNitzTime;
+
+ // Time Zone detection state.
+
+ /** We always keep the last NITZ signal received in mLatestNitzSignal. */
+ private TimestampedValue<NitzData> mLatestNitzSignal;
+
+ /**
+ * Records whether the device should have a country code available via
+ * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+ * received is (almost always) not enough to determine time zone. On test networks the country
+ * code should be available but can still be an empty string but this flag indicates that the
+ * information available is unlikely to improve.
+ */
+ private boolean mGotCountryCode = false;
+
+ /**
+ * The last time zone ID that has been determined. It may not have been set as the device time
+ * zone if automatic time zone detection is disabled but may later be used to set the time zone
+ * if the user enables automatic time zone detection.
+ */
+ private String mSavedTimeZoneId;
+
+ /**
+ * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
+ * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
+ * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
+ * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
+ * less reliable algorithms than NITZ-based detection such as by just using network country
+ * code.
+ */
+ private boolean mNitzTimeZoneDetectionSuccessful = false;
+
+ // Miscellaneous dependencies and helpers not related to detection state.
+ private final LocalLog mTimeLog = new LocalLog(15);
+ private final LocalLog mTimeZoneLog = new LocalLog(15);
+ private final GsmCdmaPhone mPhone;
+ private final DeviceState mDeviceState;
+ private final OldTimeServiceHelper mTimeServiceHelper;
+ private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+ /** Wake lock used while setting time of day. */
+ private final PowerManager.WakeLock mWakeLock;
+ private static final String WAKELOCK_TAG = "NitzStateMachine";
+
+ public OldNitzStateMachine(GsmCdmaPhone phone) {
+ this(phone,
+ new OldTimeServiceHelper(phone.getContext()),
+ new DeviceState(phone),
+ new TimeZoneLookupHelper());
+ }
+
+ @VisibleForTesting
+ public OldNitzStateMachine(GsmCdmaPhone phone, OldTimeServiceHelper timeServiceHelper,
+ DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
+ mPhone = phone;
+
+ Context context = phone.getContext();
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+ mDeviceState = deviceState;
+ mTimeZoneLookupHelper = timeZoneLookupHelper;
+ mTimeServiceHelper = timeServiceHelper;
+ mTimeServiceHelper.setListener(new OldTimeServiceHelper.Listener() {
+ @Override
+ public void onTimeDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeEnabled();
+ }
+ }
+
+ @Override
+ public void onTimeZoneDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeZoneEnabled();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void handleNetworkCountryCodeSet(boolean countryChanged) {
+ boolean hadCountryCode = mGotCountryCode;
+ mGotCountryCode = true;
+
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
+ updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+ }
+
+ if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+ updateTimeZoneFromCountryAndNitz();
+ }
+ }
+
+ private void updateTimeZoneFromCountryAndNitz() {
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+
+ // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
+ // been set which makes it difficult to tell if it's what the user / time zone detection
+ // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
+ // device has ever been explicit set by the user or code.
+ final boolean isTimeZoneSettingInitialized =
+ mTimeServiceHelper.isTimeZoneSettingInitialized();
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode);
+ }
+
+ try {
+ NitzData nitzData = nitzSignal.getValue();
+
+ String zoneId;
+ if (nitzData.getEmulatorHostTimeZone() != null) {
+ zoneId = nitzData.getEmulatorHostTimeZone().getID();
+ } else if (!mGotCountryCode) {
+ // We don't have a country code so we won't try to look up the time zone.
+ zoneId = null;
+ } else if (TextUtils.isEmpty(isoCountryCode)) {
+ // We have a country code but it's empty. This is most likely because we're on a
+ // test network that's using a bogus MCC (eg, "001"). Obtain a TimeZone based only
+ // on the NITZ parameters: it's only going to be correct in a few cases but it
+ // should at least have the correct offset.
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ String logMsg = "updateTimeZoneFromCountryAndNitz: lookupByNitz returned"
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ } else if (mLatestNitzSignal == null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: No cached NITZ data available,"
+ + " not setting zone");
+ }
+ zoneId = null;
+ } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) {
+ String logMsg = "updateTimeZoneFromCountryAndNitz: Received NITZ looks bogus, "
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = null;
+ } else {
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+ nitzData, isoCountryCode);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: using"
+ + " lookupByNitzCountry(nitzData, isoCountryCode),"
+ + " nitzData=" + nitzData
+ + " isoCountryCode=" + isoCountryCode
+ + " lookupResult=" + lookupResult);
+ }
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ }
+
+ // Log the action taken to the dedicated time zone log.
+ final String tmpLog = "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal
+ + " zoneId=" + zoneId
+ + " isTimeZoneDetectionEnabled()="
+ + mTimeServiceHelper.isTimeZoneDetectionEnabled();
+ mTimeZoneLog.log(tmpLog);
+
+ // Set state as needed.
+ if (zoneId != null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: zoneId=" + zoneId);
+ }
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: skip changing zone"
+ + " as isTimeZoneDetectionEnabled() is false");
+ }
+ }
+ mSavedTimeZoneId = zoneId;
+ mNitzTimeZoneDetectionSuccessful = true;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: zoneId == null, do nothing");
+ }
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeZoneFromCountryAndNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " ex=" + ex);
+ }
+ }
+
+ /**
+ * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+ */
+ private boolean isNitzSignalOffsetInfoBogus(
+ TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
+
+ if (TextUtils.isEmpty(isoCountryCode)) {
+ // We cannot say for sure.
+ return false;
+ }
+
+ NitzData newNitzData = nitzSignal.getValue();
+ boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
+ return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
+ }
+
+ private boolean countryUsesUtc(
+ String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
+ return mTimeZoneLookupHelper.countryUsesUtc(
+ isoCountryCode,
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ }
+
+ @Override
+ public void handleNetworkAvailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
+ + mNitzTimeZoneDetectionSuccessful
+ + ", Setting mNitzTimeZoneDetectionSuccessful=false");
+ }
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNetworkUnavailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+ }
+
+ mGotCountryCode = false;
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ // Always store the latest NITZ signal received.
+ mLatestNitzSignal = nitzSignal;
+
+ updateTimeZoneFromCountryAndNitz();
+ updateTimeFromNitz();
+ }
+
+ private void updateTimeFromNitz() {
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+ try {
+ boolean ignoreNitz = mDeviceState.getIgnoreNitz();
+ if (ignoreNitz) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
+ }
+ return;
+ }
+
+ try {
+ // Acquire the wake lock as we are reading the elapsed realtime clock and system
+ // clock.
+ mWakeLock.acquire();
+
+ // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ long millisSinceNitzReceived =
+ elapsedRealtime - nitzSignal.getReferenceTimeMillis();
+ if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " nitzSignal=" + nitzSignal);
+ }
+ return;
+ }
+
+ // Adjust the NITZ time by the delay since it was received to get the time now.
+ long adjustedCurrentTimeMillis =
+ nitzSignal.getValue().getCurrentTimeInMillis() + millisSinceNitzReceived;
+ long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
+
+ if (mTimeServiceHelper.isTimeDetectionEnabled()) {
+ String logMsg = "updateTimeFromNitz:"
+ + " nitzSignal=" + nitzSignal
+ + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis
+ + " millisSinceNitzReceived= " + millisSinceNitzReceived
+ + " gained=" + gained;
+
+ if (mSavedNitzTime == null) {
+ logMsg += ": First update received.";
+ setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
+ } else {
+ long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
+ - mSavedNitzTime.getReferenceTimeMillis();
+ int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
+ int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
+ if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
+ || Math.abs(gained) > nitzUpdateDiff) {
+ // Either it has been a while since we received an update, or the gain
+ // is sufficiently large that we want to act on it.
+ logMsg += ": New update received.";
+ setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg + ": Update throttled.");
+ }
+
+ // Return early. This means that we don't reset the
+ // mSavedNitzTime for next time and that we may act on more
+ // NITZ time signals overall but should end up with a system clock that
+ // tracks NITZ more closely than if we saved throttled values (which
+ // would reset mSavedNitzTime.elapsedRealtime used to calculate time
+ // since the last NITZ signal was received).
+ return;
+ }
+ }
+ }
+
+ // Save the last NITZ time signal used so we can return to it later
+ // if auto-time detection is toggled.
+ mSavedNitzTime = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ } finally {
+ mWakeLock.release();
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeFromNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " ex=" + ex);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
+ }
+ mTimeServiceHelper.setDeviceTimeZone(zoneId);
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
+ + " zoneId=" + zoneId);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTime(String msg, long time) {
+ if (!mWakeLock.isHeld()) {
+ Rlog.w(LOG_TAG, "setAndBroadcastNetworkSetTime: Wake lock not held while setting device"
+ + " time (msg=" + msg + ")");
+ }
+
+ msg = "setAndBroadcastNetworkSetTime: [Setting time to time=" + time + "]:" + msg;
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ mTimeLog.log(msg);
+ mTimeServiceHelper.setDeviceTime(time);
+ TelephonyMetrics.getInstance().writeNITZEvent(mPhone.getPhoneId(), time);
+ }
+
+ private void handleAutoTimeEnabled() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleAutoTimeEnabled: Reverting to NITZ Time:"
+ + " mSavedNitzTime=" + mSavedNitzTime);
+ }
+ if (mSavedNitzTime != null) {
+ try {
+ // Acquire the wakelock as we're reading the elapsed realtime clock here.
+ mWakeLock.acquire();
+
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ String msg = "mSavedNitzTime: Reverting to NITZ time"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " mSavedNitzTime=" + mSavedNitzTime;
+ long adjustedCurrentTimeMillis = mSavedNitzTime.getValue()
+ + (elapsedRealtime - mSavedNitzTime.getReferenceTimeMillis());
+ setAndBroadcastNetworkSetTime(msg, adjustedCurrentTimeMillis);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ private void handleAutoTimeZoneEnabled() {
+ String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+ + " mSavedTimeZoneId=" + mSavedTimeZoneId;
+ if (DBG) {
+ Rlog.d(LOG_TAG, tmpLog);
+ }
+ mTimeZoneLog.log(tmpLog);
+ if (mSavedTimeZoneId != null) {
+ setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
+ }
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // Time Detection State
+ pw.println(" mSavedTime=" + mSavedNitzTime);
+
+ // Time Zone Detection State
+ pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
+ pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
+
+ // Miscellaneous
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.flush();
+ }
+
+ @Override
+ public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ ipw.println(" Time Logs:");
+ ipw.increaseIndent();
+ mTimeLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println(" Time zone Logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Update time zone by network country code, works well on countries which only have one time
+ * zone or multiple zones with the same offset.
+ *
+ * @param iso Country code from network MCC
+ */
+ private void updateTimeZoneFromNetworkCountryCode(String iso) {
+ CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+ iso, mTimeServiceHelper.currentTimeMillis());
+ if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+ String logMsg = "updateTimeZoneFromNetworkCountryCode: tz result found"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeZoneLog.log(logMsg);
+ String zoneId = lookupResult.zoneId;
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ }
+ mSavedTimeZoneId = zoneId;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromNetworkCountryCode: no good zone for"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult);
+ }
+ }
+ }
+
+ public boolean getNitzTimeZoneDetectionSuccessful() {
+ return mNitzTimeZoneDetectionSuccessful;
+ }
+
+ @Override
+ public NitzData getCachedNitzData() {
+ return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+ }
+
+ @Override
+ public String getSavedTimeZoneId() {
+ return mSavedTimeZoneId;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/OldTimeServiceHelper.java b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
new file mode 100644
index 0000000..9c7a763
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 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;
+
+import android.app.AlarmManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * An interface to various time / time zone detection behaviors that should be centralized into a
+ * new service.
+ */
+// Non-final to allow mocking.
+public class OldTimeServiceHelper {
+
+ /**
+ * Callback interface for automatic detection enable/disable changes.
+ */
+ public interface Listener {
+ /**
+ * Automatic time detection has been enabled or disabled.
+ */
+ void onTimeDetectionChange(boolean enabled);
+
+ /**
+ * Automatic time zone detection has been enabled or disabled.
+ */
+ void onTimeZoneDetectionChange(boolean enabled);
+ }
+
+ private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+
+ private final Context mContext;
+ private final ContentResolver mCr;
+
+ private Listener mListener;
+
+ /** Creates a TimeServiceHelper */
+ public OldTimeServiceHelper(Context context) {
+ mContext = context;
+ mCr = context.getContentResolver();
+ }
+
+ /**
+ * Sets a listener that will be called when the automatic time / time zone detection setting
+ * changes.
+ */
+ public void setListener(Listener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener==null");
+ }
+ if (mListener != null) {
+ throw new IllegalStateException("listener already set");
+ }
+ this.mListener = listener;
+ mCr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange) {
+ listener.onTimeDetectionChange(isTimeDetectionEnabled());
+ }
+ });
+ mCr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange) {
+ listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
+ }
+ });
+ }
+
+ /**
+ * Returns the same value as {@link System#currentTimeMillis()}.
+ */
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the same value as {@link SystemClock#elapsedRealtime()}.
+ */
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Returns true if the device has an explicit time zone set.
+ */
+ public boolean isTimeZoneSettingInitialized() {
+ return isTimeZoneSettingInitializedStatic();
+
+ }
+
+ /**
+ * Returns true if automatic time detection is enabled in settings.
+ */
+ public boolean isTimeDetectionEnabled() {
+ try {
+ return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ public boolean isTimeZoneDetectionEnabled() {
+ try {
+ return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ /**
+ * Set the device time zone and send out a sticky broadcast so the system can
+ * determine if the timezone was set by the carrier.
+ *
+ * @param zoneId timezone set by carrier
+ */
+ public void setDeviceTimeZone(String zoneId) {
+ setDeviceTimeZoneStatic(mContext, zoneId);
+ }
+
+ /**
+ * Set the time and Send out a sticky broadcast so the system can determine
+ * if the time was set by the carrier.
+ *
+ * @param time time set by network
+ */
+ public void setDeviceTime(long time) {
+ SystemClock.setCurrentTimeMillis(time);
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time", time);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
+ * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
+ * situation real service events may come in while a TelephonyTest is running, leading to flakes
+ * as the real / fake instance of TimeServiceHelper is swapped in and out from
+ * {@link TelephonyComponentFactory}.
+ */
+ static boolean isTimeZoneSettingInitializedStatic() {
+ // timezone.equals("GMT") will be true and only true if the timezone was
+ // set to a default value by the system server (when starting, system server
+ // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
+ // any code that sets it explicitly (in case where something sets GMT explicitly,
+ // "Etc/GMT" Olsen ID would be used).
+ // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
+ // better way of telling if the value has been defaulted.
+
+ String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
+ return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
+ }
+
+ /**
+ * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
+ * explanation.
+ */
+ static void setDeviceTimeZoneStatic(Context context, String zoneId) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.setTimeZone(zoneId);
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", zoneId);
+ context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 6595c56..899f1fc 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -28,7 +28,6 @@
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -49,12 +48,15 @@
import android.telephony.ClientRequestStats;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.telephony.VoLteServiceState;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import com.android.ims.ImsCall;
@@ -120,14 +122,10 @@
if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
mImsServiceReady = true;
updateImsPhone();
- ImsManager.updateImsServiceConfig(mContext, mPhoneId, false);
+ ImsManager.getInstance(mContext, mPhoneId).updateImsServiceConfig(false);
} else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
mImsServiceReady = false;
updateImsPhone();
- } else if (intent.getAction().equals(ImsConfig.ACTION_IMS_CONFIG_CHANGED)) {
- int item = intent.getIntExtra(ImsConfig.EXTRA_CHANGED_ITEM, -1);
- String value = intent.getStringExtra(ImsConfig.EXTRA_NEW_VALUE);
- ImsManager.onProvisionedValueChanged(context, item, value);
}
}
}
@@ -276,11 +274,11 @@
public SmsUsageMonitor mSmsUsageMonitor;
protected AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
-
- private TelephonyTester mTelephonyTester;
+ TelephonyTester mTelephonyTester;
private String mName;
private final String mActionDetached;
private final String mActionAttached;
+ protected DeviceStateMonitor mDeviceStateMonitor;
protected int mPhoneId;
@@ -413,14 +411,24 @@
}
/**
- * Set a system property, unless we're in unit test mode
+ * Set a system property for the current phone, unless we're in unit test mode
*/
// CAF_MSIM TODO this need to be replated with TelephonyManager API ?
public void setSystemProperty(String property, String value) {
- if(getUnitTestMode()) {
+ if (getUnitTestMode()) {
return;
}
- SystemProperties.set(property, value);
+ TelephonyManager.setTelephonyProperty(mPhoneId, property, value);
+ }
+
+ /**
+ * Set a system property for all phones, unless we're in unit test mode
+ */
+ public void setGlobalSystemProperty(String property, String value) {
+ if (getUnitTestMode()) {
+ return;
+ }
+ TelephonyManager.setTelephonyProperty(property, value);
}
/**
@@ -564,7 +572,6 @@
filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
}
- filter.addAction(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
mContext.registerReceiver(mImsIntentReceiver, filter);
// Monitor IMS service - but first poll to see if already up (could miss
@@ -662,7 +669,7 @@
String dialString = (String) ar.result;
if (TextUtils.isEmpty(dialString)) return;
try {
- dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, null);
+ dialInternal(dialString, new DialArgs.Builder().build());
} catch (CallStateException e) {
Rlog.e(LOG_TAG, "silent redial failed: " + e);
}
@@ -1413,8 +1420,6 @@
*/
public void registerForServiceStateChanged(
Handler h, int what, Object obj) {
- checkCorrectThread(h);
-
mServiceStateRegistrants.add(h, what, obj);
}
@@ -1797,7 +1802,7 @@
int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED :
IccRecords.CALL_FORWARDING_STATUS_DISABLED;
int subId = getSubId();
- Rlog.d(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
+ Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
" in pref " + CF_STATUS + subId);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -1839,6 +1844,9 @@
if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) {
callForwardingIndicator = getCallForwardingIndicatorFromSharedPref();
}
+ Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null
+ ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag="
+ + getCallForwardingIndicatorFromSharedPref());
return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED);
}
@@ -2173,6 +2181,10 @@
mNotifier.notifyDataActivationStateChanged(this, state);
}
+ public void notifyUserMobileDataStateChanged(boolean state) {
+ mNotifier.notifyUserMobileDataStateChanged(this, state);
+ }
+
public void notifySignalStrength() {
mNotifier.notifySignalStrength(this);
}
@@ -2181,6 +2193,11 @@
mNotifier.notifyCellInfo(this, privatizeCellInfoList(cellInfo));
}
+ /** Notify {@link PhysicalChannelConfig} changes. */
+ public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+ mNotifier.notifyPhysicalChannelConfiguration(this, configs);
+ }
+
public void notifyVoLteServiceStateChanged(VoLteServiceState lteState) {
mNotifier.notifyVoLteServiceStateChanged(this, lteState);
}
@@ -2208,7 +2225,7 @@
}
public void setIsInEcm(boolean isInEcm) {
- setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, String.valueOf(isInEcm));
+ setGlobalSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, String.valueOf(isInEcm));
mIsPhoneInEcmState = isInEcm;
}
@@ -2930,6 +2947,13 @@
}
/**
+ * Retrieves the EF_PNN from the UICC For GSM/UMTS phones.
+ */
+ public String getPlmn() {
+ return null;
+ }
+
+ /**
* Get the current for the default apn DataState. No change notification
* exists at this interface -- use
* {@link android.telephony.PhoneStateListener} instead.
@@ -3034,6 +3058,27 @@
return;
}
+ public int getCarrierId() {
+ return TelephonyManager.UNKNOWN_CARRIER_ID;
+ }
+
+ public String getCarrierName() {
+ return null;
+ }
+
+ public int getCarrierIdListVersion() {
+ return TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
+ }
+
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ */
+ public void resetCarrierKeysForImsiEncryption() {
+ return;
+ }
+
/**
* Return if UT capability of ImsPhone is enabled or not
*/
@@ -3073,14 +3118,11 @@
* Dials a number.
*
* @param dialString The number to dial.
- * @param uusInfo The UUSInfo.
- * @param videoState The video state for the call.
- * @param intentExtras Extras from the original CALL intent.
+ * @param dialArgs Parameters to dial with.
* @return The Connection.
* @throws CallStateException
*/
- protected Connection dialInternal(
- String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ protected Connection dialInternal(String dialString, DialArgs dialArgs)
throws CallStateException {
// dialInternal shall be overriden by GsmCdmaPhone
return null;
@@ -3090,6 +3132,13 @@
* Returns the subscription id.
*/
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);
}
@@ -3219,6 +3268,20 @@
return isVolteEnabled;
}
+ /**
+ * @return the IMS MmTel Registration technology for this Phone, defined in
+ * {@link ImsRegistrationImplBase}.
+ */
+ public int getImsRegistrationTech() {
+ Phone imsPhone = mImsPhone;
+ int regTech = ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ if (imsPhone != null) {
+ regTech = imsPhone.getImsRegistrationTech();
+ }
+ Rlog.d(LOG_TAG, "getImsRegistrationTechnology =" + regTech);
+ return regTech;
+ }
+
private boolean getRoamingOverrideHelper(String prefix, String key) {
String iccId = getIccSerialNumber();
if (TextUtils.isEmpty(iccId) || TextUtils.isEmpty(key)) {
@@ -3365,12 +3428,11 @@
* @return {@code true} if IMS calling is enabled.
*/
public boolean isImsUseEnabled() {
- boolean imsUseEnabled =
- ((ImsManager.isVolteEnabledByPlatform(mContext) &&
- ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext)) ||
- (ImsManager.isWfcEnabledByPlatform(mContext) &&
- ImsManager.isWfcEnabledByUser(mContext)) &&
- ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext));
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform()
+ && imsManager.isEnhanced4gLteModeSettingEnabledByUser())
+ || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser())
+ && imsManager.isNonTtyOrTtyOnVolteEnabled());
return imsUseEnabled;
}
@@ -3429,6 +3491,16 @@
mCi.setAllowedCarriers(carriers, response);
}
+ /** Sets the SignalStrength reporting criteria. */
+ public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
+ // no-op default implementation
+ }
+
+ /** Sets the SignalStrength reporting criteria. */
+ public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) {
+ // no-op default implementation
+ }
+
/**
* Get allowed carriers
*/
@@ -3491,13 +3563,13 @@
return false;
}
- public static void checkWfcWifiOnlyModeBeforeDial(Phone imsPhone, Context context)
+ public static void checkWfcWifiOnlyModeBeforeDial(Phone imsPhone, int phoneId, Context context)
throws CallStateException {
if (imsPhone == null || !imsPhone.isWifiCallingEnabled()) {
- boolean wfcWiFiOnly = (ImsManager.isWfcEnabledByPlatform(context) &&
- ImsManager.isWfcEnabledByUser(context) &&
- (ImsManager.getWfcMode(context) ==
- ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+ ImsManager imsManager = ImsManager.getInstance(context, phoneId);
+ boolean wfcWiFiOnly = (imsManager.isWfcEnabledByPlatform()
+ && imsManager.isWfcEnabledByUser() && (imsManager.getWfcMode()
+ == ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
if (wfcWiFiOnly) {
throw new CallStateException(
CallStateException.ERROR_OUT_OF_SERVICE,
@@ -3579,6 +3651,16 @@
mCi.setSimCardPower(state, null);
}
+ public void setRadioIndicationUpdateMode(int filters, int mode) {
+ if (mDeviceStateMonitor != null) {
+ mDeviceStateMonitor.setIndicationUpdateMode(filters, mode);
+ }
+ }
+
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Phone: subId=" + getSubId());
pw.println(" mPhoneId=" + mPhoneId);
@@ -3694,6 +3776,12 @@
pw.println("++++++++++++++++++++++++++++++++");
}
+ if (mDeviceStateMonitor != null) {
+ pw.println("DeviceStateMonitor:");
+ mDeviceStateMonitor.dump(fd, pw, args);
+ pw.println("++++++++++++++++++++++++++++++++");
+ }
+
if (mCi != null && mCi instanceof RIL) {
try {
((RIL)mCi).dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index d2a65d9..ba77747 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -35,14 +35,15 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
+import com.android.internal.telephony.euicc.EuiccCardController;
import com.android.internal.telephony.euicc.EuiccController;
import com.android.internal.telephony.ims.ImsResolver;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneFactory;
import com.android.internal.telephony.sip.SipPhone;
import com.android.internal.telephony.sip.SipPhoneFactory;
-import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.util.IndentingPrintWriter;
@@ -73,6 +74,7 @@
static private UiccController sUiccController;
private static IntentBroadcaster sIntentBroadcaster;
private static @Nullable EuiccController sEuiccController;
+ private static @Nullable EuiccCardController sEuiccCardController;
static private CommandsInterface sCommandsInterface = null;
static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
@@ -138,21 +140,22 @@
int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TELEPHONY_EUICC)) {
- sEuiccController = EuiccController.init(context);
- }
-
/* In case of multi SIM mode two instances of Phone, RIL are created,
where as in single SIM mode only instance. isMultiSimEnabled() function checks
whether it is single SIM or multi SIM mode */
int numPhones = TelephonyManager.getDefault().getPhoneCount();
- // Start ImsResolver and bind to ImsServices.
+ // Return whether or not the device should use dynamic binding or the static
+ // implementation (deprecated)
+ boolean isDynamicBinding = sContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dynamic_bind_ims);
+ // Get the package name of the default IMS implementation.
String defaultImsPackage = sContext.getResources().getString(
com.android.internal.R.string.config_ims_package);
+ // Start ImsResolver and bind to ImsServices.
Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
- sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones);
- sImsResolver.populateCacheAndStartBind();
+ sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
+ isDynamicBinding);
+ sImsResolver.initPopulateCacheAndStartBind();
int[] networkModes = new int[numPhones];
sPhones = new Phone[numPhones];
@@ -175,6 +178,12 @@
// call getInstance()
sUiccController = UiccController.make(context, sCommandsInterfaces);
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_EUICC)) {
+ sEuiccController = EuiccController.init(context);
+ sEuiccCardController = EuiccCardController.init(context);
+ }
+
for (int i = 0; i < numPhones; i++) {
Phone phone = null;
int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
@@ -298,6 +307,10 @@
}
}
+ public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() {
+ return sSubInfoRecordUpdater;
+ }
+
public static ImsResolver getImsResolver() {
return sImsResolver;
}
@@ -321,9 +334,22 @@
public static int calculatePreferredNetworkType(Context context, int phoneSubId) {
int networkType = android.provider.Settings.Global.getInt(context.getContentResolver(),
android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
- RILConstants.PREFERRED_NETWORK_MODE);
+ -1 /* invalid network mode */);
Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneSubId = " + phoneSubId +
" networkType = " + networkType);
+
+ if (networkType == -1) {
+ networkType = RILConstants.PREFERRED_NETWORK_MODE;
+ try {
+ networkType = TelephonyManager.getIntAtIndex(context.getContentResolver(),
+ android.provider.Settings.Global.PREFERRED_NETWORK_MODE,
+ SubscriptionController.getInstance().getPhoneId(phoneSubId));
+ } catch (SettingNotFoundException retrySnfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
+ + "Settings.Global.PREFERRED_NETWORK_MODE");
+ }
+ }
+
return networkType;
}
@@ -432,7 +458,10 @@
pw.println("++++++++++++++++++++++++++++++++");
try {
- ((IccCardProxy)phone.getIccCard()).dump(fd, pw, args);
+ UiccProfile uiccProfile = (UiccProfile) phone.getIccCard();
+ if (uiccProfile != null) {
+ uiccProfile.dump(fd, pw, args);
+ }
} catch (Exception e) {
e.printStackTrace();
}
@@ -467,6 +496,7 @@
pw.increaseIndent();
try {
sEuiccController.dump(fd, pw, args);
+ sEuiccCardController.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 7dbd8d3..91b7b28 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,12 +16,14 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.os.WorkSource;
import android.telephony.CarrierConfigManager;
+import android.telecom.VideoProfile;
import android.telephony.CellLocation;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
@@ -62,6 +64,51 @@
UNKNOWN, SWITCH, SEPARATE, TRANSFER, CONFERENCE, REJECT, HANGUP, RESUME, HOLD;
}
+ /**
+ * Arguments that control behavior of dialing a call.
+ */
+ public static class DialArgs {
+ public static class Builder<T extends Builder<T>> {
+ protected UUSInfo mUusInfo;
+ protected int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ protected Bundle mIntentExtras;
+
+ public T setUusInfo(UUSInfo uusInfo) {
+ mUusInfo = uusInfo;
+ return (T) this;
+ }
+
+ public T setVideoState(int videoState) {
+ mVideoState = videoState;
+ return (T) this;
+ }
+
+ public T setIntentExtras(Bundle intentExtras) {
+ this.mIntentExtras = intentExtras;
+ return (T) this;
+ }
+
+ public PhoneInternalInterface.DialArgs build() {
+ return new DialArgs(this);
+ }
+ }
+
+ /** The UUSInfo */
+ public final UUSInfo uusInfo;
+
+ /** The desired video state for the connection. */
+ public final int videoState;
+
+ /** The extras from the original CALL intent. */
+ public final Bundle intentExtras;
+
+ protected DialArgs(Builder b) {
+ this.uusInfo = b.mUusInfo;
+ this.videoState = b.mVideoState;
+ this.intentExtras = b.mIntentExtras;
+ }
+ }
+
// "Features" accessible through the connectivity manager
static final String FEATURE_ENABLE_MMS = "enableMMS";
static final String FEATURE_ENABLE_SUPL = "enableSUPL";
@@ -401,34 +448,13 @@
* assigned) until PhoneStateChanged notification has occurred.
*
* @param dialString The dial string.
- * @param videoState The desired video state for the connection.
- * @exception CallStateException if a new outgoing call is not currently
- * possible because no more call slots exist or a call exists that is
- * dialing, alerting, ringing, or waiting. Other errors are
- * handled asynchronously.
- */
- Connection dial(String dialString, int videoState) throws CallStateException;
-
- /**
- * Initiate a new voice connection with supplementary User to User
- * Information. This happens asynchronously, so you cannot assume the audio
- * path is connected (or a call index has been assigned) until
- * PhoneStateChanged notification has occurred.
- *
- * NOTE: If adding another parameter, consider creating a DialArgs parameter instead to
- * encapsulate all dial arguments and decrease scaffolding headache.
- *
- * @param dialString The dial string.
- * @param uusInfo The UUSInfo.
- * @param videoState The desired video state for the connection.
- * @param intentExtras The extras from the original CALL intent.
+ * @param dialArgs Parameters to perform the dial with.
* @exception CallStateException if a new outgoing call is not currently
* possible because no more call slots exist or a call exists
* that is dialing, alerting, ringing, or waiting. Other
* errors are handled asynchronously.
*/
- Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
- throws CallStateException;
+ Connection dial(String dialString, @NonNull DialArgs dialArgs) throws CallStateException;
/**
* Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
@@ -592,6 +618,35 @@
Message onComplete);
/**
+ * Gets a call barring option. The return value of ((AsyncResult) onComplete.obj) will be an
+ * Integer representing the sum of enabled serivice classes (sum of SERVICE_CLASS_*)
+ *
+ * @param facility is one of CB_FACILTY_*
+ * @param password is password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param onComplete is callback message when the action is completed.
+ */
+ public void getCallBarring(String facility,
+ String password,
+ Message onComplete,
+ int serviceClass);
+
+ /**
+ * Sets a call barring option.
+ *
+ * @param facility is one of CB_FACILTY_*
+ * @param lockState is true means lock, false means unlock
+ * @param password is password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param onComplete is callback message when the action is completed.
+ */
+ public void setCallBarring(String facility,
+ boolean lockState,
+ String password,
+ Message onComplete,
+ int serviceClass);
+
+ /**
* getOutgoingCallerIdDisplay
* gets outgoing caller id display. The return value of
* ((AsyncResult)onComplete.obj) is an array of int, with a length of 2.
@@ -707,19 +762,6 @@
boolean getMute();
/**
- * Get the current active Data Call list
- *
- * @param response <strong>On success</strong>, "response" bytes is
- * made available as:
- * (String[])(((AsyncResult)response.obj).result).
- * <strong>On failure</strong>,
- * (((AsyncResult)response.obj).result) == null and
- * (((AsyncResult)response.obj).exception) being an instance of
- * com.android.internal.telephony.gsm.CommandException
- */
- void getDataCallList(Message response);
-
- /**
* Update the ServiceState CellLocation for current network registration.
*/
void updateServiceLocation();
@@ -747,12 +789,17 @@
/**
* @return true if user has enabled data
*/
- boolean getDataEnabled();
+ boolean isUserDataEnabled();
+
+ /**
+ * @return true if data is enabled considering all factors
+ */
+ boolean isDataEnabled();
/**
* @param @enable set {@code true} if enable data connection
*/
- void setDataEnabled(boolean enable);
+ void setUserDataEnabled(boolean enable);
/**
* Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
@@ -847,4 +894,9 @@
* decrypt the permanent identity.
*/
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
+
+ /**
+ * Resets the Carrier Keys, by deleting them from the database and sending a download intent.
+ */
+ public void resetCarrierKeysForImsiEncryption();
}
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 2caf5e2..5c8faa6 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.telephony.CellInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.VoLteServiceState;
import java.util.List;
@@ -50,6 +51,9 @@
public void notifyCellInfo(Phone sender, List<CellInfo> cellInfo);
+ /** Notify of change to PhysicalChannelConfiguration. */
+ void notifyPhysicalChannelConfiguration(Phone sender, List<PhysicalChannelConfig> configs);
+
public void notifyPreciseCallState(Phone sender);
public void notifyDisconnectCause(int cause, int preciseCause);
@@ -63,5 +67,7 @@
public void notifyDataActivationStateChanged(Phone sender, int activationState);
+ public void notifyUserMobileDataStateChanged(Phone sender, boolean state);
+
public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 9cd088f..23eb0b8 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -18,28 +18,24 @@
package com.android.internal.telephony;
+import static android.Manifest.permission.CALL_PRIVILEGED;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.PhoneNumberUtils;
-import android.telephony.SubscriptionManager;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.uicc.IsimRecords;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
-import static android.Manifest.permission.CALL_PRIVILEGED;
-import static android.Manifest.permission.READ_PHONE_NUMBERS;
-import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-import static android.Manifest.permission.READ_SMS;
-import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
-
public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
private static final String TAG = "PhoneSubInfoController";
private static final boolean DBG = true;
@@ -64,14 +60,15 @@
}
public String getDeviceIdForPhone(int phoneId, String callingPackage) {
- if (!checkReadPhoneState(callingPackage, "getDeviceId")) {
- return null;
- }
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
phoneId = 0;
}
final Phone phone = mPhone[phoneId];
if (phone != null) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, phone.getSubId(), callingPackage, "getDeviceId")) {
+ return null;
+ }
return phone.getDeviceId();
} else {
loge("getDeviceIdForPhone phone " + phoneId + " is null");
@@ -82,7 +79,8 @@
public String getNaiForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getNai")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getNai")) {
return null;
}
return phone.getNai();
@@ -95,7 +93,8 @@
public String getImeiForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getImei")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getImei")) {
return null;
}
return phone.getImei();
@@ -106,10 +105,12 @@
}
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType,
- String callingPackage) {
+ String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getCarrierInfoForImsiEncryption")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage,
+ "getCarrierInfoForImsiEncryption")) {
return null;
}
return phone.getCarrierInfoForImsiEncryption(keyType);
@@ -123,9 +124,11 @@
ImsiEncryptionInfo imsiEncryptionInfo) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "setCarrierInfoForImsiEncryption")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "setCarrierInfoForImsiEncryption")) {
return;
}
+ enforceModifyPermission();
phone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
} else {
loge("setCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
@@ -133,6 +136,25 @@
}
}
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * @param subId
+ * @param callingPackage
+ */
+ public void resetCarrierKeysForImsiEncryption(int subId, String callingPackage) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ enforceModifyPermission();
+ phone.resetCarrierKeysForImsiEncryption();
+ return;
+ } else {
+ loge("resetCarrierKeysForImsiEncryption phone is null for Subscription:" + subId);
+ return;
+ }
+ }
+
public String getDeviceSvn(String callingPackage) {
return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
@@ -141,7 +163,8 @@
public String getDeviceSvnUsingSubId(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getDeviceSvn")) {
return null;
}
return phone.getDeviceSvn();
@@ -158,7 +181,8 @@
public String getSubscriberIdForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getSubscriberId")) {
return null;
}
return phone.getSubscriberId();
@@ -178,7 +202,8 @@
public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getIccSerialNumber")) {
return null;
}
return phone.getIccSerialNumber();
@@ -196,7 +221,8 @@
Phone phone = getPhone(subId);
if (phone != null) {
// This is open to apps with WRITE_SMS.
- if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(
+ mContext, subId, callingPackage, "getLine1Number")) {
return null;
}
return phone.getLine1Number();
@@ -213,7 +239,8 @@
public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getLine1AlphaTag")) {
return null;
}
return phone.getLine1AlphaTag();
@@ -230,7 +257,8 @@
public String getMsisdnForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getMsisdn")) {
return null;
}
return phone.getMsisdn();
@@ -247,7 +275,8 @@
public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getVoiceMailNumber")) {
return null;
}
String number = PhoneNumberUtils.extractNetworkPortion(phone.getVoiceMailNumber());
@@ -284,7 +313,8 @@
public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getVoiceMailAlphaTag")) {
return null;
}
return phone.getVoiceMailAlphaTag();
@@ -310,21 +340,22 @@
*
* @throws SecurityException if the caller does not have the required permission/privilege
*/
- private void enforcePrivilegedPermissionOrCarrierPrivilege(Phone phone) {
+ private void enforcePrivilegedPermissionOrCarrierPrivilege(int subId, String message) {
int permissionResult = mContext.checkCallingOrSelfPermission(
READ_PRIVILEGED_PHONE_STATE);
if (permissionResult == PackageManager.PERMISSION_GRANTED) {
return;
}
- log("No read privileged phone permission, check carrier privilege next.");
- UiccCard uiccCard = phone.getUiccCard();
- if (uiccCard == null) {
- throw new SecurityException("No Carrier Privilege: No UICC");
- }
- if (uiccCard.getCarrierPrivilegeStatusForCurrentTransaction(
- mContext.getPackageManager()) != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- throw new SecurityException("No Carrier Privilege.");
- }
+ if (VDBG) log("No read privileged phone permission, check carrier privilege next.");
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, message);
+ }
+
+ /**
+ * Make sure caller has modify phone state permission.
+ */
+ private void enforceModifyPermission() {
+ mContext.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE,
+ "Requires MODIFY_PHONE_STATE");
}
private int getDefaultSubscription() {
@@ -432,22 +463,13 @@
}
}
- public String getIsimChallengeResponse(String nonce) throws RemoteException {
- Phone phone = getPhone(getDefaultSubscription());
- mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
- "Requires READ_PRIVILEGED_PHONE_STATE");
- IsimRecords isim = phone.getIsimRecords();
- if (isim != null) {
- return isim.getIsimChallengeResponse(nonce);
- } else {
- return null;
- }
- }
-
public String getIccSimChallengeResponse(int subId, int appType, int authType, String data)
throws RemoteException {
+ // TODO(b/73660190): Migrate to
+ // TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivileges and delete
+ // this helper method.
+ enforcePrivilegedPermissionOrCarrierPrivilege(subId, "getIccSimChallengeResponse");
Phone phone = getPhone(subId);
- enforcePrivilegedPermissionOrCarrierPrivilege(phone);
UiccCard uiccCard = phone.getUiccCard();
if (uiccCard == null) {
loge("getIccSimChallengeResponse() UiccCard is null");
@@ -473,14 +495,11 @@
return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
}
- public String getGroupIdLevel1(String callingPackage) {
- return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
- }
-
public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
- if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getGroupIdLevel1")) {
return null;
}
return phone.getGroupIdLevel1();
@@ -490,65 +509,6 @@
}
}
- private boolean checkReadPhoneState(String callingPackage, String message) {
- try {
- mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, message);
-
- // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
- return true;
- } catch (SecurityException e) {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, message);
- }
-
- return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) == AppOpsManager.MODE_ALLOWED;
- }
-
- /**
- * Besides READ_PHONE_STATE, READ_PHONE_NUMBERS, WRITE_SMS and READ_SMS also allow apps to get
- * phone numbers.
- */
- private boolean checkReadPhoneNumber(String callingPackage, String message) {
- // Default SMS app can always read it.
- if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
- Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
- try {
- return checkReadPhoneState(callingPackage, message);
- } catch (SecurityException readPhoneStateSecurityException) {
- }
- try {
- // Can be read with READ_SMS too.
- mContext.enforceCallingOrSelfPermission(READ_SMS, message);
- int opCode = mAppOps.permissionToOpCode(READ_SMS);
- if (opCode != AppOpsManager.OP_NONE) {
- return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
- == AppOpsManager.MODE_ALLOWED;
- } else {
- return true;
- }
- } catch (SecurityException readSmsSecurityException) {
- }
- try {
- // Can be read with READ_PHONE_NUMBERS too.
- mContext.enforceCallingOrSelfPermission(READ_PHONE_NUMBERS, message);
- int opCode = mAppOps.permissionToOpCode(READ_PHONE_NUMBERS);
- if (opCode != AppOpsManager.OP_NONE) {
- return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
- == AppOpsManager.MODE_ALLOWED;
- } else {
- return true;
- }
- } catch (SecurityException readPhoneNumberSecurityException) {
- }
- // Throw exception with message including READ_PHONE_STATE, READ_SMS, and READ_PHONE_NUMBERS
- // permissions
- throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
- " nor current process has " + READ_PHONE_STATE + ", " +
- READ_SMS + ", or " + READ_PHONE_STATE + ".");
- }
-
private void log(String s) {
Rlog.d(TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 1c6e023..cd28b2b 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -124,7 +124,8 @@
mCommandsInterfaces = cis;
try {
- tr.addOnSubscriptionsChangedListener("PhoneSwitcher", mSubscriptionsChangedListener);
+ tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
+ mSubscriptionsChangedListener);
} catch (RemoteException e) {
}
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index f6d42af..1426bc7 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -335,7 +335,10 @@
private void onStartRadioCapabilityResponse(Message msg) {
synchronized (mSetRadioAccessFamilyStatus) {
AsyncResult ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
+ // Abort here only in Single SIM case, in Multi SIM cases
+ // send FINISH with failure so that below layers can re-bind
+ // old logical modems.
+ if ((TelephonyManager.getDefault().getPhoneCount() == 1) && (ar.exception != null)) {
// just abort now. They didn't take our start so we don't have to revert
logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
@@ -501,10 +504,14 @@
// Increment the sessionId as we are completing the transaction below
// so we don't want it completed when the FINISH phase is done.
- int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
+ mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
+ // Reset the status counter as existing session failed
+ mRadioAccessFamilyStatusCounter = 0;
+
// send FINISH request with fail status and then uniqueDifferentId
mTransactionFailed = true;
- issueFinish(uniqueDifferentId);
+ issueFinish(mRadioCapabilitySessionId);
}
}
@@ -519,8 +526,10 @@
i,
sessionId,
RadioCapability.RC_PHASE_FINISH,
- mOldRadioAccessFamily[i],
- mCurrentLogicalModemIds[i],
+ (mTransactionFailed ? mOldRadioAccessFamily[i] :
+ mNewRadioAccessFamily[i]),
+ (mTransactionFailed ? mCurrentLogicalModemIds[i] :
+ mNewLogicalModemIds[i]),
(mTransactionFailed ? RadioCapability.RC_STATUS_FAIL :
RadioCapability.RC_STATUS_SUCCESS),
EVENT_FINISH_RC_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 78fbd77..9f7ea05 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright 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.
@@ -29,6 +29,7 @@
import android.hardware.radio.V1_0.CellInfoCdma;
import android.hardware.radio.V1_0.CellInfoGsm;
import android.hardware.radio.V1_0.CellInfoLte;
+import android.hardware.radio.V1_0.CellInfoTdscdma;
import android.hardware.radio.V1_0.CellInfoType;
import android.hardware.radio.V1_0.CellInfoWcdma;
import android.hardware.radio.V1_0.DataProfileInfo;
@@ -39,6 +40,7 @@
import android.hardware.radio.V1_0.IRadio;
import android.hardware.radio.V1_0.IccIo;
import android.hardware.radio.V1_0.ImsSmsMessage;
+import android.hardware.radio.V1_0.IndicationFilter;
import android.hardware.radio.V1_0.LceDataInfo;
import android.hardware.radio.V1_0.MvnoType;
import android.hardware.radio.V1_0.NvWriteItem;
@@ -48,13 +50,16 @@
import android.hardware.radio.V1_0.RadioResponseType;
import android.hardware.radio.V1_0.ResetNvType;
import android.hardware.radio.V1_0.SelectUiccSub;
-import android.hardware.radio.V1_0.SetupDataCallResult;
import android.hardware.radio.V1_0.SimApdu;
import android.hardware.radio.V1_0.SmsWriteArgs;
import android.hardware.radio.V1_0.UusInfo;
+import android.hardware.radio.V1_2.AccessNetwork;
import android.hardware.radio.deprecated.V1_0.IOemHook;
import android.net.ConnectivityManager;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.HwBinder;
import android.os.Message;
@@ -66,7 +71,11 @@
import android.os.SystemProperties;
import android.os.WorkSource;
import android.service.carrier.CarrierIdentifier;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
import android.telephony.CellInfo;
+import android.telephony.CellSignalStrengthCdma;
import android.telephony.ClientRequestStats;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.ModemActivityInfo;
@@ -75,20 +84,23 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.RadioAccessFamily;
import android.telephony.RadioAccessSpecifier;
-import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
import android.telephony.TelephonyHistogram;
import android.telephony.TelephonyManager;
+import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cat.ComprehensionTlv;
+import com.android.internal.telephony.cat.ComprehensionTlvTag;
import com.android.internal.telephony.cdma.CdmaInformationRecords;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
@@ -99,179 +111,21 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
- * {@hide}
- */
-
-class RILRequest {
- static final String LOG_TAG = "RilRequest";
-
- //***** Class Variables
- static Random sRandom = new Random();
- static AtomicInteger sNextSerial = new AtomicInteger(0);
- private static Object sPoolSync = new Object();
- private static RILRequest sPool = null;
- private static int sPoolSize = 0;
- private static final int MAX_POOL_SIZE = 4;
-
- //***** Instance Variables
- int mSerial;
- int mRequest;
- Message mResult;
- RILRequest mNext;
- int mWakeLockType;
- WorkSource mWorkSource;
- String mClientId;
- // time in ms when RIL request was made
- long mStartTimeMs;
-
- /**
- * Retrieves a new RILRequest instance from the pool.
- *
- * @param request RIL_REQUEST_*
- * @param result sent when operation completes
- * @return a RILRequest instance from the pool.
- */
- private static RILRequest obtain(int request, Message result) {
- RILRequest rr = null;
-
- synchronized(sPoolSync) {
- if (sPool != null) {
- rr = sPool;
- sPool = rr.mNext;
- rr.mNext = null;
- sPoolSize--;
- }
- }
-
- if (rr == null) {
- rr = new RILRequest();
- }
-
- rr.mSerial = sNextSerial.getAndIncrement();
-
- rr.mRequest = request;
- rr.mResult = result;
-
- rr.mWakeLockType = RIL.INVALID_WAKELOCK;
- rr.mWorkSource = null;
- rr.mStartTimeMs = SystemClock.elapsedRealtime();
- if (result != null && result.getTarget() == null) {
- throw new NullPointerException("Message target must not be null");
- }
-
- return rr;
- }
-
-
- /**
- * Retrieves a new RILRequest instance from the pool and sets the clientId
- *
- * @param request RIL_REQUEST_*
- * @param result sent when operation completes
- * @param workSource WorkSource to track the client
- * @return a RILRequest instance from the pool.
- */
- static RILRequest obtain(int request, Message result, WorkSource workSource) {
- RILRequest rr = null;
-
- rr = obtain(request, result);
- if(workSource != null) {
- rr.mWorkSource = workSource;
- rr.mClientId = String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
- } else {
- Rlog.e(LOG_TAG, "null workSource " + request);
- }
-
- return rr;
- }
-
- /**
- * Returns a RILRequest instance to the pool.
- *
- * Note: This should only be called once per use.
- */
- void release() {
- synchronized (sPoolSync) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- sPoolSize++;
- mResult = null;
- if(mWakeLockType != RIL.INVALID_WAKELOCK) {
- //This is OK for some wakelock types and not others
- if(mWakeLockType == RIL.FOR_WAKELOCK) {
- Rlog.e(LOG_TAG, "RILRequest releasing with held wake lock: "
- + serialString());
- }
- }
- }
- }
- }
-
- private RILRequest() {
- }
-
- static void
- resetSerial() {
- // use a random so that on recovery we probably don't mix old requests
- // with new.
- sNextSerial.set(sRandom.nextInt());
- }
-
- String
- serialString() {
- //Cheesy way to do %04d
- StringBuilder sb = new StringBuilder(8);
- String sn;
-
- long adjustedSerial = (((long)mSerial) - Integer.MIN_VALUE)%10000;
-
- sn = Long.toString(adjustedSerial);
-
- //sb.append("J[");
- sb.append('[');
- for (int i = 0, s = sn.length() ; i < 4 - s; i++) {
- sb.append('0');
- }
-
- sb.append(sn);
- sb.append(']');
- return sb.toString();
- }
-
- void
- onError(int error, Object ret) {
- CommandException ex;
-
- ex = CommandException.fromRilErrno(error);
-
- if (RIL.RILJ_LOGD) Rlog.d(LOG_TAG, serialString() + "< "
- + RIL.requestToString(mRequest)
- + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
-
- if (mResult != null) {
- AsyncResult.forMessage(mResult, ret, ex);
- mResult.sendToTarget();
- }
- }
-}
-
-
-/**
* RIL implementation of the CommandsInterface.
*
* {@hide}
*/
-public final class RIL extends BaseCommands implements CommandsInterface {
+public class RIL extends BaseCommands implements CommandsInterface {
static final String RILJ_LOG_TAG = "RILJ";
// Have a separate wakelock instance for Ack
static final String RILJ_ACK_WAKELOCK_NAME = "RILJ_ACK_WL";
@@ -353,6 +207,9 @@
static final int IRADIO_GET_SERVICE_DELAY_MILLIS = 4 * 1000;
+ static final String EMPTY_ALPHA_LONG = "";
+ static final String EMPTY_ALPHA_SHORT = "";
+
public static List<TelephonyHistogram> getTelephonyRILTimingHistograms() {
List<TelephonyHistogram> list;
synchronized (mRilTimeHistograms) {
@@ -365,10 +222,13 @@
return list;
}
- class RilHandler extends Handler {
+ /** The handler used to handle the internal event of RIL. */
+ @VisibleForTesting
+ public class RilHandler extends Handler {
+
//***** Handler implementation
- @Override public void
- handleMessage(Message msg) {
+ @Override
+ public void handleMessage(Message msg) {
RILRequest rr;
switch (msg.what) {
@@ -433,11 +293,6 @@
" mRadioProxyCookie = " + mRadioProxyCookie.get());
if ((long) msg.obj == mRadioProxyCookie.get()) {
resetProxyAndRequestList();
-
- // todo: rild should be back up since message was sent with a delay. this is
- // a hack.
- getRadioProxy(null);
- getOemHookProxy(null);
}
break;
}
@@ -472,11 +327,7 @@
public void serviceDied(long cookie) {
// Deal with service going away
riljLog("serviceDied");
- // todo: temp hack to send delayed message so that rild is back up by then
- //mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
- mRilHandler.sendMessageDelayed(
- mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie),
- IRADIO_GET_SERVICE_DELAY_MILLIS);
+ mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
}
}
@@ -493,12 +344,13 @@
// Clear request list on close
clearRequestList(RADIO_NOT_AVAILABLE, false);
- // todo: need to get service right away so setResponseFunctions() can be called for
- // unsolicited indications. getService() is not a blocking call, so it doesn't help to call
- // it here. Current hack is to call getService() on death notification after a delay.
+ getRadioProxy(null);
+ getOemHookProxy(null);
}
- private IRadio getRadioProxy(Message result) {
+ /** Returns a {@link IRadio} instance or null if the service is not available. */
+ @VisibleForTesting
+ public IRadio getRadioProxy(Message result) {
if (!mIsMobileNetworkSupported) {
if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
if (result != null) {
@@ -514,7 +366,8 @@
}
try {
- mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+ mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId],
+ true);
if (mRadioProxy != null) {
mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
mRadioProxyCookie.incrementAndGet());
@@ -528,23 +381,21 @@
}
if (mRadioProxy == null) {
+ // getService() is a blocking call, so this should never happen
+ riljLoge("getRadioProxy: mRadioProxy == null");
if (result != null) {
AsyncResult.forMessage(result, null,
CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
result.sendToTarget();
}
-
- // if service is not up, treat it like death notification to try to get service again
- mRilHandler.sendMessageDelayed(
- mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
- mRadioProxyCookie.incrementAndGet()),
- IRADIO_GET_SERVICE_DELAY_MILLIS);
}
return mRadioProxy;
}
- private IOemHook getOemHookProxy(Message result) {
+ /** Returns an {@link IOemHook} instance or null if the service is not available. */
+ @VisibleForTesting
+ public IOemHook getOemHookProxy(Message result) {
if (!mIsMobileNetworkSupported) {
if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
if (result != null) {
@@ -561,7 +412,7 @@
try {
mOemHookProxy = IOemHook.getService(
- HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+ HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId], true);
if (mOemHookProxy != null) {
// not calling linkToDeath() as ril service runs in the same process and death
// notification for that should be sufficient
@@ -580,12 +431,6 @@
CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
result.sendToTarget();
}
-
- // if service is not up, treat it like death notification to try to get service again
- mRilHandler.sendMessageDelayed(
- mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
- mRadioProxyCookie.incrementAndGet()),
- IRADIO_GET_SERVICE_DELAY_MILLIS);
}
return mOemHookProxy;
@@ -644,8 +489,8 @@
getOemHookProxy(null);
}
- @Override public void
- setOnNITZTime(Handler h, int what, Object obj) {
+ @Override
+ public void setOnNITZTime(Handler h, int what, Object obj) {
super.setOnNITZTime(h, what, obj);
// Send the last NITZ time if we have it
@@ -673,13 +518,6 @@
private void handleRadioProxyExceptionForRR(RILRequest rr, String caller, Exception e) {
riljLoge(caller + ": " + e);
resetProxyAndRequestList();
-
- // service most likely died, handle exception like death notification to try to get service
- // again
- mRilHandler.sendMessageDelayed(
- mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
- mRadioProxyCookie.incrementAndGet()),
- IRADIO_GET_SERVICE_DELAY_MILLIS);
}
private String convertNullToEmptyString(String string) {
@@ -687,8 +525,7 @@
}
@Override
- public void
- getIccCardStatus(Message result) {
+ public void getIccCardStatus(Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result,
@@ -704,13 +541,31 @@
}
}
- @Override public void
- supplyIccPin(String pin, Message result) {
+ @Override
+ public void getIccSlotsStatus(Message result) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ @Override
+ public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ @Override
+ public void supplyIccPin(String pin, Message result) {
supplyIccPinForApp(pin, null, result);
}
- @Override public void
- supplyIccPinForApp(String pin, String aid, Message result) {
+ @Override
+ public void supplyIccPinForApp(String pin, String aid, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN, result,
@@ -759,13 +614,13 @@
}
}
- @Override public void
- supplyIccPin2(String pin, Message result) {
+ @Override
+ public void supplyIccPin2(String pin, Message result) {
supplyIccPin2ForApp(pin, null, result);
}
- @Override public void
- supplyIccPin2ForApp(String pin, String aid, Message result) {
+ @Override
+ public void supplyIccPin2ForApp(String pin, String aid, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN2, result,
@@ -786,13 +641,13 @@
}
}
- @Override public void
- supplyIccPuk2(String puk2, String newPin2, Message result) {
+ @Override
+ public void supplyIccPuk2(String puk2, String newPin2, Message result) {
supplyIccPuk2ForApp(puk2, newPin2, null, result);
}
- @Override public void
- supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
+ @Override
+ public void supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK2, result,
@@ -814,13 +669,13 @@
}
}
- @Override public void
- changeIccPin(String oldPin, String newPin, Message result) {
+ @Override
+ public void changeIccPin(String oldPin, String newPin, Message result) {
changeIccPinForApp(oldPin, newPin, null, result);
}
- @Override public void
- changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
+ @Override
+ public void changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN, result,
@@ -842,13 +697,13 @@
}
}
- @Override public void
- changeIccPin2(String oldPin2, String newPin2, Message result) {
+ @Override
+ public void changeIccPin2(String oldPin2, String newPin2, Message result) {
changeIccPin2ForApp(oldPin2, newPin2, null, result);
}
- @Override public void
- changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
+ @Override
+ public void changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN2, result,
@@ -1273,23 +1128,23 @@
private static DataProfileInfo convertToHalDataProfile(DataProfile dp) {
DataProfileInfo dpi = new DataProfileInfo();
- dpi.profileId = dp.profileId;
- dpi.apn = dp.apn;
- dpi.protocol = dp.protocol;
- dpi.roamingProtocol = dp.roamingProtocol;
- dpi.authType = dp.authType;
- dpi.user = dp.user;
- dpi.password = dp.password;
- dpi.type = dp.type;
- dpi.maxConnsTime = dp.maxConnsTime;
- dpi.maxConns = dp.maxConns;
- dpi.waitTime = dp.waitTime;
- dpi.enabled = dp.enabled;
- dpi.supportedApnTypesBitmap = dp.supportedApnTypesBitmap;
- dpi.bearerBitmap = dp.bearerBitmap;
- dpi.mtu = dp.mtu;
- dpi.mvnoType = convertToHalMvnoType(dp.mvnoType);
- dpi.mvnoMatchData = dp.mvnoMatchData;
+ dpi.profileId = dp.getProfileId();
+ dpi.apn = dp.getApn();
+ dpi.protocol = dp.getProtocol();
+ dpi.roamingProtocol = dp.getRoamingProtocol();
+ dpi.authType = dp.getAuthType();
+ dpi.user = dp.getUserName();
+ dpi.password = dp.getPassword();
+ dpi.type = dp.getType();
+ dpi.maxConnsTime = dp.getMaxConnsTime();
+ dpi.maxConns = dp.getMaxConns();
+ dpi.waitTime = dp.getWaitTime();
+ dpi.enabled = dp.isEnabled();
+ dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmap();
+ dpi.bearerBitmap = dp.getBearerBitmap();
+ dpi.mtu = dp.getMtu();
+ dpi.mvnoType = convertToHalMvnoType(dp.getMvnoType());
+ dpi.mvnoMatchData = dp.getMvnoMatchData();
return dpi;
}
@@ -1314,31 +1169,13 @@
return -1;
}
- /**
- * Convert SetupDataCallResult defined in types.hal into DataCallResponse
- * @param dcResult setup data call result
- * @return converted DataCallResponse object
- */
- static DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) {
- return new DataCallResponse(dcResult.status,
- dcResult.suggestedRetryTime,
- dcResult.cid,
- dcResult.active,
- dcResult.type,
- dcResult.ifname,
- dcResult.addresses,
- dcResult.dnses,
- dcResult.gateways,
- dcResult.pcscf,
- dcResult.mtu
- );
- }
-
@Override
- public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result) {
IRadio radioProxy = getRadioProxy(result);
+
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
@@ -1347,17 +1184,56 @@
// Convert to HAL data profile
DataProfileInfo dpi = convertToHalDataProfile(dataProfile);
- if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
- + ",radioTechnology=" + radioTechnology + ",isRoaming="
- + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile);
- }
-
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
try {
- radioProxy.setupDataCall(rr.mSerial, radioTechnology, dpi,
- dataProfile.modemCognitive, allowRoaming, isRoaming);
- mMetrics.writeRilSetupDataCall(mPhoneId, rr.mSerial, radioTechnology, dpi.profileId,
- dpi.apn, dpi.authType, dpi.protocol);
+ if (radioProxy12 == null) {
+ // IRadio V1.0
+
+ // Getting data RAT here is just a workaround to support the older 1.0 vendor
+ // RIL. The new data service interface passes access network type instead of
+ // RAT for setup data request. It is impossible to convert access network
+ // type back to RAT here, so we directly get the data RAT from phone.
+ int dataRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ Phone phone = PhoneFactory.getPhone(mPhoneId);
+ if (phone != null) {
+ ServiceState ss = phone.getServiceState();
+ if (ss != null) {
+ dataRat = ss.getRilDataRadioTechnology();
+ }
+ }
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + ",dataRat=" + dataRat + ",isRoaming=" + isRoaming
+ + ",allowRoaming=" + allowRoaming + "," + dataProfile);
+ }
+
+ radioProxy.setupDataCall(rr.mSerial, dataRat, dpi,
+ dataProfile.isModemCognitive(), allowRoaming, isRoaming);
+ } else {
+ // IRadio V1.2
+ ArrayList<String> addresses = new ArrayList<>();
+ ArrayList<String> dnses = new ArrayList<>();
+ if (linkProperties != null) {
+ for (InetAddress address : linkProperties.getAddresses()) {
+ addresses.add(address.getHostAddress());
+ }
+ for (InetAddress dns : linkProperties.getDnsServers()) {
+ dnses.add(dns.getHostAddress());
+ }
+ }
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + ",accessNetworkType=" + accessNetworkType + ",isRoaming="
+ + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
+ + ",addresses=" + addresses + ",dnses=" + dnses);
+ }
+
+ radioProxy12.setupDataCall_1_2(rr.mSerial, accessNetworkType, dpi,
+ dataProfile.isModemCognitive(), allowRoaming, isRoaming, reason,
+ addresses, dnses);
+ }
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "setupDataCall", e);
}
@@ -1379,12 +1255,16 @@
mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> iccIO: "
- + requestToString(rr.mRequest) + " command = 0x"
- + Integer.toHexString(command) + " fileId = 0x"
- + Integer.toHexString(fileId) + " path = " + path + " p1 = "
- + p1 + " p2 = " + p2 + " p3 = " + " data = " + data
- + " aid = " + aid);
+ if (Build.IS_DEBUGGABLE) {
+ riljLog(rr.serialString() + "> iccIO: "
+ + requestToString(rr.mRequest) + " command = 0x"
+ + Integer.toHexString(command) + " fileId = 0x"
+ + Integer.toHexString(fileId) + " path = " + path + " p1 = "
+ + p1 + " p2 = " + p2 + " p3 = " + " data = " + data
+ + " aid = " + aid);
+ } else {
+ riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest));
+ }
}
IccIo iccIo = new IccIo();
@@ -1637,10 +1517,17 @@
+ requestToString(rr.mRequest) + " cid = " + cid + " reason = " + reason);
}
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+
try {
- radioProxy.deactivateDataCall(rr.mSerial, cid, (reason == 0) ? false : true);
- mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial,
- cid, reason);
+ if (radioProxy12 == null) {
+ radioProxy.deactivateDataCall(rr.mSerial, cid,
+ (reason == DataService.REQUEST_REASON_SHUTDOWN));
+ } else {
+ radioProxy12.deactivateDataCall_1_2(rr.mSerial, cid, reason);
+ }
+ mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "deactivateDataCall", e);
}
@@ -1813,31 +1700,31 @@
RadioAccessSpecifier ras) {
android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
new android.hardware.radio.V1_1.RadioAccessSpecifier();
- rasInHalFormat.radioAccessNetwork = ras.radioAccessNetwork;
+ rasInHalFormat.radioAccessNetwork = ras.getRadioAccessNetwork();
List<Integer> bands = null;
- switch (ras.radioAccessNetwork) {
- case RadioAccessNetworks.GERAN:
+ switch (ras.getRadioAccessNetwork()) {
+ case AccessNetworkType.GERAN:
bands = rasInHalFormat.geranBands;
break;
- case RadioAccessNetworks.UTRAN:
+ case AccessNetworkType.UTRAN:
bands = rasInHalFormat.utranBands;
break;
- case RadioAccessNetworks.EUTRAN:
+ case AccessNetworkType.EUTRAN:
bands = rasInHalFormat.eutranBands;
break;
default:
- Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
+ Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.getRadioAccessNetwork()
+ " not supported!");
return null;
}
- if (ras.bands != null) {
- for (int band : ras.bands) {
+ if (ras.getBands() != null) {
+ for (int band : ras.getBands()) {
bands.add(band);
}
}
- if (ras.channels != null) {
- for (int channel : ras.channels) {
+ if (ras.getChannels() != null) {
+ for (int channel : ras.getChannels()) {
rasInHalFormat.channels.add(channel);
}
}
@@ -1854,13 +1741,13 @@
if (radioProxy12 != null) {
android.hardware.radio.V1_2.NetworkScanRequest request =
new android.hardware.radio.V1_2.NetworkScanRequest();
- request.type = nsr.scanType;
- request.interval = nsr.searchPeriodicity;
- request.maxSearchTime = nsr.maxSearchTime;
- request.incrementalResultsPeriodicity = nsr.incrementalResultsPeriodicity;
- request.incrementalResults = nsr.incrementalResults;
+ request.type = nsr.getScanType();
+ request.interval = nsr.getSearchPeriodicity();
+ request.maxSearchTime = nsr.getMaxSearchTime();
+ request.incrementalResultsPeriodicity = nsr.getIncrementalResultsPeriodicity();
+ request.incrementalResults = nsr.getIncrementalResults();
- for (RadioAccessSpecifier ras : nsr.specifiers) {
+ for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
convertRadioAccessSpecifierToRadioHAL(ras);
@@ -1871,7 +1758,7 @@
request.specifiers.add(rasInHalFormat);
}
- request.mccMncs.addAll(nsr.mccMncs);
+ request.mccMncs.addAll(nsr.getPlmns());
RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
mRILDefaultWorkSource);
@@ -1896,9 +1783,9 @@
} else {
android.hardware.radio.V1_1.NetworkScanRequest request =
new android.hardware.radio.V1_1.NetworkScanRequest();
- request.type = nsr.scanType;
- request.interval = nsr.searchPeriodicity;
- for (RadioAccessSpecifier ras : nsr.specifiers) {
+ request.type = nsr.getScanType();
+ request.interval = nsr.getSearchPeriodicity();
+ for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
convertRadioAccessSpecifierToRadioHAL(ras);
if (rasInHalFormat == null) {
@@ -2121,8 +2008,12 @@
try {
oemHookProxy.sendRequestRaw(rr.mSerial, primitiveArrayToArrayList(data));
} catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
+ handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestRaw", e);
}
+ } else {
+ // OEM Hook service is disabled for P and later devices.
+ // Deprecated OEM Hook APIs will perform dummy before being removed.
+ if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
}
}
@@ -2148,6 +2039,10 @@
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
}
+ } else {
+ // OEM Hook service is disabled for P and later devices.
+ // Deprecated OEM Hook APIs will perform dummy before being removed.
+ if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
}
}
@@ -2284,7 +2179,7 @@
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
- + contents);
+ + (Build.IS_DEBUGGABLE ? contents : censoredTerminalResponse(contents)));
}
try {
@@ -2296,6 +2191,33 @@
}
}
+ private String censoredTerminalResponse(String terminalResponse) {
+ try {
+ byte[] bytes = IccUtils.hexStringToBytes(terminalResponse);
+ if (bytes != null) {
+ List<ComprehensionTlv> ctlvs = ComprehensionTlv.decodeMany(bytes, 0);
+ int from = 0;
+ for (ComprehensionTlv ctlv : ctlvs) {
+ // Find text strings which might be personal information input by user,
+ // then replace it with "********".
+ 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), "********");
+ }
+ // The text string tag and the length field should also be hidden.
+ from = ctlv.getValueIndex() + ctlv.getLength();
+ }
+ }
+ } catch (Exception e) {
+ Rlog.e(RILJ_LOG_TAG, "Could not censor the terminal response: " + e);
+ terminalResponse = null;
+ }
+
+ return terminalResponse;
+ }
+
@Override
public void sendEnvelopeWithStatus(String contents, Message result) {
IRadio radioProxy = getRadioProxy(result);
@@ -3005,26 +2927,6 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message result) {
- IRadio radioProxy = getRadioProxy(result);
- if (radioProxy != null) {
- RILRequest rr = obtainRequest(RIL_REQUEST_ISIM_AUTHENTICATION, result,
- mRILDefaultWorkSource);
-
- if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
- + " nonce = " + nonce);
- }
-
- try {
- radioProxy.requestIsimAuthentication(rr.mSerial, convertNullToEmptyString(nonce));
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "requestIsimAuthentication", e);
- }
- }
- }
-
- @Override
public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
@@ -3121,7 +3023,7 @@
try {
radioProxy.setInitialAttachApn(rr.mSerial, convertToHalDataProfile(dataProfile),
- dataProfile.modemCognitive, isRoaming);
+ dataProfile.isModemCognitive(), isRoaming);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "setInitialAttachApn", e);
}
@@ -3160,7 +3062,7 @@
ImsSmsMessage msg = new ImsSmsMessage();
msg.tech = RILConstants.GSM_PHONE;
- msg.retry = (byte) retry == 1 ? true : false;
+ msg.retry = (byte) retry >= 1 ? true : false;
msg.messageRef = messageRef;
GsmSmsMessage gsmMsg = constructGsmSendSmsRilRequest(smscPdu, pdu);
@@ -3187,7 +3089,7 @@
ImsSmsMessage msg = new ImsSmsMessage();
msg.tech = RILConstants.CDMA_PHONE;
- msg.retry = (byte) retry == 1 ? true : false;
+ msg.retry = (byte) retry >= 1 ? true : false;
msg.messageRef = messageRef;
CdmaSmsMessage cdmaMsg = new CdmaSmsMessage();
@@ -3197,7 +3099,7 @@
try {
radioProxy.sendImsSms(rr.mSerial, msg);
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
- SmsSession.Event.Format.SMS_FORMAT_3GPP);
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
}
@@ -3226,9 +3128,13 @@
mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
- + " cla = " + cla + " instruction = " + instruction
- + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+ if (Build.IS_DEBUGGABLE) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " cla = " + cla + " instruction = " + instruction
+ + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+ } else {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
}
SimApdu msg = createSimApdu(0, cla, instruction, p1, p2, p3, data);
@@ -3248,8 +3154,12 @@
mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
- + " p2 = " + p2);
+ if (Build.IS_DEBUGGABLE) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
+ + " p2 = " + p2);
+ } else {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
}
try {
@@ -3295,9 +3205,13 @@
mRILDefaultWorkSource);
if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " channel = "
- + channel + " cla = " + cla + " instruction = " + instruction
- + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+ if (Build.IS_DEBUGGABLE) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " channel = "
+ + channel + " cla = " + cla + " instruction = " + instruction
+ + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+ } else {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
}
SimApdu msg = createSimApdu(channel, cla, instruction, p1, p2, p3, data);
@@ -3449,8 +3363,7 @@
}
@Override
- public void
- getHardwareConfig (Message result) {
+ public void getHardwareConfig(Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_HARDWARE_CONFIG, result,
@@ -3588,6 +3501,14 @@
@Override
public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
IRadio radioProxy = getRadioProxy(result);
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 != null) {
+ // 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.
+ return;
+ }
+
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_START_LCE, result,
mRILDefaultWorkSource);
@@ -3608,6 +3529,14 @@
@Override
public void stopLceService(Message result) {
IRadio radioProxy = getRadioProxy(result);
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 != null) {
+ // 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.
+ return;
+ }
+
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_STOP_LCE, result,
mRILDefaultWorkSource);
@@ -3624,6 +3553,17 @@
}
}
+ /**
+ * This will only be called if the LCE service is started in PULL mode, which is
+ * only enabled when using Radio HAL versions 1.1 and earlier.
+ *
+ * It is still possible for vendors to override this behavior and use the 1.1 version
+ * of LCE; however, this is strongly discouraged and this functionality will be removed
+ * when HAL 1.x support is dropped.
+ *
+ * @deprecated HAL 1.2 and later use an always-on LCE that relies on indications.
+ */
+ @Deprecated
@Override
public void pullLceData(Message response) {
IRadio radioProxy = getRadioProxy(response);
@@ -3779,15 +3719,105 @@
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + filter);
}
- try {
- radioProxy.setIndicationFilter(rr.mSerial, filter);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+
+ if (radioProxy12 != null) {
+ try {
+ radioProxy12.setIndicationFilter_1_2(rr.mSerial, filter);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setIndicationFilter_1_2", e);
+ }
+ } else {
+ try {
+ int filter10 = filter & IndicationFilter.ALL;
+ radioProxy.setIndicationFilter(rr.mSerial, filter10);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
+ }
}
}
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 == null) {
+ riljLoge("setSignalStrengthReportingCriteria ignored. RadioProxy 1.2 is null!");
+ return;
+ }
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
+ result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial, hysteresisMs,
+ hysteresisDb, primitiveArrayToArrayList(thresholdsDbm),
+ convertRanToHalRan(ran));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setSignalStrengthReportingCriteria", e);
+ }
+ }
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 == null) {
+ riljLoge("setLinkCapacityReportingCriteria ignored. RadioProxy 1.2 is null!");
+ return;
+ }
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA,
+ result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy12.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
+ hysteresisDlKbps, hysteresisUlKbps,
+ primitiveArrayToArrayList(thresholdsDlKbps),
+ primitiveArrayToArrayList(thresholdsUlKbps), convertRanToHalRan(ran));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setLinkCapacityReportingCriteria", e);
+ }
+ }
+ }
+
+ private static int convertRanToHalRan(int radioAccessNetwork) {
+ switch (radioAccessNetwork) {
+ case AccessNetworkType.GERAN:
+ return AccessNetwork.GERAN;
+ case AccessNetworkType.UTRAN:
+ return AccessNetwork.UTRAN;
+ case AccessNetworkType.EUTRAN:
+ return AccessNetwork.EUTRAN;
+ case AccessNetworkType.CDMA2000:
+ return AccessNetwork.CDMA2000;
+ case AccessNetworkType.IWLAN:
+ return AccessNetwork.IWLAN;
+ case AccessNetworkType.UNKNOWN:
+ default:
+ return 0;
+ }
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
@@ -3874,6 +3904,94 @@
}
@Override
+ public void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
+ checkNotNull(packetData, "KeepaliveRequest cannot be null.");
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy == null) {
+ riljLoge("Radio Proxy object is null!");
+ return;
+ }
+
+ android.hardware.radio.V1_1.IRadio radioProxy11 =
+ android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+ if (radioProxy11 == null) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ return;
+ }
+
+ RILRequest rr = obtainRequest(
+ RIL_REQUEST_START_KEEPALIVE, result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ android.hardware.radio.V1_1.KeepaliveRequest req =
+ new android.hardware.radio.V1_1.KeepaliveRequest();
+
+ req.cid = contextId;
+
+ if (packetData.dstAddress instanceof Inet4Address) {
+ req.type = android.hardware.radio.V1_1.KeepaliveType.NATT_IPV4;
+ } else if (packetData.dstAddress instanceof Inet6Address) {
+ req.type = android.hardware.radio.V1_1.KeepaliveType.NATT_IPV6;
+ } else {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(INVALID_ARGUMENTS));
+ result.sendToTarget();
+ return;
+ }
+
+ appendPrimitiveArrayToArrayList(
+ packetData.srcAddress.getAddress(), req.sourceAddress);
+ req.sourcePort = packetData.srcPort;
+ appendPrimitiveArrayToArrayList(
+ packetData.dstAddress.getAddress(), req.destinationAddress);
+ req.destinationPort = packetData.dstPort;
+ req.maxKeepaliveIntervalMillis = intervalMillis;
+
+ radioProxy11.startKeepalive(rr.mSerial, req);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "startNattKeepalive", e);
+ }
+ }
+
+ @Override
+ public void stopNattKeepalive(int sessionHandle, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy == null) {
+ Rlog.e(RIL.RILJ_LOG_TAG, "Radio Proxy object is null!");
+ return;
+ }
+
+ android.hardware.radio.V1_1.IRadio radioProxy11 =
+ android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+ if (radioProxy11 == null) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ return;
+ }
+
+ RILRequest rr = obtainRequest(
+ RIL_REQUEST_STOP_KEEPALIVE, result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy11.stopKeepalive(rr.mSerial, sessionHandle);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "stopNattKeepalive", e);
+ }
+ }
+
+ @Override
public void getIMEI(Message result) {
throw new RuntimeException("getIMEI not expected to be called");
}
@@ -3986,7 +4104,8 @@
* @param responseInfo RadioResponseInfo received in response callback
* @return RILRequest corresponding to the response
*/
- RILRequest processResponse(RadioResponseInfo responseInfo) {
+ @VisibleForTesting
+ public RILRequest processResponse(RadioResponseInfo responseInfo) {
int serial = responseInfo.serial;
int error = responseInfo.error;
int type = responseInfo.type;
@@ -4086,7 +4205,8 @@
* @param responseInfo RadioResponseInfo received in the callback
* @param ret object to be returned to request sender
*/
- void processResponseDone(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
+ @VisibleForTesting
+ public void processResponseDone(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
if (responseInfo.error == 0) {
if (RILJ_LOGD) {
riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
@@ -4155,7 +4275,6 @@
* There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't
* happen often.
*/
-
private void acquireWakeLock(RILRequest rr, int wakeLockType) {
synchronized (rr) {
if (rr.mWakeLockType != INVALID_WAKELOCK) {
@@ -4206,6 +4325,24 @@
}
}
+ /** Returns the wake lock of the given type. */
+ @VisibleForTesting
+ public WakeLock getWakeLock(int wakeLockType) {
+ return wakeLockType == FOR_WAKELOCK ? mWakeLock : mAckWakeLock;
+ }
+
+ /** Returns the {@link RilHandler} instance. */
+ @VisibleForTesting
+ public RilHandler getRilHandler() {
+ return mRilHandler;
+ }
+
+ /** Returns the Ril request list. */
+ @VisibleForTesting
+ public SparseArray<RILRequest> getRilRequestList() {
+ return mRequestList;
+ }
+
private void decrementWakeLock(RILRequest rr) {
synchronized (rr) {
switch(rr.mWakeLockType) {
@@ -4783,6 +4920,18 @@
return "RIL_REQUEST_START_NETWORK_SCAN";
case RIL_REQUEST_STOP_NETWORK_SCAN:
return "RIL_REQUEST_STOP_NETWORK_SCAN";
+ case RIL_REQUEST_GET_SLOT_STATUS:
+ return "RIL_REQUEST_GET_SLOT_STATUS";
+ case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
+ return "RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
+ case RIL_REQUEST_START_KEEPALIVE:
+ return "RIL_REQUEST_START_KEEPALIVE";
+ case RIL_REQUEST_STOP_KEEPALIVE:
+ return "RIL_REQUEST_STOP_KEEPALIVE";
+ case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
+ return "RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA";
+ case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA:
+ return "RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA";
default: return "<unknown request>";
}
}
@@ -4887,6 +5036,12 @@
return "RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
case RIL_UNSOL_NETWORK_SCAN_RESULT:
return "RIL_UNSOL_NETWORK_SCAN_RESULT";
+ case RIL_UNSOL_ICC_SLOT_STATUS:
+ return "RIL_UNSOL_ICC_SLOT_STATUS";
+ case RIL_UNSOL_KEEPALIVE_STATUS:
+ return "RIL_UNSOL_KEEPALIVE_STATUS";
+ case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
+ return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
default:
return "<unknown response>";
}
@@ -4960,13 +5115,20 @@
}
pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
- mClientWakelockTracker.dumpClientRequestTracker();
+ mClientWakelockTracker.dumpClientRequestTracker(pw);
}
public List<ClientRequestStats> getClientRequestStats() {
return mClientWakelockTracker.getClientRequestStats();
}
+ /** Append the data to the end of an ArrayList */
+ public static void appendPrimitiveArrayToArrayList(byte[] src, ArrayList<Byte> dst) {
+ for (byte b : src) {
+ dst.add(b);
+ }
+ }
+
public static ArrayList<Byte> primitiveArrayToArrayList(byte[] arr) {
ArrayList<Byte> arrayList = new ArrayList<>(arr.length);
for (byte b : arr) {
@@ -4975,6 +5137,16 @@
return arrayList;
}
+ /** Convert a primitive int array to an ArrayList<Integer>. */
+ public static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) {
+ ArrayList<Integer> arrayList = new ArrayList<>(arr.length);
+ for (int i : arr) {
+ arrayList.add(i);
+ }
+ return arrayList;
+ }
+
+ /** Convert an ArrayList of Bytes to an exactly-sized primitive array */
public static byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
byte[] ret = new byte[bytes.size()];
for (int i = 0; i < ret.length; i++) {
@@ -5041,24 +5213,113 @@
return rc;
}
- static ArrayList<Integer> convertHalLceData(LceDataInfo lce, RIL ril) {
- final ArrayList<Integer> capacityResponse = new ArrayList<Integer>();
- final int capacityDownKbps = lce.lastHopCapacityKbps;
- final int confidenceLevel = Byte.toUnsignedInt(lce.confidenceLevel);
- final int lceSuspended = lce.lceSuspended ? 1 : 0;
+ static LinkCapacityEstimate convertHalLceData(LceDataInfo halData, RIL ril) {
+ final LinkCapacityEstimate lce = new LinkCapacityEstimate(
+ halData.lastHopCapacityKbps,
+ Byte.toUnsignedInt(halData.confidenceLevel),
+ halData.lceSuspended ? LinkCapacityEstimate.STATUS_SUSPENDED
+ : LinkCapacityEstimate.STATUS_ACTIVE);
- ril.riljLog("LCE capacity information received:" +
- " capacity=" + capacityDownKbps +
- " confidence=" + confidenceLevel +
- " lceSuspended=" + lceSuspended);
-
- capacityResponse.add(capacityDownKbps);
- capacityResponse.add(confidenceLevel);
- capacityResponse.add(lceSuspended);
- return capacityResponse;
+ ril.riljLog("LCE capacity information received:" + lce);
+ return lce;
}
- static ArrayList<CellInfo> convertHalCellInfoList(
+ static LinkCapacityEstimate convertHalLceData(
+ android.hardware.radio.V1_2.LinkCapacityEstimate halData, RIL ril) {
+ final LinkCapacityEstimate lce = new LinkCapacityEstimate(
+ halData.downlinkCapacityKbps,
+ halData.uplinkCapacityKbps);
+ ril.riljLog("LCE capacity information received:" + lce);
+ return lce;
+ }
+
+ private static void writeToParcelForGsm(
+ Parcel p, int lac, int cid, int arfcn, int bsic, String mcc, String mnc,
+ String al, String as, int ss, int ber, int ta) {
+ p.writeInt(CellIdentity.TYPE_GSM);
+ p.writeString(mcc);
+ p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
+ p.writeInt(lac);
+ p.writeInt(cid);
+ p.writeInt(arfcn);
+ p.writeInt(bsic);
+ p.writeInt(ss);
+ p.writeInt(ber);
+ p.writeInt(ta);
+ }
+
+ private static void writeToParcelForCdma(
+ Parcel p, int ni, int si, int bsi, int lon, int lat, String al, String as,
+ int dbm, int ecio, int eDbm, int eEcio, int eSnr) {
+ new CellIdentityCdma(ni, si, bsi, lon, lat, al, as).writeToParcel(p, 0);
+ new CellSignalStrengthCdma(dbm, ecio, eDbm, eEcio, eSnr).writeToParcel(p, 0);
+ }
+
+ private static void writeToParcelForLte(
+ Parcel p, int ci, int pci, int tac, int earfcn, int bandwidth, String mcc, String mnc,
+ String al, String as, int ss, int rsrp, int rsrq, int rssnr, int cqi, int ta) {
+ p.writeInt(CellIdentity.TYPE_LTE);
+ p.writeString(mcc);
+ p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
+ p.writeInt(ci);
+ p.writeInt(pci);
+ p.writeInt(tac);
+ p.writeInt(earfcn);
+ p.writeInt(bandwidth);
+ p.writeInt(ss);
+ p.writeInt(rsrp);
+ p.writeInt(rsrq);
+ p.writeInt(rssnr);
+ p.writeInt(cqi);
+ p.writeInt(ta);
+ }
+
+ private static void writeToParcelForWcdma(
+ Parcel p, int lac, int cid, int psc, int uarfcn, String mcc, String mnc,
+ String al, String as, int ss, int ber, int rscp, int ecno) {
+ p.writeInt(CellIdentity.TYPE_WCDMA);
+ p.writeString(mcc);
+ p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
+ p.writeInt(lac);
+ p.writeInt(cid);
+ p.writeInt(psc);
+ p.writeInt(uarfcn);
+ p.writeInt(ss);
+ p.writeInt(ber);
+ p.writeInt(rscp);
+ p.writeInt(ecno);
+ }
+
+ private static void writeToParcelForTdscdma(
+ Parcel p, int lac, int cid, int cpid, int uarfcn, String mcc, String mnc,
+ String al, String as, int ss, int ber, int rscp) {
+ p.writeInt(CellIdentity.TYPE_TDSCDMA);
+ p.writeString(mcc);
+ p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
+ p.writeInt(lac);
+ p.writeInt(cid);
+ p.writeInt(cpid);
+ p.writeInt(uarfcn);
+ p.writeInt(ss);
+ p.writeInt(ber);
+ p.writeInt(rscp);
+ }
+
+ /**
+ * Convert CellInfo defined in 1.0/types.hal to CellInfo type.
+ * @param records List of CellInfo defined in 1.0/types.hal
+ * @return List of converted CellInfo object
+ */
+ @VisibleForTesting
+ public static ArrayList<CellInfo> convertHalCellInfoList(
ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
@@ -5069,63 +5330,229 @@
p.writeInt(record.registered ? 1 : 0);
p.writeInt(record.timeStampType);
p.writeLong(record.timeStamp);
+ p.writeInt(CellInfo.CONNECTION_UNKNOWN);
switch (record.cellInfoType) {
case CellInfoType.GSM: {
CellInfoGsm cellInfoGsm = record.gsm.get(0);
- p.writeInt(Integer.parseInt(cellInfoGsm.cellIdentityGsm.mcc));
- p.writeInt(Integer.parseInt(cellInfoGsm.cellIdentityGsm.mnc));
- p.writeInt(cellInfoGsm.cellIdentityGsm.lac);
- p.writeInt(cellInfoGsm.cellIdentityGsm.cid);
- p.writeInt(cellInfoGsm.cellIdentityGsm.arfcn);
- p.writeInt(Byte.toUnsignedInt(cellInfoGsm.cellIdentityGsm.bsic));
- p.writeInt(cellInfoGsm.signalStrengthGsm.signalStrength);
- p.writeInt(cellInfoGsm.signalStrengthGsm.bitErrorRate);
- p.writeInt(cellInfoGsm.signalStrengthGsm.timingAdvance);
+ writeToParcelForGsm(
+ p,
+ cellInfoGsm.cellIdentityGsm.lac,
+ cellInfoGsm.cellIdentityGsm.cid,
+ cellInfoGsm.cellIdentityGsm.arfcn,
+ Byte.toUnsignedInt(cellInfoGsm.cellIdentityGsm.bsic),
+ cellInfoGsm.cellIdentityGsm.mcc,
+ cellInfoGsm.cellIdentityGsm.mnc,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ cellInfoGsm.signalStrengthGsm.signalStrength,
+ cellInfoGsm.signalStrengthGsm.bitErrorRate,
+ cellInfoGsm.signalStrengthGsm.timingAdvance);
break;
}
case CellInfoType.CDMA: {
CellInfoCdma cellInfoCdma = record.cdma.get(0);
- p.writeInt(cellInfoCdma.cellIdentityCdma.networkId);
- p.writeInt(cellInfoCdma.cellIdentityCdma.systemId);
- p.writeInt(cellInfoCdma.cellIdentityCdma.baseStationId);
- p.writeInt(cellInfoCdma.cellIdentityCdma.longitude);
- p.writeInt(cellInfoCdma.cellIdentityCdma.latitude);
- p.writeInt(cellInfoCdma.signalStrengthCdma.dbm);
- p.writeInt(cellInfoCdma.signalStrengthCdma.ecio);
- p.writeInt(cellInfoCdma.signalStrengthEvdo.dbm);
- p.writeInt(cellInfoCdma.signalStrengthEvdo.ecio);
- p.writeInt(cellInfoCdma.signalStrengthEvdo.signalNoiseRatio);
+ writeToParcelForCdma(
+ p,
+ cellInfoCdma.cellIdentityCdma.networkId,
+ cellInfoCdma.cellIdentityCdma.systemId,
+ cellInfoCdma.cellIdentityCdma.baseStationId,
+ cellInfoCdma.cellIdentityCdma.longitude,
+ cellInfoCdma.cellIdentityCdma.latitude,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ cellInfoCdma.signalStrengthCdma.dbm,
+ cellInfoCdma.signalStrengthCdma.ecio,
+ cellInfoCdma.signalStrengthEvdo.dbm,
+ cellInfoCdma.signalStrengthEvdo.ecio,
+ cellInfoCdma.signalStrengthEvdo.signalNoiseRatio);
break;
}
case CellInfoType.LTE: {
CellInfoLte cellInfoLte = record.lte.get(0);
- p.writeInt(Integer.parseInt(cellInfoLte.cellIdentityLte.mcc));
- p.writeInt(Integer.parseInt(cellInfoLte.cellIdentityLte.mnc));
- p.writeInt(cellInfoLte.cellIdentityLte.ci);
- p.writeInt(cellInfoLte.cellIdentityLte.pci);
- p.writeInt(cellInfoLte.cellIdentityLte.tac);
- p.writeInt(cellInfoLte.cellIdentityLte.earfcn);
- p.writeInt(cellInfoLte.signalStrengthLte.signalStrength);
- p.writeInt(cellInfoLte.signalStrengthLte.rsrp);
- p.writeInt(cellInfoLte.signalStrengthLte.rsrq);
- p.writeInt(cellInfoLte.signalStrengthLte.rssnr);
- p.writeInt(cellInfoLte.signalStrengthLte.cqi);
- p.writeInt(cellInfoLte.signalStrengthLte.timingAdvance);
+ writeToParcelForLte(
+ p,
+ cellInfoLte.cellIdentityLte.ci,
+ cellInfoLte.cellIdentityLte.pci,
+ cellInfoLte.cellIdentityLte.tac,
+ cellInfoLte.cellIdentityLte.earfcn,
+ Integer.MAX_VALUE,
+ cellInfoLte.cellIdentityLte.mcc,
+ cellInfoLte.cellIdentityLte.mnc,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ cellInfoLte.signalStrengthLte.signalStrength,
+ cellInfoLte.signalStrengthLte.rsrp,
+ cellInfoLte.signalStrengthLte.rsrq,
+ cellInfoLte.signalStrengthLte.rssnr,
+ cellInfoLte.signalStrengthLte.cqi,
+ cellInfoLte.signalStrengthLte.timingAdvance);
break;
}
case CellInfoType.WCDMA: {
CellInfoWcdma cellInfoWcdma = record.wcdma.get(0);
- p.writeInt(Integer.parseInt(cellInfoWcdma.cellIdentityWcdma.mcc));
- p.writeInt(Integer.parseInt(cellInfoWcdma.cellIdentityWcdma.mnc));
- p.writeInt(cellInfoWcdma.cellIdentityWcdma.lac);
- p.writeInt(cellInfoWcdma.cellIdentityWcdma.cid);
- p.writeInt(cellInfoWcdma.cellIdentityWcdma.psc);
- p.writeInt(cellInfoWcdma.cellIdentityWcdma.uarfcn);
- p.writeInt(cellInfoWcdma.signalStrengthWcdma.signalStrength);
- p.writeInt(cellInfoWcdma.signalStrengthWcdma.bitErrorRate);
+ writeToParcelForWcdma(
+ p,
+ cellInfoWcdma.cellIdentityWcdma.lac,
+ cellInfoWcdma.cellIdentityWcdma.cid,
+ cellInfoWcdma.cellIdentityWcdma.psc,
+ cellInfoWcdma.cellIdentityWcdma.uarfcn,
+ cellInfoWcdma.cellIdentityWcdma.mcc,
+ cellInfoWcdma.cellIdentityWcdma.mnc,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ cellInfoWcdma.signalStrengthWcdma.signalStrength,
+ cellInfoWcdma.signalStrengthWcdma.bitErrorRate,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE);
+ break;
+ }
+
+ case CellInfoType.TD_SCDMA: {
+ CellInfoTdscdma cellInfoTdscdma = record.tdscdma.get(0);
+ writeToParcelForTdscdma(
+ p,
+ cellInfoTdscdma.cellIdentityTdscdma.lac,
+ cellInfoTdscdma.cellIdentityTdscdma.cid,
+ cellInfoTdscdma.cellIdentityTdscdma.cpid,
+ Integer.MAX_VALUE,
+ cellInfoTdscdma.cellIdentityTdscdma.mcc,
+ cellInfoTdscdma.cellIdentityTdscdma.mnc,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ convertTdscdmaRscpTo1_2(cellInfoTdscdma.signalStrengthTdscdma.rscp));
+ break;
+ }
+ default:
+ throw new RuntimeException("unexpected cellinfotype: " + record.cellInfoType);
+ }
+
+ p.setDataPosition(0);
+ CellInfo InfoRec = CellInfo.CREATOR.createFromParcel(p);
+ p.recycle();
+ response.add(InfoRec);
+ }
+
+ return response;
+ }
+
+ /**
+ * Convert CellInfo defined in 1.2/types.hal to CellInfo type.
+ * @param records List of CellInfo defined in 1.2/types.hal
+ * @return List of converted CellInfo object
+ */
+ @VisibleForTesting
+ public static ArrayList<CellInfo> convertHalCellInfoList_1_2(
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records) {
+ ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
+
+ for (android.hardware.radio.V1_2.CellInfo record : records) {
+ // first convert RIL CellInfo to Parcel
+ Parcel p = Parcel.obtain();
+ p.writeInt(record.cellInfoType);
+ p.writeInt(record.registered ? 1 : 0);
+ p.writeInt(record.timeStampType);
+ p.writeLong(record.timeStamp);
+ p.writeInt(record.connectionStatus);
+ switch (record.cellInfoType) {
+ case CellInfoType.GSM: {
+ android.hardware.radio.V1_2.CellInfoGsm cellInfoGsm = record.gsm.get(0);
+ writeToParcelForGsm(
+ p,
+ cellInfoGsm.cellIdentityGsm.base.lac,
+ cellInfoGsm.cellIdentityGsm.base.cid,
+ cellInfoGsm.cellIdentityGsm.base.arfcn,
+ Byte.toUnsignedInt(cellInfoGsm.cellIdentityGsm.base.bsic),
+ cellInfoGsm.cellIdentityGsm.base.mcc,
+ cellInfoGsm.cellIdentityGsm.base.mnc,
+ cellInfoGsm.cellIdentityGsm.operatorNames.alphaLong,
+ cellInfoGsm.cellIdentityGsm.operatorNames.alphaShort,
+ cellInfoGsm.signalStrengthGsm.signalStrength,
+ cellInfoGsm.signalStrengthGsm.bitErrorRate,
+ cellInfoGsm.signalStrengthGsm.timingAdvance);
+ break;
+ }
+
+ case CellInfoType.CDMA: {
+ android.hardware.radio.V1_2.CellInfoCdma cellInfoCdma = record.cdma.get(0);
+ writeToParcelForCdma(
+ p,
+ cellInfoCdma.cellIdentityCdma.base.networkId,
+ cellInfoCdma.cellIdentityCdma.base.systemId,
+ cellInfoCdma.cellIdentityCdma.base.baseStationId,
+ cellInfoCdma.cellIdentityCdma.base.longitude,
+ cellInfoCdma.cellIdentityCdma.base.latitude,
+ cellInfoCdma.cellIdentityCdma.operatorNames.alphaLong,
+ cellInfoCdma.cellIdentityCdma.operatorNames.alphaShort,
+ cellInfoCdma.signalStrengthCdma.dbm,
+ cellInfoCdma.signalStrengthCdma.ecio,
+ cellInfoCdma.signalStrengthEvdo.dbm,
+ cellInfoCdma.signalStrengthEvdo.ecio,
+ cellInfoCdma.signalStrengthEvdo.signalNoiseRatio);
+ break;
+ }
+
+ case CellInfoType.LTE: {
+ android.hardware.radio.V1_2.CellInfoLte cellInfoLte = record.lte.get(0);
+ writeToParcelForLte(
+ p,
+ cellInfoLte.cellIdentityLte.base.ci,
+ cellInfoLte.cellIdentityLte.base.pci,
+ cellInfoLte.cellIdentityLte.base.tac,
+ cellInfoLte.cellIdentityLte.base.earfcn,
+ cellInfoLte.cellIdentityLte.bandwidth,
+ cellInfoLte.cellIdentityLte.base.mcc,
+ cellInfoLte.cellIdentityLte.base.mnc,
+ cellInfoLte.cellIdentityLte.operatorNames.alphaLong,
+ cellInfoLte.cellIdentityLte.operatorNames.alphaShort,
+ cellInfoLte.signalStrengthLte.signalStrength,
+ cellInfoLte.signalStrengthLte.rsrp,
+ cellInfoLte.signalStrengthLte.rsrq,
+ cellInfoLte.signalStrengthLte.rssnr,
+ cellInfoLte.signalStrengthLte.cqi,
+ cellInfoLte.signalStrengthLte.timingAdvance);
+ break;
+ }
+
+ case CellInfoType.WCDMA: {
+ android.hardware.radio.V1_2.CellInfoWcdma cellInfoWcdma = record.wcdma.get(0);
+ writeToParcelForWcdma(
+ p,
+ cellInfoWcdma.cellIdentityWcdma.base.lac,
+ cellInfoWcdma.cellIdentityWcdma.base.cid,
+ cellInfoWcdma.cellIdentityWcdma.base.psc,
+ cellInfoWcdma.cellIdentityWcdma.base.uarfcn,
+ cellInfoWcdma.cellIdentityWcdma.base.mcc,
+ cellInfoWcdma.cellIdentityWcdma.base.mnc,
+ cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaLong,
+ cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaShort,
+ cellInfoWcdma.signalStrengthWcdma.base.signalStrength,
+ cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate,
+ cellInfoWcdma.signalStrengthWcdma.rscp,
+ cellInfoWcdma.signalStrengthWcdma.ecno);
+ break;
+ }
+
+ case CellInfoType.TD_SCDMA: {
+ android.hardware.radio.V1_2.CellInfoTdscdma cellInfoTdscdma =
+ record.tdscdma.get(0);
+ writeToParcelForTdscdma(
+ p,
+ cellInfoTdscdma.cellIdentityTdscdma.base.lac,
+ cellInfoTdscdma.cellIdentityTdscdma.base.cid,
+ cellInfoTdscdma.cellIdentityTdscdma.base.cpid,
+ cellInfoTdscdma.cellIdentityTdscdma.uarfcn,
+ cellInfoTdscdma.cellIdentityTdscdma.base.mcc,
+ cellInfoTdscdma.cellIdentityTdscdma.base.mnc,
+ cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaLong,
+ cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaShort,
+ cellInfoTdscdma.signalStrengthTdscdma.signalStrength,
+ cellInfoTdscdma.signalStrengthTdscdma.bitErrorRate,
+ cellInfoTdscdma.signalStrengthTdscdma.rscp);
break;
}
@@ -5142,9 +5569,24 @@
return response;
}
- static SignalStrength convertHalSignalStrength(
+ private static int convertTdscdmaRscpTo1_2(int rscp) {
+ // The HAL 1.0 range is 25..120; the ASU/ HAL 1.2 range is 0..96;
+ // yes, this means the range in 1.0 cannot express -24dBm = 96
+ if (rscp >= 25 && rscp <= 120) {
+ // First we flip the sign to convert from the HALs -rscp to the actual RSCP value.
+ int rscpDbm = -rscp;
+ // Then to convert from RSCP to ASU, we apply the offset which aligns 0 ASU to -120dBm.
+ return rscpDbm + 120;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ /** Convert HAL 1.0 Signal Strength to android SignalStrength */
+ @VisibleForTesting
+ public static SignalStrength convertHalSignalStrength(
android.hardware.radio.V1_0.SignalStrength signalStrength) {
- return new SignalStrength(signalStrength.gw.signalStrength,
+ return new SignalStrength(
+ signalStrength.gw.signalStrength,
signalStrength.gw.bitErrorRate,
signalStrength.cdma.dbm,
signalStrength.cdma.ecio,
@@ -5156,7 +5598,28 @@
signalStrength.lte.rsrq,
signalStrength.lte.rssnr,
signalStrength.lte.cqi,
+ convertTdscdmaRscpTo1_2(signalStrength.tdScdma.rscp));
+ }
+
+ /** Convert HAL 1.2 Signal Strength to android SignalStrength */
+ @VisibleForTesting
+ public static SignalStrength convertHalSignalStrength_1_2(
+ android.hardware.radio.V1_2.SignalStrength signalStrength) {
+ return new SignalStrength(
+ signalStrength.gsm.signalStrength,
+ signalStrength.gsm.bitErrorRate,
+ signalStrength.cdma.dbm,
+ signalStrength.cdma.ecio,
+ signalStrength.evdo.dbm,
+ signalStrength.evdo.ecio,
+ signalStrength.evdo.signalNoiseRatio,
+ signalStrength.lte.signalStrength,
+ signalStrength.lte.rsrp,
+ signalStrength.lte.rsrq,
+ signalStrength.lte.rssnr,
+ signalStrength.lte.cqi,
signalStrength.tdScdma.rscp,
- false /* gsmFlag - don't care; will be changed by SST */);
+ signalStrength.wcdma.base.signalStrength,
+ signalStrength.wcdma.rscp);
}
}
diff --git a/src/java/com/android/internal/telephony/RILRequest.java b/src/java/com/android/internal/telephony/RILRequest.java
new file mode 100644
index 0000000..d20ce22
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RILRequest.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.telephony.Rlog;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * {@hide}
+ */
+
+public class RILRequest {
+ static final String LOG_TAG = "RilRequest";
+
+ //***** Class Variables
+ static Random sRandom = new Random();
+ static AtomicInteger sNextSerial = new AtomicInteger(0);
+ private static Object sPoolSync = new Object();
+ private static RILRequest sPool = null;
+ private static int sPoolSize = 0;
+ private static final int MAX_POOL_SIZE = 4;
+
+ //***** Instance Variables
+ int mSerial;
+ int mRequest;
+ Message mResult;
+ RILRequest mNext;
+ int mWakeLockType;
+ WorkSource mWorkSource;
+ String mClientId;
+ // time in ms when RIL request was made
+ long mStartTimeMs;
+
+ public int getSerial() {
+ return mSerial;
+ }
+
+ public int getRequest() {
+ return mRequest;
+ }
+
+ public Message getResult() {
+ return mResult;
+ }
+
+ /**
+ * Retrieves a new RILRequest instance from the pool.
+ *
+ * @param request RIL_REQUEST_*
+ * @param result sent when operation completes
+ * @return a RILRequest instance from the pool.
+ */
+ private static RILRequest obtain(int request, Message result) {
+ RILRequest rr = null;
+
+ synchronized (sPoolSync) {
+ if (sPool != null) {
+ rr = sPool;
+ sPool = rr.mNext;
+ rr.mNext = null;
+ sPoolSize--;
+ }
+ }
+
+ if (rr == null) {
+ rr = new RILRequest();
+ }
+
+ // Increment serial number. Wrap to 0 when reaching Integer.MAX_VALUE.
+ rr.mSerial = sNextSerial.getAndUpdate(n -> ((n + 1) % Integer.MAX_VALUE));
+
+ rr.mRequest = request;
+ rr.mResult = result;
+
+ rr.mWakeLockType = RIL.INVALID_WAKELOCK;
+ rr.mWorkSource = null;
+ rr.mStartTimeMs = SystemClock.elapsedRealtime();
+ if (result != null && result.getTarget() == null) {
+ throw new NullPointerException("Message target must not be null");
+ }
+
+ return rr;
+ }
+
+
+ /**
+ * Retrieves a new RILRequest instance from the pool and sets the clientId
+ *
+ * @param request RIL_REQUEST_*
+ * @param result sent when operation completes
+ * @param workSource WorkSource to track the client
+ * @return a RILRequest instance from the pool.
+ */
+ static RILRequest obtain(int request, Message result, WorkSource workSource) {
+ RILRequest rr = null;
+
+ rr = obtain(request, result);
+ if (workSource != null) {
+ rr.mWorkSource = workSource;
+ rr.mClientId = String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
+ } else {
+ Rlog.e(LOG_TAG, "null workSource " + request);
+ }
+
+ return rr;
+ }
+
+ /**
+ * Returns a RILRequest instance to the pool.
+ *
+ * Note: This should only be called once per use.
+ */
+ void release() {
+ synchronized (sPoolSync) {
+ if (sPoolSize < MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ sPoolSize++;
+ mResult = null;
+ if (mWakeLockType != RIL.INVALID_WAKELOCK) {
+ //This is OK for some wakelock types and not others
+ if (mWakeLockType == RIL.FOR_WAKELOCK) {
+ Rlog.e(LOG_TAG, "RILRequest releasing with held wake lock: "
+ + serialString());
+ }
+ }
+ }
+ }
+ }
+
+ private RILRequest() {
+ }
+
+ static void resetSerial() {
+ // Use a non-negative random number so that on recovery we probably don't mix old requests
+ // with new.
+ sNextSerial.set(sRandom.nextInt(Integer.MAX_VALUE));
+ }
+
+ String serialString() {
+ //Cheesy way to do %04d
+ StringBuilder sb = new StringBuilder(8);
+ String sn;
+
+ // Truncate mSerial to a number with maximum 4 digits.
+ int adjustedSerial = mSerial % 10000;
+ sn = Integer.toString(adjustedSerial);
+
+ //sb.append("J[");
+ sb.append('[');
+ for (int i = 0, s = sn.length(); i < 4 - s; i++) {
+ sb.append('0');
+ }
+
+ sb.append(sn);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ void onError(int error, Object ret) {
+ CommandException ex;
+
+ ex = CommandException.fromRilErrno(error);
+
+ if (RIL.RILJ_LOGD) {
+ Rlog.d(LOG_TAG, serialString() + "< "
+ + RIL.requestToString(mRequest)
+ + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
+ }
+
+ if (mResult != null) {
+ AsyncResult.forMessage(mResult, ret, ex);
+ mResult.sendToTarget();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
new file mode 100644
index 0000000..764fc10
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -0,0 +1,342 @@
+/*
+ * 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;
+
+import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
+
+import android.content.Context;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.V1_0.RadioResponseType;
+import android.hardware.radio.config.V1_0.IRadioConfig;
+import android.hardware.radio.config.V1_0.SimSlotStatus;
+import android.net.ConnectivityManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HwBinder;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RemoteException;
+import android.os.WorkSource;
+import android.telephony.Rlog;
+import android.util.SparseArray;
+
+import com.android.internal.telephony.uicc.IccSlotStatus;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This class provides wrapper APIs for IRadioConfig interface.
+ */
+public class RadioConfig extends Handler {
+ private static final String TAG = "RadioConfig";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; //STOPSHIP if true
+
+ private static final String RADIO_CONFIG_SERVICE_NAME = "radioconfig";
+ private static final int EVENT_SERVICE_DEAD = 1;
+
+ private final boolean mIsMobileNetworkSupported;
+ private volatile IRadioConfig mRadioConfigProxy = null;
+ private final ServiceDeathRecipient mServiceDeathRecipient;
+ private final AtomicLong mRadioConfigProxyCookie = new AtomicLong(0);
+ private final RadioConfigResponse mRadioConfigResponse;
+ private final RadioConfigIndication mRadioConfigIndication;
+ private final SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>();
+ /* default work source which will blame phone process */
+ private final WorkSource mDefaultWorkSource;
+ private static RadioConfig sRadioConfig;
+
+ protected Registrant mSimSlotStatusRegistrant;
+
+ final class ServiceDeathRecipient implements HwBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ // Deal with service going away
+ logd("serviceDied");
+ sendMessage(obtainMessage(EVENT_SERVICE_DEAD, cookie));
+ }
+ }
+
+ private RadioConfig(Context context) {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
+ mRadioConfigResponse = new RadioConfigResponse(this);
+ mRadioConfigIndication = new RadioConfigIndication(this);
+ mServiceDeathRecipient = new ServiceDeathRecipient();
+
+ mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
+ context.getPackageName());
+ }
+
+ /**
+ * Returns the singleton static instance of RadioConfig
+ */
+ public static RadioConfig getInstance(Context context) {
+ if (sRadioConfig == null) {
+ sRadioConfig = new RadioConfig(context);
+ }
+ return sRadioConfig;
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case EVENT_SERVICE_DEAD:
+ logd("handleMessage: EVENT_SERVICE_DEAD cookie = " + message.obj
+ + " mRadioConfigProxyCookie = " + mRadioConfigProxyCookie.get());
+ if ((long) message.obj == mRadioConfigProxyCookie.get()) {
+ resetProxyAndRequestList("EVENT_SERVICE_DEAD", null);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Release each request in mRequestList then clear the list
+ * @param error is the RIL_Errno sent back
+ * @param loggable true means to print all requests in mRequestList
+ */
+ private void clearRequestList(int error, boolean loggable) {
+ RILRequest rr;
+ synchronized (mRequestList) {
+ int count = mRequestList.size();
+ if (DBG && loggable) {
+ logd("clearRequestList: mRequestList=" + count);
+ }
+
+ for (int i = 0; i < count; i++) {
+ rr = mRequestList.valueAt(i);
+ if (DBG && loggable) {
+ logd(i + ": [" + rr.mSerial + "] " + requestToString(rr.mRequest));
+ }
+ rr.onError(error, null);
+ rr.release();
+ }
+ mRequestList.clear();
+ }
+ }
+
+ private void resetProxyAndRequestList(String caller, Exception e) {
+ loge(caller + ": " + e);
+ mRadioConfigProxy = null;
+
+ // increment the cookie so that death notification can be ignored
+ mRadioConfigProxyCookie.incrementAndGet();
+
+ RILRequest.resetSerial();
+ // Clear request list on close
+ clearRequestList(RADIO_NOT_AVAILABLE, false);
+
+ getRadioConfigProxy(null);
+ }
+
+ /** Returns a {@link IRadioConfig} instance or null if the service is not available. */
+ public IRadioConfig getRadioConfigProxy(Message result) {
+ if (!mIsMobileNetworkSupported) {
+ if (VDBG) logd("getRadioConfigProxy: Not calling getService(): wifi-only");
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+ result.sendToTarget();
+ }
+ return null;
+ }
+
+ if (mRadioConfigProxy != null) {
+ return mRadioConfigProxy;
+ }
+
+ try {
+ mRadioConfigProxy = IRadioConfig.getService(RADIO_CONFIG_SERVICE_NAME, true);
+ if (mRadioConfigProxy != null) {
+ mRadioConfigProxy.linkToDeath(mServiceDeathRecipient,
+ mRadioConfigProxyCookie.incrementAndGet());
+ mRadioConfigProxy.setResponseFunctions(mRadioConfigResponse,
+ mRadioConfigIndication);
+ } else {
+ loge("getRadioConfigProxy: mRadioConfigProxy == null");
+ }
+ } catch (RemoteException | RuntimeException e) {
+ mRadioConfigProxy = null;
+ loge("getRadioConfigProxy: RadioConfigProxy getService/setResponseFunctions: " + e);
+ }
+
+ if (mRadioConfigProxy == null) {
+ // getService() is a blocking call, so this should never happen
+ loge("getRadioConfigProxy: mRadioConfigProxy == null");
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+ result.sendToTarget();
+ }
+ }
+
+ return mRadioConfigProxy;
+ }
+
+ private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
+ RILRequest rr = RILRequest.obtain(request, result, workSource);
+ synchronized (mRequestList) {
+ mRequestList.append(rr.mSerial, rr);
+ }
+ return rr;
+ }
+
+ private RILRequest findAndRemoveRequestFromList(int serial) {
+ RILRequest rr;
+ synchronized (mRequestList) {
+ rr = mRequestList.get(serial);
+ if (rr != null) {
+ mRequestList.remove(serial);
+ }
+ }
+
+ return rr;
+ }
+
+ /**
+ * This is a helper function to be called when a RadioConfigResponse callback is called.
+ * It finds and returns RILRequest corresponding to the response if one is found.
+ * @param responseInfo RadioResponseInfo received in response callback
+ * @return RILRequest corresponding to the response
+ */
+ public RILRequest processResponse(RadioResponseInfo responseInfo) {
+ int serial = responseInfo.serial;
+ int error = responseInfo.error;
+ int type = responseInfo.type;
+
+ if (type != RadioResponseType.SOLICITED) {
+ loge("processResponse: Unexpected response type " + type);
+ }
+
+ RILRequest rr = findAndRemoveRequestFromList(serial);
+ if (rr == null) {
+ loge("processResponse: Unexpected response! serial: " + serial + " error: " + error);
+ return null;
+ }
+
+ return rr;
+ }
+
+ /**
+ * Wrapper function for IRadioConfig.getSimSlotsStatus().
+ */
+ public void getSimSlotsStatus(Message result) {
+ IRadioConfig radioConfigProxy = getRadioConfigProxy(result);
+ if (radioConfigProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLOT_STATUS, result, mDefaultWorkSource);
+
+ if (DBG) {
+ logd(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioConfigProxy.getSimSlotsStatus(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ resetProxyAndRequestList("getSimSlotsStatus", e);
+ }
+ }
+ }
+
+ /**
+ * Wrapper function for IRadioConfig.getSimSlotsStatus().
+ */
+ public void setSimSlotsMapping(int[] physicalSlots, Message result) {
+ IRadioConfig radioConfigProxy = getRadioConfigProxy(result);
+ if (radioConfigProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING, result,
+ mDefaultWorkSource);
+
+ if (DBG) {
+ logd(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + Arrays.toString(physicalSlots));
+ }
+
+ try {
+ radioConfigProxy.setSimSlotsMapping(rr.mSerial,
+ primitiveArrayToArrayList(physicalSlots));
+ } catch (RemoteException | RuntimeException e) {
+ resetProxyAndRequestList("setSimSlotsMapping", e);
+ }
+ }
+ }
+
+ private static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) {
+ ArrayList<Integer> arrayList = new ArrayList<>(arr.length);
+ for (int i : arr) {
+ arrayList.add(i);
+ }
+ return arrayList;
+ }
+
+ static String requestToString(int request) {
+ switch (request) {
+ case RIL_REQUEST_GET_SLOT_STATUS:
+ return "GET_SLOT_STATUS";
+ case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
+ return "SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
+ default:
+ return "<unknown request>";
+ }
+ }
+
+ /**
+ * Register a handler to get SIM slot status changed notifications.
+ */
+ public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) {
+ mSimSlotStatusRegistrant = new Registrant(h, what, obj);
+ }
+
+ /**
+ * Unregister corresponding to registerForSimSlotStatusChanged().
+ */
+ public void unregisterForSimSlotStatusChanged(Handler h) {
+ if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) {
+ mSimSlotStatusRegistrant.clear();
+ mSimSlotStatusRegistrant = null;
+ }
+ }
+
+ static ArrayList<IccSlotStatus> convertHalSlotStatus(
+ ArrayList<SimSlotStatus> halSlotStatusList) {
+ ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size());
+ for (SimSlotStatus slotStatus : halSlotStatusList) {
+ IccSlotStatus iccSlotStatus = new IccSlotStatus();
+ iccSlotStatus.setCardState(slotStatus.cardState);
+ iccSlotStatus.setSlotState(slotStatus.slotState);
+ iccSlotStatus.logicalSlotIndex = slotStatus.logicalSlotId;
+ iccSlotStatus.atr = slotStatus.atr;
+ iccSlotStatus.iccid = slotStatus.iccid;
+ response.add(iccSlotStatus);
+ }
+ return response;
+ }
+
+ private static void logd(String log) {
+ Rlog.d(TAG, log);
+ }
+
+ private static void loge(String log) {
+ Rlog.e(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndication.java b/src/java/com/android/internal/telephony/RadioConfigIndication.java
new file mode 100644
index 0000000..686282c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioConfigIndication.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import android.hardware.radio.config.V1_0.IRadioConfigIndication;
+import android.hardware.radio.config.V1_0.SimSlotStatus;
+import android.os.AsyncResult;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.uicc.IccSlotStatus;
+
+import java.util.ArrayList;
+
+/**
+ * This class is the implementation of IRadioConfigIndication interface.
+ */
+public class RadioConfigIndication extends IRadioConfigIndication.Stub {
+ private final RadioConfig mRadioConfig;
+ private static final String TAG = "RadioConfigIndication";
+
+ public RadioConfigIndication(RadioConfig radioConfig) {
+ mRadioConfig = radioConfig;
+ }
+
+ /**
+ * Unsolicited indication for slot status changed
+ */
+ public void simSlotsStatusChanged(int indicationType, ArrayList<SimSlotStatus> slotStatus) {
+ ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus(slotStatus);
+ Rlog.d(TAG, "[UNSL]< " + " UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
+ if (mRadioConfig.mSimSlotStatusRegistrant != null) {
+ mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
+ new AsyncResult(null, ret, null));
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
new file mode 100644
index 0000000..8177b2d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.config.V1_0.IRadioConfigResponse;
+import android.hardware.radio.config.V1_0.SimSlotStatus;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.uicc.IccSlotStatus;
+
+import java.util.ArrayList;
+
+/**
+ * This class is the implementation of IRadioConfigResponse interface.
+ */
+public class RadioConfigResponse extends IRadioConfigResponse.Stub {
+ private final RadioConfig mRadioConfig;
+ private static final String TAG = "RadioConfigResponse";
+
+ public RadioConfigResponse(RadioConfig radioConfig) {
+ mRadioConfig = radioConfig;
+ }
+
+ /**
+ * Response function for IRadioConfig.getSimSlotsStatus().
+ */
+ public void getSimSlotsStatusResponse(RadioResponseInfo responseInfo,
+ ArrayList<SimSlotStatus> slotStatus) {
+ RILRequest rr = mRadioConfig.processResponse(responseInfo);
+
+ if (rr != null) {
+ ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus(slotStatus);
+ if (responseInfo.error == RadioError.NONE) {
+ // send response
+ RadioResponse.sendMessageResponse(rr.mResult, ret);
+ Rlog.d(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " " + ret.toString());
+ } else {
+ rr.onError(responseInfo.error, ret);
+ Rlog.e(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " error "
+ + responseInfo.error);
+ }
+
+ } else {
+ Rlog.e(TAG, "getSimSlotsStatusResponse: Error " + responseInfo.toString());
+ }
+ }
+
+ /**
+ * Response function for IRadioConfig.setSimSlotsMapping().
+ */
+ public void setSimSlotsMappingResponse(RadioResponseInfo responseInfo) {
+ RILRequest rr = mRadioConfig.processResponse(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ // send response
+ RadioResponse.sendMessageResponse(rr.mResult, null);
+ Rlog.d(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest));
+ } else {
+ rr.onError(responseInfo.error, null);
+ Rlog.e(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " error "
+ + responseInfo.error);
+ }
+ } else {
+ Rlog.e(TAG, "setSimSlotsMappingResponse: Error " + responseInfo.toString());
+ }
+ }
+
+
+}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index a7d2418..f7a7943 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -28,6 +28,7 @@
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;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
@@ -35,6 +36,7 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESEND_INCALL_MUTE;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED;
@@ -80,19 +82,20 @@
import android.hardware.radio.V1_0.SsInfoData;
import android.hardware.radio.V1_0.StkCcUnsolSsResult;
import android.hardware.radio.V1_0.SuppSvcNotification;
-import android.hardware.radio.V1_1.IRadioIndication;
-import android.hardware.radio.V1_1.KeepaliveStatus;
+import android.hardware.radio.V1_2.CellConnectionStatus;
+import android.hardware.radio.V1_2.IRadioIndication;
import android.os.AsyncResult;
import android.os.SystemProperties;
import android.telephony.CellInfo;
import android.telephony.PcoData;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.SignalStrength;
import android.telephony.SmsMessage;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.cdma.CdmaInformationRecords;
import com.android.internal.telephony.cdma.SmsMessageConverter;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.dataconnection.KeepaliveStatus;
import com.android.internal.telephony.gsm.SsData;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
@@ -100,6 +103,7 @@
import com.android.internal.telephony.uicc.IccUtils;
import java.util.ArrayList;
+import java.util.List;
public class RadioIndication extends IRadioIndication.Stub {
RIL mRil;
@@ -233,19 +237,78 @@
}
}
+ /**
+ * Indicates current link capacity estimate.
+ */
+ public void currentLinkCapacityEstimate(int indicationType,
+ android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
+ mRil.processIndication(indicationType);
+
+ LinkCapacityEstimate response = RIL.convertHalLceData(lce, mRil);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+
+ if (mRil.mLceInfoRegistrants != null) {
+ mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+ }
+ }
+
+ /**
+ * Indicates the current signal strength of the camped or primary serving cell.
+ */
+ public void currentSignalStrength_1_2(int indicationType,
+ android.hardware.radio.V1_2.SignalStrength signalStrength) {
+ mRil.processIndication(indicationType);
+
+ SignalStrength ss = RIL.convertHalSignalStrength_1_2(signalStrength);
+ // Note this is set to "verbose" because it happens frequently
+ if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+
+ if (mRil.mSignalStrengthRegistrant != null) {
+ mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
+ }
+ }
+
+ /**
+ * Indicates current physical channel configuration.
+ */
+ public void currentPhysicalChannelConfigs(int indicationType,
+ ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) {
+ List<PhysicalChannelConfig> response = new ArrayList<>(configs.size());
+
+ for (android.hardware.radio.V1_2.PhysicalChannelConfig config : configs) {
+ int status;
+ switch (config.status) {
+ case CellConnectionStatus.PRIMARY_SERVING:
+ status = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+ break;
+ case CellConnectionStatus.SECONDARY_SERVING:
+ status = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
+ break;
+ default:
+ // only PRIMARY_SERVING and SECONDARY_SERVING are supported.
+ mRil.riljLoge("Unsupported CellConnectionStatus in PhysicalChannelConfig: "
+ + config.status);
+ status = PhysicalChannelConfig.CONNECTION_UNKNOWN;
+ break;
+ }
+
+ response.add(new PhysicalChannelConfig(status, config.cellBandwidthDownlink));
+ }
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+
+ mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
+ new AsyncResult(null, response, null));
+ }
+
public void dataCallListChanged(int indicationType, ArrayList<SetupDataCallResult> dcList) {
mRil.processIndication(indicationType);
- ArrayList<DataCallResponse> response = new ArrayList<>();
-
- for (SetupDataCallResult dcResult : dcList) {
- response.add(RIL.convertDataCallResult(dcResult));
- }
-
- if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, response);
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
mRil.mDataCallListChangedRegistrants.notifyRegistrants(
- new AsyncResult(null, response, null));
+ new AsyncResult(null, dcList, null));
}
public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
@@ -623,6 +686,7 @@
new AsyncResult (null, response, null));
}
+ /** Get unsolicited message for cellInfoList */
public void cellInfoList(int indicationType,
ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
mRil.processIndication(indicationType);
@@ -631,7 +695,19 @@
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
- mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
+ mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+ }
+
+ /** 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(indicationType);
+
+ ArrayList<CellInfo> response = RIL.convertHalCellInfoList_1_2(records);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+
+ mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
/** Incremental network scan results */
@@ -640,6 +716,12 @@
responseCellInfos(indicationType, result);
}
+ /** Incremental network scan results with HAL V1_2 */
+ public void networkScanResult_1_2(int indicationType,
+ android.hardware.radio.V1_2.NetworkScanResult result) {
+ responseCellInfos_1_2(indicationType, result);
+ }
+
public void imsNetworkStateChanged(int indicationType) {
mRil.processIndication(indicationType);
@@ -760,12 +842,12 @@
public void lceData(int indicationType, LceDataInfo lce) {
mRil.processIndication(indicationType);
- ArrayList<Integer> response = RIL.convertHalLceData(lce, mRil);
+ LinkCapacityEstimate response = RIL.convertHalLceData(lce, mRil);
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
- if (mRil.mLceInfoRegistrant != null) {
- mRil.mLceInfoRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
+ if (mRil.mLceInfoRegistrants != null) {
+ mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
}
@@ -807,10 +889,19 @@
/**
* Indicates a change in the status of an ongoing Keepalive session
* @param indicationType RadioIndicationType
- * @param keepaliveStatus Status of the ongoing Keepalive session
+ * @param halStatus Status of the ongoing Keepalive session
*/
- public void keepaliveStatus(int indicationType, KeepaliveStatus keepaliveStatus) {
- throw new UnsupportedOperationException("keepaliveStatus Indications are not implemented");
+ public void keepaliveStatus(
+ int indicationType, android.hardware.radio.V1_1.KeepaliveStatus halStatus) {
+ mRil.processIndication(indicationType);
+
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS,
+ "handle=" + halStatus.sessionHandle + " code=" + halStatus.code);
+ }
+
+ KeepaliveStatus ks = new KeepaliveStatus(halStatus.sessionHandle, halStatus.code);
+ mRil.mNattKeepaliveStatusRegistrants.notifyRegistrants(new AsyncResult(null, ks, null));
}
private CommandsInterface.RadioState getRadioStateFromInt(int stateInt) {
@@ -842,4 +933,15 @@
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
+
+ private void responseCellInfos_1_2(int indicationType,
+ android.hardware.radio.V1_2.NetworkScanResult result) {
+ mRil.processIndication(indicationType);
+
+ NetworkScanResult nsr = null;
+ ArrayList<CellInfo> infos = RIL.convertHalCellInfoList_1_2(result.networkInfos);
+ nsr = new NetworkScanResult(result.status, result.error, infos);
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index caf4477..0fc5279 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -33,8 +33,7 @@
import android.hardware.radio.V1_0.SendSmsResult;
import android.hardware.radio.V1_0.SetupDataCallResult;
import android.hardware.radio.V1_0.VoiceRegStateResult;
-import android.hardware.radio.V1_1.IRadioResponse;
-import android.hardware.radio.V1_1.KeepaliveStatus;
+import android.hardware.radio.V1_2.IRadioResponse;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemClock;
@@ -48,7 +47,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.dataconnection.KeepaliveStatus;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
@@ -105,6 +104,15 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param cardStatus ICC card status as defined by CardStatus in 1.2/types.hal
+ */
+ public void getIccCardStatusResponse_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.CardStatus cardStatus) {
+ responseIccCardStatus_1_2(responseInfo, cardStatus);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
@@ -171,6 +179,15 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param calls Current call list
+ */
+ public void getCurrentCallsResponse_1_2(RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_2.Call> calls) {
+ responseCurrentCalls_1_2(responseInfo, calls);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
*/
public void dialResponse(RadioResponseInfo responseInfo) {
responseVoid(responseInfo);
@@ -243,7 +260,17 @@
responseSignalStrength(responseInfo, sigStrength);
}
- /*
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param signalStrength Current signal strength of camped/connected cells
+ */
+ public void getSignalStrengthResponse_1_2(
+ RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.SignalStrength signalStrength) {
+ responseSignalStrength_1_2(responseInfo, signalStrength);
+ }
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
* in types.hal
@@ -262,6 +289,23 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
+ * in 1.2/types.hal
+ */
+ public void getVoiceRegistrationStateResponse_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.VoiceRegStateResult voiceRegResponse) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, voiceRegResponse);
+ }
+ mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
* types.hal
*/
@@ -279,6 +323,23 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
+ * 1.2/types.hal
+ */
+ public void getDataRegistrationStateResponse_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.DataRegStateResult dataRegResponse) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, dataRegResponse);
+ }
+ mRil.processResponseDone(rr, responseInfo, dataRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param longName is long alpha ONS or EONS or empty string if unregistered
* @param shortName is short alpha ONS or EONS or empty string if unregistered
* @param numeric is 5 or 6 digit numeric code (MCC + MNC) or empty string if unregistered
@@ -908,12 +969,13 @@
}
/**
+ * This method is deprecated and should not be used.
*
* @param responseInfo Response info struct containing response type, serial no. and error
* @param response response string of the challenge/response algo for ISIM auth in base64 format
*/
public void requestIsimAuthenticationResponse(RadioResponseInfo responseInfo, String response) {
- responseString(responseInfo, response);
+ throw new RuntimeException("Inexplicable response received for requestIsimAuthentication");
}
/**
@@ -949,6 +1011,16 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param cellInfo List of current cell information known to radio
+ */
+ public void getCellInfoListResponse_1_2(
+ RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_2.CellInfo> cellInfo) {
+ responseCellInfoList_1_2(responseInfo, cellInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
responseVoid(responseInfo);
@@ -1214,59 +1286,154 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
+ public void setSignalStrengthReportingCriteriaResponse(RadioResponseInfo responseInfo) {
+ responseVoid(responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) {
+ responseVoid(responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
public void setSimCardPowerResponse_1_1(RadioResponseInfo responseInfo) {
responseVoid(responseInfo);
}
+
/**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param keepaliveStatus status of the keepalive with a handle for the session
*/
public void startKeepaliveResponse(RadioResponseInfo responseInfo,
- KeepaliveStatus keepaliveStatus) {
- throw new UnsupportedOperationException("startKeepaliveResponse not implemented");
+ android.hardware.radio.V1_1.KeepaliveStatus keepaliveStatus) {
+
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr == null) {
+ return;
+ }
+
+ KeepaliveStatus ret = null;
+
+ switch(responseInfo.error) {
+ case RadioError.NONE:
+ int convertedStatus = convertHalKeepaliveStatusCode(keepaliveStatus.code);
+ if (convertedStatus < 0) {
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED);
+ } else {
+ ret = new KeepaliveStatus(keepaliveStatus.sessionHandle, convertedStatus);
+ }
+ // If responseInfo.error is NONE, response function sends the response message
+ // even if result is actually an error.
+ sendMessageResponse(rr.mResult, ret);
+ break;
+ case RadioError.REQUEST_NOT_SUPPORTED:
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED);
+ break;
+ case RadioError.NO_RESOURCES:
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_NO_RESOURCES);
+ break;
+ default:
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN);
+ break;
+ }
+ // If responseInfo.error != NONE, the processResponseDone sends the response message.
+ mRil.processResponseDone(rr, responseInfo, ret);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
- throw new UnsupportedOperationException("stopKeepaliveResponse not implemented");
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr == null) {
+ return;
+ }
+
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, null);
+ mRil.processResponseDone(rr, responseInfo, null);
+ } else {
+ //TODO: Error code translation
+ }
+ }
+
+ private int convertHalKeepaliveStatusCode(int halCode) {
+ switch (halCode) {
+ case android.hardware.radio.V1_1.KeepaliveStatusCode.ACTIVE:
+ return KeepaliveStatus.STATUS_ACTIVE;
+ case android.hardware.radio.V1_1.KeepaliveStatusCode.INACTIVE:
+ return KeepaliveStatus.STATUS_INACTIVE;
+ case android.hardware.radio.V1_1.KeepaliveStatusCode.PENDING:
+ return KeepaliveStatus.STATUS_PENDING;
+ default:
+ mRil.riljLog("Invalid Keepalive Status" + halCode);
+ return -1;
+ }
+ }
+
+ private IccCardStatus convertHalCardStatus(CardStatus cardStatus) {
+ IccCardStatus iccCardStatus = new IccCardStatus();
+ iccCardStatus.setCardState(cardStatus.cardState);
+ iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
+ iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
+ iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
+ iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex;
+ int numApplications = cardStatus.applications.size();
+
+ // limit to maximum allowed applications
+ if (numApplications
+ > com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
+ numApplications =
+ com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
+ }
+ iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
+ for (int i = 0; i < numApplications; i++) {
+ AppStatus rilAppStatus = cardStatus.applications.get(i);
+ IccCardApplicationStatus appStatus = new IccCardApplicationStatus();
+ appStatus.app_type = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
+ appStatus.app_state = appStatus.AppStateFromRILInt(rilAppStatus.appState);
+ appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
+ rilAppStatus.persoSubstate);
+ appStatus.aid = rilAppStatus.aidPtr;
+ appStatus.app_label = rilAppStatus.appLabelPtr;
+ appStatus.pin1_replaced = rilAppStatus.pin1Replaced;
+ appStatus.pin1 = appStatus.PinStateFromRILInt(rilAppStatus.pin1);
+ appStatus.pin2 = appStatus.PinStateFromRILInt(rilAppStatus.pin2);
+ iccCardStatus.mApplications[i] = appStatus;
+ mRil.riljLog("IccCardApplicationStatus " + i + ":" + appStatus.toString());
+ }
+ return iccCardStatus;
}
private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- IccCardStatus iccCardStatus = new IccCardStatus();
- iccCardStatus.setCardState(cardStatus.cardState);
- iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
- iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
- iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
- iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex;
- int numApplications = cardStatus.applications.size();
+ IccCardStatus iccCardStatus = convertHalCardStatus(cardStatus);
+ mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, iccCardStatus);
+ }
+ mRil.processResponseDone(rr, responseInfo, iccCardStatus);
+ }
+ }
- // limit to maximum allowed applications
- if (numApplications
- > com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
- numApplications =
- com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
- }
- iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
- for (int i = 0; i < numApplications; i++) {
- AppStatus rilAppStatus = cardStatus.applications.get(i);
- IccCardApplicationStatus appStatus = new IccCardApplicationStatus();
- appStatus.app_type = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
- appStatus.app_state = appStatus.AppStateFromRILInt(rilAppStatus.appState);
- appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
- rilAppStatus.persoSubstate);
- appStatus.aid = rilAppStatus.aidPtr;
- appStatus.app_label = rilAppStatus.appLabelPtr;
- appStatus.pin1_replaced = rilAppStatus.pin1Replaced;
- appStatus.pin1 = appStatus.PinStateFromRILInt(rilAppStatus.pin1);
- appStatus.pin2 = appStatus.PinStateFromRILInt(rilAppStatus.pin2);
- iccCardStatus.mApplications[i] = appStatus;
- }
+ private void responseIccCardStatus_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.CardStatus cardStatus) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ IccCardStatus iccCardStatus = convertHalCardStatus(cardStatus.base);
+ iccCardStatus.physicalSlotIndex = cardStatus.physicalSlotId;
+ iccCardStatus.atr = cardStatus.atr;
+ iccCardStatus.iccid = cardStatus.iccid;
mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, iccCardStatus);
@@ -1377,6 +1544,88 @@
}
}
+ private void responseCurrentCalls_1_2(RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_2.Call> calls) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ int num = calls.size();
+ ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+ DriverCall dc;
+
+ for (int i = 0; i < num; i++) {
+ dc = new DriverCall();
+ // TODO: change name of function stateFromCLCC() in DriverCall.java to name
+ // clarifying what is CLCC
+ dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).base.state));
+ dc.index = calls.get(i).base.index;
+ dc.TOA = calls.get(i).base.toa;
+ dc.isMpty = calls.get(i).base.isMpty;
+ dc.isMT = calls.get(i).base.isMT;
+ dc.als = calls.get(i).base.als;
+ dc.isVoice = calls.get(i).base.isVoice;
+ dc.isVoicePrivacy = calls.get(i).base.isVoicePrivacy;
+ dc.number = calls.get(i).base.number;
+ dc.numberPresentation =
+ DriverCall.presentationFromCLIP(
+ (int) (calls.get(i).base.numberPresentation));
+ dc.name = calls.get(i).base.name;
+ dc.namePresentation =
+ DriverCall.presentationFromCLIP((int) (calls.get(i).base.namePresentation));
+ if (calls.get(i).base.uusInfo.size() == 1) {
+ dc.uusInfo = new UUSInfo();
+ dc.uusInfo.setType(calls.get(i).base.uusInfo.get(0).uusType);
+ dc.uusInfo.setDcs(calls.get(i).base.uusInfo.get(0).uusDcs);
+ if (!TextUtils.isEmpty(calls.get(i).base.uusInfo.get(0).uusData)) {
+ byte[] userData = calls.get(i).base.uusInfo.get(0).uusData.getBytes();
+ dc.uusInfo.setUserData(userData);
+ } else {
+ mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
+ }
+
+ mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+ dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+ dc.uusInfo.getUserData().length));
+ mRil.riljLogv("Incoming UUS : data (hex): "
+ + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+ } else {
+ mRil.riljLogv("Incoming UUS : NOT present!");
+ }
+
+ // Make sure there's a leading + on addresses with a TOA of 145
+ dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+
+ dc.audioQuality = (int) (calls.get(i).audioQuality);
+
+ dcCalls.add(dc);
+
+ if (dc.isVoicePrivacy) {
+ mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
+ mRil.riljLog("InCall VoicePrivacy is enabled");
+ } else {
+ mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
+ mRil.riljLog("InCall VoicePrivacy is disabled");
+ }
+ }
+
+ Collections.sort(dcCalls);
+
+ if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
+ if (mRil.mEmergencyCallbackModeRegistrant != null) {
+ mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+ + " notify ECM Registrants");
+ mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+ }
+ }
+
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, dcCalls);
+ }
+ mRil.processResponseDone(rr, responseInfo, dcCalls);
+ }
+ }
+
+
private void responseVoid(RadioResponseInfo responseInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -1439,12 +1688,27 @@
}
}
- private void responseSignalStrength(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.SignalStrength sigStrength) {
+ private void responseSignalStrength(
+ RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_0.SignalStrength signalStrength) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- SignalStrength ret = RIL.convertHalSignalStrength(sigStrength);
+ SignalStrength ret = RIL.convertHalSignalStrength(signalStrength);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone(rr, responseInfo, ret);
+ }
+ }
+
+ private void responseSignalStrength_1_2(
+ RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.SignalStrength signalStrength) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ SignalStrength ret = RIL.convertHalSignalStrength_1_2(signalStrength);
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, ret);
}
@@ -1469,11 +1733,10 @@
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- DataCallResponse ret = RIL.convertDataCallResult(setupDataCallResult);
if (responseInfo.error == RadioError.NONE) {
- sendMessageResponse(rr.mResult, ret);
+ sendMessageResponse(rr.mResult, setupDataCallResult);
}
- mRil.processResponseDone(rr, responseInfo, ret);
+ mRil.processResponseDone(rr, responseInfo, setupDataCallResult);
}
}
@@ -1564,14 +1827,10 @@
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- ArrayList<DataCallResponse> dcResponseList = new ArrayList<>();
- for (SetupDataCallResult dcResult : dataCallResultList) {
- dcResponseList.add(RIL.convertDataCallResult(dcResult));
- }
if (responseInfo.error == RadioError.NONE) {
- sendMessageResponse(rr.mResult, dcResponseList);
+ sendMessageResponse(rr.mResult, dataCallResultList);
}
- mRil.processResponseDone(rr, responseInfo, dcResponseList);
+ mRil.processResponseDone(rr, responseInfo, dataCallResultList);
}
}
@@ -1684,6 +1943,20 @@
}
}
+ private void responseCellInfoList_1_2(
+ RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_2.CellInfo> cellInfo) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList_1_2(cellInfo);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone(rr, responseInfo, ret);
+ }
+ }
+
private void responseActivityData(RadioResponseInfo responseInfo,
ActivityStatsInfo activityInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -1773,7 +2046,7 @@
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- ArrayList<Integer> ret = RIL.convertHalLceData(lceInfo, mRil);
+ LinkCapacityEstimate ret = RIL.convertHalLceData(lceInfo, mRil);
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, ret);
}
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index d582af0..3745182 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -28,6 +28,7 @@
import android.util.SparseIntArray;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* This class loads configuration from CarrierConfig and uses it to determine
@@ -48,6 +49,30 @@
private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>();
private final Phone mPhone;
+ private boolean mVoiceRatchetEnabled = true;
+ private boolean mDataRatchetEnabled = true;
+
+ /**
+ * Updates the ServiceState with a new set of cell bandwidths IFF the new bandwidth list has a
+ * higher aggregate bandwidth.
+ *
+ * @return Whether the bandwidths were updated.
+ */
+ public static boolean updateBandwidths(int[] bandwidths, ServiceState serviceState) {
+ if (bandwidths == null) {
+ return false;
+ }
+
+ int ssAggregateBandwidth = Arrays.stream(serviceState.getCellBandwidths()).sum();
+ int newAggregateBandwidth = Arrays.stream(bandwidths).sum();
+
+ if (newAggregateBandwidth > ssAggregateBandwidth) {
+ serviceState.setCellBandwidths(bandwidths);
+ return true;
+ }
+
+ return false;
+ }
/** Constructor */
public RatRatcheter(Phone phone) {
@@ -60,7 +85,7 @@
resetRatFamilyMap();
}
- public int ratchetRat(int oldRat, int newRat) {
+ private int ratchetRat(int oldRat, int newRat) {
synchronized (mRatFamilyMap) {
final SparseIntArray oldFamily = mRatFamilyMap.get(oldRat);
if (oldFamily == null) return newRat;
@@ -75,19 +100,52 @@
}
}
- public void ratchetRat(ServiceState oldSS, ServiceState newSS) {
- int newVoiceRat = ratchetRat(oldSS.getRilVoiceRadioTechnology(),
- newSS.getRilVoiceRadioTechnology());
- int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
- newSS.getRilDataRadioTechnology());
- boolean newUsingCA = oldSS.isUsingCarrierAggregation() ||
- newSS.isUsingCarrierAggregation();
+ /** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
+ public void ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange) {
+ if (!locationChange && isSameRatFamily(oldSS, newSS)) {
+ updateBandwidths(oldSS.getCellBandwidths(), newSS);
+ }
+ // temporarily disable rat ratchet on location change.
+ if (locationChange) {
+ mVoiceRatchetEnabled = false;
+ mDataRatchetEnabled = false;
+ return;
+ }
+ if (mVoiceRatchetEnabled) {
+ int newVoiceRat = ratchetRat(oldSS.getRilVoiceRadioTechnology(),
+ newSS.getRilVoiceRadioTechnology());
+ newSS.setRilVoiceRadioTechnology(newVoiceRat);
+ } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+ // resume rat ratchet on following rat change within the same location
+ mVoiceRatchetEnabled = true;
+ }
- newSS.setRilVoiceRadioTechnology(newVoiceRat);
- newSS.setRilDataRadioTechnology(newDataRat);
+ if (mDataRatchetEnabled) {
+ int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
+ newSS.getRilDataRadioTechnology());
+ newSS.setRilDataRadioTechnology(newDataRat);
+ } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+ // resume rat ratchet on following rat change within the same location
+ mVoiceRatchetEnabled = true;
+ }
+
+ boolean newUsingCA = oldSS.isUsingCarrierAggregation()
+ || newSS.isUsingCarrierAggregation()
+ || newSS.getCellBandwidths().length > 1;
newSS.setIsUsingCarrierAggregation(newUsingCA);
}
+ private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
+ synchronized (mRatFamilyMap) {
+ // Either the two technologies are the same or their families must be non-null
+ // and the same.
+ if (ss1.getRilDataRadioTechnology() == ss2.getRilDataRadioTechnology()) return true;
+ if (mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) == null) return false;
+ return mRatFamilyMap.get(ss1.getRilDataRadioTechnology())
+ == mRatFamilyMap.get(ss2.getRilDataRadioTechnology());
+ }
+ }
+
private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 23c3498..0b4bc3c 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -22,11 +22,10 @@
import android.os.SystemProperties;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.Pair;
-import com.android.internal.telephony.dataconnection.ApnSetting;
-
import java.util.ArrayList;
import java.util.Random;
@@ -506,7 +505,9 @@
if (++index == mWaitingApns.size()) index = 0;
// Stop if we find the non-failed APN.
- if (mWaitingApns.get(index).permanentFailed == false) break;
+ if (!mWaitingApns.get(index).getPermanentFailed()) {
+ break;
+ }
// If we've already cycled through all the APNs, that means there is no APN we can try
if (index == mCurrentApnIndex) return null;
@@ -553,7 +554,9 @@
if (++index >= mWaitingApns.size()) index = 0;
// Stop if we find the non-failed APN.
- if (mWaitingApns.get(index).permanentFailed == false) break;
+ if (!mWaitingApns.get(index).getPermanentFailed()) {
+ break;
+ }
// If we've already cycled through all the APNs, that means all APNs have
// permanently failed
@@ -594,7 +597,7 @@
* */
public void markApnPermanentFailed(ApnSetting apn) {
if (apn != null) {
- apn.permanentFailed = true;
+ apn.setPermanentFailed(true);
}
}
@@ -627,7 +630,7 @@
configureRetry();
for (ApnSetting apn : mWaitingApns) {
- apn.permanentFailed = false;
+ apn.setPermanentFailed(false);
}
log("Setting " + mWaitingApns.size() + " waiting APNs.");
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 2ec5101..1e5afc7 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony;
import static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
+import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
+import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
@@ -61,6 +63,7 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.Html;
import android.text.Spanned;
@@ -77,6 +80,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
@@ -92,6 +96,13 @@
static final String TAG = "SMSDispatcher"; // accessed from inner class
static final boolean DBG = false;
private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
+ protected static final String MAP_KEY_PDU = "pdu";
+ protected static final String MAP_KEY_SMSC = "smsc";
+ protected static final String MAP_KEY_DEST_ADDR = "destAddr";
+ protected static final String MAP_KEY_SC_ADDR = "scAddr";
+ protected static final String MAP_KEY_DEST_PORT = "destPort";
+ protected static final String MAP_KEY_DATA = "data";
+ protected static final String MAP_KEY_TEXT = "text";
private static final int PREMIUM_RULE_USE_SIM = 1;
private static final int PREMIUM_RULE_USE_NETWORK = 2;
@@ -123,18 +134,11 @@
/** Handle status report from {@code CdmaInboundSmsHandler}. */
protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
- /** Radio is ON */
- protected static final int EVENT_RADIO_ON = 11;
-
- /** IMS registration/SMS format changed */
- protected static final int EVENT_IMS_STATE_CHANGED = 12;
-
- /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
- protected static final int EVENT_IMS_STATE_DONE = 13;
-
// 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;
+
protected Phone mPhone;
protected final Context mContext;
@@ -159,10 +163,7 @@
*/
private static int sConcatenatedRef = new Random().nextInt(256);
- /** Outgoing message counter. Shared by all dispatchers. */
- private SmsUsageMonitor mUsageMonitor;
-
- private ImsSMSDispatcher mImsSMSDispatcher;
+ protected SmsDispatchersController mSmsDispatchersController;
/** Number of outgoing SmsTrackers waiting for user confirmation. */
private int mPendingTrackerCount;
@@ -179,16 +180,13 @@
/**
* Create a new SMS dispatcher.
* @param phone the Phone to use
- * @param usageMonitor the SmsUsageMonitor to use
*/
- protected SMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
- ImsSMSDispatcher imsSMSDispatcher) {
+ protected SMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
mPhone = phone;
- mImsSMSDispatcher = imsSMSDispatcher;
+ mSmsDispatchersController = smsDispatchersController;
mContext = phone.getContext();
mResolver = mContext.getContentResolver();
mCi = phone.mCi;
- mUsageMonitor = usageMonitor;
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext);
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
@@ -224,7 +222,6 @@
protected void updatePhoneObject(Phone phone) {
mPhone = phone;
- mUsageMonitor = phone.mSmsUsageMonitor;
Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
}
@@ -392,7 +389,7 @@
@Override
protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
HashMap<String, Object> map = mTracker.getData();
- String text = (String) map.get("text");
+ String text = (String) map.get(MAP_KEY_TEXT);
if (text != null) {
try {
@@ -424,8 +421,8 @@
@Override
protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
HashMap<String, Object> map = mTracker.getData();
- byte[] data = (byte[]) map.get("data");
- int destPort = (int) map.get("destPort");
+ byte[] data = (byte[]) map.get(MAP_KEY_DATA);
+ int destPort = (int) map.get(MAP_KEY_DEST_PORT);
if (data != null) {
try {
@@ -630,7 +627,19 @@
/**
* Send an SMS PDU. Usually just calls {@link sendRawPdu}.
*/
- protected abstract void sendSubmitPdu(SmsTracker tracker);
+ private void sendSubmitPdu(SmsTracker tracker) {
+ if (shouldBlockSmsForEcbm()) {
+ Rlog.d(TAG, "Block SMS in Emergency Callback mode");
+ tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
+ } else {
+ sendRawPdu(tracker);
+ }
+ }
+
+ /**
+ * @return true if MO SMS should be blocked for Emergency Callback Mode.
+ */
+ protected abstract boolean shouldBlockSmsForEcbm();
/**
* Called when SMS send completes. Broadcasts a sentIntent on success.
@@ -727,7 +736,9 @@
} else {
sentIntent.send(RESULT_ERROR_NO_SERVICE);
}
- } catch (CanceledException ex) {}
+ } catch (CanceledException ex) {
+ Rlog.e(TAG, "Failed to send result");
+ }
}
}
@@ -768,8 +779,25 @@
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/
- protected abstract void sendData(String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
+ protected void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
+ scAddr, destAddr, destPort, data, (deliveryIntent != null));
+ if (pdu != null) {
+ HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
+ SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+ null /*messageUri*/, false /*expectMore*/,
+ null /*fullMessageText*/, false /*isText*/,
+ true /*persistMessage*/);
+
+ if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) {
+ sendSubmitPdu(tracker);
+ }
+ } else {
+ Rlog.e(TAG, "SMSDispatcher.sendData(): getSubmitPdu() returned null");
+ triggerSentIntentForFailure(sentIntent);
+ }
+ }
/**
* Send a text based SMS.
@@ -797,22 +825,80 @@
* @param callingPkg the calling package name
* @param persistMessage whether to save the sent message into SMS DB for a
* non-default SMS app.
- */
- protected abstract void sendText(String destAddr, String scAddr, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
- String callingPkg, boolean persistMessage);
-
- /**
- * Inject an SMS PDU into the android platform.
*
- * @param pdu is the byte array of pdu to be injected into android telephony layer
- * @param format is the format of SMS pdu (3gpp or 3gpp2)
- * @param receivedIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully received by the
- * android telephony layer. This intent is broadcasted at
- * the same time an SMS received from radio is responded back.
+ * @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.
*/
- protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent);
+ 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) {
+ Rlog.d(TAG, "sendText");
+ SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
+ scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod);
+ if (pdu != null) {
+ HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
+ SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+ messageUri, expectMore, text, true /*isText*/,
+ persistMessage, priority, validityPeriod);
+
+ if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
+ sendSubmitPdu(tracker);
+ }
+ } else {
+ Rlog.e(TAG, "SmsDispatcher.sendText(): getSubmitPdu() returned null");
+ triggerSentIntentForFailure(sentIntent);
+ }
+ }
+
+ private void triggerSentIntentForFailure(PendingIntent sentIntent) {
+ if (sentIntent != null) {
+ try {
+ sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (CanceledException ex) {
+ Rlog.e(TAG, "Intent has been canceled!");
+ }
+ }
+ }
+
+ private boolean sendSmsByCarrierApp(boolean isDataSms, SmsTracker tracker ) {
+ String carrierPackage = getCarrierAppPackageName();
+ if (carrierPackage != null) {
+ Rlog.d(TAG, "Found carrier package.");
+ SmsSender smsSender;
+ if (isDataSms) {
+ smsSender = new DataSmsSender(tracker);
+ } else {
+ smsSender = new TextSmsSender(tracker);
+ }
+ smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+ return true;
+ }
+
+ return false;
+ }
+
+ protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader,
+ int priority, int validityPeriod);
+
+ protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested);
/**
* Calculate the number of septets needed to encode the message. This function should only be
@@ -852,11 +938,28 @@
* @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.
*/
protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
- boolean persistMessage) {
+ boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
final String fullMessageText = getMultipartMessageText(parts);
int refNumber = getNextConcatenatedRef() & 0x00FF;
int msgCount = parts.size();
@@ -913,7 +1016,8 @@
trackers[i] =
getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding,
sentIntent, deliveryIntent, (i == (msgCount - 1)),
- unsentPartCount, anyPartFailed, messageUri, fullMessageText);
+ unsentPartCount, anyPartFailed, messageUri,
+ fullMessageText, priority, expectMore, validityPeriod);
trackers[i].mPersistMessage = persistMessage;
}
@@ -927,7 +1031,8 @@
if (carrierPackage != null) {
Rlog.d(TAG, "Found carrier package.");
MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
- smsSender.sendSmsByCarrierApp(carrierPackage, new MultipartSmsSenderCallback(smsSender));
+ smsSender.sendSmsByCarrierApp(carrierPackage,
+ new MultipartSmsSenderCallback(smsSender));
} else {
Rlog.v(TAG, "No carrier package.");
for (SmsTracker tracker : trackers) {
@@ -943,11 +1048,57 @@
/**
* Create a new SubmitPdu and return the SMS tracker.
*/
- protected abstract SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
+ private SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
String message, SmsHeader smsHeader, int encoding,
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
- String fullMessageText);
+ String fullMessageText, int priority, boolean expectMore, int validityPeriod) {
+ if (isCdmaMo()) {
+ UserData uData = new UserData();
+ uData.payloadStr = message;
+ uData.userDataHeader = smsHeader;
+ if (encoding == SmsConstants.ENCODING_7BIT) {
+ uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+ } else { // assume UTF-16
+ uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+ }
+ uData.msgEncodingSet = true;
+
+ /* By setting the statusReportRequested bit only for the
+ * last message fragment, this will result in only one
+ * callback to the sender when that last fragment delivery
+ * has been acknowledged. */
+ //TODO FIX
+ SmsMessageBase.SubmitPduBase submitPdu =
+ com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(destinationAddress,
+ uData, (deliveryIntent != null) && lastPart, priority);
+
+ HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
+ message, submitPdu);
+ return getSmsTracker(map, sentIntent, deliveryIntent,
+ getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
+ (!lastPart || expectMore), fullMessageText, true /*isText*/,
+ true /*persistMessage*/, priority, validityPeriod);
+
+ } else {
+ SmsMessageBase.SubmitPduBase pdu =
+ com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, deliveryIntent != null,
+ SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable,
+ smsHeader.languageShiftTable, validityPeriod);
+ if (pdu != null) {
+ HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
+ message, pdu);
+ return getSmsTracker(map, sentIntent,
+ deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
+ smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/,
+ false /*persistMessage*/, priority, validityPeriod);
+ } else {
+ Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
+ return null;
+ }
+ }
+ }
/**
* Send an SMS
@@ -974,7 +1125,7 @@
@VisibleForTesting
public void sendRawPdu(SmsTracker tracker) {
HashMap map = tracker.getData();
- byte pdu[] = (byte[]) map.get("pdu");
+ byte pdu[] = (byte[]) map.get(MAP_KEY_PDU);
if (mSmsSendDisabled) {
Rlog.e(TAG, "Device does not support sending sms.");
@@ -1016,7 +1167,8 @@
// handler with the SmsTracker to request user confirmation before sending.
if (checkDestination(tracker)) {
// check for excessive outgoing SMS usage by this app
- if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
+ if (!mSmsDispatchersController.getUsageMonitor().check(
+ appInfo.packageName, SINGLE_PART_SMS)) {
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
return;
}
@@ -1050,7 +1202,8 @@
simCountryIso = mTelephonyManager.getNetworkCountryIso();
}
- smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
+ smsCategory = mSmsDispatchersController.getUsageMonitor().checkDestination(
+ tracker.mDestAddress, simCountryIso);
}
if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
@@ -1060,7 +1213,8 @@
}
smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
- mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
+ mSmsDispatchersController.getUsageMonitor().checkDestination(
+ tracker.mDestAddress, networkCountryIso));
}
if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
@@ -1076,7 +1230,8 @@
}
// Wait for user confirmation unless the user has set permission to always allow/deny
- int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
+ int premiumSmsPermission =
+ mSmsDispatchersController.getUsageMonitor().getPremiumSmsPermission(
tracker.getAppPackageName());
if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
// First time trying to send to premium SMS.
@@ -1233,32 +1388,6 @@
}
/**
- * Returns the premium SMS permission for the specified package. If the package has never
- * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
- * will be returned.
- * @param packageName the name of the package to query permission
- * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
- * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
- * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
- * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
- */
- public int getPremiumSmsPermission(String packageName) {
- return mUsageMonitor.getPremiumSmsPermission(packageName);
- }
-
- /**
- * Sets the premium SMS permission for the specified package and save the value asynchronously
- * to persistent storage.
- * @param packageName the name of the package to set permission
- * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
- * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
- * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
- */
- public void setPremiumSmsPermission(String packageName, int permission) {
- mUsageMonitor.setPremiumSmsPermission(packageName, permission);
- }
-
- /**
* Send the message along to the radio.
*
* @param tracker holds the SMS message to send
@@ -1266,23 +1395,16 @@
protected abstract void sendSms(SmsTracker tracker);
/**
- * Send the SMS via the PSTN network.
- *
- * @param tracker holds the Sms tracker ready to be sent
- */
- protected abstract void sendSmsByPstn(SmsTracker tracker);
-
- /**
* Retry the message along to the radio.
*
* @param tracker holds the SMS message to send
*/
public void sendRetrySms(SmsTracker tracker) {
- // re-routing to ImsSMSDispatcher
- if (mImsSMSDispatcher != null) {
- mImsSMSDispatcher.sendRetrySms(tracker);
+ // re-routing to SmsDispatchersController
+ if (mSmsDispatchersController != null) {
+ mSmsDispatchersController.sendRetrySms(tracker);
} else {
- Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
+ Rlog.e(TAG, mSmsDispatchersController + " is null. Retry failed");
}
}
@@ -1320,7 +1442,8 @@
}
sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
- null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
+ null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage, tracker.mPriority,
+ tracker.mExpectMore, tracker.mValidityPeriod);
}
/**
@@ -1331,9 +1454,17 @@
// fields need to be public for derived SmsDispatchers
private final HashMap<String, Object> mData;
public int mRetryCount;
- public int mImsRetry; // nonzero indicates initial message was sent over Ims
+ // IMS retry counter. Nonzero indicates initial message was sent over IMS channel in RIL and
+ // counts how many retries have been made on the IMS channel.
+ // Used in older implementations where the message is sent over IMS using the RIL.
+ public int mImsRetry;
+ // Tag indicating that this SMS is being handled by the ImsSmsDispatcher. This tracker
+ // should not try to use SMS over IMS over the RIL interface in this case when falling back.
+ public boolean mUsesImsServiceForIms;
public int mMessageRef;
public boolean mExpectMore;
+ public int mValidityPeriod;
+ public int mPriority;
String mFormat;
public final PendingIntent mSentIntent;
@@ -1367,8 +1498,9 @@
private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
- SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
- boolean isText, boolean persistMessage, int userId) {
+ SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId,
+ boolean isText, boolean persistMessage, int userId, int priority,
+ int validityPeriod) {
mData = data;
mSentIntent = sentIntent;
mDeliveryIntent = deliveryIntent;
@@ -1376,8 +1508,9 @@
mAppInfo = appInfo;
mDestAddress = destAddr;
mFormat = format;
- mExpectMore = isExpectMore;
+ mExpectMore = expectMore;
mImsRetry = 0;
+ mUsesImsServiceForIms = false;
mMessageRef = 0;
mUnsentPartCount = unsentPartCount;
mAnyPartFailed = anyPartFailed;
@@ -1388,6 +1521,8 @@
mIsText = isText;
mPersistMessage = persistMessage;
mUserId = userId;
+ mPriority = priority;
+ mValidityPeriod = validityPeriod;
}
/**
@@ -1603,7 +1738,8 @@
protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
- boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
+ boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
+ int priority, int validityPeriod) {
// Get calling app package name via UID from Binder call
PackageManager pm = mContext.getPackageManager();
String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
@@ -1624,38 +1760,49 @@
// and before displaying the number to the user if confirmation is required.
String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
- unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
- fullMessageText, getSubId(), isText, persistMessage, userId);
+ unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore,
+ fullMessageText, getSubId(), isText, persistMessage, userId, priority,
+ validityPeriod);
}
protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
- PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
+ PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore,
String fullMessageText, boolean isText, boolean persistMessage) {
return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
- null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
- fullMessageText, isText, persistMessage);
+ null/*anyPartFailed*/, messageUri, null/*smsHeader*/, expectMore,
+ fullMessageText, isText, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
+ }
+
+ protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore,
+ String fullMessageText, boolean isText, boolean persistMessage, int priority,
+ int validityPeriod) {
+ return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
+ null/*anyPartFailed*/, messageUri, null/*smsHeader*/, expectMore, fullMessageText,
+ isText, persistMessage, priority, validityPeriod);
}
protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
String text, SmsMessageBase.SubmitPduBase pdu) {
HashMap<String, Object> map = new HashMap<String, Object>();
- map.put("destAddr", destAddr);
- map.put("scAddr", scAddr);
- map.put("text", text);
- map.put("smsc", pdu.encodedScAddress);
- map.put("pdu", pdu.encodedMessage);
+ map.put(MAP_KEY_DEST_ADDR, destAddr);
+ map.put(MAP_KEY_SC_ADDR, scAddr);
+ map.put(MAP_KEY_TEXT, text);
+ map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
+ map.put(MAP_KEY_PDU, pdu.encodedMessage);
return map;
}
protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
HashMap<String, Object> map = new HashMap<String, Object>();
- map.put("destAddr", destAddr);
- map.put("scAddr", scAddr);
- map.put("destPort", destPort);
- map.put("data", data);
- map.put("smsc", pdu.encodedScAddress);
- map.put("pdu", pdu.encodedMessage);
+ map.put(MAP_KEY_DEST_ADDR, destAddr);
+ map.put(MAP_KEY_SC_ADDR, scAddr);
+ map.put(MAP_KEY_DEST_PORT, destPort);
+ map.put(MAP_KEY_DATA, data);
+ map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
+ map.put(MAP_KEY_PDU, pdu.encodedMessage);
return map;
}
@@ -1720,7 +1867,8 @@
}
sendMessage(msg);
}
- setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
+ mSmsDispatchersController.setPremiumSmsPermission(mTracker.getAppPackageName(),
+ newSmsPermission);
}
@Override
@@ -1755,23 +1903,14 @@
}
public boolean isIms() {
- if (mImsSMSDispatcher != null) {
- return mImsSMSDispatcher.isIms();
+ if (mSmsDispatchersController != null) {
+ return mSmsDispatchersController.isIms();
} else {
- Rlog.e(TAG, mImsSMSDispatcher + " is null");
+ Rlog.e(TAG, "mSmsDispatchersController is null");
return false;
}
}
- public String getImsSmsFormat() {
- if (mImsSMSDispatcher != null) {
- return mImsSMSDispatcher.getImsSmsFormat();
- } else {
- Rlog.e(TAG, mImsSMSDispatcher + " is null");
- return null;
- }
- }
-
private String getMultipartMessageText(ArrayList<String> parts) {
final StringBuilder sb = new StringBuilder();
for (String part : parts) {
@@ -1819,4 +1958,8 @@
throw new SecurityException("Caller is not phone or carrier app!");
}
}
+
+ protected boolean isCdmaMo() {
+ return mSmsDispatchersController.isCdmaMo();
+ }
}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 1369204..b572934 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -32,18 +32,14 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.hardware.radio.V1_0.CellInfoType;
-import android.hardware.radio.V1_0.DataRegStateResult;
-import android.hardware.radio.V1_0.RegState;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
import android.os.AsyncResult;
import android.os.BaseBundle;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Message;
import android.os.PersistableBundle;
-import android.os.PowerManager;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemClock;
@@ -52,33 +48,45 @@
import android.os.WorkSource;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.CellLocation;
+import android.telephony.DataSpecificRegistrationStates;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
+import android.telephony.VoiceSpecificRegistrationStates;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.LocalLog;
import android.util.Pair;
+import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.TimestampedValue;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriInfo;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccRecords;
@@ -94,8 +102,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
@@ -105,8 +111,8 @@
* {@hide}
*/
public class ServiceStateTracker extends Handler {
- private static final String LOG_TAG = "SST";
- private static final boolean DBG = true;
+ static final String LOG_TAG = "SST";
+ static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
private static final String PROP_FORCE_ROAMING = "telephony.test.forceRoaming";
@@ -124,6 +130,7 @@
private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
private long mLastCellInfoListTime;
private List<CellInfo> mLastCellInfoList = null;
+ private List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = null;
private SignalStrength mSignalStrength;
@@ -135,7 +142,8 @@
* and ignore stale responses. The value is a count-down of
* expected responses in this pollingContext.
*/
- private int[] mPollingContext;
+ @VisibleForTesting
+ public int[] mPollingContext;
private boolean mDesiredPowerState;
/**
@@ -156,6 +164,7 @@
private RegistrantList mNetworkDetachedRegistrants = new RegistrantList();
private RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
private RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
+ private RegistrantList mImsCapabilityChangedRegistrants = new RegistrantList();
/* Radio power off pending flag and tag counter */
private boolean mPendingRadioPowerOffAfterDataOff = false;
@@ -212,37 +221,8 @@
protected static final int EVENT_RADIO_POWER_FROM_CARRIER = 51;
protected static final int EVENT_SIM_NOT_INSERTED = 52;
protected static final int EVENT_IMS_SERVICE_STATE_CHANGED = 53;
-
- protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
-
- /**
- * List of ISO codes for countries that can have an offset of
- * GMT+0 when not in daylight savings time. This ignores some
- * small places such as the Canary Islands (Spain) and
- * Danmarkshavn (Denmark). The list must be sorted by code.
- */
- protected static final String[] GMT_COUNTRY_CODES = {
- "bf", // Burkina Faso
- "ci", // Cote d'Ivoire
- "eh", // Western Sahara
- "fo", // Faroe Islands, Denmark
- "gb", // United Kingdom of Great Britain and Northern Ireland
- "gh", // Ghana
- "gm", // Gambia
- "gn", // Guinea
- "gw", // Guinea Bissau
- "ie", // Ireland
- "lr", // Liberia
- "is", // Iceland
- "ma", // Morocco
- "ml", // Mali
- "mr", // Mauritania
- "pt", // Portugal
- "sl", // Sierra Leone
- "sn", // Senegal
- "st", // Sao Tome and Principe
- "tg", // Togo
- };
+ protected static final int EVENT_RADIO_POWER_OFF_DONE = 54;
+ protected static final int EVENT_PHYSICAL_CHANNEL_CONFIG = 55;
private class CellInfoResult {
List<CellInfo> list;
@@ -268,7 +248,9 @@
private String mCurPlmn = null;
private boolean mCurShowPlmn = false;
private boolean mCurShowSpn = false;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ @VisibleForTesting
+ public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mImsRegistered = false;
@@ -280,13 +262,14 @@
private final RatRatcheter mRatRatcheter;
+ private final HandlerThread mHandlerThread;
+ private final LocaleTracker mLocaleTracker;
+
private final LocalLog mRoamingLog = new LocalLog(10);
private final LocalLog mAttachLog = new LocalLog(10);
private final LocalLog mPhoneTypeLog = new LocalLog(10);
private final LocalLog mRatLog = new LocalLog(20);
private final LocalLog mRadioPowerLog = new LocalLog(20);
- private final LocalLog mTimeLog = new LocalLog(15);
- private final LocalLog mTimeZoneLog = new LocalLog(15);
private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
public final AtomicInteger mPreviousSubId =
@@ -301,6 +284,7 @@
if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");
// Set the network type, in case the radio does not restore it.
int subId = mPhone.getSubId();
+ ServiceStateTracker.this.mPrevSubId = mPreviousSubId.get();
if (mPreviousSubId.getAndSet(subId) != subId) {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
Context context = mPhone.getContext();
@@ -369,44 +353,13 @@
};
//Common
- private GsmCdmaPhone mPhone;
+ private final GsmCdmaPhone mPhone;
+
public CellLocation mCellLoc;
private CellLocation mNewCellLoc;
- public 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;
- /**
- * Sometimes we get the NITZ time before we know what country we
- * are in. Keep the time zone information from the NITZ string so
- * we can fix the time zone once know the country.
- */
- private boolean mNeedFixZoneAfterNitz = false;
- private int mZoneOffset;
- private boolean mZoneDst;
- private long mZoneTime;
- private boolean mGotCountryCode = false;
- private String mSavedTimeZone;
- private long mSavedTime;
- private long mSavedAtTime;
- /** Wake lock used while setting time of day. */
- private PowerManager.WakeLock mWakeLock;
- public static final String WAKELOCK_TAG = "ServiceStateTracker";
- private ContentResolver mCr;
- private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- Rlog.i(LOG_TAG, "Auto time state changed");
- revertToNitzTime();
- }
- };
-
- private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- Rlog.i(LOG_TAG, "Auto time zone state changed");
- revertToNitzTimeZone();
- }
- };
+ private static final int MS_PER_HOUR = 60 * 60 * 1000;
+ private final NitzStateMachine mNitzState;
+ private final ContentResolver mCr;
//GSM
private int mPreferredNetworkType;
@@ -436,8 +389,6 @@
* Mark when service state is in emergency call only mode
*/
private boolean mEmergencyOnly = false;
- /** Boolean is true is setTimeFromNITZString was called */
- private boolean mNitzUpdatedTime = false;
/** Started the recheck process after finding gprs should registered but not. */
private boolean mStartedGprsRegCheck;
/** Already sent the event-log for no gprs register. */
@@ -470,7 +421,7 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- updateLteEarfcnLists();
+ onCarrierConfigChanged();
return;
}
@@ -496,14 +447,6 @@
public static final String UNACTIVATED_MIN_VALUE = "1111110111";
// Current Otasp value
private int mCurrentOtaspMode = TelephonyManager.OTASP_UNINITIALIZED;
- /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
- public static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
- private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
- NITZ_UPDATE_SPACING_DEFAULT);
- /** If mNitzUpdateSpacing hasn't been exceeded but update is > mNitzUpdate do the update */
- public static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
- private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
- NITZ_UPDATE_DIFF_DEFAULT);
private int mRoamingIndicator;
private boolean mIsInPrl;
private int mDefaultRoamingIndicator;
@@ -528,6 +471,9 @@
private String mRegistrationDeniedReason;
private String mCurrentCarrier = null;
+ private final TransportManager mTransportManager;
+ private final SparseArray<NetworkRegistrationManager> mRegStateManagers = new SparseArray<>();
+
/* list of LTE EARFCNs (E-UTRA Absolute Radio Frequency Channel Number,
* Reference: 3GPP TS 36.104 5.4.3)
* inclusive ranges for which the lte rsrp boost is applied */
@@ -539,6 +485,7 @@
private static final int INVALID_LTE_EARFCN = -1;
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
+ mNitzState = TelephonyComponentFactory.getInstance().makeNitzStateMachine(phone);
mPhone = phone;
mCi = ci;
@@ -550,6 +497,7 @@
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);
+ mCi.registerForPhysicalChannelConfiguration(this, EVENT_PHYSICAL_CHANNEL_CONFIG, null);
mSubscriptionController = SubscriptionController.getInstance();
mSubscriptionManager = SubscriptionManager.from(phone.getContext());
@@ -557,14 +505,24 @@
.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
mRestrictedState = new RestrictedState();
+ mTransportManager = new TransportManager();
+
+ for (int transportType : mTransportManager.getAvailableTransports()) {
+ mRegStateManagers.append(transportType, new NetworkRegistrationManager(
+ transportType, phone));
+ mRegStateManagers.get(transportType).registerForNetworkRegistrationStateChanged(
+ this, EVENT_NETWORK_STATE_CHANGED, null);
+ }
+
+ // Create a new handler thread dedicated for locale tracker because the blocking
+ // getAllCellInfo call requires clients calling from a different thread.
+ mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName());
+ mHandlerThread.start();
+ mLocaleTracker = TelephonyComponentFactory.getInstance().makeLocaleTracker(
+ mPhone, mHandlerThread.getLooper());
+
mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
-
- PowerManager powerManager =
- (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
-
mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
- mCi.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
mCr = phone.getContext().getContentResolver();
@@ -577,12 +535,6 @@
enableCellularOnBoot);
- mCr.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
- mAutoTimeObserver);
- mCr.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
- mAutoTimeZoneObserver);
setSignalStrengthDefaultValues();
mPhone.getCarrierActionAgent().registerForCarrierAction(CARRIER_ACTION_SET_RADIO_ENABLED,
this, EVENT_RADIO_POWER_FROM_CARRIER, null, false);
@@ -614,6 +566,8 @@
CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
registerForDataConnectionDetached(mCSST,
CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
+ registerForImsCapabilityChanged(mCSST,
+ CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
}
@VisibleForTesting
@@ -631,11 +585,17 @@
}
// If we are previously in service, we need to notify that we are out of service now.
+ if (mSS != null && mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+ mNetworkDetachedRegistrants.notifyRegistrants();
+ }
+
+ // If we are previously in service, we need to notify that we are out of service now.
if (mSS != null && mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
mDetachedRegistrants.notifyRegistrants();
}
mSS = new ServiceState();
+ mSS.setStateOutOfService();
mNewSS = new ServiceState();
mLastCellInfoListTime = 0;
mLastCellInfoList = null;
@@ -646,7 +606,7 @@
mMin = null;
mPrlVersion = null;
mIsMinInfoReady = false;
- mNitzUpdatedTime = false;
+ mNitzState.handleNetworkUnavailable();
//cancel any pending pollstate request on voice tech switching
cancelPollState();
@@ -665,9 +625,7 @@
mCellLoc = new GsmCellLocation();
mNewCellLoc = new GsmCellLocation();
} else {
- if (mPhone.isPhoneTypeCdmaLte()) {
- mPhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
- }
+ mPhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
mCellLoc = new CdmaCellLocation();
mNewCellLoc = new CdmaCellLocation();
mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(mPhone.getContext(), mCi, this,
@@ -715,11 +673,17 @@
mCi.unSetOnSignalStrengthUpdate(this);
mUiccController.unregisterForIccChanged(this);
mCi.unregisterForCellInfoList(this);
+ mCi.unregisterForPhysicalChannelConfiguration(this);
mSubscriptionManager
.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+ mHandlerThread.quit();
mCi.unregisterForImsNetworkStateChanged(this);
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CARRIER_ACTION_SET_RADIO_ENABLED);
+ if (mCSST != null) {
+ mCSST.dispose();
+ mCSST = null;
+ }
}
public boolean getDesiredPowerState() {
@@ -942,55 +906,37 @@
}
}
- private void processCellLocationInfo(CellLocation cellLocation,
- VoiceRegStateResult voiceRegStateResult) {
+ private void processCellLocationInfo(CellLocation cellLocation, CellIdentity cellIdentity) {
if (mPhone.isPhoneTypeGsm()) {
int psc = -1;
int cid = -1;
int lac = -1;
- switch(voiceRegStateResult.cellIdentity.cellInfoType) {
- case CellInfoType.GSM: {
- if (voiceRegStateResult.cellIdentity.cellIdentityGsm.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityGsm cellIdentityGsm =
- voiceRegStateResult.cellIdentity.cellIdentityGsm.get(0);
- cid = cellIdentityGsm.cid;
- lac = cellIdentityGsm.lac;
+ if (cellIdentity != null) {
+ switch (cellIdentity.getType()) {
+ case CellInfoType.GSM: {
+ cid = ((CellIdentityGsm) cellIdentity).getCid();
+ lac = ((CellIdentityGsm) cellIdentity).getLac();
+ break;
}
- break;
- }
- case CellInfoType.WCDMA: {
- if (voiceRegStateResult.cellIdentity.cellIdentityWcdma.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityWcdma cellIdentityWcdma =
- voiceRegStateResult.cellIdentity.cellIdentityWcdma.get(0);
- cid = cellIdentityWcdma.cid;
- lac = cellIdentityWcdma.lac;
- psc = cellIdentityWcdma.psc;
+ case CellInfoType.WCDMA: {
+ cid = ((CellIdentityWcdma) cellIdentity).getCid();
+ lac = ((CellIdentityWcdma) cellIdentity).getLac();
+ psc = ((CellIdentityWcdma) cellIdentity).getPsc();
+ break;
}
- break;
- }
- case CellInfoType.TD_SCDMA: {
- if (voiceRegStateResult.cellIdentity.cellIdentityTdscdma.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityTdscdma
- cellIdentityTdscdma =
- voiceRegStateResult.cellIdentity.cellIdentityTdscdma.get(0);
- cid = cellIdentityTdscdma.cid;
- lac = cellIdentityTdscdma.lac;
+ case CellInfoType.TD_SCDMA: {
+ cid = ((CellIdentityTdscdma) cellIdentity).getCid();
+ lac = ((CellIdentityTdscdma) cellIdentity).getLac();
+ break;
}
- break;
- }
- case CellInfoType.LTE: {
- if (voiceRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
- voiceRegStateResult.cellIdentity.cellIdentityLte.get(0);
- cid = cellIdentityLte.ci;
-
- /* Continuing the historical behaviour of using tac as lac. */
- lac = cellIdentityLte.tac;
+ case CellInfoType.LTE: {
+ cid = ((CellIdentityLte) cellIdentity).getCi();
+ lac = ((CellIdentityLte) cellIdentity).getTac();
+ break;
}
- break;
- }
- default: {
- break;
+ default: {
+ break;
+ }
}
}
// LAC and CID are -1 if not avail
@@ -1003,21 +949,19 @@
int systemId = 0;
int networkId = 0;
- switch(voiceRegStateResult.cellIdentity.cellInfoType) {
- case CellInfoType.CDMA: {
- if (voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
- voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
- baseStationId = cellIdentityCdma.baseStationId;
- baseStationLatitude = cellIdentityCdma.latitude;
- baseStationLongitude = cellIdentityCdma.longitude;
- systemId = cellIdentityCdma.systemId;
- networkId = cellIdentityCdma.networkId;
+ if (cellIdentity != null) {
+ switch (cellIdentity.getType()) {
+ case CellInfoType.CDMA: {
+ baseStationId = ((CellIdentityCdma) cellIdentity).getBasestationId();
+ baseStationLatitude = ((CellIdentityCdma) cellIdentity).getLatitude();
+ baseStationLongitude = ((CellIdentityCdma) cellIdentity).getLongitude();
+ systemId = ((CellIdentityCdma) cellIdentity).getSystemId();
+ networkId = ((CellIdentityCdma) cellIdentity).getNetworkId();
+ break;
}
- break;
- }
- default: {
- break;
+ default: {
+ break;
+ }
}
}
@@ -1033,19 +977,17 @@
}
}
- private int getLteEarfcn(DataRegStateResult dataRegStateResult) {
+ private int getLteEarfcn(CellIdentity cellIdentity) {
int lteEarfcn = INVALID_LTE_EARFCN;
- switch(dataRegStateResult.cellIdentity.cellInfoType) {
- case CellInfoType.LTE: {
- if (dataRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
- dataRegStateResult.cellIdentity.cellIdentityLte.get(0);
- lteEarfcn = cellIdentityLte.earfcn;
+ if (cellIdentity != null) {
+ switch (cellIdentity.getType()) {
+ case CellInfoType.LTE: {
+ lteEarfcn = ((CellIdentityLte) cellIdentity).getEarfcn();
+ break;
}
- break;
- }
- default: {
- break;
+ default: {
+ break;
+ }
}
}
@@ -1134,11 +1076,23 @@
}
break;
+ case EVENT_RADIO_POWER_OFF_DONE:
+ if (DBG) log("EVENT_RADIO_POWER_OFF_DONE");
+ if (mDeviceShuttingDown && mCi.getRadioState().isAvailable()) {
+ // during shutdown the modem may not send radio state changed event
+ // as a result of radio power request
+ // Hence, issuing shut down regardless of radio power response
+ mCi.requestShutdown(null);
+ }
+ break;
+
// GSM
case EVENT_SIM_READY:
// Reset the mPreviousSubId so we treat a SIM power bounce
// as a first boot. See b/19194287
- mOnSubscriptionsChangedListener.mPreviousSubId.set(-1);
+ mOnSubscriptionsChangedListener.mPreviousSubId.set(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mIsSimReady = true;
pollState();
// Signal strength polling stops when radio is off
@@ -1181,7 +1135,9 @@
case EVENT_GET_LOC_DONE:
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- processCellLocationInfo(mCellLoc, (VoiceRegStateResult) ar.result);
+ CellIdentity cellIdentity = ((NetworkRegistrationState) ar.result)
+ .getCellIdentity();
+ processCellLocationInfo(mCellLoc, cellIdentity);
mPhone.notifyLocationChanged();
}
@@ -1254,7 +1210,9 @@
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
+ mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+ .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_CS,
+ obtainMessage(EVENT_GET_LOC_DONE, null));
}
break;
@@ -1318,8 +1276,11 @@
break;
case EVENT_SIM_NOT_INSERTED:
- if (DBG) log("EVENT_SIM_NOT_INSERTED, cancelling notifications.");
+ if (DBG) log("EVENT_SIM_NOT_INSERTED");
cancelAllNotifications();
+ mMdn = null;
+ mMin = null;
+ mIsMinInfoReady = false;
break;
case EVENT_ALL_DATA_DISCONNECTED:
@@ -1345,6 +1306,7 @@
case EVENT_IMS_CAPABILITY_CHANGED:
if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED");
updateSpnDisplay();
+ mImsCapabilityChangedRegistrants.notifyRegistrants();
break;
case EVENT_IMS_SERVICE_STATE_CHANGED:
@@ -1493,12 +1455,37 @@
}
break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ List<PhysicalChannelConfig> list = (List<PhysicalChannelConfig>) ar.result;
+ if (VDBG) {
+ log("EVENT_PHYSICAL_CHANNEL_CONFIG: size=" + list.size() + " list="
+ + list);
+ }
+ mPhone.notifyPhysicalChannelConfiguration(list);
+ mLastPhysicalChannelConfigList = list;
+
+ // only notify if bandwidths changed
+ if (RatRatcheter.updateBandwidths(getBandwidthsFromConfigs(list), mSS)) {
+ mPhone.notifyServiceStateChanged(mSS);
+ }
+ }
+ break;
+
default:
log("Unhandled message with number: " + msg.what);
break;
}
}
+ private int[] getBandwidthsFromConfigs(List<PhysicalChannelConfig> list) {
+ return list.stream()
+ .map(PhysicalChannelConfig::getCellBandwidthDownlink)
+ .mapToInt(Integer::intValue)
+ .toArray();
+ }
+
protected boolean isSidsAllZeros() {
if (mHomeSystemId != null) {
for (int i=0; i < mHomeSystemId.length; i++) {
@@ -1650,6 +1637,10 @@
if (ar.exception != null) {
CommandException.Error err=null;
+ if (ar.exception instanceof IllegalStateException) {
+ log("handlePollStateResult exception " + ar.exception);
+ }
+
if (ar.exception instanceof CommandException) {
err = ((CommandException)(ar.exception)).getCommandError();
}
@@ -1673,12 +1664,12 @@
mPollingContext[0]--;
if (mPollingContext[0] == 0) {
+ mNewSS.setEmergencyOnly(mEmergencyOnly);
if (mPhone.isPhoneTypeGsm()) {
updateRoamingState();
- mNewSS.setEmergencyOnly(mEmergencyOnly);
} else {
boolean namMatch = false;
- if (!isSidsAllZeros() && isHomeSid(mNewSS.getSystemId())) {
+ if (!isSidsAllZeros() && isHomeSid(mNewSS.getCdmaSystemId())) {
namMatch = true;
}
@@ -1796,47 +1787,28 @@
return cdmaRoaming && !isSameOperatorNameFromSimAndSS(s);
}
- private int getRegStateFromHalRegState(int regState) {
- switch (regState) {
- case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
- return ServiceState.RIL_REG_STATE_NOT_REG;
- case RegState.REG_HOME:
- return ServiceState.RIL_REG_STATE_HOME;
- case RegState.NOT_REG_MT_SEARCHING_OP:
- return ServiceState.RIL_REG_STATE_SEARCHING;
- case RegState.REG_DENIED:
- return ServiceState.RIL_REG_STATE_DENIED;
- case RegState.UNKNOWN:
- return ServiceState.RIL_REG_STATE_UNKNOWN;
- case RegState.REG_ROAMING:
- return ServiceState.RIL_REG_STATE_ROAMING;
- case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
- return ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED;
- case RegState.NOT_REG_MT_SEARCHING_OP_EM:
- return ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED;
- case RegState.REG_DENIED_EM:
- return ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED;
- case RegState.UNKNOWN_EM:
- return ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED;
- default:
- return ServiceState.REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING;
- }
- }
-
void handlePollStateResultMessage(int what, AsyncResult ar) {
int ints[];
switch (what) {
case EVENT_POLL_STATE_REGISTRATION: {
- VoiceRegStateResult voiceRegStateResult = (VoiceRegStateResult) ar.result;
- int registrationState = getRegStateFromHalRegState(voiceRegStateResult.regState);
- int cssIndicator = voiceRegStateResult.cssSupported ? 1 : 0;
+ NetworkRegistrationState networkRegState = (NetworkRegistrationState) ar.result;
+ VoiceSpecificRegistrationStates voiceSpecificStates =
+ networkRegState.getVoiceSpecificStates();
+
+ int registrationState = networkRegState.getRegState();
+ int cssIndicator = voiceSpecificStates.cssSupported ? 1 : 0;
+ int newVoiceRat = ServiceState.networkTypeToRilRadioTechnology(
+ networkRegState.getAccessNetworkTechnology());
mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
mNewSS.setCssIndicator(cssIndicator);
- mNewSS.setRilVoiceRadioTechnology(voiceRegStateResult.rat);
+ mNewSS.setRilVoiceRadioTechnology(newVoiceRat);
+ mNewSS.addNetworkRegistrationState(networkRegState);
+ setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
//Denial reason if registrationState = 3
- int reasonForDenial = voiceRegStateResult.reasonForDenial;
+ int reasonForDenial = networkRegState.getRejectCause();
+ mEmergencyOnly = networkRegState.isEmergencyEnabled();
if (mPhone.isPhoneTypeGsm()) {
mGsmRoaming = regCodeIsRoaming(registrationState);
@@ -1844,27 +1816,14 @@
boolean isVoiceCapable = mPhone.getContext().getResources()
.getBoolean(com.android.internal.R.bool.config_voice_capable);
- if (((registrationState
- == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED)
- || (registrationState
- == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED)
- || (registrationState
- == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED)
- || (registrationState
- == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED))
- && isVoiceCapable) {
- mEmergencyOnly = true;
- } else {
- mEmergencyOnly = false;
- }
} else {
- int roamingIndicator = voiceRegStateResult.roamingIndicator;
+ int roamingIndicator = voiceSpecificStates.roamingIndicator;
//Indicates if current system is in PR
- int systemIsInPrl = voiceRegStateResult.systemIsInPrl;
+ int systemIsInPrl = voiceSpecificStates.systemIsInPrl;
//Is default roaming indicator from PRL
- int defaultRoamingIndicator = voiceRegStateResult.defaultRoamingIndicator;
+ int defaultRoamingIndicator = voiceSpecificStates.defaultRoamingIndicator;
mRegistrationState = registrationState;
// When registration state is roaming and TSB58
@@ -1881,14 +1840,12 @@
int systemId = 0;
int networkId = 0;
- if (voiceRegStateResult.cellIdentity.cellInfoType == CellInfoType.CDMA
- && voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
- android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
- voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
- systemId = cellIdentityCdma.systemId;
- networkId = cellIdentityCdma.networkId;
+ CellIdentity cellIdentity = networkRegState.getCellIdentity();
+ if (cellIdentity != null && cellIdentity.getType() == CellInfoType.CDMA) {
+ systemId = ((CellIdentityCdma) cellIdentity).getSystemId();
+ networkId = ((CellIdentityCdma) cellIdentity).getNetworkId();
}
- mNewSS.setSystemAndNetworkId(systemId, networkId);
+ mNewSS.setCdmaSystemAndNetworkId(systemId, networkId);
if (reasonForDenial == 0) {
mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
@@ -1903,48 +1860,60 @@
}
}
- processCellLocationInfo(mNewCellLoc, voiceRegStateResult);
+ processCellLocationInfo(mNewCellLoc, networkRegState.getCellIdentity());
if (DBG) {
log("handlPollVoiceRegResultMessage: regState=" + registrationState
- + " radioTechnology=" + voiceRegStateResult.rat);
+ + " radioTechnology=" + newVoiceRat);
}
break;
}
case EVENT_POLL_STATE_GPRS: {
- DataRegStateResult dataRegStateResult = (DataRegStateResult) ar.result;
- int regState = getRegStateFromHalRegState(dataRegStateResult.regState);
- int dataRegState = regCodeToServiceState(regState);
- int newDataRat = dataRegStateResult.rat;
- mNewSS.setDataRegState(dataRegState);
+ NetworkRegistrationState networkRegState = (NetworkRegistrationState) ar.result;
+ DataSpecificRegistrationStates dataSpecificStates =
+ networkRegState.getDataSpecificStates();
+ int registrationState = networkRegState.getRegState();
+ int serviceState = regCodeToServiceState(registrationState);
+ int newDataRat = ServiceState.networkTypeToRilRadioTechnology(
+ networkRegState.getAccessNetworkTechnology());
+ mNewSS.setDataRegState(serviceState);
mNewSS.setRilDataRadioTechnology(newDataRat);
+ mNewSS.addNetworkRegistrationState(networkRegState);
+
+ // When we receive OOS reset the PhyChanConfig list so that non-return-to-idle
+ // implementers of PhyChanConfig unsol will not carry forward a CA report
+ // (2 or more cells) to a new cell if they camp for emergency service only.
+ if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ mLastPhysicalChannelConfigList = null;
+ }
+ setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
if (mPhone.isPhoneTypeGsm()) {
- mNewReasonDataDenied = dataRegStateResult.reasonDataDenied;
- mNewMaxDataCalls = dataRegStateResult.maxDataCalls;
- mDataRoaming = regCodeIsRoaming(regState);
+ mNewReasonDataDenied = networkRegState.getRejectCause();
+ mNewMaxDataCalls = dataSpecificStates.maxDataCalls;
+ mDataRoaming = regCodeIsRoaming(registrationState);
// Save the data roaming state reported by modem registration before resource
// overlay or carrier config possibly overrides it.
mNewSS.setDataRoamingFromRegistration(mDataRoaming);
if (DBG) {
- log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
- + " regState=" + regState
+ log("handlPollStateResultMessage: GsmSST dataServiceState=" + serviceState
+ + " regState=" + registrationState
+ " dataRadioTechnology=" + newDataRat);
}
} else if (mPhone.isPhoneTypeCdma()) {
- boolean isDataRoaming = regCodeIsRoaming(regState);
+ boolean isDataRoaming = regCodeIsRoaming(registrationState);
mNewSS.setDataRoaming(isDataRoaming);
// Save the data roaming state reported by modem registration before resource
// overlay or carrier config possibly overrides it.
mNewSS.setDataRoamingFromRegistration(isDataRoaming);
if (DBG) {
- log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
- + " regState=" + regState
+ log("handlPollStateResultMessage: cdma dataServiceState=" + serviceState
+ + " regState=" + registrationState
+ " dataRadioTechnology=" + newDataRat);
}
} else {
@@ -1965,19 +1934,20 @@
}
// voice roaming state in done while handling EVENT_POLL_STATE_REGISTRATION_CDMA
- boolean isDataRoaming = regCodeIsRoaming(regState);
+ boolean isDataRoaming = regCodeIsRoaming(registrationState);
mNewSS.setDataRoaming(isDataRoaming);
// Save the data roaming state reported by modem registration before resource
// overlay or carrier config possibly overrides it.
mNewSS.setDataRoamingFromRegistration(isDataRoaming);
if (DBG) {
- log("handlPollStateResultMessage: CdmaLteSST setDataRegState="
- + dataRegState + " regState=" + regState + " dataRadioTechnology="
- + newDataRat);
+ log("handlPollStateResultMessage: CdmaLteSST dataServiceState="
+ + serviceState + " registrationState=" + registrationState
+ + " dataRadioTechnology=" + newDataRat);
}
}
- updateServiceStateLteEarfcnBoost(mNewSS, getLteEarfcn(dataRegStateResult));
+ updateServiceStateLteEarfcnBoost(mNewSS,
+ getLteEarfcn(networkRegState.getCellIdentity()));
break;
}
@@ -1987,8 +1957,9 @@
if (opNames != null && opNames.length >= 3) {
// FIXME: Giving brandOverride higher precedence, is this desired?
- String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null ?
- mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() : null;
+ String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null
+ ? mUiccController.getUiccCard(getPhoneId())
+ .getOperatorBrandOverride() : null;
if (brandOverride != null) {
log("EVENT_POLL_STATE_OPERATOR: use brandOverride=" + brandOverride);
mNewSS.setOperatorName(brandOverride, brandOverride, opNames[2]);
@@ -2018,8 +1989,9 @@
// NV device (as opposed to CSIM)
mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
} else {
- String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null ?
- mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() : null;
+ String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null
+ ? mUiccController.getUiccCard(getPhoneId())
+ .getOperatorBrandOverride() : null;
if (brandOverride != null) {
mNewSS.setOperatorName(brandOverride, brandOverride, opNames[2]);
} else {
@@ -2054,6 +2026,78 @@
}
}
+ private static boolean isValidLteBandwidthKhz(int bandwidth) {
+ // Valid bandwidths, see 3gpp 36.101 sec. 5.6
+ switch (bandwidth) {
+ case 1400:
+ case 3000:
+ case 5000:
+ case 10000:
+ case 15000:
+ case 20000:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setPhyCellInfoFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
+ if (cellIdentity == null) {
+ if (DBG) {
+ log("Could not set ServiceState channel number. CellIdentity null");
+ }
+ return;
+ }
+
+ ss.setChannelNumber(cellIdentity.getChannelNumber());
+ if (VDBG) {
+ log("Setting channel number: " + cellIdentity.getChannelNumber());
+ }
+
+ if (cellIdentity instanceof CellIdentityLte) {
+ CellIdentityLte cl = (CellIdentityLte) cellIdentity;
+ int[] bandwidths = null;
+ // Prioritize the PhysicalChannelConfig list because we might already be in carrier
+ // aggregation by the time poll state is performed.
+ if (!ArrayUtils.isEmpty(mLastPhysicalChannelConfigList)) {
+ bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
+ for (int bw : bandwidths) {
+ if (!isValidLteBandwidthKhz(bw)) {
+ loge("Invalid LTE Bandwidth in RegistrationState, " + bw);
+ bandwidths = null;
+ break;
+ }
+ }
+ }
+ // If we don't have a PhysicalChannelConfig[] list, then pull from CellIdentityLte.
+ // This is normal if we're in idle mode and the PhysicalChannelConfig[] has already
+ // been updated. This is also a fallback in case the PhysicalChannelConfig info
+ // is invalid (ie, broken).
+ // Also, for vendor implementations that do not report return-to-idle, we should
+ // prioritize the bandwidth report in the CellIdentity, because the physical channel
+ // config report may be stale in the case where a single carrier was used previously
+ // and we transition to camped-for-emergency (since we never have a physical
+ // channel active). In the normal case of single-carrier non-return-to-idle, the
+ // values *must* be the same, so it doesn't matter which is chosen.
+ if (bandwidths == null || bandwidths.length == 1) {
+ final int cbw = cl.getBandwidth();
+ if (isValidLteBandwidthKhz(cbw)) {
+ bandwidths = new int[] {cbw};
+ } else if (cbw == Integer.MAX_VALUE) {
+ // Bandwidth is unreported; c'est la vie. This is not an error because
+ // pre-1.2 HAL implementations do not support bandwidth reporting.
+ } else {
+ loge("Invalid LTE Bandwidth in RegistrationState, " + cbw);
+ }
+ }
+ if (bandwidths != null) {
+ ss.setCellBandwidths(bandwidths);
+ }
+ } else {
+ if (VDBG) log("Skipping bandwidth update for Non-LTE cell.");
+ }
+ }
+
/**
* Determine whether a roaming indicator is in the carrier-specified list of ERIs for
* home system
@@ -2148,7 +2192,7 @@
if (configLoader != null) {
try {
PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
- String systemId = Integer.toString(mNewSS.getSystemId());
+ String systemId = Integer.toString(mNewSS.getCdmaSystemId());
if (alwaysOnHomeNetwork(b)) {
log("updateRoamingState: carrier config override always on home network");
@@ -2191,8 +2235,32 @@
mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
}
+ private void updateOperatorNameFromCarrierConfig() {
+ // Brand override gets a priority over carrier config. If brand override is not available,
+ // override the operator name in home network. Also do this only for CDMA. This is temporary
+ // and should be fixed in a proper way in a later release.
+ if (!mPhone.isPhoneTypeGsm() && !mSS.getRoaming()) {
+ boolean hasBrandOverride = mUiccController.getUiccCard(getPhoneId()) != null
+ && mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() != null;
+ if (!hasBrandOverride) {
+ PersistableBundle config = getCarrierConfig();
+ if (config.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());
+ }
+ }
+ }
+ }
+
protected void updateSpnDisplay() {
updateOperatorNameFromEri();
+ // carrier config gets a priority over ERI
+ updateOperatorNameFromCarrierConfig();
String wfcVoiceSpnFormat = null;
String wfcDataSpnFormat = null;
@@ -2251,7 +2319,8 @@
IccRecords iccRecords = mIccRecords;
String plmn = null;
boolean showPlmn = false;
- int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
+ int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS) : 0;
+ boolean noService = false;
if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE
|| combinedRegState == ServiceState.STATE_EMERGENCY_ONLY) {
showPlmn = true;
@@ -2268,6 +2337,7 @@
// No service at all
plmn = Resources.getSystem().
getText(com.android.internal.R.string.lockscreen_carrier_default).toString();
+ noService = true;
}
if (DBG) log("updateSpnDisplay: radio is on but out " +
"of service, set plmn='" + plmn + "'");
@@ -2292,7 +2362,7 @@
// EXTRA_DATA_SPN = dataSpn
String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
String dataSpn = spn;
- boolean showSpn = !TextUtils.isEmpty(spn)
+ boolean showSpn = !noService && !TextUtils.isEmpty(spn)
&& ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
== SIMRecords.SPN_RULE_SHOW_SPN);
@@ -2318,11 +2388,7 @@
showSpn = false;
}
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
- if (subIds != null && subIds.length > 0) {
- subId = subIds[0];
- }
+ int subId = mPhone.getSubId();
// Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
if (mSubId != subId ||
@@ -2364,11 +2430,7 @@
showPlmn = plmn != null;
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
- if (subIds != null && subIds.length > 0) {
- subId = subIds[0];
- }
+ int subId = mPhone.getSubId();
if (!TextUtils.isEmpty(plmn) && !TextUtils.isEmpty(wfcVoiceSpnFormat)) {
// In Wi-Fi Calling mode show SPN+WiFi
@@ -2527,12 +2589,12 @@
mRatLog.log(mSS.toString());
}
- protected void log(String s) {
- Rlog.d(LOG_TAG, s);
+ protected final void log(String s) {
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + s);
}
- protected void loge(String s) {
- Rlog.e(LOG_TAG, s);
+ protected final void loge(String s) {
+ Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + s);
}
/**
@@ -2621,8 +2683,7 @@
mNewSS.setStateOutOfService();
mNewCellLoc.setStateInvalid();
setSignalStrengthDefaultValues();
- mGotCountryCode = false;
- mNitzUpdatedTime = false;
+ mNitzState.handleNetworkUnavailable();
pollStateDone();
break;
@@ -2630,8 +2691,7 @@
mNewSS.setStateOff();
mNewCellLoc.setStateInvalid();
setSignalStrengthDefaultValues();
- mGotCountryCode = false;
- mNitzUpdatedTime = false;
+ mNitzState.handleNetworkUnavailable();
// don't poll when device is shutting down or the poll was not modemTrigged
// (they sent us new radio data) and current network is not IWLAN
if (mDeviceShuttingDown ||
@@ -2644,15 +2704,19 @@
default:
// Issue all poll-related commands at once then count down the responses, which
// are allowed to arrive out-of-order
+ // TODO: Add WLAN support.
mPollingContext[0]++;
mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));
mPollingContext[0]++;
- mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
+ mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+ .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_PS,
+ obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
mPollingContext[0]++;
- mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION,
- mPollingContext));
+ mRegStateManagers.get(AccessNetworkConstants.TransportType.WWAN)
+ .getNetworkRegistrationState(NetworkRegistrationState.DOMAIN_CS,
+ obtainMessage(EVENT_POLL_STATE_REGISTRATION, mPollingContext));
if (mPhone.isPhoneTypeGsm()) {
mPollingContext[0]++;
@@ -2675,6 +2739,10 @@
useDataRegStateForDataOnlyDevices();
resetServiceStateInIwlanMode();
+ if (Build.IS_DEBUGGABLE && mPhone.mTelephonyTester != null) {
+ mPhone.mTelephonyTester.overrideServiceState(mNewSS);
+ }
+
if (DBG) {
log("Poll ServiceState done: "
+ " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]"
@@ -2708,10 +2776,11 @@
boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
- // ratchet the new tech up through it's rat family but don't drop back down
- // until cell change
- if (!hasLocationChanged) {
- mRatRatcheter.ratchetRat(mSS, mNewSS);
+ // ratchet the new tech up through its rat family but don't drop back down
+ // until cell change or device is OOS
+ boolean isDataInService = mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
+ if (isDataInService) {
+ mRatRatcheter.ratchet(mSS, mNewSS, hasLocationChanged);
}
boolean hasRilVoiceRadioTechnologyChanged =
@@ -2857,16 +2926,12 @@
if (hasRegistered) {
mNetworkAttachedRegistrants.notifyRegistrants();
-
- if (DBG) {
- log("pollStateDone: registering current mNitzUpdatedTime=" + mNitzUpdatedTime
- + " changing to false");
- }
- mNitzUpdatedTime = false;
+ mNitzState.handleNetworkAvailable();
}
if (hasDeregistered) {
mNetworkDetachedRegistrants.notifyRegistrants();
+ mNitzState.handleNetworkUnavailable();
}
if (hasRejectCauseChanged) {
@@ -2879,56 +2944,59 @@
tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha());
String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
+ String prevCountryIsoCode = tm.getNetworkCountryIso(mPhone.getPhoneId());
String operatorNumeric = mSS.getOperatorNumeric();
if (!mPhone.isPhoneTypeGsm()) {
// try to fix the invalid Operator Numeric
if (isInvalidOperatorNumeric(operatorNumeric)) {
- int sid = mSS.getSystemId();
+ int sid = mSS.getCdmaSystemId();
operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
}
}
tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
- updateCarrierMccMncConfiguration(operatorNumeric,
- prevOperatorNumeric, mPhone.getContext());
+
if (isInvalidOperatorNumeric(operatorNumeric)) {
if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid");
- tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
- mGotCountryCode = false;
- mNitzUpdatedTime = false;
+ // Passing empty string is important for the first update. The initial value of
+ // operator numeric in locale tracker is null. The async update will allow getting
+ // cell info from the modem instead of using the cached one.
+ mLocaleTracker.updateOperatorNumericAsync("");
+ mNitzState.handleNetworkUnavailable();
} else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
- // Update time zone, ISO, and IDD.
- //
// If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
// the SIM as if we were talking to towers. Telephony code then uses that with
// mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN
- String iso = "";
- String mcc = "";
- try {
- mcc = operatorNumeric.substring(0, 3);
- iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
- } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
- loge("pollStateDone: countryCodeForMcc error: " + ex);
- }
-
- tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), iso);
- mGotCountryCode = true;
-
- if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso)
- && getAutoTimeZone()) {
- updateTimeZoneByNetworkCountryCode(iso);
- }
-
+ // Update IDD.
if (!mPhone.isPhoneTypeGsm()) {
setOperatorIdd(operatorNumeric);
}
- if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
- mNeedFixZoneAfterNitz)) {
- fixTimeZone(iso);
+ mLocaleTracker.updateOperatorNumericSync(operatorNumeric);
+ String countryIsoCode = mLocaleTracker.getCurrentCountry();
+
+ // Update Time Zone.
+ boolean iccCardExists = iccCardExists();
+ boolean networkIsoChanged =
+ networkCountryIsoChanged(countryIsoCode, prevCountryIsoCode);
+
+ // Determine countryChanged: networkIso is only reliable if there's an ICC card.
+ boolean countryChanged = iccCardExists && networkIsoChanged;
+ if (DBG) {
+ long ctm = System.currentTimeMillis();
+ log("Before handleNetworkCountryCodeKnown:"
+ + " countryChanged=" + countryChanged
+ + " iccCardExist=" + iccCardExists
+ + " countryIsoChanged=" + networkIsoChanged
+ + " operatorNumeric=" + operatorNumeric
+ + " prevOperatorNumeric=" + prevOperatorNumeric
+ + " countryIsoCode=" + countryIsoCode
+ + " prevCountryIsoCode=" + prevCountryIsoCode
+ + " ltod=" + TimeUtils.logTimeOfDay(ctm));
}
+ mNitzState.handleNetworkCountryCodeSet(countryChanged);
}
tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
@@ -2966,6 +3034,9 @@
if (hasRilDataRadioTechnologyChanged || hasRilVoiceRadioTechnologyChanged) {
logRatChange();
+
+ updateRatTypeForSignalStrength();
+ notifySignalStrength();
}
if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
@@ -3074,9 +3145,9 @@
((RuimRecords) mIccRecords).getCsimSpnDisplayCondition();
int iconIndex = mSS.getCdmaEriIconIndex();
- if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) &&
- isInHomeSidNid(mSS.getSystemId(), mSS.getNetworkId()) &&
- mIccRecords != null) {
+ if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF)
+ && isInHomeSidNid(mSS.getCdmaSystemId(), mSS.getCdmaNetworkId())
+ && mIccRecords != null) {
mSS.setOperatorAlphaLong(mIccRecords.getServiceProviderName());
}
}
@@ -3117,116 +3188,62 @@
String idd = mHbpcdUtils.getIddByMcc(
Integer.parseInt(operatorNumeric.substring(0,3)));
if (idd != null && !idd.isEmpty()) {
- mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING,
+ mPhone.setGlobalSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING,
idd);
} else {
// use default "+", since we don't know the current IDP
- mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING, "+");
+ mPhone.setGlobalSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING, "+");
}
}
- protected boolean isInvalidOperatorNumeric(String operatorNumeric) {
+ private boolean isInvalidOperatorNumeric(String operatorNumeric) {
return operatorNumeric == null || operatorNumeric.length() < 5 ||
operatorNumeric.startsWith(INVALID_MCC);
}
- protected String fixUnknownMcc(String operatorNumeric, int sid) {
+ private String fixUnknownMcc(String operatorNumeric, int sid) {
if (sid <= 0) {
// no cdma information is available, do nothing
return operatorNumeric;
}
// resolve the mcc from sid;
- // if mSavedTimeZone is null, TimeZone would get the default timeZone,
- // and the fixTimeZone couldn't help, because it depends on operator Numeric;
+ // if mNitzState.getSavedTimeZoneId() is null, TimeZone would get the default timeZone,
+ // and the mNitzState.fixTimeZone() couldn't help, because it depends on operator Numeric;
// if the sid is conflict and timezone is unavailable, the mcc may be not right.
- boolean isNitzTimeZone = false;
- int timeZone = 0;
- TimeZone tzone = null;
- if (mSavedTimeZone != null) {
- timeZone =
- TimeZone.getTimeZone(mSavedTimeZone).getRawOffset()/MS_PER_HOUR;
+ boolean isNitzTimeZone;
+ TimeZone tzone;
+ if (mNitzState.getSavedTimeZoneId() != null) {
+ tzone = TimeZone.getTimeZone(mNitzState.getSavedTimeZoneId());
isNitzTimeZone = true;
} else {
- tzone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
- if (tzone != null)
- timeZone = tzone.getRawOffset()/MS_PER_HOUR;
+ NitzData lastNitzData = mNitzState.getCachedNitzData();
+ if (lastNitzData == null) {
+ tzone = null;
+ } else {
+ tzone = TimeZoneLookupHelper.guessZoneByNitzStatic(lastNitzData);
+ if (ServiceStateTracker.DBG) {
+ log("fixUnknownMcc(): guessNitzTimeZone returned "
+ + (tzone == null ? tzone : tzone.getID()));
+ }
+ }
+ isNitzTimeZone = false;
}
- int mcc = mHbpcdUtils.getMcc(sid,
- timeZone, (mZoneDst ? 1 : 0), isNitzTimeZone);
+ int utcOffsetHours = 0;
+ if (tzone != null) {
+ utcOffsetHours = tzone.getRawOffset() / MS_PER_HOUR;
+ }
+
+ NitzData nitzData = mNitzState.getCachedNitzData();
+ boolean isDst = nitzData != null && nitzData.isDst();
+ int mcc = mHbpcdUtils.getMcc(sid, utcOffsetHours, (isDst ? 1 : 0), isNitzTimeZone);
if (mcc > 0) {
operatorNumeric = Integer.toString(mcc) + DEFAULT_MNC;
}
return operatorNumeric;
}
- protected void fixTimeZone(String isoCountryCode) {
- TimeZone zone = null;
- // If the offset is (0, false) and the time zone property
- // is set, use the time zone property rather than GMT.
- final String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
- if (DBG) {
- log("fixTimeZone zoneName='" + zoneName +
- "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
- " iso-cc='" + isoCountryCode +
- "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode));
- }
- if ("".equals(isoCountryCode) && mNeedFixZoneAfterNitz) {
- // Country code not found. This is likely a test network.
- // Get a TimeZone based only on the NITZ parameters (best guess).
- zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
- if (DBG) log("pollStateDone: using NITZ TimeZone");
- } else if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null)
- && (zoneName.length() > 0)
- && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) {
- // For NITZ string without time zone,
- // need adjust time to reflect default time zone setting
- zone = TimeZone.getDefault();
- if (mNeedFixZoneAfterNitz) {
- long ctm = System.currentTimeMillis();
- long tzOffset = zone.getOffset(ctm);
- if (DBG) {
- log("fixTimeZone: tzOffset=" + tzOffset +
- " ltod=" + TimeUtils.logTimeOfDay(ctm));
- }
- if (getAutoTime()) {
- long adj = ctm - tzOffset;
- if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
- setAndBroadcastNetworkSetTime(adj);
- } else {
- // Adjust the saved NITZ time to account for tzOffset.
- mSavedTime = mSavedTime - tzOffset;
- if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
- }
- }
- if (DBG) log("fixTimeZone: using default TimeZone");
- } else {
- zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode);
- if (DBG) log("fixTimeZone: using getTimeZone(off, dst, time, iso)");
- }
-
- final String tmpLog = "fixTimeZone zoneName=" + zoneName + " mZoneOffset=" + mZoneOffset
- + " mZoneDst=" + mZoneDst + " iso-cc=" + isoCountryCode + " mNeedFixZoneAfterNitz="
- + mNeedFixZoneAfterNitz + " zone=" + (zone != null ? zone.getID() : "NULL");
- mTimeZoneLog.log(tmpLog);
-
- if (zone != null) {
- log("fixTimeZone: zone != null zone.getID=" + zone.getID());
- if (getAutoTimeZone()) {
- setAndBroadcastNetworkSetTimeZone(zone.getID());
- } else {
- log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
- }
- if (mNeedFixZoneAfterNitz) {
- saveNitzTimeZone(zone.getID());
- }
- } else {
- log("fixTimeZone: zone == null, do nothing for zone");
- }
- mNeedFixZoneAfterNitz = false;
- }
-
/**
* Check if GPRS got registered while voice is registered.
*
@@ -3239,45 +3256,12 @@
(dataRegState != ServiceState.STATE_IN_SERVICE));
}
- /**
- * Returns a TimeZone object based only on parameters from the NITZ string.
- */
- private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
- TimeZone guess = findTimeZone(offset, dst, when);
- if (guess == null) {
- // Couldn't find a proper timezone. Perhaps the DST data is wrong.
- guess = findTimeZone(offset, !dst, when);
- }
- if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
- return guess;
- }
-
- private TimeZone findTimeZone(int offset, boolean dst, long when) {
- int rawOffset = offset;
- if (dst) {
- rawOffset -= MS_PER_HOUR;
- }
- String[] zones = TimeZone.getAvailableIDs(rawOffset);
- TimeZone guess = null;
- Date d = new Date(when);
- for (String zone : zones) {
- TimeZone tz = TimeZone.getTimeZone(zone);
- if (tz.getOffset(when) == offset &&
- tz.inDaylightTime(d) == dst) {
- guess = tz;
- break;
- }
- }
-
- return guess;
- }
-
/** convert ServiceState registration code
* to service state */
private int regCodeToServiceState(int code) {
switch (code) {
- case ServiceState.RIL_REG_STATE_HOME:
- case ServiceState.RIL_REG_STATE_ROAMING:
+ case NetworkRegistrationState.REG_STATE_HOME:
+ case NetworkRegistrationState.REG_STATE_ROAMING:
return ServiceState.STATE_IN_SERVICE;
default:
return ServiceState.STATE_OUT_OF_SERVICE;
@@ -3289,7 +3273,7 @@
* returns true if registered roam, false otherwise
*/
private boolean regCodeIsRoaming (int code) {
- return ServiceState.RIL_REG_STATE_ROAMING == code;
+ return NetworkRegistrationState.REG_STATE_ROAMING == code;
}
private boolean isSameOperatorNameFromSimAndSS(ServiceState s) {
@@ -3569,326 +3553,41 @@
/**
* nitzReceiveTime is time_t that the NITZ time was posted
*/
- private void setTimeFromNITZString (String nitz, long nitzReceiveTime) {
- // "yy/mm/dd,hh:mm:ss(+/-)tz"
- // tz is in number of quarter-hours
-
+ private void setTimeFromNITZString(String nitzString, long nitzReceiveTime) {
long start = SystemClock.elapsedRealtime();
if (DBG) {
- log("NITZ: " + nitz + "," + nitzReceiveTime
+ Rlog.d(LOG_TAG, "NITZ: " + nitzString + "," + nitzReceiveTime
+ " start=" + start + " delay=" + (start - nitzReceiveTime));
}
-
- try {
- /* 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) */
- Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
-
- c.clear();
- c.set(Calendar.DST_OFFSET, 0);
-
- String[] nitzSubs = nitz.split("[/:,+-]");
-
- int year = 2000 + Integer.parseInt(nitzSubs[0]);
- if (year > MAX_NITZ_YEAR) {
- if (DBG) loge("NITZ year: " + year + " exceeds limit, skip NITZ time update");
- return;
- }
- c.set(Calendar.YEAR, year);
-
- // month is 0 based!
- int month = Integer.parseInt(nitzSubs[1]) - 1;
- c.set(Calendar.MONTH, month);
-
- int date = Integer.parseInt(nitzSubs[2]);
- c.set(Calendar.DATE, date);
-
- int hour = Integer.parseInt(nitzSubs[3]);
- c.set(Calendar.HOUR, hour);
-
- int minute = Integer.parseInt(nitzSubs[4]);
- c.set(Calendar.MINUTE, minute);
-
- int second = Integer.parseInt(nitzSubs[5]);
- c.set(Calendar.SECOND, second);
-
- boolean sign = (nitz.indexOf('-') == -1);
-
- int tzOffset = Integer.parseInt(nitzSubs[6]);
-
- int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) : 0;
-
- // The zone offset received from NITZ is for current local time,
- // so DST correction is already applied. Don't add it again.
- //
- // tzOffset += dst * 4;
- //
- // We could unapply it if we wanted the raw offset.
-
- tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
-
- TimeZone zone = null;
-
- // As a special extension, the Android emulator appends the name of
- // the host computer's timezone to the nitz string. this is zoneinfo
- // timezone name of the form Area!Location or Area!Location!SubLocation
- // so we need to convert the ! into /
- if (nitzSubs.length >= 9) {
- String tzname = nitzSubs[8].replace('!','/');
- zone = TimeZone.getTimeZone( tzname );
- }
-
- String iso = ((TelephonyManager) mPhone.getContext().
- getSystemService(Context.TELEPHONY_SERVICE)).
- getNetworkCountryIsoForPhone(mPhone.getPhoneId());
-
- if (zone == null) {
-
- if (mGotCountryCode) {
- if (iso != null && iso.length() > 0) {
- zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
- c.getTimeInMillis(),
- iso);
- } else {
- // We don't have a valid iso country code. This is
- // most likely because we're on a test network that's
- // using a bogus MCC (eg, "001"), so get a TimeZone
- // based only on the NITZ parameters.
- zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
- }
- }
- }
-
- if ((zone == null) || (mZoneOffset != tzOffset) || (mZoneDst != (dst != 0))){
- // We got the time before the country or the zone has changed
- // so we don't know how to identify the DST rules yet. Save
- // the information and hope to fix it up later.
-
- mNeedFixZoneAfterNitz = true;
- mZoneOffset = tzOffset;
- mZoneDst = dst != 0;
- mZoneTime = c.getTimeInMillis();
- }
-
- String tmpLog = "NITZ: nitz=" + nitz + " nitzReceiveTime=" + nitzReceiveTime
- + " tzOffset=" + tzOffset + " dst=" + dst + " zone="
- + (zone != null ? zone.getID() : "NULL")
- + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode
- + " mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz
- + " getAutoTimeZone()=" + getAutoTimeZone();
- if (DBG) {
- log(tmpLog);
- }
- mTimeZoneLog.log(tmpLog);
-
- if (zone != null) {
- if (getAutoTimeZone()) {
- setAndBroadcastNetworkSetTimeZone(zone.getID());
- }
- saveNitzTimeZone(zone.getID());
- }
-
- String ignore = SystemProperties.get("gsm.ignore-nitz");
- if (ignore != null && ignore.equals("yes")) {
- log("NITZ: Not setting clock because gsm.ignore-nitz is set");
- return;
- }
-
+ NitzData newNitzData = NitzData.parse(nitzString);
+ if (newNitzData != null) {
try {
- mWakeLock.acquire();
-
- if (!mPhone.isPhoneTypeGsm() || getAutoTime()) {
- long millisSinceNitzReceived
- = SystemClock.elapsedRealtime() - nitzReceiveTime;
-
- if (millisSinceNitzReceived < 0) {
- // Sanity check: something is wrong
- if (DBG) {
- log("NITZ: not setting time, clock has rolled "
- + "backwards since NITZ time was received, "
- + nitz);
- }
- return;
- }
-
- if (millisSinceNitzReceived > Integer.MAX_VALUE) {
- // If the time is this far off, something is wrong > 24 days!
- if (DBG) {
- log("NITZ: not setting time, processing has taken "
- + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
- + " days");
- }
- return;
- }
-
- // Note: with range checks above, cast to int is safe
- c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
-
- tmpLog = "NITZ: nitz=" + nitz + " nitzReceiveTime=" + nitzReceiveTime
- + " Setting time of day to " + c.getTime()
- + " NITZ receive delay(ms): " + millisSinceNitzReceived
- + " gained(ms): "
- + (c.getTimeInMillis() - System.currentTimeMillis())
- + " from " + nitz;
- if (DBG) {
- log(tmpLog);
- }
- mTimeLog.log(tmpLog);
- if (mPhone.isPhoneTypeGsm()) {
- setAndBroadcastNetworkSetTime(c.getTimeInMillis());
- Rlog.i(LOG_TAG, "NITZ: after Setting time of day");
- } else {
- if (getAutoTime()) {
- /**
- * Update system time automatically
- */
- long gained = c.getTimeInMillis() - System.currentTimeMillis();
- long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
- int nitzUpdateSpacing = Settings.Global.getInt(mCr,
- Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
- int nitzUpdateDiff = Settings.Global.getInt(mCr,
- Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
-
- if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
- || (Math.abs(gained) > nitzUpdateDiff)) {
- if (DBG) {
- log("NITZ: Auto updating time of day to " + c.getTime()
- + " NITZ receive delay=" + millisSinceNitzReceived
- + "ms gained=" + gained + "ms from " + nitz);
- }
-
- setAndBroadcastNetworkSetTime(c.getTimeInMillis());
- } else {
- if (DBG) {
- log("NITZ: ignore, a previous update was "
- + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
- }
- return;
- }
- }
- }
- }
- SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
- saveNitzTime(c.getTimeInMillis());
- mNitzUpdatedTime = true;
+ TimestampedValue<NitzData> nitzSignal =
+ new TimestampedValue<>(nitzReceiveTime, newNitzData);
+ mNitzState.handleNitzReceived(nitzSignal);
} finally {
if (DBG) {
long end = SystemClock.elapsedRealtime();
- log("NITZ: end=" + end + " dur=" + (end - start));
+ Rlog.d(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start));
}
- mWakeLock.release();
- }
- } catch (RuntimeException ex) {
- loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
- }
- }
-
- private boolean getAutoTime() {
- try {
- return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
- } catch (Settings.SettingNotFoundException snfe) {
- return true;
- }
- }
-
- private boolean getAutoTimeZone() {
- try {
- return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
- } catch (Settings.SettingNotFoundException snfe) {
- return true;
- }
- }
-
- private void saveNitzTimeZone(String zoneId) {
- mSavedTimeZone = zoneId;
- }
-
- private void saveNitzTime(long time) {
- mSavedTime = time;
- mSavedAtTime = SystemClock.elapsedRealtime();
- }
-
- /**
- * Set the timezone and send out a sticky broadcast so the system can
- * determine if the timezone was set by the carrier.
- *
- * @param zoneId timezone set by carrier
- */
- private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
- if (DBG) log("setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
- AlarmManager alarm =
- (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
- alarm.setTimeZone(zoneId);
- Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time-zone", zoneId);
- mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- if (DBG) {
- log("setAndBroadcastNetworkSetTimeZone: call alarm.setTimeZone and broadcast zoneId=" +
- zoneId);
- }
- }
-
- /**
- * Set the time and Send out a sticky broadcast so the system can determine
- * if the time was set by the carrier.
- *
- * @param time time set by network
- */
- private void setAndBroadcastNetworkSetTime(long time) {
- if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
- SystemClock.setCurrentTimeMillis(time);
- Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time", time);
- mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
- TelephonyMetrics.getInstance().writeNITZEvent(mPhone.getPhoneId(), time);
- }
-
- private void revertToNitzTime() {
- if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME, 0) == 0) {
- return;
- }
- if (DBG) {
- log("Reverting to NITZ Time: mSavedTime=" + mSavedTime + " mSavedAtTime=" +
- mSavedAtTime);
- }
- if (mSavedTime != 0 && mSavedAtTime != 0) {
- long currTime = SystemClock.elapsedRealtime();
- mTimeLog.log("Reverting to NITZ time, currTime=" + currTime
- + " mSavedAtTime=" + mSavedAtTime + " mSavedTime=" + mSavedTime);
- setAndBroadcastNetworkSetTime(mSavedTime + (currTime - mSavedAtTime));
- }
- }
-
- private void revertToNitzTimeZone() {
- if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 0) == 0) {
- return;
- }
- String tmpLog = "Reverting to NITZ TimeZone: tz=" + mSavedTimeZone;
- if (DBG) log(tmpLog);
- mTimeZoneLog.log(tmpLog);
- if (mSavedTimeZone != null) {
- setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
- } else {
- String iso = ((TelephonyManager) mPhone.getContext().getSystemService(
- Context.TELEPHONY_SERVICE)).getNetworkCountryIsoForPhone(mPhone.getPhoneId());
- if (!TextUtils.isEmpty(iso)) {
- updateTimeZoneByNetworkCountryCode(iso);
}
}
}
/**
- * Cancels all notifications posted to NotificationManager. These notifications for restricted
- * state and rejection cause for cs registration are no longer valid after the SIM has been
- * removed.
+ * Cancels all notifications posted to NotificationManager for this subId. These notifications
+ * for restricted state and rejection cause for cs registration are no longer valid after the
+ * SIM has been removed.
*/
private void cancelAllNotifications() {
+ if (DBG) log("cancelAllNotifications: mPrevSubId=" + mPrevSubId);
NotificationManager notificationManager = (NotificationManager)
mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancelAll();
+ if (SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
+ notificationManager.cancel(Integer.toString(mPrevSubId), PS_NOTIFICATION);
+ notificationManager.cancel(Integer.toString(mPrevSubId), CS_NOTIFICATION);
+ notificationManager.cancel(Integer.toString(mPrevSubId), CS_REJECT_CAUSE_NOTIFICATION);
+ }
}
/**
@@ -3901,6 +3600,12 @@
public void setNotification(int notifyType) {
if (DBG) log("setNotification: create notification " + notifyType);
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ // notifications are posted per-sub-id, so return if current sub-id is invalid
+ loge("cannot setNotification on invalid subid mSubId=" + mSubId);
+ return;
+ }
+
// Needed because sprout RIL sends these when they shouldn't?
boolean isSetNotification = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_user_notification_of_restrictied_mobile_access);
@@ -3932,6 +3637,10 @@
int notificationId = CS_NOTIFICATION;
int icon = com.android.internal.R.drawable.stat_sys_warning;
+ final boolean multipleSubscriptions = (((TelephonyManager) mPhone.getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE)).getPhoneCount() > 1);
+ final int simNumber = mSubscriptionController.getSlotIndex(mSubId) + 1;
+
switch (notifyType) {
case PS_ENABLED:
long dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3940,37 +3649,52 @@
}
notificationId = PS_NOTIFICATION;
title = context.getText(com.android.internal.R.string.RestrictedOnDataTitle);
- details = context.getText(com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case PS_DISABLED:
notificationId = PS_NOTIFICATION;
break;
case CS_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnAllVoiceTitle);
- details = context.getText(
- com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_NORMAL_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnNormalTitle);
- details = context.getText(com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_EMERGENCY_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnEmergencyTitle);
- details = context.getText(
- com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_DISABLED:
// do nothing and cancel the notification later
break;
case CS_REJECT_CAUSE_ENABLED:
notificationId = CS_REJECT_CAUSE_NOTIFICATION;
- int resId = selectResourceForRejectCode(mRejectCode);
+ int resId = selectResourceForRejectCode(mRejectCode, multipleSubscriptions);
if (0 == resId) {
loge("setNotification: mRejectCode=" + mRejectCode + " is not handled.");
return;
} else {
icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn;
- title = Resources.getSystem().getString(resId);
+ // if using the single SIM resource, simNumber will be ignored
+ title = context.getString(resId, simNumber);
details = null;
}
break;
@@ -3978,7 +3702,7 @@
if (DBG) {
log("setNotification, create notification, notifyType: " + notifyType
- + ", title: " + title + ", details: " + details);
+ + ", title: " + title + ", details: " + details + ", subId: " + mSubId);
}
mNotification = new Notification.Builder(context)
@@ -3989,6 +3713,7 @@
.setColor(context.getResources().getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
.setContentText(details)
.setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
.build();
@@ -3998,10 +3723,25 @@
if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
// cancel previous post notification
- notificationManager.cancel(notificationId);
+ notificationManager.cancel(Integer.toString(mSubId), notificationId);
} else {
- // update restricted state notification
- notificationManager.notify(notificationId, mNotification);
+ boolean show = false;
+ if (mSS.isEmergencyOnly() && notifyType == CS_EMERGENCY_ENABLED) {
+ // if reg state is emergency only, always show restricted emergency notification.
+ show = true;
+ } else if (notifyType == CS_REJECT_CAUSE_ENABLED) {
+ // always show notification due to CS reject irrespective of service state.
+ show = true;
+ } else if (mSS.getState() == ServiceState.STATE_IN_SERVICE) {
+ // for non in service states, we have system UI and signal bar to indicate limited
+ // service. No need to show notification again. This also helps to mitigate the
+ // issue if phone go to OOS and camp to other networks and received restricted ind.
+ show = true;
+ }
+ // update restricted state notification for this subId
+ if (show) {
+ notificationManager.notify(Integer.toString(mSubId), notificationId, mNotification);
+ }
}
}
@@ -4011,20 +3751,28 @@
*
* @param rejCode should be compatible with TS 24.008.
*/
- private int selectResourceForRejectCode(int rejCode) {
+ private int selectResourceForRejectCode(int rejCode, boolean multipleSubscriptions) {
int rejResourceId = 0;
switch (rejCode) {
case 1:// Authentication reject
- rejResourceId = com.android.internal.R.string.mmcc_authentication_reject;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_authentication_reject_msim_template :
+ com.android.internal.R.string.mmcc_authentication_reject;
break;
case 2:// IMSI unknown in HLR
- rejResourceId = com.android.internal.R.string.mmcc_imsi_unknown_in_hlr;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_imsi_unknown_in_hlr_msim_template :
+ com.android.internal.R.string.mmcc_imsi_unknown_in_hlr;
break;
case 3:// Illegal MS
- rejResourceId = com.android.internal.R.string.mmcc_illegal_ms;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_illegal_ms_msim_template :
+ com.android.internal.R.string.mmcc_illegal_ms;
break;
case 6:// Illegal ME
- rejResourceId = com.android.internal.R.string.mmcc_illegal_me;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_illegal_me_msim_template :
+ com.android.internal.R.string.mmcc_illegal_me;
break;
default:
// The other codes are not defined or not required by operators till now.
@@ -4199,6 +3947,25 @@
}
/**
+ * Registers for IMS capability changed.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForImsCapabilityChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mImsCapabilityChangedRegistrants.add(r);
+ }
+
+ /**
+ * Unregisters for IMS capability changed.
+ * @param h handler to notify
+ */
+ public void unregisterForImsCapabilityChanged(Handler h) {
+ mImsCapabilityChangedRegistrants.remove(h);
+ }
+
+ /**
* Clean up existing voice and data connection then turn off radio power.
*
* Hang up the existing voice calls to decrease call drop rate.
@@ -4206,86 +3973,44 @@
public void powerOffRadioSafely(DcTracker dcTracker) {
synchronized (this) {
if (!mPendingRadioPowerOffAfterDataOff) {
- if (mPhone.isPhoneTypeGsm() || mPhone.isPhoneTypeCdmaLte()) {
- int dds = SubscriptionManager.getDefaultDataSubscriptionId();
- // To minimize race conditions we call cleanUpAllConnections on
- // both if else paths instead of before this isDisconnected test.
- if (dcTracker.isDisconnected()
- && (dds == mPhone.getSubId()
- || (dds != mPhone.getSubId()
- && ProxyController.getInstance().isDataDisconnected(dds)))) {
- // To minimize race conditions we do this after isDisconnected
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (DBG) log("Data disconnected, turn off radio right away.");
- 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();
- }
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (dds != mPhone.getSubId()
- && !ProxyController.getInstance().isDataDisconnected(dds)) {
- if (DBG) log("Data is active on DDS. Wait for all data disconnect");
- // 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, null);
- mPendingRadioPowerOffAfterDataOff = true;
- }
- Message msg = Message.obtain(this);
- msg.what = EVENT_SET_RADIO_POWER_OFF;
- msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
- if (sendMessageDelayed(msg, 30000)) {
- if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- log("Cannot send delayed Msg, turn off radio right away.");
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOff = false;
- }
- }
+ int dds = SubscriptionManager.getDefaultDataSubscriptionId();
+ // To minimize race conditions we call cleanUpAllConnections on
+ // both if else paths instead of before this isDisconnected test.
+ if (dcTracker.isDisconnected()
+ && (dds == mPhone.getSubId()
+ || (dds != mPhone.getSubId()
+ && ProxyController.getInstance().isDataDisconnected(dds)))) {
+ // To minimize race conditions we do this after isDisconnected
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
} else {
- // In some network, deactivate PDP connection cause releasing of RRC connection,
- // which MM/IMSI detaching request needs. Without this detaching, network can
- // not release the network resources previously attached.
- // So we are avoiding data detaching on these networks.
- String[] networkNotClearData = mPhone.getContext().getResources()
- .getStringArray(com.android.internal.R.array.networks_not_clear_data);
- String currentNetwork = mSS.getOperatorNumeric();
- if ((networkNotClearData != null) && (currentNetwork != null)) {
- for (int i = 0; i < networkNotClearData.length; i++) {
- if (currentNetwork.equals(networkNotClearData[i])) {
- // Don't clear data connection for this carrier
- if (DBG)
- log("Not disconnecting data for " + currentNetwork);
- hangupAndPowerOff();
- 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();
}
- // To minimize race conditions we call cleanUpAllConnections on
- // both if else paths instead of before this isDisconnected test.
- if (dcTracker.isDisconnected()) {
- // To minimize race conditions we do this after isDisconnected
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (DBG) log("Data disconnected, turn off radio right away.");
- hangupAndPowerOff();
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+ if (dds != mPhone.getSubId()
+ && !ProxyController.getInstance().isDataDisconnected(dds)) {
+ if (DBG) log("Data is active on DDS. Wait for all data disconnect");
+ // 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, null);
+ mPendingRadioPowerOffAfterDataOff = true;
+ }
+ Message msg = Message.obtain(this);
+ msg.what = EVENT_SET_RADIO_POWER_OFF;
+ msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+ if (sendMessageDelayed(msg, 30000)) {
+ if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
} else {
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- Message msg = Message.obtain(this);
- msg.what = EVENT_SET_RADIO_POWER_OFF;
- msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
- if (sendMessageDelayed(msg, 30000)) {
- if (DBG)
- log("Wait upto 30s for data to disconnect, then turn off radio.");
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- log("Cannot send delayed Msg, turn off radio right away.");
- hangupAndPowerOff();
- }
+ log("Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
}
}
}
@@ -4377,19 +4102,37 @@
return earfcnPairList;
}
- private void updateLteEarfcnLists() {
+
+ private void onCarrierConfigChanged() {
CarrierConfigManager configManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+
+ if (config != null) {
+ updateLteEarfcnLists(config);
+ updateReportingCriteria(config);
+ }
+ }
+
+ private void updateLteEarfcnLists(PersistableBundle config) {
synchronized (mLteRsrpBoostLock) {
- mLteRsrpBoost = b.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
- String[] earfcnsStringArrayForRsrpBoost = b.getStringArray(
+ mLteRsrpBoost = config.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+ String[] earfcnsStringArrayForRsrpBoost = config.getStringArray(
CarrierConfigManager.KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY);
mEarfcnPairListForRsrpBoost = convertEarfcnStringArrayToPairList(
earfcnsStringArrayForRsrpBoost);
}
}
+ private void updateReportingCriteria(PersistableBundle config) {
+ mPhone.setSignalStrengthReportingCriteria(
+ config.getIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY),
+ AccessNetworkType.EUTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ config.getIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY),
+ AccessNetworkType.UTRAN);
+ }
+
private void updateServiceStateLteEarfcnBoost(ServiceState serviceState, int lteEarfcn) {
synchronized (mLteRsrpBoostLock) {
if ((lteEarfcn != INVALID_LTE_EARFCN)
@@ -4408,18 +4151,6 @@
* @return true if the signal strength changed and a notification was sent.
*/
protected boolean onSignalStrengthResult(AsyncResult ar) {
- boolean isGsm = false;
- int dataRat = mSS.getRilDataRadioTechnology();
- int voiceRat = mSS.getRilVoiceRadioTechnology();
-
- // Override isGsm based on currently camped data and voice RATs
- // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
- if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- && ServiceState.isGsm(dataRat))
- || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- && ServiceState.isGsm(voiceRat))) {
- isGsm = true;
- }
// This signal is used for both voice and data radio signal so parse
// all fields
@@ -4427,21 +4158,50 @@
if ((ar.exception == null) && (ar.result != null)) {
mSignalStrength = (SignalStrength) ar.result;
mSignalStrength.validateInput();
+ mSignalStrength.setLteRsrpBoost(mSS.getLteEarfcnRsrpBoost());
+
+ PersistableBundle config = getCarrierConfig();
+ mSignalStrength.setUseOnlyRsrpForLteLevel(config.getBoolean(
+ CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL));
+ mSignalStrength.setLteRsrpThresholds(config.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY));
+ mSignalStrength.setWcdmaDefaultSignalMeasurement(config.getString(
+ CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING));
+ mSignalStrength.setWcdmaRscpThresholds(config.getIntArray(
+ CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY));
+ } else {
+ log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
+ mSignalStrength = new SignalStrength(true);
+ }
+
+ updateRatTypeForSignalStrength();
+ boolean ssChanged = notifySignalStrength();
+
+ return ssChanged;
+ }
+
+ private void updateRatTypeForSignalStrength() {
+ if (mSignalStrength != null) {
+ boolean isGsm = false;
+ int dataRat = mSS.getRilDataRadioTechnology();
+ int voiceRat = mSS.getRilVoiceRadioTechnology();
+
+ // Override isGsm based on currently camped data and voice RATs
+ // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
+ if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ && ServiceState.isGsm(dataRat))
+ || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ && ServiceState.isGsm(voiceRat))) {
+ isGsm = true;
+ }
+
if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
&& voiceRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
mSignalStrength.fixType();
} else {
mSignalStrength.setGsm(isGsm);
}
- mSignalStrength.setLteRsrpBoost(mSS.getLteEarfcnRsrpBoost());
- } else {
- log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
- mSignalStrength = new SignalStrength(isGsm);
}
-
- boolean ssChanged = notifySignalStrength();
-
- return ssChanged;
}
/**
@@ -4455,7 +4215,7 @@
mPhone.mCT.mForegroundCall.hangupIfAlive();
}
- mCi.setRadioPower(false, null);
+ mCi.setRadioPower(false, obtainMessage(EVENT_RADIO_POWER_OFF_DONE));
}
@@ -4466,60 +4226,36 @@
}
/**
- * Return true if time zone needs fixing.
- *
- * @param phone
- * @param operatorNumeric
- * @param prevOperatorNumeric
- * @param needToFixTimeZone
- * @return true if time zone needs to be fixed
+ * Return true if the network operator's country code changed.
*/
- protected boolean shouldFixTimeZoneNow(Phone phone, String operatorNumeric,
- String prevOperatorNumeric, boolean needToFixTimeZone) {
- // Return false if the mcc isn't valid as we don't know where we are.
- // Return true if we have an IccCard and the mcc changed or we
- // need to fix it because when the NITZ time came in we didn't
- // know the country code.
+ private boolean networkCountryIsoChanged(String newCountryIsoCode, String prevCountryIsoCode) {
+ // Return false if the new ISO code isn't valid as we don't know where we are.
+ // Return true if the previous ISO code wasn't valid, or if it was and the new one differs.
- // If mcc is invalid then we'll return false
- int mcc;
- try {
- mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
- } catch (Exception e) {
+ // If newCountryIsoCode is invalid then we'll return false
+ if (TextUtils.isEmpty(newCountryIsoCode)) {
if (DBG) {
- log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
- " retVal=false");
+ log("countryIsoChanged: no new country ISO code");
}
return false;
}
- // If prevMcc is invalid will make it different from mcc
- // so we'll return true if the card exists.
- int prevMcc;
- try {
- prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
- } catch (Exception e) {
- prevMcc = mcc + 1;
+ if (TextUtils.isEmpty(prevCountryIsoCode)) {
+ if (DBG) {
+ log("countryIsoChanged: no previous country ISO code");
+ }
+ return true;
}
+ return !newCountryIsoCode.equals(prevCountryIsoCode);
+ }
- // Determine if the Icc card exists
+ // Determine if the Icc card exists
+ private boolean iccCardExists() {
boolean iccCardExist = false;
if (mUiccApplcation != null) {
iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
}
-
- // Determine retVal
- boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
- if (DBG) {
- long ctm = System.currentTimeMillis();
- log("shouldFixTimeZoneNow: retVal=" + retVal +
- " iccCardExist=" + iccCardExist +
- " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
- " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
- " needToFixTimeZone=" + needToFixTimeZone +
- " ltod=" + TimeUtils.logTimeOfDay(ctm));
- }
- return retVal;
+ return iccCardExist;
}
public String getSystemProperty(String property, String defValue) {
@@ -4691,20 +4427,12 @@
pw.println(" mGsmRoaming=" + mGsmRoaming);
pw.println(" mDataRoaming=" + mDataRoaming);
pw.println(" mEmergencyOnly=" + mEmergencyOnly);
- pw.println(" mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
pw.flush();
- pw.println(" mZoneOffset=" + mZoneOffset);
- pw.println(" mZoneDst=" + mZoneDst);
- pw.println(" mZoneTime=" + mZoneTime);
- pw.println(" mGotCountryCode=" + mGotCountryCode);
- pw.println(" mNitzUpdatedTime=" + mNitzUpdatedTime);
- pw.println(" mSavedTimeZone=" + mSavedTimeZone);
- pw.println(" mSavedTime=" + mSavedTime);
- pw.println(" mSavedAtTime=" + mSavedAtTime);
+ mNitzState.dumpState(pw);
+ pw.flush();
pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck);
pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg);
pw.println(" mNotification=" + mNotification);
- pw.println(" mWakeLock=" + mWakeLock);
pw.println(" mCurSpn=" + mCurSpn);
pw.println(" mCurDataSpn=" + mCurDataSpn);
pw.println(" mCurShowSpn=" + mCurShowSpn);
@@ -4738,6 +4466,8 @@
pw.println(" mLteRsrpBoost=" + mLteRsrpBoost);
dumpEarfcnPairList(pw);
+ mLocaleTracker.dump(fd, pw, args);
+
pw.println(" Roaming Log:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
@@ -4763,15 +4493,7 @@
ipw.increaseIndent();
mRadioPowerLog.dump(fd, ipw, args);
- ipw.println(" Time Logs:");
- ipw.increaseIndent();
- mTimeLog.dump(fd, ipw, args);
- ipw.decreaseIndent();
-
- ipw.println(" Time zone Logs:");
- ipw.increaseIndent();
- mTimeZoneLog.dump(fd, ipw, args);
- ipw.decreaseIndent();
+ mNitzState.dumpLogs(fd, ipw, args);
}
public boolean isImsRegistered() {
@@ -4798,15 +4520,6 @@
return value;
}
- protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) {
- // if we have a change in operator, notify wifi (even to/from none)
- if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) ||
- ((newOp != null) && (newOp.equals(oldOp) == false))) {
- log("update mccmnc=" + newOp + " fromServiceState=true");
- MccTable.updateMccMncConfiguration(context, newOp, true);
- }
- }
-
/**
* Check ISO country by MCC to see if phone is roaming in same registered country
*/
@@ -4823,8 +4536,8 @@
boolean inSameCountry = true;
final String networkMCC = operatorNumeric.substring(0, 3);
final String homeMCC = homeNumeric.substring(0, 3);
- final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC));
- final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC));
+ final String networkCountry = MccTable.countryCodeForMcc(networkMCC);
+ final String homeCountry = MccTable.countryCodeForMcc(homeMCC);
if (networkCountry.isEmpty() || homeCountry.isEmpty()) {
// Not a valid country
return false;
@@ -5047,33 +4760,26 @@
}
/**
- * Update time zone by network country code, works on countries which only have one time zone.
- * @param iso Country code from network MCC
+ * Gets the carrier configuration values for a particular subscription.
+ *
+ * @return A {@link PersistableBundle} containing the config for the given subId,
+ * or default values for an invalid subId.
*/
- private void updateTimeZoneByNetworkCountryCode(String iso) {
- // Test both paths if ignore nitz is true
- boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
- TelephonyProperties.PROPERTY_IGNORE_NITZ, false)
- && ((SystemClock.uptimeMillis() & 1) == 0);
-
- List<String> uniqueZoneIds = TimeUtils.getTimeZoneIdsWithUniqueOffsets(iso);
- if ((uniqueZoneIds.size() == 1) || testOneUniqueOffsetPath) {
- String zoneId = uniqueZoneIds.get(0);
- if (DBG) {
- log("updateTimeZoneByNetworkCountryCode: no nitz but one TZ for iso-cc=" + iso
- + " with zone.getID=" + zoneId
- + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
- }
- mTimeZoneLog.log("updateTimeZoneByNetworkCountryCode: set time zone=" + zoneId
- + " iso=" + iso);
- setAndBroadcastNetworkSetTimeZone(zoneId);
- } else {
- if (DBG) {
- log("updateTimeZoneByNetworkCountryCode: there are " + uniqueZoneIds.size()
- + " unique offsets for iso-cc='" + iso
- + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath
- + "', do nothing");
+ 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();
+ }
+
+ public LocaleTracker getLocaleTracker() {
+ return mLocaleTracker;
}
}
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index f2f2aa3..4fb02f0 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -23,9 +23,11 @@
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.SQLException;
-import android.os.UserHandle;
+import android.os.PersistableBundle;
import android.os.UserManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
@@ -45,7 +47,7 @@
private static final boolean DBG = InboundSmsHandler.DBG;
/** Delete any partial message segments older than 30 days. */
- static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
+ static final long DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
/**
* Query projection for dispatching pending messages at boot time.
@@ -97,7 +99,7 @@
@Override
public void run() {
- scanRawTable();
+ scanRawTable(context);
InboundSmsHandler.cancelNewMessageNotification(context);
}
}
@@ -140,7 +142,7 @@
/**
* Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
*/
- private void scanRawTable() {
+ private void scanRawTable(Context context) {
if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
long startTime = System.nanoTime();
HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
@@ -176,8 +178,9 @@
Integer receivedCount = multiPartReceivedCount.get(reference);
if (receivedCount == null) {
multiPartReceivedCount.put(reference, 1); // first segment seen
+ long expirationTime = getUndeliveredSmsExpirationTime(context);
if (tracker.getTimestamp() <
- (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
+ (System.currentTimeMillis() - expirationTime)) {
// older than 30 days; delete if we don't find all the segments
oldMultiPartMessages.add(reference);
}
@@ -236,6 +239,20 @@
}
}
+ private long getUndeliveredSmsExpirationTime(Context context) {
+ int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle bundle = configManager.getConfigForSubId(subId);
+
+ if (bundle != null) {
+ return bundle.getLong(CarrierConfigManager.KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME,
+ DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE);
+ } else {
+ return DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE;
+ }
+ }
+
/**
* Used as the HashMap key for matching concatenated message segments.
*/
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
new file mode 100644
index 0000000..d8906e7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -0,0 +1,637 @@
+/*
+ * 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 static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
+import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Telephony.Sms;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Rlog;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.util.Pair;
+
+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.gsm.GsmInboundSmsHandler;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ *
+ */
+public class SmsDispatchersController extends Handler {
+ private static final String TAG = "SmsDispatchersController";
+
+ /** Radio is ON */
+ private static final int EVENT_RADIO_ON = 11;
+
+ /** IMS registration/SMS format changed */
+ private static final int EVENT_IMS_STATE_CHANGED = 12;
+
+ /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
+ private static final int EVENT_IMS_STATE_DONE = 13;
+
+ private SMSDispatcher mCdmaDispatcher;
+ private SMSDispatcher mGsmDispatcher;
+ private ImsSmsDispatcher mImsSmsDispatcher;
+
+ private GsmInboundSmsHandler mGsmInboundSmsHandler;
+ private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
+
+ private Phone mPhone;
+ /** Outgoing message counter. Shared by all dispatchers. */
+ private final SmsUsageMonitor mUsageMonitor;
+ private final CommandsInterface mCi;
+ private final Context mContext;
+
+ /** true if IMS is registered and sms is supported, false otherwise.*/
+ private boolean mIms = false;
+ private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+
+ public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
+ SmsUsageMonitor usageMonitor) {
+ Rlog.d(TAG, "SmsDispatchersController created");
+
+ mContext = phone.getContext();
+ mUsageMonitor = usageMonitor;
+ mCi = phone.mCi;
+ mPhone = phone;
+
+ // Create dispatchers, inbound SMS handlers and
+ // broadcast undelivered messages in raw table.
+ mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
+ mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
+ mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+ storageMonitor, phone);
+ mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+ storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
+ mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
+ SmsBroadcastUndelivered.initialize(phone.getContext(),
+ mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+ InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
+
+ mCi.registerForOn(this, EVENT_RADIO_ON, null);
+ mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
+ }
+
+ /* Updates the phone object when there is a change */
+ protected void updatePhoneObject(Phone phone) {
+ Rlog.d(TAG, "In IMS updatePhoneObject ");
+ mCdmaDispatcher.updatePhoneObject(phone);
+ mGsmDispatcher.updatePhoneObject(phone);
+ mGsmInboundSmsHandler.updatePhoneObject(phone);
+ mCdmaInboundSmsHandler.updatePhoneObject(phone);
+ }
+
+ public void dispose() {
+ mCi.unregisterForOn(this);
+ mCi.unregisterForImsNetworkStateChanged(this);
+ mGsmDispatcher.dispose();
+ mCdmaDispatcher.dispose();
+ mGsmInboundSmsHandler.dispose();
+ mCdmaInboundSmsHandler.dispose();
+ }
+
+ /**
+ * Handles events coming from the phone stack. Overridden from handler.
+ *
+ * @param msg the message to handle
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case EVENT_RADIO_ON:
+ case EVENT_IMS_STATE_CHANGED: // received unsol
+ mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
+ break;
+
+ case EVENT_IMS_STATE_DONE:
+ ar = (AsyncResult) msg.obj;
+
+ if (ar.exception == null) {
+ updateImsInfo(ar);
+ } else {
+ Rlog.e(TAG, "IMS State query failed with exp "
+ + ar.exception);
+ }
+ break;
+
+ default:
+ if (isCdmaMo()) {
+ mCdmaDispatcher.handleMessage(msg);
+ } else {
+ mGsmDispatcher.handleMessage(msg);
+ }
+ }
+ }
+
+ private void setImsSmsFormat(int format) {
+ switch (format) {
+ case PhoneConstants.PHONE_TYPE_GSM:
+ mImsSmsFormat = SmsConstants.FORMAT_3GPP;
+ break;
+ case PhoneConstants.PHONE_TYPE_CDMA:
+ mImsSmsFormat = SmsConstants.FORMAT_3GPP2;
+ break;
+ default:
+ mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+ break;
+ }
+ }
+
+ private void updateImsInfo(AsyncResult ar) {
+ int[] responseArray = (int[]) ar.result;
+ setImsSmsFormat(responseArray[1]);
+ mIms = responseArray[0] == 1 && !SmsConstants.FORMAT_UNKNOWN.equals(mImsSmsFormat);
+ Rlog.d(TAG, "IMS registration state: " + mIms + " format: " + mImsSmsFormat);
+ }
+
+ /**
+ * Inject an SMS PDU into the android platform only if it is class 1.
+ *
+ * @param pdu is the byte array of pdu to be injected into android telephony layer
+ * @param format is the format of SMS pdu (3gpp or 3gpp2)
+ * @param callback if not NULL this callback is triggered when the message is successfully
+ * received by the android telephony layer. This callback is triggered at
+ * the same time an SMS received from radio is responded back.
+ */
+ @VisibleForTesting
+ public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
+ // TODO We need to decide whether we should allow injecting GSM(3gpp)
+ // 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 */);
+ }
+
+ /**
+ * Inject an SMS PDU into the android platform.
+ *
+ * @param msg is the {@link SmsMessage} to be injected into android telephony layer
+ * @param format is the format of SMS pdu (3gpp or 3gpp2)
+ * @param callback if not NULL this callback is triggered when the message is successfully
+ * received by the android telephony layer. This callback is triggered at
+ * the same time an SMS received from radio is responded back.
+ * @param ignoreClass if set to false, this method will inject class 1 sms only.
+ */
+ @VisibleForTesting
+ public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
+ boolean ignoreClass) {
+ Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
+ try {
+ if (msg == null) {
+ Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
+ callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+ return;
+ }
+
+ if (!ignoreClass
+ && msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
+ Rlog.e(TAG, "injectSmsPdu: not class 1");
+ callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+ return;
+ }
+
+ AsyncResult ar = new AsyncResult(callback, msg, null);
+
+ if (format.equals(SmsConstants.FORMAT_3GPP)) {
+ Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ + ", format=" + format + "to mGsmInboundSmsHandler");
+ mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
+ Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ + ", format=" + format + "to mCdmaInboundSmsHandler");
+ mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ } else {
+ // Invalid pdu format.
+ Rlog.e(TAG, "Invalid pdu format: " + format);
+ callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+ }
+ } catch (Exception e) {
+ Rlog.e(TAG, "injectSmsPdu failed: ", e);
+ callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+ }
+ }
+
+ /**
+ * 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;
+
+ // newFormat will be based on voice technology
+ String newFormat =
+ (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType())
+ ? mCdmaDispatcher.getFormat() : mGsmDispatcher.getFormat();
+
+ // was previously sent sms format match with voice tech?
+ if (oldFormat.equals(newFormat)) {
+ if (isCdmaFormat(newFormat)) {
+ Rlog.d(TAG, "old format matched new format (cdma)");
+ mCdmaDispatcher.sendSms(tracker);
+ return;
+ } else {
+ Rlog.d(TAG, "old format matched new format (gsm)");
+ mGsmDispatcher.sendSms(tracker);
+ return;
+ }
+ }
+
+ // format didn't match, need to re-encode.
+ HashMap map = tracker.getData();
+
+ // to re-encode, fields needed are: scAddr, destAddr, and
+ // text if originally sent as sendText or
+ // data and destPort if originally sent as sendData.
+ if (!(map.containsKey("scAddr") && map.containsKey("destAddr")
+ && (map.containsKey("text")
+ || (map.containsKey("data") && map.containsKey("destPort"))))) {
+ // should never come here...
+ Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
+ tracker.onFailed(mContext, SmsManager.RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
+ return;
+ }
+ String scAddr = (String) map.get("scAddr");
+ String destAddr = (String) map.get("destAddr");
+
+ SmsMessageBase.SubmitPduBase pdu = null;
+ // figure out from tracker if this was sendText/Data
+ if (map.containsKey("text")) {
+ Rlog.d(TAG, "sms failed was text");
+ String text = (String) map.get("text");
+
+ if (isCdmaFormat(newFormat)) {
+ Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
+ pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+ scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+ } else {
+ Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
+ pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
+ scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+ }
+ } else if (map.containsKey("data")) {
+ Rlog.d(TAG, "sms failed was data");
+ byte[] data = (byte[]) map.get("data");
+ Integer destPort = (Integer) map.get("destPort");
+
+ if (isCdmaFormat(newFormat)) {
+ Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
+ pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+ scAddr, destAddr, destPort.intValue(), data,
+ (tracker.mDeliveryIntent != null));
+ } else {
+ Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
+ pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
+ scAddr, destAddr, destPort.intValue(), data,
+ (tracker.mDeliveryIntent != null));
+ }
+ }
+
+ // replace old smsc and pdu with newly encoded ones
+ map.put("smsc", pdu.encodedScAddress);
+ map.put("pdu", pdu.encodedMessage);
+
+ SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? mCdmaDispatcher : mGsmDispatcher;
+
+ tracker.mFormat = dispatcher.getFormat();
+ dispatcher.sendSms(tracker);
+ }
+
+ public boolean isIms() {
+ return mIms;
+ }
+
+ public String getImsSmsFormat() {
+ return mImsSmsFormat;
+ }
+
+ /**
+ * Determines whether or not to use CDMA format for MO SMS.
+ * If SMS over IMS is supported, then format is based on IMS SMS format,
+ * otherwise format is based on current phone type.
+ *
+ * @return true if Cdma format should be used for MO SMS, false otherwise.
+ */
+ protected boolean isCdmaMo() {
+ if (!isIms()) {
+ // 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(mImsSmsFormat);
+ }
+
+ /**
+ * Determines whether or not format given is CDMA format.
+ *
+ * @param format
+ * @return true if format given is CDMA format, false otherwise.
+ */
+ public boolean isCdmaFormat(String format) {
+ return (mCdmaDispatcher.getFormat().equals(format));
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @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 data 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>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>.
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> 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
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ protected void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (mImsSmsDispatcher.isAvailable()) {
+ mImsSmsDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent,
+ deliveryIntent);
+ } else if (isCdmaMo()) {
+ mCdmaDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+ } else {
+ mGsmDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+ }
+ }
+
+ /**
+ * 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>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>.
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> 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.
+ */
+ 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) {
+ if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
+ mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+ messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
+ } else {
+ if (isCdmaMo()) {
+ mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+ messageUri, callingPkg, persistMessage, priority, expectMore,
+ validityPeriod);
+ } else {
+ mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+ messageUri, callingPkg, persistMessage, priority, expectMore,
+ validityPeriod);
+ }
+ }
+ }
+
+ /**
+ * Send a multi-part 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 parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>
+ * <code>RESULT_ERROR_NO_SERVICE</code>.
+ * 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 deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in 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.
+
+ */
+ protected void sendMultipartText(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) {
+ if (mImsSmsDispatcher.isAvailable()) {
+ mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
+ deliveryIntents, messageUri, callingPkg, persistMessage,
+ SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+ false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
+ } else {
+ if (isCdmaMo()) {
+ mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
+ deliveryIntents, messageUri, callingPkg, persistMessage, priority,
+ expectMore, validityPeriod);
+ } else {
+ mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
+ deliveryIntents, messageUri, callingPkg, persistMessage, priority,
+ expectMore, validityPeriod);
+ }
+ }
+ }
+
+ /**
+ * Returns the premium SMS permission for the specified package. If the package has never
+ * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
+ * will be returned.
+ * @param packageName the name of the package to query permission
+ * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
+ * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
+ * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
+ * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
+ */
+ public int getPremiumSmsPermission(String packageName) {
+ return mUsageMonitor.getPremiumSmsPermission(packageName);
+ }
+
+ /**
+ * Sets the premium SMS permission for the specified package and save the value asynchronously
+ * to persistent storage.
+ * @param packageName the name of the package to set permission
+ * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
+ * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
+ * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
+ */
+ public void setPremiumSmsPermission(String packageName, int permission) {
+ mUsageMonitor.setPremiumSmsPermission(packageName, permission);
+ }
+
+ public SmsUsageMonitor getUsageMonitor() {
+ return mUsageMonitor;
+ }
+
+ /**
+ * Triggers the correct method for handling the sms status report based on the format.
+ *
+ * @param tracker the sms tracker.
+ * @param format the format.
+ * @param pdu the pdu of the report.
+ * @return a Pair in which the first boolean is whether the report was handled successfully
+ * or not and the second boolean is whether processing the sms is complete and the
+ * tracker no longer need to be kept track of, false if we should expect more callbacks
+ * and the tracker should be kept.
+ */
+ public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
+ String format, byte[] pdu) {
+ if (isCdmaFormat(format)) {
+ return handleCdmaStatusReport(tracker, format, pdu);
+ } else {
+ return handleGsmStatusReport(tracker, format, pdu);
+ }
+ }
+
+ private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
+ String format, byte[] pdu) {
+ tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
+ boolean success = triggerDeliveryIntent(tracker, format, pdu);
+ return new Pair(success, true /* complete */);
+ }
+
+ private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
+ String format, byte[] pdu) {
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
+ boolean complete = false;
+ boolean success = false;
+ if (sms != null) {
+ int tpStatus = sms.getStatus();
+ if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
+ // Update the message status (COMPLETE or FAILED)
+ tracker.updateSentMessageStatus(mContext, tpStatus);
+ complete = true;
+ }
+ success = triggerDeliveryIntent(tracker, format, pdu);
+ }
+ return new Pair(success, complete);
+ }
+
+ private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
+ byte[] pdu) {
+ PendingIntent intent = tracker.mDeliveryIntent;
+ Intent fillIn = new Intent();
+ fillIn.putExtra("pdu", pdu);
+ fillIn.putExtra("format", format);
+ try {
+ intent.send(mContext, Activity.RESULT_OK, fillIn);
+ return true;
+ } catch (CanceledException ex) {
+ return false;
+ }
+ }
+
+
+ public interface SmsInjectionCallback {
+ void onSmsInjectedResult(int result);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mGsmInboundSmsHandler.dump(fd, pw, args);
+ mCdmaInboundSmsHandler.dump(fd, pw, args);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 0354c18..1c5b35e 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -45,6 +45,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -84,9 +87,14 @@
static final String LOG_TAG = "SubscriptionController";
static final boolean DBG = true;
static final boolean VDBG = false;
+ static final boolean DBG_CACHE = false;
static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed
+ private static final int DEPRECATED_SETTING = -1;
private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
+ /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
+ private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();
+
/**
* Copied from android.util.LocalLog with flush() adding flush and line number
* TODO: Update LocalLog
@@ -154,6 +162,7 @@
private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
private int[] colorArr;
+ private long mLastISubServiceRegTime;
public static SubscriptionController init(Phone phone) {
synchronized (SubscriptionController.class) {
@@ -188,6 +197,7 @@
protected SubscriptionController(Context c) {
init(c);
+ migrateImsSettings();
}
protected void init(Context c) {
@@ -198,7 +208,8 @@
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
if(ServiceManager.getService("isub") == null) {
- ServiceManager.addService("isub", this);
+ ServiceManager.addService("isub", this);
+ mLastISubServiceRegTime = System.currentTimeMillis();
}
if (DBG) logdl("[SubscriptionController] init by Context");
@@ -217,35 +228,11 @@
ServiceManager.addService("isub", this);
}
+ migrateImsSettings();
+
if (DBG) logdl("[SubscriptionController] init by Phone");
}
- /**
- * Make sure the caller can read phone state which requires holding the
- * READ_PHONE_STATE permission and the OP_READ_PHONE_STATE app op being
- * set to MODE_ALLOWED.
- *
- * @param callingPackage The package claiming to make the IPC.
- * @param message The name of the access protected method.
- *
- * @throws SecurityException if the caller does not have READ_PHONE_STATE permission.
- */
- private boolean canReadPhoneState(String callingPackage, String message) {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
-
- // SKIP checking run-time permission since self or using PRIVILEDGED permission
- return true;
- } catch (SecurityException e) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
- message);
- }
-
- return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) == AppOpsManager.MODE_ALLOWED;
- }
-
private void enforceModifyPhoneState(String message) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, message);
@@ -303,10 +290,12 @@
// Get the blank bitmap for this SubInfoRecord
Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(),
com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
- int mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MCC));
- int mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MNC));
+ String mcc = cursor.getString(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.MCC_STRING));
+ String mnc = cursor.getString(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.MNC_STRING));
+ String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.CARD_ID));
// FIXME: consider stick this into database too
String countryIso = getSubscriptionCountryIso(id);
boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow(
@@ -321,11 +310,13 @@
if (VDBG) {
String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
+ String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId);
logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:"
+ simSlotIndex + " displayName:" + displayName + " nameSource:" + nameSource
+ " iconTint:" + iconTint + " dataRoaming:" + dataRoaming
+ " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso + " isEmbedded:"
- + isEmbedded + " accessRules:" + Arrays.toString(accessRules));
+ + isEmbedded + " accessRules:" + Arrays.toString(accessRules)
+ + " cardId:" + cardIdToPrint);
}
// If line1number has been set to a different number, use it instead.
@@ -335,7 +326,7 @@
}
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules);
+ isEmbedded, accessRules, cardId);
}
/**
@@ -378,7 +369,7 @@
subList = new ArrayList<SubscriptionInfo>();
}
subList.add(subInfo);
- }
+ }
}
} else {
if (DBG) logd("Query fail");
@@ -427,7 +418,8 @@
*/
@Override
public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfo")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getActiveSubscriptionInfo")) {
return null;
}
@@ -466,12 +458,29 @@
*/
@Override
public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForIccId") ||
- iccId == null) {
+ // Query the subscriptions unconditionally, and then check whether the caller has access to
+ // the given subscription.
+ final SubscriptionInfo si = getActiveSubscriptionInfoForIccIdInternal(iccId);
+
+ final int subId = si != null
+ ? si.getSubscriptionId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getActiveSubscriptionInfoForIccId")) {
return null;
}
- // Now that all security checks passes, perform the operation as ourselves.
+ return si;
+ }
+
+ /**
+ * 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(
@@ -505,7 +514,16 @@
@Override
public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
+ 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,
+ "getActiveSubscriptionInfoForSimSlotIndex")) {
return null;
}
@@ -550,7 +568,11 @@
public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) {
if (DBG) logd("[getAllSubInfoList]+");
- if (!canReadPhoneState(callingPackage, "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,
+ "getAllSubInfoList")) {
return null;
}
@@ -577,34 +599,71 @@
*/
@Override
public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
-
- if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoList")) {
+ if (!isSubInfoReady()) {
+ if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
return null;
}
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
+ boolean canReadAllPhoneState;
try {
- if (!isSubInfoReady()) {
- if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
- return null;
+ canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
+ Binder.getCallingUid(), callingPackage, "getActiveSubscriptionInfoList");
+ } catch (SecurityException e) {
+ canReadAllPhoneState = false;
+ }
+
+ synchronized (mCacheActiveSubInfoList) {
+ // If the caller can read all phone state, just return the full list.
+ if (canReadAllPhoneState) {
+ return new ArrayList<>(mCacheActiveSubInfoList);
}
- List<SubscriptionInfo> subList = getSubInfo(
+ // Filter the list to only include subscriptions which the caller can manage.
+ return mCacheActiveSubInfoList.stream()
+ .filter(subscriptionInfo -> {
+ try {
+ return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
+ subscriptionInfo.getSubscriptionId(), callingPackage,
+ "getActiveSubscriptionInfoList");
+ } catch (SecurityException e) {
+ return false;
+ }
+ })
+ .collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Refresh the cache of SubInfoRecord(s) of the currently inserted SIM(s)
+ */
+ @VisibleForTesting // For mockito to mock this method
+ public void refreshCachedActiveSubscriptionInfoList() {
+ if (!isSubInfoReady()) {
+ if (DBG_CACHE) {
+ logdl("[refreshCachedActiveSubscriptionInfoList] "
+ + "Sub Controller not ready ");
+ }
+ return;
+ }
+
+ synchronized (mCacheActiveSubInfoList) {
+ mCacheActiveSubInfoList.clear();
+ List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
-
- if (subList != null) {
- // FIXME: Unnecessary when an insertion sort is used!
- subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
-
- if (VDBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
- } else {
- if (DBG) logdl("[getActiveSubInfoList]- no info return");
+ if (activeSubscriptionInfoList != null) {
+ mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
}
-
- return subList;
- } finally {
- Binder.restoreCallingIdentity(identity);
+ if (DBG_CACHE) {
+ if (!mCacheActiveSubInfoList.isEmpty()) {
+ for (SubscriptionInfo si : mCacheActiveSubInfoList) {
+ logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info="
+ + si);
+ }
+ } else {
+ logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
+ }
+ }
}
}
@@ -615,24 +674,14 @@
*/
@Override
public int getActiveSubInfoCount(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
+ // Let getActiveSubscriptionInfoList perform permission checks / filtering.
+ List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage);
+ if (records == null) {
+ if (VDBG) logd("[getActiveSubInfoCount] records null");
return 0;
}
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
- mContext.getOpPackageName());
- if (records == null) {
- if (VDBG) logd("[getActiveSubInfoCount] records null");
- return 0;
- }
- if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
- return records.size();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+ return records.size();
}
/**
@@ -644,7 +693,11 @@
public int getAllSubInfoCount(String callingPackage) {
if (DBG) logd("[getAllSubInfoCount]+");
- if (!canReadPhoneState(callingPackage, "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,
+ "getAllSubInfoCount")) {
return 0;
}
@@ -683,7 +736,11 @@
@Override
public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getAvailableSubscriptionInfoList")) {
+ // 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,
+ "getAvailableSubscriptionInfoList")) {
throw new SecurityException("Need READ_PHONE_STATE to call "
+ " getAvailableSubscriptionInfoList");
}
@@ -854,8 +911,10 @@
ContentResolver resolver = mContext.getContentResolver();
Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
- SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
- SubscriptionManager.ICC_ID + "=?", new String[]{iccId}, null);
+ SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE,
+ SubscriptionManager.ICC_ID, SubscriptionManager.CARD_ID},
+ SubscriptionManager.ICC_ID + "=?" + " OR " + SubscriptionManager.ICC_ID + "=?",
+ new String[]{iccId, IccUtils.getDecimalSubstring(iccId)}, null);
boolean setDisplayName = false;
try {
@@ -867,6 +926,8 @@
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);
ContentValues value = new ContentValues();
if (slotIndex != oldSimInfoId) {
@@ -877,10 +938,26 @@
setDisplayName = true;
}
+ if (oldIccId != null && oldIccId.length() < iccId.length()
+ && (oldIccId.equals(IccUtils.getDecimalSubstring(iccId)))) {
+ value.put(SubscriptionManager.ICC_ID, iccId);
+ }
+
+ UiccCard card = UiccController.getInstance().getUiccCardForPhone(slotIndex);
+ if (card != null) {
+ String cardId = card.getCardId();
+ if (cardId != null && cardId != oldCardId) {
+ value.put(SubscriptionManager.CARD_ID, cardId);
+ }
+ }
+
if (value.size() > 0) {
resolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
}
if (DBG) logdl("[addSubInfoRecord] Record already exists");
@@ -974,6 +1051,9 @@
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Long.toString(subId), null);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
}
@@ -1006,7 +1086,24 @@
value.put(SubscriptionManager.COLOR, color);
value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
value.put(SubscriptionManager.CARRIER_NAME, "");
- return resolver.insert(SubscriptionManager.CONTENT_URI, value);
+ UiccCard card = UiccController.getInstance().getUiccCardForPhone(slotIndex);
+ if (card != null) {
+ String cardId = card.getCardId();
+ if (cardId != null) {
+ value.put(SubscriptionManager.CARD_ID, cardId);
+ } else {
+ value.put(SubscriptionManager.CARD_ID, iccId);
+ }
+ } else {
+ value.put(SubscriptionManager.CARD_ID, iccId);
+ }
+
+ Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
+ return uri;
}
/**
@@ -1072,6 +1169,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1103,6 +1204,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1163,6 +1268,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1205,6 +1314,10 @@
result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ "=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
if (DBG) logd("[setDisplayNumber]- update result :" + result);
notifySubscriptionInfoChanged();
@@ -1241,6 +1354,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1256,21 +1373,29 @@
* @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(mccMnc.substring(0,3));
- mnc = Integer.parseInt(mccMnc.substring(3));
+ mcc = Integer.parseInt(mccString);
+ mnc = Integer.parseInt(mncString);
} catch (NumberFormatException e) {
loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
}
if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
- ContentValues value = new ContentValues(2);
+ 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);
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1558,7 +1683,12 @@
broadcastDefaultVoiceSubIdChanged(subId);
}
- private void broadcastDefaultVoiceSubIdChanged(int subId) {
+ /**
+ * 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);
@@ -1573,7 +1703,7 @@
int subId = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
+ if (VDBG) slogd("[getDefaultVoiceSubId] subId=" + subId);
return subId;
}
@@ -1669,7 +1799,7 @@
mDefaultFallbackSubId = subId;
// Update MCC MNC device configuration information
String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
- MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
+ MccTable.updateMccMncConfiguration(mContext, defaultMccMnc);
// Broadcast an Intent for default sub change
Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
@@ -1750,57 +1880,48 @@
return subIds[0];
}
- public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotIndex,
- boolean needCheck,
- String callingPackage) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]+ slotIndex:" + slotIndex);
- if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
+ /** Must be public for access from instrumentation tests. */
+ @VisibleForTesting
+ public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex,
+ boolean needCheck) {
+ 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;
}
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
+ if (needCheck && !isSubInfoReady()) {
+ if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- not ready");
+ 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 (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
- slotIndex = getSlotIndex(getDefaultSubId());
- }
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- invalid slotIndex");
- return null;
- }
-
- if (needCheck && !isSubInfoReady()) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- not ready");
- 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);
+ 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;
} finally {
- Binder.restoreCallingIdentity(identity);
+ if (cursor != null) {
+ cursor.close();
+ }
}
+ if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
+
+ return subList;
}
private void validateSubId(int subId) {
@@ -1893,6 +2014,17 @@
enforceModifyPhoneState("setSubscriptionProperty");
final long token = Binder.clearCallingIdentity();
ContentResolver resolver = mContext.getContentResolver();
+
+ setSubscriptionPropertyIntoContentResolver(subId, propKey, propValue, resolver);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private static void setSubscriptionPropertyIntoContentResolver(
+ int subId, String propKey, String propValue, ContentResolver resolver) {
ContentValues value = new ContentValues();
switch (propKey) {
case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
@@ -1907,17 +2039,22 @@
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:
value.put(propKey, Integer.parseInt(propValue));
break;
default:
- if(DBG) logd("Invalid column name");
+ if (DBG) slogd("Invalid column name");
break;
}
resolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Integer.toString(subId), null);
- Binder.restoreCallingIdentity(token);
}
/**
@@ -1929,7 +2066,8 @@
*/
@Override
public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, "getSubscriptionProperty")) {
return null;
}
String resultValue = null;
@@ -1955,6 +2093,12 @@
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:
resultValue = cursor.getInt(0) + "";
break;
default:
@@ -1997,6 +2141,7 @@
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());
@@ -2048,4 +2193,47 @@
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();
+ 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) {
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 2b8e9c6..c16ba7d 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -16,15 +16,14 @@
package com.android.internal.telephony;
+import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.UserSwitchObserver;
-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.pm.IPackageManager;
import android.os.AsyncResult;
@@ -36,6 +35,8 @@
import android.os.ServiceManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.provider.Settings.SettingNotFoundException;
import android.service.euicc.EuiccProfileInfo;
import android.service.euicc.EuiccService;
import android.service.euicc.GetEuiccProfileInfoListResult;
@@ -50,9 +51,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.euicc.EuiccController;
-import com.android.internal.telephony.uicc.IccCardProxy;
-import com.android.internal.telephony.uicc.IccConstants;
-import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
@@ -68,7 +66,7 @@
private static final String LOG_TAG = "SubscriptionInfoUpdater";
private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount();
- private static final int EVENT_SIM_LOCKED_QUERY_ICCID_DONE = 1;
+ 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;
@@ -76,7 +74,10 @@
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_REFRESH_EMBEDDED_SUBSCRIPTIONS = 9;
+ 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 String ICCID_STRING_FOR_NO_SIM = "";
/**
@@ -109,6 +110,8 @@
private static Context mContext = null;
private static String mIccId[] = new String[PROJECT_SIM_NUM];
private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
+ private static int[] sSimCardState = new int[PROJECT_SIM_NUM];
+ private static int[] sSimApplicationState = new int[PROJECT_SIM_NUM];
private SubscriptionManager mSubscriptionManager = null;
private EuiccManager mEuiccManager;
private IPackageManager mPackageManager;
@@ -128,10 +131,6 @@
mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mContext.registerReceiver(sReceiver, intentFilter);
-
mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
initializeCarrierApps();
}
@@ -169,64 +168,31 @@
mCurrentlyActiveUserId);
}
- private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- logd("[Receiver]+");
- String action = intent.getAction();
- logd("Action: " + action);
-
- if (!action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED) &&
- !action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
- return;
- }
-
- int slotIndex = intent.getIntExtra(PhoneConstants.PHONE_KEY,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- logd("slotIndex: " + slotIndex);
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- logd("ACTION_SIM_STATE_CHANGED contains invalid slotIndex: " + slotIndex);
- return;
- }
-
- String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- logd("simStatus: " + simStatus);
-
- // TODO: All of the below should be converted to ACTION_INTERNAL_SIM_STATE_CHANGED to
- // ensure that the SubscriptionInfo is updated before the broadcasts are sent out.
- if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_RESTRICTED, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_NOT_READY.equals(simStatus)) {
- // ICC_NOT_READY is a terminal state for an eSIM on the boot profile. At this
- // phase, the subscription list is accessible.
- // TODO(b/64216093): Clean up this special case, likely by treating NOT_READY
- // as equivalent to ABSENT, once the rest of the system can handle it. Currently
- // this breaks SystemUI which shows a "No SIM" icon.
- sendEmptyMessage(EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS);
- } else {
- logd("Ignoring simStatus: " + simStatus);
- }
- } else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
- if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
- String reason = intent.getStringExtra(
- IccCardConstants.INTENT_KEY_LOCKED_REASON);
- sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotIndex, -1, reason));
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_LOADED, slotIndex, -1));
- } else {
- logd("Ignoring simStatus: " + simStatus);
- }
- }
- logd("[Receiver]-");
+ public void updateInternalIccState(String simStatus, String reason, int slotId) {
+ logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason
+ + " slotId " + slotId);
+ int message = internalIccStateToMessage(simStatus);
+ if (message != EVENT_INVALID) {
+ sendMessage(obtainMessage(message, slotId, -1, reason));
}
- };
+ }
+
+ 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;
+ }
+ }
private boolean isAllIccIdQueryDone() {
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
@@ -240,60 +206,9 @@
return true;
}
- public void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) {
- SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
- if (subInfo != null) {
- // overwrite SIM display name if it is not assigned by user
- int oldNameSource = subInfo.getNameSource();
- CharSequence oldSubName = subInfo.getDisplayName();
- logd("[setDisplayNameForNewSub] subId = " + subInfo.getSubscriptionId()
- + ", oldSimName = " + oldSubName + ", oldNameSource = " + oldNameSource
- + ", newSubName = " + newSubName + ", newNameSource = " + newNameSource);
- if (oldSubName == null ||
- (oldNameSource ==
- SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE && newSubName != null) ||
- (oldNameSource == SubscriptionManager.NAME_SOURCE_SIM_SOURCE && newSubName != null
- && !newSubName.equals(oldSubName))) {
- mSubscriptionManager.setDisplayName(newSubName, subInfo.getSubscriptionId(),
- newNameSource);
- }
- } else {
- logd("SUB" + (subId + 1) + " SubInfo not created yet");
- }
- }
-
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case EVENT_SIM_LOCKED_QUERY_ICCID_DONE: {
- AsyncResult ar = (AsyncResult)msg.obj;
- QueryIccIdUserObj uObj = (QueryIccIdUserObj) ar.userObj;
- int slotId = uObj.slotId;
- logd("handleMessage : <EVENT_SIM_LOCKED_QUERY_ICCID_DONE> SIM" + (slotId + 1));
- if (ar.exception == null) {
- if (ar.result != null) {
- byte[] data = (byte[])ar.result;
- mIccId[slotId] = IccUtils.bcdToString(data, 0, data.length);
- } else {
- logd("Null ar");
- mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
- }
- } else {
- mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
- logd("Query IccId fail: " + ar.exception);
- }
- logd("sIccId[" + slotId + "] = " + mIccId[slotId]);
- if (isAllIccIdQueryDone()) {
- updateSubscriptionInfoByIccId();
- }
- broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED,
- uObj.reason);
- if (!ICCID_STRING_FOR_NO_SIM.equals(mIccId[slotId])) {
- updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- }
- break;
- }
-
case EVENT_GET_NETWORK_SELECTION_MODE_DONE: {
AsyncResult ar = (AsyncResult)msg.obj;
Integer slotId = (Integer)ar.userObj;
@@ -308,7 +223,7 @@
break;
}
- case EVENT_SIM_LOADED:
+ case EVENT_SIM_LOADED:
handleSimLoaded(msg.arg1);
break;
@@ -322,6 +237,9 @@
case EVENT_SIM_UNKNOWN:
updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
+ broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null);
+ broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
+ broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
break;
case EVENT_SIM_IO_ERROR:
@@ -330,8 +248,35 @@
case EVENT_SIM_RESTRICTED:
updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_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);
break;
+ case EVENT_SIM_READY:
+ broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_READY, null);
+ broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_PRESENT);
+ broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY);
+ break;
+
+ case EVENT_SIM_IMSI:
+ broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_IMSI, null);
+ break;
+
+ case EVENT_SIM_NOT_READY:
+ broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
+ null);
+ broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_PRESENT);
+ broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY);
+ // intentional fall through
+ // ICC_NOT_READY is a terminal state for an eSIM on the boot profile. At this
+ // phase, the subscription list is accessible.
+ // TODO(b/64216093): Clean up this special case, likely by treating NOT_READY
+ // as equivalent to ABSENT, once the rest of the system can handle it. Currently
+ // this breaks SystemUI which shows a "No SIM" icon.
+
case EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS:
if (updateEmbeddedSubscriptions()) {
SubscriptionController.getInstance().notifySubscriptionInfoChanged();
@@ -350,40 +295,56 @@
sendMessage(obtainMessage(EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS, callback));
}
- private static class QueryIccIdUserObj {
- public String reason;
- public int slotId;
-
- QueryIccIdUserObj(String reason, int slotId) {
- this.reason = reason;
- this.slotId = slotId;
- }
- };
-
private void handleSimLocked(int slotId, String reason) {
if (mIccId[slotId] != null && mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (slotId + 1) + " hot plug in");
mIccId[slotId] = null;
}
-
- IccFileHandler fileHandler = mPhone[slotId].getIccCard() == null ? null :
- mPhone[slotId].getIccCard().getIccFileHandler();
-
- if (fileHandler != null) {
- String iccId = mIccId[slotId];
- if (iccId == null) {
- logd("Querying IccId");
- fileHandler.loadEFTransparent(IccConstants.EF_ICCID,
- obtainMessage(EVENT_SIM_LOCKED_QUERY_ICCID_DONE,
- new QueryIccIdUserObj(reason, slotId)));
- } else {
- logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
- updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
+ String iccId = mIccId[slotId];
+ if (iccId == null) {
+ IccCard iccCard = mPhone[slotId].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;
+ }
+ mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
} else {
- logd("sFh[" + slotId + "] is null, ignore");
+ logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
+ }
+
+ if (isAllIccIdQueryDone()) {
+ updateSubscriptionInfoByIccId();
+ }
+
+ updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+ broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
+ broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_PRESENT);
+ broadcastSimApplicationStateChanged(slotId, getSimStateFromLockedReason(reason));
+ }
+
+ 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;
}
}
@@ -394,16 +355,21 @@
// removed or a refresh RESET that the IccRecords could be null. The right behavior is to
// not broadcast the SIM loaded.
int loadedSlotId = slotId;
- IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
+ IccCard iccCard = mPhone[slotId].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 (records.getIccId() == null) {
+ if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) {
logd("handleSimLoaded: IccID null");
return;
}
- mIccId[slotId] = records.getIccId();
+ mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
if (isAllIccIdQueryDone()) {
updateSubscriptionInfoByIccId();
@@ -416,7 +382,7 @@
if (!TextUtils.isEmpty(operator)) {
if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
- MccTable.updateMccMncConfiguration(mContext, operator, false);
+ MccTable.updateMccMncConfiguration(mContext, operator);
}
SubscriptionController.getInstance().setMccMnc(operator, subId);
} else {
@@ -432,6 +398,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, number,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Long.toString(subId), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
@@ -451,6 +420,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, name,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ "=" + Long.toString(subId), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
/* Update preferred network type and network selection mode on SIM change.
@@ -460,13 +432,29 @@
int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
if (storedSubId != subId) {
- int networkType = RILConstants.PREFERRED_NETWORK_MODE;
+ int networkType = Settings.Global.getInt(
+ mPhone[slotId].getContext().getContentResolver(),
+ Settings.Global.PREFERRED_NETWORK_MODE + subId,
+ -1 /* invalid network mode */);
+
+ if (networkType == -1) {
+ networkType = RILConstants.PREFERRED_NETWORK_MODE;
+ try {
+ networkType = TelephonyManager.getIntAtIndex(
+ mContext.getContentResolver(),
+ Settings.Global.PREFERRED_NETWORK_MODE, slotId);
+ } catch (SettingNotFoundException retrySnfe) {
+ Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
+ + "Settings.Global.PREFERRED_NETWORK_MODE");
+ }
+ Settings.Global.putInt(
+ mPhone[slotId].getContext().getContentResolver(),
+ Global.PREFERRED_NETWORK_MODE + subId,
+ networkType);
+ }
// Set the modem network mode
mPhone[slotId].setPreferredNetworkType(networkType, null);
- Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
- Settings.Global.PREFERRED_NETWORK_MODE + subId,
- networkType);
// Only support automatic selection mode on SIM change.
mPhone[slotId].getNetworkSelectionMode(
@@ -487,6 +475,8 @@
mContext.getContentResolver(), mCurrentlyActiveUserId);
broadcastSimStateChanged(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
+ broadcastSimCardStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_PRESENT);
+ broadcastSimApplicationStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_LOADED);
updateCarrierServices(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
}
@@ -506,6 +496,9 @@
updateSubscriptionInfoByIccId();
}
updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+ broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
+ broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_ABSENT);
+ broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
}
private void handleSimError(int slotId) {
@@ -517,6 +510,10 @@
updateSubscriptionInfoByIccId();
}
updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+ broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
+ IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+ broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
+ broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
}
/**
@@ -563,16 +560,18 @@
ContentResolver contentResolver = mContext.getContentResolver();
String[] oldIccId = new String[PROJECT_SIM_NUM];
+ String[] decIccId = new String[PROJECT_SIM_NUM];
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
oldIccId[i] = null;
- List<SubscriptionInfo> oldSubInfo =
- SubscriptionController.getInstance().getSubInfoUsingSlotIndexWithCheck(i, false,
- mContext.getOpPackageName());
+ List<SubscriptionInfo> oldSubInfo = SubscriptionController.getInstance()
+ .getSubInfoUsingSlotIndexPrivileged(i, false);
+ decIccId[i] = IccUtils.getDecimalSubstring(mIccId[i]);
if (oldSubInfo != null && oldSubInfo.size() > 0) {
oldIccId[i] = oldSubInfo.get(0).getIccId();
logd("updateSubscriptionInfoByIccId: oldSubId = "
+ oldSubInfo.get(0).getSubscriptionId());
- if (mInsertSimState[i] == SIM_NOT_CHANGE && !mIccId[i].equals(oldIccId[i])) {
+ if (mInsertSimState[i] == SIM_NOT_CHANGE && !(mIccId[i].equals(oldIccId[i])
+ || (decIccId[i] != null && decIccId[i].equals(oldIccId[i])))) {
mInsertSimState[i] = SIM_CHANGED;
}
if (mInsertSimState[i] != SIM_NOT_CHANGE) {
@@ -582,6 +581,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
} else {
if (mInsertSimState[i] == SIM_NOT_CHANGE) {
@@ -599,8 +601,6 @@
}
//check if the inserted SIM is new SIM
- int nNewCardCount = 0;
- int nNewSimStatus = 0;
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
if (mInsertSimState[i] == SIM_NOT_INSERT) {
logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time");
@@ -612,25 +612,11 @@
+ Integer.toString(mInsertSimState[i]), i);
logd("SUB" + (i + 1) + " has invalid IccId");
} else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
+ logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
+ + mIccId[i] + "slot: " + i);
mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
}
- if (isNewSim(mIccId[i], oldIccId)) {
- nNewCardCount++;
- switch (i) {
- case PhoneConstants.SUB1:
- nNewSimStatus |= STATUS_SIM1_INSERTED;
- break;
- case PhoneConstants.SUB2:
- nNewSimStatus |= STATUS_SIM2_INSERTED;
- break;
- case PhoneConstants.SUB3:
- nNewSimStatus |= STATUS_SIM3_INSERTED;
- break;
- //case PhoneConstants.SUB3:
- // nNewSimStatus |= STATUS_SIM4_INSERTED;
- // break;
- }
-
+ if (isNewSim(mIccId[i], decIccId[i], oldIccId)) {
mInsertSimState[i] = SIM_NEW;
}
}
@@ -659,6 +645,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(temp.getSubscriptionId()), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
}
@@ -696,20 +685,25 @@
}
final EuiccProfileInfo[] embeddedProfiles;
- if (result.result == EuiccService.RESULT_OK) {
- embeddedProfiles = result.profiles;
+ if (result.getResult() == EuiccService.RESULT_OK) {
+ List<EuiccProfileInfo> list = result.getProfiles();
+ if (list == null || list.size() == 0) {
+ embeddedProfiles = new EuiccProfileInfo[0];
+ } else {
+ embeddedProfiles = list.toArray(new EuiccProfileInfo[list.size()]);
+ }
} else {
- logd("updatedEmbeddedSubscriptions: error " + result.result + " listing profiles");
+ logd("updatedEmbeddedSubscriptions: error " + result.getResult() + " listing profiles");
// If there's an error listing profiles, treat it equivalently to a successful
// listing which returned no profiles under the assumption that none are currently
// accessible.
embeddedProfiles = new EuiccProfileInfo[0];
}
- final boolean isRemovable = result.isRemovable;
+ final boolean isRemovable = result.getIsRemovable();
final String[] embeddedIccids = new String[embeddedProfiles.length];
for (int i = 0; i < embeddedProfiles.length; i++) {
- embeddedIccids[i] = embeddedProfiles[i].iccid;
+ embeddedIccids[i] = embeddedProfiles[i].getIccid();
}
// Note that this only tracks whether we make any writes to the DB. It's possible this will
@@ -727,25 +721,33 @@
ContentResolver contentResolver = mContext.getContentResolver();
for (EuiccProfileInfo embeddedProfile : embeddedProfiles) {
int index =
- findSubscriptionInfoForIccid(existingSubscriptions, embeddedProfile.iccid);
+ findSubscriptionInfoForIccid(existingSubscriptions, embeddedProfile.getIccid());
if (index < 0) {
// No existing entry for this ICCID; create an empty one.
SubscriptionController.getInstance().insertEmptySubInfoRecord(
- embeddedProfile.iccid, SubscriptionManager.SIM_NOT_INSERTED);
+ embeddedProfile.getIccid(), SubscriptionManager.SIM_NOT_INSERTED);
} else {
existingSubscriptions.remove(index);
}
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,
- embeddedProfile.accessRules == null ? null :
- UiccAccessRule.encodeRules(embeddedProfile.accessRules));
+ isRuleListEmpty ? null : UiccAccessRule.encodeRules(
+ ruleList.toArray(new UiccAccessRule[ruleList.size()])));
values.put(SubscriptionManager.IS_REMOVABLE, isRemovable);
- values.put(SubscriptionManager.DISPLAY_NAME, embeddedProfile.nickname);
+ values.put(SubscriptionManager.DISPLAY_NAME, embeddedProfile.getNickname());
values.put(SubscriptionManager.NAME_SOURCE, SubscriptionManager.NAME_SOURCE_USER_INPUT);
hasChanges = true;
contentResolver.update(SubscriptionManager.CONTENT_URI, values,
- SubscriptionManager.ICC_ID + "=\"" + embeddedProfile.iccid + "\"", null);
+ SubscriptionManager.ICC_ID + "=\"" + embeddedProfile.getIccid() + "\"", null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
// Remove all remaining subscriptions which have embedded = true. We set embedded to false
@@ -766,6 +768,9 @@
values.put(SubscriptionManager.IS_EMBEDDED, 0);
hasChanges = true;
contentResolver.update(SubscriptionManager.CONTENT_URI, values, whereClause, null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
return hasChanges;
@@ -780,12 +785,15 @@
return -1;
}
- private boolean isNewSim(String iccId, String[] oldIccId) {
+ private boolean isNewSim(String iccId, String decIccId, String[] oldIccId) {
boolean newSim = true;
for(int i = 0; i < PROJECT_SIM_NUM; i++) {
if(iccId.equals(oldIccId[i])) {
newSim = false;
break;
+ } else if (decIccId != null && decIccId.equals(oldIccId[i])) {
+ newSim = false;
+ break;
}
}
logd("newSim = " + newSim);
@@ -810,9 +818,68 @@
IntentBroadcaster.getInstance().broadcastStickyIntent(i, slotId);
}
- public void dispose() {
- logd("[dispose]");
- mContext.unregisterReceiver(sReceiver);
+ private 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.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
+ logd("Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED " + simStateString(state)
+ + " for phone: " + phoneId);
+ mContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+ }
+
+ private 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
+ if (!(state == sSimApplicationState[phoneId]
+ || (state == TelephonyManager.SIM_STATE_NOT_READY
+ && sSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN))) {
+ sSimApplicationState[phoneId] = state;
+ Intent i = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ i.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
+ logd("Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED " + simStateString(state)
+ + " for phone: " + phoneId);
+ mContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+ }
+
+ private static String simStateString(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";
+ }
}
private void logd(String message) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionMonitor.java b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
index 4307875..8ccec6f 100644
--- a/src/java/com/android/internal/telephony/SubscriptionMonitor.java
+++ b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
@@ -70,7 +70,7 @@
public SubscriptionMonitor(ITelephonyRegistry tr, Context context,
SubscriptionController subscriptionController, int numPhones) {
try {
- tr.addOnSubscriptionsChangedListener("SubscriptionMonitor",
+ tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
mSubscriptionsChangedListener);
} catch (RemoteException e) {
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 193d29e..dc8d9fa 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -20,7 +20,9 @@
import android.database.Cursor;
import android.os.Handler;
import android.os.IDeviceIdleController;
+import android.os.Looper;
import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants.TransportType;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
@@ -28,7 +30,9 @@
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
-import com.android.internal.telephony.uicc.IccCardProxy;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccProfile;
/**
* This class has one-line methods to instantiate objects only. The purpose is to make code
@@ -61,12 +65,27 @@
return new ServiceStateTracker(phone, ci);
}
+ /**
+ * Sets the NitzStateMachine implementation to use during implementation. This boolean
+ * should be removed once the new implementation is stable.
+ */
+ static final boolean USE_NEW_NITZ_STATE_MACHINE = true;
+
+ /**
+ * Returns a new {@link NitzStateMachine} instance.
+ */
+ public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) {
+ return USE_NEW_NITZ_STATE_MACHINE
+ ? new NewNitzStateMachine(phone)
+ : new OldNitzStateMachine(phone);
+ }
+
public SimActivationTracker makeSimActivationTracker(Phone phone) {
return new SimActivationTracker(phone);
}
public DcTracker makeDcTracker(Phone phone) {
- return new DcTracker(phone);
+ return new DcTracker(phone, TransportType.WWAN);
}
public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
@@ -77,6 +96,10 @@
return new CarrierActionAgent(phone);
}
+ public CarrierIdentifier makeCarrierIdentifier(Phone phone) {
+ return new CarrierIdentifier(phone);
+ }
+
public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) {
return new IccPhoneBookInterfaceManager(phone);
}
@@ -85,8 +108,12 @@
return new IccSmsInterfaceManager(phone);
}
- public IccCardProxy makeIccCardProxy(Context context, CommandsInterface ci, int phoneId) {
- return new IccCardProxy(context, ci, phoneId);
+ /**
+ * Create a new UiccProfile object.
+ */
+ public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics,
+ int phoneId, UiccCard uiccCard, Object lock) {
+ return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock);
}
public EriManager makeEriManager(Phone phone, Context context, int eriFileSource) {
@@ -154,4 +181,7 @@
return IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
}
+ public LocaleTracker makeLocaleTracker(Phone phone, Looper looper) {
+ return new LocaleTracker(phone, looper);
+ }
}
diff --git a/src/java/com/android/internal/telephony/TelephonyDevController.java b/src/java/com/android/internal/telephony/TelephonyDevController.java
index 448e5bd..3cda417 100644
--- a/src/java/com/android/internal/telephony/TelephonyDevController.java
+++ b/src/java/com/android/internal/telephony/TelephonyDevController.java
@@ -154,6 +154,7 @@
if (item.uuid.compareTo(hw.uuid) == 0) {
if (DBG) logd("updateOrInsert: removing: " + item);
list.remove(i);
+ break;
}
}
if (DBG) logd("updateOrInsert: inserting: " + hw);
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 0e80937..52235e4 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -25,12 +25,12 @@
import android.os.Build;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsExternalCallState;
+import android.telephony.ims.ImsReasonInfo;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsExternalCallState;
-import com.android.ims.ImsReasonInfo;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
@@ -50,6 +50,10 @@
* adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached
* adb shell am broadcast -a com.android.internal.telephony.TestConferenceEventPackage -e filename
* test_filename.xml
+ * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --ei data_rat 10 --ei
+ * data_roaming_type 3
+ * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset
+ *
*/
public class TelephonyTester {
private static final String LOG_TAG = "TelephonyTester";
@@ -98,9 +102,26 @@
"com.android.internal.telephony.TestSuppSrvcNotification";
private static final String EXTRA_CODE = "code";
+ private static final String EXTRA_TYPE = "type";
+
+ private static final String ACTION_TEST_SERVICE_STATE =
+ "com.android.internal.telephony.TestServiceState";
+
+ private static final String EXTRA_ACTION = "action";
+ 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";
+ private static final String EXTRA_DATA_REG_STATE = "data_reg_state";
+ private static final String EXTRA_VOICE_ROAMING_TYPE = "voice_roaming_type";
+ private static final String EXTRA_DATA_ROAMING_TYPE = "data_roaming_type";
+ private static final String EXTRA_OPERATOR = "operator";
+
+ private static final String ACTION_RESET = "reset";
private static List<ImsExternalCallState> mImsExternalCallStates = null;
+ private Intent mServiceStateTestIntent;
+
private Phone mPhone;
// The static intent receiver one for all instances and we assume this
@@ -133,6 +154,13 @@
} else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) {
log("handle supp service notification test intent");
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);
} else {
if (DBG) log("onReceive: unknown action=" + action);
}
@@ -162,6 +190,9 @@
filter.addAction(ACTION_TEST_HANDOVER_FAIL);
filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
+ } else {
+ filter.addAction(ACTION_TEST_SERVICE_STATE);
+ log("register for intent action=" + ACTION_TEST_SERVICE_STATE);
}
phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
@@ -286,8 +317,9 @@
}
private void sendTestSuppServiceNotification(Intent intent) {
- if (intent.hasExtra(EXTRA_CODE)) {
+ if (intent.hasExtra(EXTRA_CODE) && intent.hasExtra(EXTRA_TYPE)) {
int code = intent.getIntExtra(EXTRA_CODE, -1);
+ int type = intent.getIntExtra(EXTRA_TYPE, -1);
ImsPhone imsPhone = (ImsPhone) mPhone;
if (imsPhone == null) {
return;
@@ -295,7 +327,52 @@
log("Test supp service notification:" + code);
SuppServiceNotification suppServiceNotification = new SuppServiceNotification();
suppServiceNotification.code = code;
+ suppServiceNotification.notificationType = type;
imsPhone.notifySuppSvcNotification(suppServiceNotification);
}
}
-}
+
+ void overrideServiceState(ServiceState ss) {
+ if (mServiceStateTestIntent == null || ss == null) return;
+ if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION)
+ && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) {
+ log("Service state override reset");
+ return;
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
+ ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
+ ServiceState.STATE_OUT_OF_SERVICE));
+ log("Override voice service state with " + ss.getVoiceRegState());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
+ ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
+ ServiceState.STATE_OUT_OF_SERVICE));
+ log("Override data service state with " + ss.getDataRegState());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) {
+ ss.setRilVoiceRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_RAT,
+ ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
+ log("Override voice rat with " + ss.getRilVoiceRadioTechnology());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_RAT)) {
+ ss.setRilDataRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_RAT,
+ ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
+ log("Override data rat with " + ss.getRilDataRadioTechnology());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_ROAMING_TYPE)) {
+ ss.setVoiceRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_ROAMING_TYPE,
+ ServiceState.ROAMING_TYPE_UNKNOWN));
+ log("Override voice roaming type with " + ss.getVoiceRoamingType());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_ROAMING_TYPE)) {
+ ss.setDataRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_ROAMING_TYPE,
+ ServiceState.ROAMING_TYPE_UNKNOWN));
+ log("Override data roaming type with " + ss.getDataRoamingType());
+ }
+ if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
+ String operator = mServiceStateTestIntent.getStringExtra(EXTRA_OPERATOR);
+ ss.setOperatorName(operator, operator, "");
+ log("Override operator with " + operator);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
new file mode 100644
index 0000000..101fddd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 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;
+
+import android.text.TextUtils;
+
+import libcore.util.CountryTimeZones;
+import libcore.util.TimeZoneFinder;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * An interface to various time zone lookup behaviors.
+ */
+// Non-final to allow mocking.
+public class TimeZoneLookupHelper {
+
+ /**
+ * The result of looking up a time zone using offset information (and possibly more).
+ */
+ public static final class OffsetResult {
+
+ /** A zone that matches the supplied criteria. See also {@link #isOnlyMatch}. */
+ public final String zoneId;
+
+ /** True if there is only one matching time zone for the supplied criteria. */
+ public final boolean isOnlyMatch;
+
+ public OffsetResult(String zoneId, boolean isOnlyMatch) {
+ this.zoneId = zoneId;
+ this.isOnlyMatch = isOnlyMatch;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ OffsetResult result = (OffsetResult) o;
+
+ if (isOnlyMatch != result.isOnlyMatch) {
+ return false;
+ }
+ return zoneId.equals(result.zoneId);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = zoneId.hashCode();
+ result = 31 * result + (isOnlyMatch ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Result{"
+ + "zoneId='" + zoneId + '\''
+ + ", isOnlyMatch=" + isOnlyMatch
+ + '}';
+ }
+ }
+
+ /**
+ * The result of looking up a time zone using country information.
+ */
+ public static final class CountryResult {
+
+ /** A time zone for the country. */
+ public final String zoneId;
+
+ /**
+ * True if all the time zones in the country have the same offset at {@link #whenMillis}.
+ */
+ public final boolean allZonesHaveSameOffset;
+
+ /** The time associated with {@link #allZonesHaveSameOffset}. */
+ public final long whenMillis;
+
+ public CountryResult(String zoneId, boolean allZonesHaveSameOffset, long whenMillis) {
+ this.zoneId = zoneId;
+ this.allZonesHaveSameOffset = allZonesHaveSameOffset;
+ this.whenMillis = whenMillis;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CountryResult that = (CountryResult) o;
+
+ if (allZonesHaveSameOffset != that.allZonesHaveSameOffset) {
+ return false;
+ }
+ if (whenMillis != that.whenMillis) {
+ return false;
+ }
+ return zoneId.equals(that.zoneId);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = zoneId.hashCode();
+ result = 31 * result + (allZonesHaveSameOffset ? 1 : 0);
+ result = 31 * result + (int) (whenMillis ^ (whenMillis >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CountryResult{"
+ + "zoneId='" + zoneId + '\''
+ + ", allZonesHaveSameOffset=" + allZonesHaveSameOffset
+ + ", whenMillis=" + whenMillis
+ + '}';
+ }
+ }
+
+ private static final int MS_PER_HOUR = 60 * 60 * 1000;
+
+ /** The last CountryTimeZones object retrieved. */
+ private CountryTimeZones mLastCountryTimeZones;
+
+ public TimeZoneLookupHelper() {}
+
+ /**
+ * Looks for a time zone for the supplied NITZ and country information.
+ *
+ * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates
+ * will be returned in the result. If the current device default zone matches it will be
+ * returned in preference to other candidates. This method can return {@code null} if no
+ * matching time zones are found.
+ */
+ public OffsetResult lookupByNitzCountry(NitzData nitzData, String isoCountryCode) {
+ CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
+ if (countryTimeZones == null) {
+ return null;
+ }
+ android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
+
+ CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias(
+ nitzData.getLocalOffsetMillis(), nitzData.isDst(),
+ nitzData.getCurrentTimeInMillis(), bias);
+
+ if (offsetResult == null) {
+ return null;
+ }
+ return new OffsetResult(offsetResult.mTimeZone.getID(), offsetResult.mOneMatch);
+ }
+
+ /**
+ * Looks for a time zone using only information present in the supplied {@link NitzData} object.
+ *
+ * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given
+ * time this process is error prone; an arbitrary match is returned when there are multiple
+ * candidates. The algorithm can also return a non-exact match by assuming that the DST
+ * information provided by NITZ is incorrect. This method can return {@code null} if no matching
+ * time zones are found.
+ */
+ public OffsetResult lookupByNitz(NitzData nitzData) {
+ return lookupByNitzStatic(nitzData);
+ }
+
+ /**
+ * Returns a time zone ID for the country if possible. For counties that use a single time zone
+ * this will provide a good choice. For countries with multiple time zones, a time zone is
+ * returned if all time zones used in the country currently have the same offset (currently ==
+ * according to the device's current system clock time). If this is not the case then
+ * {@code null} can be returned.
+ */
+ public CountryResult lookupByCountry(String isoCountryCode, long whenMillis) {
+ CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
+ if (countryTimeZones == null) {
+ // Unknown country code.
+ return null;
+ }
+ if (countryTimeZones.getDefaultTimeZoneId() == null) {
+ return null;
+ }
+
+ return new CountryResult(
+ countryTimeZones.getDefaultTimeZoneId(),
+ countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis),
+ whenMillis);
+ }
+
+ /**
+ * Finds a time zone using only information present in the supplied {@link NitzData} object.
+ * This is a static method for use by {@link ServiceStateTracker}.
+ *
+ * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given
+ * time this process is error prone; an arbitrary match is returned when there are multiple
+ * candidates. The algorithm can also return a non-exact match by assuming that the DST
+ * information provided by NITZ is incorrect. This method can return {@code null} if no matching
+ * time zones are found.
+ */
+ static TimeZone guessZoneByNitzStatic(NitzData nitzData) {
+ OffsetResult result = lookupByNitzStatic(nitzData);
+ return result != null ? TimeZone.getTimeZone(result.zoneId) : null;
+ }
+
+ private static OffsetResult lookupByNitzStatic(NitzData nitzData) {
+ int utcOffsetMillis = nitzData.getLocalOffsetMillis();
+ boolean isDst = nitzData.isDst();
+ long timeMillis = nitzData.getCurrentTimeInMillis();
+
+ OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst);
+ if (match == null) {
+ // Couldn't find a proper timezone. Perhaps the DST data is wrong.
+ match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst);
+ }
+ return match;
+ }
+
+ private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis,
+ boolean isDst) {
+ int rawOffset = utcOffsetMillis;
+ if (isDst) {
+ rawOffset -= MS_PER_HOUR;
+ }
+ String[] zones = TimeZone.getAvailableIDs(rawOffset);
+ TimeZone match = null;
+ Date d = new Date(timeMillis);
+ boolean isOnlyMatch = true;
+ for (String zone : zones) {
+ TimeZone tz = TimeZone.getTimeZone(zone);
+ if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) {
+ if (match == null) {
+ match = tz;
+ } else {
+ isOnlyMatch = false;
+ break;
+ }
+ }
+ }
+
+ if (match == null) {
+ return null;
+ }
+ return new OffsetResult(match.getID(), isOnlyMatch);
+ }
+
+ /**
+ * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to
+ * use a raw offset of zero from UTC at the time specified.
+ */
+ public boolean countryUsesUtc(String isoCountryCode, long whenMillis) {
+ if (TextUtils.isEmpty(isoCountryCode)) {
+ return false;
+ }
+
+ CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
+ return countryTimeZones != null && countryTimeZones.hasUtcZone(whenMillis);
+ }
+
+ private CountryTimeZones getCountryTimeZones(String isoCountryCode) {
+ // A single entry cache of the last CountryTimeZones object retrieved since there should
+ // be strong consistency across calls.
+ synchronized (this) {
+ if (mLastCountryTimeZones != null) {
+ if (mLastCountryTimeZones.isForCountryCode(isoCountryCode)) {
+ return mLastCountryTimeZones;
+ }
+ }
+
+ // Perform the lookup. It's very unlikely to return null, but we won't cache null.
+ CountryTimeZones countryTimeZones =
+ TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode);
+ if (countryTimeZones != null) {
+ mLastCountryTimeZones = countryTimeZones;
+ }
+ return countryTimeZones;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/TransportManager.java b/src/java/com/android/internal/telephony/TransportManager.java
new file mode 100644
index 0000000..d81130a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TransportManager.java
@@ -0,0 +1,50 @@
+/*
+ * 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.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents the transport manager which manages available transports and
+ * route requests to correct transport.
+ */
+public class TransportManager {
+ private static final String TAG = TransportManager.class.getSimpleName();
+
+ private List<Integer> mAvailableTransports = new ArrayList<>();
+
+ public TransportManager() {
+ // TODO: get transpot list from AccessNetworkManager.
+ mAvailableTransports.add(TransportType.WWAN);
+ }
+
+ public List<Integer> getAvailableTransports() {
+ return new ArrayList<>(mAvailableTransports);
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
old mode 100755
new mode 100644
index e6cb972..5fb2ff3
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.net.Uri;
import android.os.Binder;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
@@ -33,13 +32,16 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
- * UiccSmsController to provide an inter-process communication to
- * access Sms in Icc.
+ * Implements the ISmsImplBase interface used in the SmsManager API.
*/
-public class UiccSmsController extends ISms.Stub {
+public class UiccSmsController extends ISmsImplBase {
static final String LOG_TAG = "RIL_UiccSmsController";
protected UiccSmsController() {
@@ -57,9 +59,8 @@
}
@Override
- public boolean
- updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
- byte[] pdu) throws android.os.RemoteException {
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index,
+ int status, byte[] pdu) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
@@ -72,7 +73,7 @@
@Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
- byte[] pdu, byte[] smsc) throws android.os.RemoteException {
+ byte[] pdu, byte[] smsc) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
@@ -84,8 +85,7 @@
}
@Override
- public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage)
- throws android.os.RemoteException {
+ public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
@@ -112,6 +112,7 @@
}
}
+ @Override
public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPackage,
String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
@@ -125,12 +126,6 @@
}
}
- public void sendText(String callingPackage, String destAddr, String scAddr,
- String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
- text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
- }
-
@Override
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
@@ -146,6 +141,7 @@
}
}
+ @Override
public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPackage,
String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, boolean persistMessage) {
@@ -159,19 +155,25 @@
}
}
- public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
- List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
- sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
- scAddr, parts, sentIntents, deliveryIntents,
- true /* persistMessageForNonDefaultSmsApp */);
+ @Override
+ public void sendTextForSubscriberWithOptions(int subId, String callingPackage,
+ String destAddr, String scAddr, String parts, PendingIntent sentIntents,
+ PendingIntent deliveryIntents, boolean persistMessage, int priority,
+ boolean expectMore, int validityPeriod) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ iccSmsIntMgr.sendTextWithOptions(callingPackage, destAddr, scAddr, parts, sentIntents,
+ deliveryIntents, persistMessage, priority, expectMore, validityPeriod);
+ } else {
+ Rlog.e(LOG_TAG,"sendTextWithOptions iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
}
@Override
public void sendMultipartTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
- throws android.os.RemoteException {
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
@@ -184,15 +186,30 @@
}
@Override
- public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws android.os.RemoteException {
+ public void sendMultipartTextForSubscriberWithOptions(int subId, String callingPackage,
+ String destAddr, String scAddr, List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessage, int priority,
+ boolean expectMore, int validityPeriod) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null ) {
+ iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, destAddr, scAddr, parts,
+ sentIntents, deliveryIntents, persistMessage, priority, expectMore,
+ validityPeriod);
+ } else {
+ Rlog.e(LOG_TAG,"sendMultipartTextWithOptions iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ }
+ }
+
+ @Override
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
@Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws android.os.RemoteException {
+ int endMessageId, int ranType) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -204,15 +221,15 @@
}
@Override
- public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws android.os.RemoteException {
+ public boolean disableCellBroadcastForSubscriber(int subId,
+ int messageIdentifier, int ranType) {
return disableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
@Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws android.os.RemoteException {
+ int endMessageId, int ranType) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -324,14 +341,6 @@
}
/**
- * Get sms interface manager object based on subscription.
- * @return ICC SMS manager
- */
- private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
- return getPhone(subId).getIccSmsInterfaceManager();
- }
-
- /**
* Get User preferred SMS subscription
* @return User preferred SMS subscription
*/
@@ -351,7 +360,7 @@
@Override
public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
- PendingIntent sentIntent, PendingIntent deliveryIntent) throws RemoteException {
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendStoredText(callingPkg, messageUri, scAddress, sentIntent,
@@ -364,8 +373,8 @@
@Override
public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
- String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents)
- throws RemoteException {
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.sendStoredMultipartText(callingPkg, messageUri, scAddress, sentIntents,
@@ -382,6 +391,36 @@
return getPhone(subId).getAppSmsManager().createAppSpecificSmsToken(callingPkg, intent);
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter indentingPW =
+ new IndentingPrintWriter(pw, " " /* singleIndent */);
+ for (Phone phone : PhoneFactory.getPhones()) {
+ int subId = phone.getSubId();
+ indentingPW.println(String.format("SmsManager for subId = %d:", subId));
+ indentingPW.increaseIndent();
+ if (getIccSmsInterfaceManager(subId) != null) {
+ getIccSmsInterfaceManager(subId).dump(fd, indentingPW, args);
+ }
+ indentingPW.decreaseIndent();
+ }
+ indentingPW.flush();
+ }
+
+ public void sendText(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
+ text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
+ }
+
+ public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
+ sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
+ scAddr, parts, sentIntents, deliveryIntents,
+ true /* persistMessageForNonDefaultSmsApp */);
+ }
+
private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
if (intent != null) {
try {
@@ -396,4 +435,12 @@
sendErrorInPendingIntent(intent, errorCode);
}
}
+
+ /**
+ * Get sms interface manager object based on subscription.
+ * @return ICC SMS manager
+ */
+ private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+ return getPhone(subId).getIccSmsInterfaceManager();
+ }
}
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index 3c4c0d6..f4c0103 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -270,7 +270,7 @@
if (parsedPdu != null && parsedPdu.getMessageType() == MESSAGE_TYPE_NOTIFICATION_IND) {
final NotificationInd nInd = (NotificationInd) parsedPdu;
if (nInd.getFrom() != null
- && BlockChecker.isBlocked(mContext, nInd.getFrom().getString())) {
+ && BlockChecker.isBlocked(mContext, nInd.getFrom().getString(), null)) {
result.statusCode = Intents.RESULT_SMS_HANDLED;
return result;
}
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 1f2d3a0..9410b98 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -78,6 +78,7 @@
SEND_SS(0x11),
SEND_USSD(0x12),
SEND_SMS(0x13),
+ RUN_AT(0x34),
SEND_DTMF(0x14),
SET_UP_EVENT_LIST(0x05),
SET_UP_IDLE_MODE_TEXT(0x28),
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 62d8869..cbad866 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -82,6 +82,8 @@
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;
@@ -121,7 +123,6 @@
mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
break;
case PROVIDE_LOCAL_INFORMATION:
- case REFRESH:
default:
break;
}
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
old mode 100755
new mode 100644
index 802944d..1084454
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -16,6 +16,13 @@
package com.android.internal.telephony.cat;
+import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
+ .IDLE_SCREEN_AVAILABLE_EVENT;
+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.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.backup.BackupManager;
@@ -31,31 +38,26 @@
import android.os.LocaleList;
import android.os.Message;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.IccRefreshResponse;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
-import com.android.internal.telephony.uicc.IccCardStatus.CardState;
-import com.android.internal.telephony.uicc.IccRefreshResponse;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Locale;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
-
class RilMessage {
int mId;
Object mData;
@@ -134,9 +136,9 @@
/* For multisim catservice should not be singleton */
private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
- Context context, IccFileHandler fh, UiccCard ic, int slotId) {
+ Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId) {
if (ci == null || ca == null || ir == null || context == null || fh == null
- || ic == null) {
+ || uiccProfile == null) {
throw new NullPointerException(
"Service: Input parameters must not be null");
}
@@ -192,15 +194,15 @@
* @return The only Service object in the system
*/
public static CatService getInstance(CommandsInterface ci,
- Context context, UiccCard ic, int slotId) {
+ Context context, UiccProfile uiccProfile, int slotId) {
UiccCardApplication ca = null;
IccFileHandler fh = null;
IccRecords ir = null;
- if (ic != null) {
+ if (uiccProfile != null) {
/* Since Cat is not tied to any application, but rather is Uicc application
* in itself - just get first FileHandler and IccRecords object
*/
- ca = ic.getApplicationIndex(0);
+ ca = uiccProfile.getApplicationIndex(0);
if (ca != null) {
fh = ca.getIccFileHandler();
ir = ca.getIccRecords();
@@ -217,11 +219,11 @@
}
if (sInstance[slotId] == null) {
if (ci == null || ca == null || ir == null || context == null || fh == null
- || ic == null) {
+ || uiccProfile == null) {
return null;
}
- sInstance[slotId] = new CatService(ci, ca, ir, context, fh, ic, slotId);
+ sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId);
} else if ((ir != null) && (mIccRecords != ir)) {
if (mIccRecords != null) {
mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]);
@@ -350,6 +352,7 @@
* Language Selection. */
case IDLE_SCREEN_AVAILABLE_EVENT:
case LANGUAGE_SELECTION_EVENT:
+ case USER_ACTIVITY_EVENT:
break;
default:
flag = false;
@@ -392,11 +395,6 @@
break;
case DISPLAY_TEXT:
break;
- case REFRESH:
- // ME side only handles refresh commands which meant to remove IDLE
- // MODE TEXT.
- cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
- break;
case SET_UP_IDLE_MODE_TEXT:
resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED
: ResultCode.OK;
@@ -437,6 +435,13 @@
case GET_INPUT:
case GET_INKEY:
break;
+ case REFRESH:
+ case RUN_AT:
+ if (STK_DEFAULT.equals(((DisplayTextParams)cmdParams).mTextMsg.text)) {
+ // Remove the default text which was temporarily added and shall not be shown
+ ((DisplayTextParams)cmdParams).mTextMsg.text = null;
+ }
+ break;
case SEND_DTMF:
case SEND_SMS:
case SEND_SS:
@@ -625,10 +630,12 @@
CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
if (cmdType != null) {
switch (cmdType) {
+ case GET_INPUT:
case GET_INKEY:
- // ETSI TS 102 384,27.22.4.2.8.4.2.
- // If it is a response for GET_INKEY command and the response timeout
- // occured, then add DURATION TLV for variable timeout case.
+ // Please refer to the clause 6.8.21 of ETSI 102.223.
+ // The terminal shall supply the command execution duration
+ // when it issues TERMINAL RESPONSE for GET INKEY command with variable timeout.
+ // GET INPUT command should also be handled in the same manner.
if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
(cmdInput != null) && (cmdInput.duration != null)) {
getInKeyResponse(buf, cmdInput);
@@ -761,6 +768,8 @@
// Language length should be 2 byte
buf.write(0x02);
break;
+ case USER_ACTIVITY_EVENT:
+ break;
default:
break;
}
@@ -1100,15 +1109,15 @@
}
public void update(CommandsInterface ci,
- Context context, UiccCard ic) {
+ Context context, UiccProfile uiccProfile) {
UiccCardApplication ca = null;
IccRecords ir = null;
- if (ic != null) {
+ if (uiccProfile != null) {
/* Since Cat is not tied to any application, but rather is Uicc application
* in itself - just get first FileHandler and IccRecords object
*/
- ca = ic.getApplicationIndex(0);
+ ca = uiccProfile.getApplicationIndex(0);
if (ca != null) {
ir = ca.getIccRecords();
}
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index eb92888..049e668 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -28,16 +28,16 @@
import java.util.List;
import java.util.Locale;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.USER_ACTIVITY_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.BROWSER_TERMINATION_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.
- SetupEventListConstants.BROWSING_STATUS_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage
+ .SetupEventListConstants.BROWSER_TERMINATION_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage
+ .SetupEventListConstants.BROWSING_STATUS_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage
+ .SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage
+ .SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage
+ .SetupEventListConstants.USER_ACTIVITY_EVENT;
/**
* Factory class, used for decoding raw byte arrays, received from baseband,
* into a CommandParams object.
@@ -61,12 +61,6 @@
static final int LOAD_SINGLE_ICON = 1;
static final int LOAD_MULTI_ICONS = 2;
- // Command Qualifier values for refresh command
- static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00;
- static final int REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02;
- static final int REFRESH_NAA_INIT = 0x03;
- static final int REFRESH_UICC_RESET = 0x04;
-
// Command Qualifier values for PLI command
static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
@@ -188,6 +182,8 @@
break;
case SEND_DTMF:
case SEND_SMS:
+ case REFRESH:
+ case RUN_AT:
case SEND_SS:
case SEND_USSD:
cmdPending = processEventNotify(cmdDet, ctlvs);
@@ -196,10 +192,6 @@
case SET_UP_CALL:
cmdPending = processSetupCall(cmdDet, ctlvs);
break;
- case REFRESH:
- processRefresh(cmdDet, ctlvs);
- cmdPending = false;
- break;
case LAUNCH_BROWSER:
cmdPending = processLaunchBrowser(cmdDet, ctlvs);
break;
@@ -549,6 +541,11 @@
input.iconSelfExplanatory = iconId.selfExplanatory;
}
+ ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
+ if (ctlv != null) {
+ input.duration = ValueParser.retrieveDuration(ctlv);
+ }
+
input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
input.echo = (cmdDet.commandQualifier & 0x04) == 0;
@@ -580,32 +577,6 @@
}
/**
- * Processes REFRESH proactive command from the SIM card.
- *
- * @param cmdDet Command Details container object.
- * @param ctlvs List of ComprehensionTlv objects following Command Details
- * object and Device Identities object within the proactive command
- */
- private boolean processRefresh(CommandDetails cmdDet,
- List<ComprehensionTlv> ctlvs) {
-
- CatLog.d(this, "process Refresh");
-
- // REFRESH proactive command is rerouted by the baseband and handled by
- // the telephony layer. IDLE TEXT should be removed for a REFRESH command
- // with "initialization" or "reset"
- switch (cmdDet.commandQualifier) {
- case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE:
- case REFRESH_NAA_INIT_AND_FILE_CHANGE:
- case REFRESH_NAA_INIT:
- case REFRESH_UICC_RESET:
- mCmdParams = new DisplayTextParams(cmdDet, null);
- break;
- }
- return false;
- }
-
- /**
* Processes SELECT_ITEM proactive command from the SIM card.
*
* @param cmdDet Command Details container object.
@@ -917,6 +888,10 @@
ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
if (ctlv != null) {
textMsg.text = ValueParser.retrieveAlphaId(ctlv);
+ // Assign the tone message text to empty string, if alpha identifier
+ // data is null. If no alpha identifier tlv is present, then tone
+ // message text will be null.
+ if (textMsg.text == null) textMsg.text = "";
}
// parse tone duration
ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index e2522a4..d4ad532 100644
--- a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -29,7 +29,7 @@
*
* {@hide}
*/
-class ComprehensionTlv {
+public class ComprehensionTlv {
private static final String LOG_TAG = "ComprehensionTlv";
private int mTag;
private boolean mCr;
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 5a40c4e..e2c178a 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Message;
-import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.SmsCbMessage;
@@ -33,7 +32,6 @@
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.TelephonyComponentFactory;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.WspTypeDecoder;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.util.HexDump;
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 1cfdc33..bbaa3ca 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -16,42 +16,30 @@
package com.android.internal.telephony.cdma;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Message;
-import android.os.SystemProperties;
-import android.provider.Telephony.Sms;
import android.telephony.Rlog;
import android.telephony.ServiceState;
-import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
+import android.util.Pair;
-import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.ImsSMSDispatcher;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsUsageMonitor;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.util.SMSDispatcherUtil;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsMessageBase;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
public class CdmaSMSDispatcher extends SMSDispatcher {
private static final String TAG = "CdmaSMSDispatcher";
private static final boolean VDBG = false;
- public CdmaSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
- ImsSMSDispatcher imsSMSDispatcher) {
- super(phone, usageMonitor, imsSMSDispatcher);
+ public CdmaSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+ super(phone, smsDispatchersController);
Rlog.d(TAG, "CdmaSMSDispatcher created");
}
@@ -79,6 +67,31 @@
}
}
+ @Override
+ protected boolean shouldBlockSmsForEcbm() {
+ // We only block outgoing SMS during ECBM when using CDMA.
+ return mPhone.isInEcm() && isCdmaMo() && !isIms();
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod) {
+ return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, message,
+ statusReportRequested, smsHeader, priority);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested) {
+ return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, destPort, message,
+ statusReportRequested);
+ }
+
+ @Override
+ protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+ return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly);
+ }
/**
* Called from parent class to handle status report from {@code CdmaInboundSmsHandler}.
* @param sms the CDMA SMS message to process
@@ -87,18 +100,12 @@
for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
SmsTracker tracker = deliveryPendingList.get(i);
if (tracker.mMessageRef == sms.mMessageRef) {
- // Found it. Remove from list and broadcast.
- deliveryPendingList.remove(i);
- // Update the message status (COMPLETE)
- tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
-
- PendingIntent intent = tracker.mDeliveryIntent;
- Intent fillIn = new Intent();
- fillIn.putExtra("pdu", sms.getPdu());
- fillIn.putExtra("format", getFormat());
- try {
- intent.send(mContext, Activity.RESULT_OK, fillIn);
- } catch (CanceledException ex) {}
+ Pair<Boolean, Boolean> result =
+ mSmsDispatchersController.handleSmsStatusReport(tracker, getFormat(),
+ sms.getPdu());
+ if (result.second) {
+ deliveryPendingList.remove(i);
+ }
break; // Only expect to see one tracker matching this message.
}
}
@@ -106,143 +113,15 @@
/** {@inheritDoc} */
@Override
- public void sendData(String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
- scAddr, destAddr, destPort, data, (deliveryIntent != null));
- if (pdu != null) {
- HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
- SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
- false /*isText*/, true /*persistMessage*/);
-
- String carrierPackage = getCarrierAppPackageName();
- if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
- DataSmsSender smsSender = new DataSmsSender(tracker);
- smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
- } else {
- Rlog.v(TAG, "No carrier package.");
- sendSubmitPdu(tracker);
- }
- } else {
- Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null");
- if (sentIntent != null) {
- try {
- sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
- } catch (CanceledException ex) {
- Rlog.e(TAG, "Intent has been canceled!");
- }
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
- boolean persistMessage) {
- SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
- scAddr, destAddr, text, (deliveryIntent != null), null);
- if (pdu != null) {
- HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
- SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);
-
- String carrierPackage = getCarrierAppPackageName();
- if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
- TextSmsSender smsSender = new TextSmsSender(tracker);
- smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
- } else {
- Rlog.v(TAG, "No carrier package.");
- sendSubmitPdu(tracker);
- }
- } else {
- Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");
- if (sentIntent != null) {
- try {
- sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
- } catch (CanceledException ex) {
- Rlog.e(TAG, "Intent has been canceled!");
- }
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
- }
-
- /** {@inheritDoc} */
- @Override
- protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
- boolean use7bitOnly) {
- return SmsMessage.calculateLength(messageBody, use7bitOnly, false);
- }
-
- /** {@inheritDoc} */
- @Override
- protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
- String message, SmsHeader smsHeader, int encoding,
- PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
- AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
- String fullMessageText) {
- UserData uData = new UserData();
- uData.payloadStr = message;
- uData.userDataHeader = smsHeader;
- if (encoding == SmsConstants.ENCODING_7BIT) {
- uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
- } else { // assume UTF-16
- uData.msgEncoding = UserData.ENCODING_UNICODE_16;
- }
- uData.msgEncodingSet = true;
-
- /* By setting the statusReportRequested bit only for the
- * last message fragment, this will result in only one
- * callback to the sender when that last fragment delivery
- * has been acknowledged. */
- SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress,
- uData, (deliveryIntent != null) && lastPart);
-
- HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
- message, submitPdu);
- return getSmsTracker(map, sentIntent, deliveryIntent,
- getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
- false /*isExpextMore*/, fullMessageText, true /*isText*/,
- true /*persistMessage*/);
- }
-
- @Override
- protected void sendSubmitPdu(SmsTracker tracker) {
- if (mPhone.isInEcm()) {
- if (VDBG) {
- Rlog.d(TAG, "Block SMS in Emergency Callback mode");
- }
- tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
- return;
- }
- sendRawPdu(tracker);
- }
-
- /** {@inheritDoc} */
- @Override
public void sendSms(SmsTracker tracker) {
Rlog.d(TAG, "sendSms: "
+ " isIms()=" + isIms()
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + mPhone.getServiceState().getState());
- sendSmsByPstn(tracker);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void sendSmsByPstn(SmsTracker tracker) {
int ss = mPhone.getServiceState().getState();
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
@@ -264,8 +143,11 @@
// sms over cdma is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
- // indicated by mImsRetry > 0
- if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {
+ // indicated by mImsRetry > 0 OR
+ // SMS over IMS is disabled because of the network type OR
+ // SMS over IMS is being handled by the ImsSmsDispatcher implementation and has indicated
+ // that the message should fall back to sending over CS.
+ if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled || tracker.mUsesImsServiceForIms) {
mCi.sendCdmaSms(pdu, reply);
} else {
mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 3cd804d..f068acd 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -22,6 +22,8 @@
import android.net.NetworkConfig;
import android.net.NetworkRequest;
import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseIntArray;
@@ -29,13 +31,11 @@
import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RetryManager;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -130,6 +130,14 @@
}
/**
+ * Gets the APN type bitmask.
+ * @return The APN type bitmask
+ */
+ public int getApnTypeBitmask() {
+ return ApnSetting.getApnTypesBitmaskFromString(mApnType);
+ }
+
+ /**
* Get the data call async channel.
* @return The data call async channel
*/
@@ -272,7 +280,10 @@
log("setState: " + s + ", previous state:" + mState);
}
- mState = s;
+ if (mState != s) {
+ mStateLocalLog.log("State changed from " + mState + " to " + s);
+ mState = s;
+ }
if (mState == DctConstants.State.FAILED) {
if (mRetryManager.getWaitingApns() != null) {
@@ -390,8 +401,8 @@
String provisioningApn = mPhone.getContext().getResources()
.getString(R.string.mobile_provisioning_apn);
if (!TextUtils.isEmpty(provisioningApn) &&
- (mApnSetting != null) && (mApnSetting.apn != null)) {
- return (mApnSetting.apn.equals(provisioningApn));
+ (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
+ return (mApnSetting.getApnName().equals(provisioningApn));
} else {
return false;
}
@@ -399,8 +410,7 @@
private final ArrayList<LocalLog> mLocalLogs = new ArrayList<>();
private final ArrayList<NetworkRequest> mNetworkRequests = new ArrayList<>();
- private final ArrayDeque<LocalLog> mHistoryLogs = new ArrayDeque<>();
- private final static int MAX_HISTORY_LOG_COUNT = 4;
+ private final LocalLog mStateLocalLog = new LocalLog(50);
public void requestLog(String str) {
synchronized (mRefCountLock) {
@@ -417,7 +427,7 @@
} else {
mLocalLogs.add(log);
mNetworkRequests.add(networkRequest);
- mDcTracker.setEnabled(apnIdForApnName(mApnType), true);
+ mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), true);
}
}
}
@@ -437,7 +447,7 @@
log.log("ApnContext.releaseNetwork left with " + mNetworkRequests.size() +
" requests.");
if (mNetworkRequests.size() == 0) {
- mDcTracker.setEnabled(apnIdForApnName(mApnType), false);
+ mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), false);
}
}
}
@@ -539,88 +549,78 @@
return mRetryManager.getRetryAfterDisconnectDelay();
}
- public static int apnIdForType(int networkType) {
+ public static int getApnTypeFromNetworkType(int networkType) {
switch (networkType) {
- case ConnectivityManager.TYPE_MOBILE:
- return DctConstants.APN_DEFAULT_ID;
- case ConnectivityManager.TYPE_MOBILE_MMS:
- return DctConstants.APN_MMS_ID;
- case ConnectivityManager.TYPE_MOBILE_SUPL:
- return DctConstants.APN_SUPL_ID;
- case ConnectivityManager.TYPE_MOBILE_DUN:
- return DctConstants.APN_DUN_ID;
- case ConnectivityManager.TYPE_MOBILE_FOTA:
- return DctConstants.APN_FOTA_ID;
- case ConnectivityManager.TYPE_MOBILE_IMS:
- return DctConstants.APN_IMS_ID;
- case ConnectivityManager.TYPE_MOBILE_CBS:
- return DctConstants.APN_CBS_ID;
- case ConnectivityManager.TYPE_MOBILE_IA:
- return DctConstants.APN_IA_ID;
- case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
- return DctConstants.APN_EMERGENCY_ID;
- default:
- return DctConstants.APN_INVALID_ID;
+ case ConnectivityManager.TYPE_MOBILE:
+ return ApnSetting.TYPE_DEFAULT;
+ case ConnectivityManager.TYPE_MOBILE_MMS:
+ return ApnSetting.TYPE_MMS;
+ case ConnectivityManager.TYPE_MOBILE_SUPL:
+ return ApnSetting.TYPE_SUPL;
+ case ConnectivityManager.TYPE_MOBILE_DUN:
+ return ApnSetting.TYPE_DUN;
+ case ConnectivityManager.TYPE_MOBILE_FOTA:
+ return ApnSetting.TYPE_FOTA;
+ case ConnectivityManager.TYPE_MOBILE_IMS:
+ return ApnSetting.TYPE_IMS;
+ case ConnectivityManager.TYPE_MOBILE_CBS:
+ return ApnSetting.TYPE_CBS;
+ case ConnectivityManager.TYPE_MOBILE_IA:
+ return ApnSetting.TYPE_IA;
+ case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
+ return ApnSetting.TYPE_EMERGENCY;
+ default:
+ return ApnSetting.TYPE_NONE;
}
}
- public static int apnIdForNetworkRequest(NetworkRequest nr) {
+ static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
NetworkCapabilities nc = nr.networkCapabilities;
// For now, ignore the bandwidth stuff
if (nc.getTransportTypes().length > 0 &&
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
- return DctConstants.APN_INVALID_ID;
+ return ApnSetting.TYPE_NONE;
}
// in the near term just do 1-1 matches.
// TODO - actually try to match the set of capabilities
- int apnId = DctConstants.APN_INVALID_ID;
+ int apnType = ApnSetting.TYPE_NONE;
boolean error = false;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- apnId = DctConstants.APN_DEFAULT_ID;
+ apnType = ApnSetting.TYPE_DEFAULT;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_MMS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_MMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_SUPL_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_SUPL;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_DUN_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_DUN;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_FOTA_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_FOTA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_IMS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_IMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_CBS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_CBS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_IA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
- Rlog.d(SLOG_TAG, "RCS APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
- Rlog.d(SLOG_TAG, "XCAP APN type not yet supported");
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_IA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_EMERGENCY_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_EMERGENCY;
}
if (error) {
// TODO: If this error condition is removed, the framework's handling of
@@ -629,66 +629,10 @@
// NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
}
- if (apnId == DctConstants.APN_INVALID_ID) {
+ if (apnType == ApnSetting.TYPE_NONE) {
Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
}
- return apnId;
- }
-
- // TODO - kill The use of these strings
- public static int apnIdForApnName(String type) {
- switch (type) {
- case PhoneConstants.APN_TYPE_DEFAULT:
- return DctConstants.APN_DEFAULT_ID;
- case PhoneConstants.APN_TYPE_MMS:
- return DctConstants.APN_MMS_ID;
- case PhoneConstants.APN_TYPE_SUPL:
- return DctConstants.APN_SUPL_ID;
- case PhoneConstants.APN_TYPE_DUN:
- return DctConstants.APN_DUN_ID;
- case PhoneConstants.APN_TYPE_HIPRI:
- return DctConstants.APN_HIPRI_ID;
- case PhoneConstants.APN_TYPE_IMS:
- return DctConstants.APN_IMS_ID;
- case PhoneConstants.APN_TYPE_FOTA:
- return DctConstants.APN_FOTA_ID;
- case PhoneConstants.APN_TYPE_CBS:
- return DctConstants.APN_CBS_ID;
- case PhoneConstants.APN_TYPE_IA:
- return DctConstants.APN_IA_ID;
- case PhoneConstants.APN_TYPE_EMERGENCY:
- return DctConstants.APN_EMERGENCY_ID;
- default:
- return DctConstants.APN_INVALID_ID;
- }
- }
-
- private static String apnNameForApnId(int id) {
- switch (id) {
- case DctConstants.APN_DEFAULT_ID:
- return PhoneConstants.APN_TYPE_DEFAULT;
- case DctConstants.APN_MMS_ID:
- return PhoneConstants.APN_TYPE_MMS;
- case DctConstants.APN_SUPL_ID:
- return PhoneConstants.APN_TYPE_SUPL;
- case DctConstants.APN_DUN_ID:
- return PhoneConstants.APN_TYPE_DUN;
- case DctConstants.APN_HIPRI_ID:
- return PhoneConstants.APN_TYPE_HIPRI;
- case DctConstants.APN_IMS_ID:
- return PhoneConstants.APN_TYPE_IMS;
- case DctConstants.APN_FOTA_ID:
- return PhoneConstants.APN_TYPE_FOTA;
- case DctConstants.APN_CBS_ID:
- return PhoneConstants.APN_TYPE_CBS;
- case DctConstants.APN_IA_ID:
- return PhoneConstants.APN_TYPE_IA;
- case DctConstants.APN_EMERGENCY_ID:
- return PhoneConstants.APN_TYPE_EMERGENCY;
- default:
- Rlog.d(SLOG_TAG, "Unknown id (" + id + ") in apnIdToType");
- return PhoneConstants.APN_TYPE_DEFAULT;
- }
+ return apnType;
}
@Override
@@ -719,13 +663,15 @@
pw.increaseIndent();
for (LocalLog l : mLocalLogs) {
l.dump(fd, pw, args);
- }
- if (mHistoryLogs.size() > 0) pw.println("Historical Logs:");
- for (LocalLog l : mHistoryLogs) {
- l.dump(fd, pw, args);
+ pw.println("-----");
}
pw.decreaseIndent();
+ pw.println("Historical APN state:");
+ pw.increaseIndent();
+ mStateLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
pw.println(mRetryManager);
+ pw.println("--------------------------");
}
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
deleted file mode 100644
index ce8318d..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ /dev/null
@@ -1,677 +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.content.Context;
-import android.hardware.radio.V1_0.ApnTypes;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.uicc.IccRecords;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This class represents a apn setting for create PDP link
- */
-public class ApnSetting {
-
- static final String LOG_TAG = "ApnSetting";
-
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
- static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
-
- public final String carrier;
- public final String apn;
- public final String proxy;
- public final String port;
- public final String mmsc;
- public final String mmsProxy;
- public final String mmsPort;
- public final String user;
- public final String password;
- public final int authType;
- public final String[] types;
- public final int typesBitmap;
- public final int id;
- public final String numeric;
- public final String protocol;
- public final String roamingProtocol;
- public final int mtu;
-
- /**
- * Current status of APN
- * true : enabled APN, false : disabled APN.
- */
- public final boolean carrierEnabled;
- /**
- * Radio Access Technology info
- * To check what values can hold, refer to ServiceState.java.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- */
- private final int bearer;
- /**
- * Radio Access Technology info
- * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
- * technologies in ServiceState.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- */
- public final int bearerBitmask;
-
- /* ID of the profile in the modem */
- public final int profileId;
- public final boolean modemCognitive;
- public final int maxConns;
- public final int waitTime;
- public final int maxConnsTime;
-
- /**
- * MVNO match type. Possible values:
- * "spn": Service provider name.
- * "imsi": IMSI.
- * "gid": Group identifier level 1.
- */
- public final String mvnoType;
- /**
- * MVNO data. Examples:
- * "spn": A MOBILE, BEN NL
- * "imsi": 302720x94, 2060188
- * "gid": 4E, 33
- */
- public final String mvnoMatchData;
-
- /**
- * Indicates this APN setting is permanently failed and cannot be
- * retried by the retry manager anymore.
- * */
- public boolean permanentFailed = false;
-
- public ApnSetting(int id, String numeric, String carrier, String apn,
- String proxy, String port,
- String mmsc, String mmsProxy, String mmsPort,
- String user, String password, int authType, String[] types,
- String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
- int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
- int waitTime, int maxConnsTime, int mtu, String mvnoType,
- String mvnoMatchData) {
- this.id = id;
- this.numeric = numeric;
- this.carrier = carrier;
- this.apn = apn;
- this.proxy = proxy;
- this.port = port;
- this.mmsc = mmsc;
- this.mmsProxy = mmsProxy;
- this.mmsPort = mmsPort;
- this.user = user;
- this.password = password;
- this.authType = authType;
- this.types = new String[types.length];
- int apnBitmap = 0;
- for (int i = 0; i < types.length; i++) {
- this.types[i] = types[i].toLowerCase();
- apnBitmap |= getApnBitmask(this.types[i]);
- }
- this.typesBitmap = apnBitmap;
- this.protocol = protocol;
- this.roamingProtocol = roamingProtocol;
- this.carrierEnabled = carrierEnabled;
- this.bearer = bearer;
- this.bearerBitmask = (bearerBitmask | ServiceState.getBitmaskForTech(bearer));
- this.profileId = profileId;
- this.modemCognitive = modemCognitive;
- this.maxConns = maxConns;
- this.waitTime = waitTime;
- this.maxConnsTime = maxConnsTime;
- this.mtu = mtu;
- this.mvnoType = mvnoType;
- this.mvnoMatchData = mvnoMatchData;
-
- }
-
- public ApnSetting(ApnSetting apn) {
- this(apn.id, apn.numeric, apn.carrier, apn.apn, apn.proxy, apn.port, apn.mmsc, apn.mmsProxy,
- apn.mmsPort, apn.user, apn.password, apn.authType, apn.types, apn.protocol,
- apn.roamingProtocol, apn.carrierEnabled, apn.bearer, apn.bearerBitmask,
- apn.profileId, apn.modemCognitive, apn.maxConns, apn.waitTime, apn.maxConnsTime,
- apn.mtu, apn.mvnoType, apn.mvnoMatchData);
- }
-
- /**
- * Creates an ApnSetting object from a string.
- *
- * @param data the string to read.
- *
- * The string must be in one of two formats (newlines added for clarity,
- * spaces are optional):
- *
- * v1 format:
- * <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...],
- *
- * v2 format:
- * [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
- *
- * v3 format:
- * [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
- * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
- * <mvnoType>, <mvnoMatchData>
- *
- * Note that the strings generated by toString() do not contain the username
- * and password and thus cannot be read by this method.
- */
- public static ApnSetting fromString(String data) {
- if (data == null) return null;
-
- int version;
- // matches() operates on the whole string, so append .* to the regex.
- if (data.matches(V3_FORMAT_REGEX + ".*")) {
- version = 3;
- data = data.replaceFirst(V3_FORMAT_REGEX, "");
- } else if (data.matches(V2_FORMAT_REGEX + ".*")) {
- version = 2;
- data = data.replaceFirst(V2_FORMAT_REGEX, "");
- } else {
- version = 1;
- }
-
- String[] a = data.split("\\s*,\\s*");
- if (a.length < 14) {
- return null;
- }
-
- int authType;
- try {
- authType = Integer.parseInt(a[12]);
- } catch (NumberFormatException e) {
- authType = 0;
- }
-
- String[] typeArray;
- String protocol, roamingProtocol;
- boolean carrierEnabled;
- int bearerBitmask = 0;
- int profileId = 0;
- boolean modemCognitive = false;
- int maxConns = 0;
- int waitTime = 0;
- int maxConnsTime = 0;
- int mtu = PhoneConstants.UNSET_MTU;
- String mvnoType = "";
- String mvnoMatchData = "";
- if (version == 1) {
- typeArray = new String[a.length - 13];
- System.arraycopy(a, 13, typeArray, 0, a.length - 13);
- protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
- roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
- carrierEnabled = true;
- } else {
- if (a.length < 18) {
- return null;
- }
- typeArray = a[13].split("\\s*\\|\\s*");
- protocol = a[14];
- roamingProtocol = a[15];
- carrierEnabled = Boolean.parseBoolean(a[16]);
-
- bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
-
- if (a.length > 22) {
- modemCognitive = Boolean.parseBoolean(a[19]);
- try {
- profileId = Integer.parseInt(a[18]);
- maxConns = Integer.parseInt(a[20]);
- waitTime = Integer.parseInt(a[21]);
- maxConnsTime = Integer.parseInt(a[22]);
- } catch (NumberFormatException e) {
- }
- }
- if (a.length > 23) {
- try {
- mtu = Integer.parseInt(a[23]);
- } catch (NumberFormatException e) {
- }
- }
- if (a.length > 25) {
- mvnoType = a[24];
- mvnoMatchData = a[25];
- }
- }
-
- return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
- a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,0,
- bearerBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
- mvnoType, mvnoMatchData);
- }
-
- /**
- * Creates an array of ApnSetting objects from a string.
- *
- * @param data the string to read.
- *
- * Builds on top of the same format used by fromString, but allows for multiple entries
- * separated by "; ".
- */
- public static List<ApnSetting> arrayFromString(String data) {
- List<ApnSetting> retVal = new ArrayList<ApnSetting>();
- if (TextUtils.isEmpty(data)) {
- return retVal;
- }
- String[] apnStrings = data.split("\\s*;\\s*");
- for (String apnString : apnStrings) {
- ApnSetting apn = fromString(apnString);
- if (apn != null) {
- retVal.add(apn);
- }
- }
- return retVal;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV3] ")
- .append(carrier)
- .append(", ").append(id)
- .append(", ").append(numeric)
- .append(", ").append(apn)
- .append(", ").append(proxy)
- .append(", ").append(mmsc)
- .append(", ").append(mmsProxy)
- .append(", ").append(mmsPort)
- .append(", ").append(port)
- .append(", ").append(authType).append(", ");
- for (int i = 0; i < types.length; i++) {
- sb.append(types[i]);
- if (i < types.length - 1) {
- sb.append(" | ");
- }
- }
- sb.append(", ").append(protocol);
- sb.append(", ").append(roamingProtocol);
- sb.append(", ").append(carrierEnabled);
- sb.append(", ").append(bearer);
- sb.append(", ").append(bearerBitmask);
- sb.append(", ").append(profileId);
- sb.append(", ").append(modemCognitive);
- sb.append(", ").append(maxConns);
- sb.append(", ").append(waitTime);
- sb.append(", ").append(maxConnsTime);
- sb.append(", ").append(mtu);
- sb.append(", ").append(mvnoType);
- sb.append(", ").append(mvnoMatchData);
- sb.append(", ").append(permanentFailed);
- return sb.toString();
- }
-
- /**
- * Returns true if there are MVNO params specified.
- */
- public boolean hasMvnoParams() {
- return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);
- }
-
- public boolean canHandleType(String type) {
- if (!carrierEnabled) return false;
- boolean wildcardable = true;
- if (PhoneConstants.APN_TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
- for (String t : types) {
- // DEFAULT handles all, and HIPRI is handled by DEFAULT
- if (t.equalsIgnoreCase(type) ||
- (wildcardable && t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL)) ||
- (t.equalsIgnoreCase(PhoneConstants.APN_TYPE_DEFAULT) &&
- type.equalsIgnoreCase(PhoneConstants.APN_TYPE_HIPRI))) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean imsiMatches(String imsiDB, String imsiSIM) {
- // Note: imsiDB value has digit number or 'x' character for seperating USIM information
- // for MVNO operator. And then digit number is matched at same order and 'x' character
- // could replace by any digit number.
- // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
- // that means first 6 digits, 8th and 9th digit
- // should be set in USIM for GG Operator.
- int len = imsiDB.length();
- int idxCompare = 0;
-
- if (len <= 0) return false;
- if (len > imsiSIM.length()) return false;
-
- for (int idx=0; idx<len; idx++) {
- char c = imsiDB.charAt(idx);
- if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
- continue;
- } else {
- return false;
- }
- }
- return true;
- }
-
- public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
- if (mvnoType.equalsIgnoreCase("spn")) {
- if ((r.getServiceProviderName() != null) &&
- r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("imsi")) {
- String imsiSIM = r.getIMSI();
- if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("gid")) {
- String gid1 = r.getGid1();
- int mvno_match_data_length = mvnoMatchData.length();
- if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
- gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if this APN type is metered.
- *
- * @param type The APN type
- * @param phone The phone object
- * @return True if the APN type is metered, otherwise false.
- */
- public static boolean isMeteredApnType(String type, Phone phone) {
- if (phone == null) {
- return true;
- }
-
- boolean isRoaming = phone.getServiceState().getDataRoaming();
- boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
- == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
- int subId = phone.getSubId();
-
- String carrierConfig;
- // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
- // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
- // the normal metered APN list.
- if (isIwlan) {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
- } else 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 + ", isIwlan=" + isIwlan);
- }
-
- 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 all types of APN are metered, then this APN setting must be metered.
- if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
- if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
- return true;
- }
-
- if (meteredApnSet.contains(type)) {
- if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
- return true;
- } else if (type.equals(PhoneConstants.APN_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, type + " is not metered.");
- return false;
- }
-
- /**
- * Check if this APN setting is metered.
- *
- * @param phone The phone object
- * @return True if this APN setting is metered, otherwise false.
- */
- public boolean isMetered(Phone phone) {
- if (phone == null) {
- return true;
- }
-
- for (String type : types) {
- // If one of the APN type is metered, then this APN setting is metered.
- if (isMeteredApnType(type, phone)) {
- return true;
- }
- }
- return false;
- }
-
- // TODO - if we have this function we should also have hashCode.
- // Also should handle changes in type order and perhaps case-insensitivity
- @Override
- public boolean equals(Object o) {
- if (o instanceof ApnSetting == false) {
- return false;
- }
-
- ApnSetting other = (ApnSetting) o;
-
- return carrier.equals(other.carrier)
- && id == other.id
- && numeric.equals(other.numeric)
- && apn.equals(other.apn)
- && proxy.equals(other.proxy)
- && mmsc.equals(other.mmsc)
- && mmsProxy.equals(other.mmsProxy)
- && TextUtils.equals(mmsPort, other.mmsPort)
- && port.equals(other.port)
- && TextUtils.equals(user, other.user)
- && TextUtils.equals(password, other.password)
- && authType == other.authType
- && Arrays.deepEquals(types, other.types)
- && typesBitmap == other.typesBitmap
- && protocol.equals(other.protocol)
- && roamingProtocol.equals(other.roamingProtocol)
- && carrierEnabled == other.carrierEnabled
- && bearer == other.bearer
- && bearerBitmask == other.bearerBitmask
- && profileId == other.profileId
- && modemCognitive == other.modemCognitive
- && maxConns == other.maxConns
- && waitTime == other.waitTime
- && maxConnsTime == other.maxConnsTime
- && mtu == other.mtu
- && mvnoType.equals(other.mvnoType)
- && mvnoMatchData.equals(other.mvnoMatchData);
- }
-
- /**
- * Compare two APN settings
- *
- * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
- * determining if tearing a data call is needed when conditions change. See
- * cleanUpConnectionsOnUpdatedApns in DcTracker.
- *
- * @param o the other object to compare
- * @param isDataRoaming True if the device is on data roaming
- * @return True if the two APN settings are same
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean equals(Object o, boolean isDataRoaming) {
- if (!(o instanceof ApnSetting)) {
- return false;
- }
-
- ApnSetting other = (ApnSetting) o;
-
- return carrier.equals(other.carrier)
- && numeric.equals(other.numeric)
- && apn.equals(other.apn)
- && proxy.equals(other.proxy)
- && mmsc.equals(other.mmsc)
- && mmsProxy.equals(other.mmsProxy)
- && TextUtils.equals(mmsPort, other.mmsPort)
- && port.equals(other.port)
- && TextUtils.equals(user, other.user)
- && TextUtils.equals(password, other.password)
- && authType == other.authType
- && Arrays.deepEquals(types, other.types)
- && typesBitmap == other.typesBitmap
- && (isDataRoaming || protocol.equals(other.protocol))
- && (!isDataRoaming || roamingProtocol.equals(other.roamingProtocol))
- && carrierEnabled == other.carrierEnabled
- && profileId == other.profileId
- && modemCognitive == other.modemCognitive
- && maxConns == other.maxConns
- && waitTime == other.waitTime
- && maxConnsTime == other.maxConnsTime
- && mtu == other.mtu
- && mvnoType.equals(other.mvnoType)
- && mvnoMatchData.equals(other.mvnoMatchData);
- }
-
- /**
- * Check if neither mention DUN and are substantially similar
- *
- * @param other The other APN settings to compare
- * @return True if two APN settings are similar
- */
- public boolean similar(ApnSetting other) {
- return (!this.canHandleType(PhoneConstants.APN_TYPE_DUN)
- && !other.canHandleType(PhoneConstants.APN_TYPE_DUN)
- && Objects.equals(this.apn, other.apn)
- && !typeSameAny(this, other)
- && xorEquals(this.proxy, other.proxy)
- && xorEquals(this.port, other.port)
- && xorEquals(this.protocol, other.protocol)
- && xorEquals(this.roamingProtocol, other.roamingProtocol)
- && this.carrierEnabled == other.carrierEnabled
- && this.bearerBitmask == other.bearerBitmask
- && this.profileId == other.profileId
- && Objects.equals(this.mvnoType, other.mvnoType)
- && Objects.equals(this.mvnoMatchData, other.mvnoMatchData)
- && xorEquals(this.mmsc, other.mmsc)
- && xorEquals(this.mmsProxy, other.mmsProxy)
- && xorEquals(this.mmsPort, other.mmsPort));
- }
-
- // check whether the types of two APN same (even only one type of each APN is same)
- private boolean typeSameAny(ApnSetting first, ApnSetting second) {
- if (VDBG) {
- StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
- for (int index1 = 0; index1 < first.types.length; index1++) {
- apnType1.append(first.types[index1]);
- apnType1.append(",");
- }
-
- StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
- for (int index1 = 0; index1 < second.types.length; index1++) {
- apnType2.append(second.types[index1]);
- apnType2.append(",");
- }
- Rlog.d(LOG_TAG, "APN1: is " + apnType1);
- Rlog.d(LOG_TAG, "APN2: is " + apnType2);
- }
-
- for (int index1 = 0; index1 < first.types.length; index1++) {
- for (int index2 = 0; index2 < second.types.length; index2++) {
- if (first.types[index1].equals(PhoneConstants.APN_TYPE_ALL)
- || second.types[index2].equals(PhoneConstants.APN_TYPE_ALL)
- || first.types[index1].equals(second.types[index2])) {
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
- return true;
- }
- }
- }
-
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
- return false;
- }
-
- // equal or one is not specified
- private boolean xorEquals(String first, String second) {
- return (Objects.equals(first, second)
- || TextUtils.isEmpty(first)
- || TextUtils.isEmpty(second));
- }
-
- // Helper function to convert APN string into a 32-bit bitmask.
- private static int getApnBitmask(String apn) {
- switch (apn) {
- case PhoneConstants.APN_TYPE_DEFAULT: return ApnTypes.DEFAULT;
- case PhoneConstants.APN_TYPE_MMS: return ApnTypes.MMS;
- case PhoneConstants.APN_TYPE_SUPL: return ApnTypes.SUPL;
- case PhoneConstants.APN_TYPE_DUN: return ApnTypes.DUN;
- case PhoneConstants.APN_TYPE_HIPRI: return ApnTypes.HIPRI;
- case PhoneConstants.APN_TYPE_FOTA: return ApnTypes.FOTA;
- case PhoneConstants.APN_TYPE_IMS: return ApnTypes.IMS;
- case PhoneConstants.APN_TYPE_CBS: return ApnTypes.CBS;
- case PhoneConstants.APN_TYPE_IA: return ApnTypes.IA;
- case PhoneConstants.APN_TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
- case PhoneConstants.APN_TYPE_ALL: return ApnTypes.ALL;
- default: return ApnTypes.NONE;
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
new file mode 100644
index 0000000..e9a6fc8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
@@ -0,0 +1,215 @@
+/*
+ * 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.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+
+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;
+
+ private static boolean iccidMatches(String mvnoData, String iccId) {
+ String[] mvnoIccidList = mvnoData.split(",");
+ for (String mvnoIccid : mvnoIccidList) {
+ if (iccId.startsWith(mvnoIccid)) {
+ Log.d(LOG_TAG, "mvno icc id match found");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean imsiMatches(String imsiDB, String imsiSIM) {
+ // Note: imsiDB value has digit number or 'x' character for seperating USIM information
+ // for MVNO operator. And then digit number is matched at same order and 'x' character
+ // could replace by any digit number.
+ // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
+ // that means first 6 digits, 8th and 9th digit
+ // should be set in USIM for GG Operator.
+ int len = imsiDB.length();
+
+ if (len <= 0) return false;
+ if (len > imsiSIM.length()) return false;
+
+ for (int idx = 0; idx < len; idx++) {
+ char c = imsiDB.charAt(idx);
+ if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if MVNO type and data match IccRecords.
+ *
+ * @param r the IccRecords
+ * @param mvnoType the MVNO type
+ * @param mvnoMatchData the MVNO match data
+ * @return {@code true} if MVNO type and data match IccRecords, {@code false} otherwise.
+ */
+ public static boolean mvnoMatches(IccRecords r, int mvnoType, String mvnoMatchData) {
+ if (mvnoType == ApnSetting.MVNO_TYPE_SPN) {
+ if ((r.getServiceProviderName() != null)
+ && r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_IMSI) {
+ String imsiSIM = r.getIMSI();
+ if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_GID) {
+ String gid1 = r.getGid1();
+ int mvno_match_data_length = mvnoMatchData.length();
+ if ((gid1 != null) && (gid1.length() >= mvno_match_data_length)
+ && gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_ICCID) {
+ String iccId = r.getIccId();
+ if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if this APN type is metered.
+ *
+ * @param type the APN type
+ * @param phone the phone object
+ * @return {@code true} if the APN type is metered, {@code false} otherwise.
+ */
+ public static boolean isMeteredApnType(String type, Phone phone) {
+ if (phone == null) {
+ return true;
+ }
+
+ boolean isRoaming = phone.getServiceState().getDataRoaming();
+ boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
+ == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ int subId = phone.getSubId();
+
+ String carrierConfig;
+ // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
+ // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
+ // the normal metered APN list.
+ if (isIwlan) {
+ carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
+ } else 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 + ", isIwlan=" + isIwlan);
+ }
+
+ 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 all types of APN are metered, then this APN setting must be metered.
+ if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
+ if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
+ return true;
+ }
+
+ if (meteredApnSet.contains(type)) {
+ if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
+ return true;
+ } else if (type.equals(PhoneConstants.APN_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, type + " is not metered.");
+ return false;
+ }
+
+ /**
+ * Check if this APN setting is metered.
+ *
+ * @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) {
+ return true;
+ }
+
+ String[] types = ApnSetting.getApnTypesStringFromBitmask(
+ apn.getApnTypeBitmask()).split(",");
+
+ for (String type : types) {
+ // If one of the APN type is metered, then this APN setting is metered.
+ if (isMeteredApnType(type, phone)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
new file mode 100644
index 0000000..cbc3efe
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
@@ -0,0 +1,334 @@
+/*
+ * 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.hardware.radio.V1_0.SetupDataCallResult;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.Rlog;
+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.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents cellular data service which handles telephony data requests and response
+ * from the cellular modem.
+ */
+public class CellularDataService extends DataService {
+ private static final String TAG = CellularDataService.class.getSimpleName();
+
+ private static final boolean DBG = false;
+
+ private static final int SETUP_DATA_CALL_COMPLETE = 1;
+ private static final int DEACTIVATE_DATA_ALL_COMPLETE = 2;
+ private static final int SET_INITIAL_ATTACH_APN_COMPLETE = 3;
+ private static final int SET_DATA_PROFILE_COMPLETE = 4;
+ private static final int GET_DATA_CALL_LIST_COMPLETE = 5;
+ private static final int DATA_CALL_LIST_CHANGED = 6;
+
+ private class CellularDataServiceProvider extends DataService.DataServiceProvider {
+
+ private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>();
+
+ private final Looper mLooper;
+
+ private final Handler mHandler;
+
+ private final Phone mPhone;
+
+ private CellularDataServiceProvider(int slotId) {
+ super(slotId);
+
+ mPhone = PhoneFactory.getPhone(getSlotId());
+
+ HandlerThread thread = new HandlerThread(CellularDataService.class.getSimpleName());
+ thread.start();
+ mLooper = thread.getLooper();
+ mHandler = new Handler(mLooper) {
+ @Override
+ public void handleMessage(Message message) {
+ DataServiceCallback callback = mCallbackMap.remove(message);
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ switch (message.what) {
+ case SETUP_DATA_CALL_COMPLETE:
+ SetupDataCallResult result = (SetupDataCallResult) ar.result;
+ callback.onSetupDataCallComplete(ar.exception != null
+ ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+ : DataServiceCallback.RESULT_SUCCESS,
+ convertDataCallResult(result));
+ break;
+ case DEACTIVATE_DATA_ALL_COMPLETE:
+ callback.onDeactivateDataCallComplete(ar.exception != null
+ ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+ : DataServiceCallback.RESULT_SUCCESS);
+ break;
+ case SET_INITIAL_ATTACH_APN_COMPLETE:
+ callback.onSetInitialAttachApnComplete(ar.exception != null
+ ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+ : DataServiceCallback.RESULT_SUCCESS);
+ break;
+ case SET_DATA_PROFILE_COMPLETE:
+ callback.onSetDataProfileComplete(ar.exception != null
+ ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+ : DataServiceCallback.RESULT_SUCCESS);
+ break;
+ case GET_DATA_CALL_LIST_COMPLETE:
+ callback.onGetDataCallListComplete(
+ ar.exception != null
+ ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+ : DataServiceCallback.RESULT_SUCCESS,
+ ar.exception != null
+ ? null
+ : getDataCallList((List<SetupDataCallResult>) ar.result)
+ );
+ break;
+ case DATA_CALL_LIST_CHANGED:
+ notifyDataCallListChanged(getDataCallList(
+ (List<SetupDataCallResult>) ar.result));
+ break;
+ default:
+ loge("Unexpected event: " + message.what);
+ return;
+ }
+ }
+ };
+
+ if (DBG) log("Register for data call list changed.");
+ mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null);
+ }
+
+ private List<DataCallResponse> getDataCallList(List<SetupDataCallResult> dcList) {
+ List<DataCallResponse> dcResponseList = new ArrayList<>();
+ for (SetupDataCallResult dcResult : dcList) {
+ dcResponseList.add(convertDataCallResult(dcResult));
+ }
+ return dcResponseList;
+ }
+
+ @Override
+ public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ DataServiceCallback callback) {
+ if (DBG) log("setupDataCall " + getSlotId());
+
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE);
+ mCallbackMap.put(message, callback);
+ }
+
+ mPhone.mCi.setupDataCall(radioTechnology, dataProfile, isRoaming, allowRoaming, reason,
+ linkProperties, message);
+ }
+
+ @Override
+ public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) {
+ if (DBG) log("deactivateDataCall " + getSlotId());
+
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
+ mCallbackMap.put(message, callback);
+ }
+
+ mPhone.mCi.deactivateDataCall(cid, reason, message);
+ }
+
+ @Override
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ DataServiceCallback callback) {
+ if (DBG) log("setInitialAttachApn " + getSlotId());
+
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE);
+ mCallbackMap.put(message, callback);
+ }
+
+ mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message);
+ }
+
+ @Override
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ DataServiceCallback callback) {
+ if (DBG) log("setDataProfile " + getSlotId());
+
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE);
+ mCallbackMap.put(message, callback);
+ }
+
+ mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message);
+ }
+
+ @Override
+ public void getDataCallList(DataServiceCallback callback) {
+ if (DBG) log("getDataCallList " + getSlotId());
+
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, GET_DATA_CALL_LIST_COMPLETE);
+ mCallbackMap.put(message, callback);
+ }
+ mPhone.mCi.getDataCallList(message);
+ }
+ }
+
+ @Override
+ public DataServiceProvider createDataServiceProvider(int slotId) {
+ log("Cellular data service created for slot " + slotId);
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ loge("Tried to cellular data service with invalid slotId " + slotId);
+ return null;
+ }
+ return new CellularDataServiceProvider(slotId);
+ }
+
+ /**
+ * Convert SetupDataCallResult defined in types.hal into DataCallResponse
+ * @param dcResult setup data call result
+ * @return converted DataCallResponse object
+ */
+ @VisibleForTesting
+ public DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) {
+ if (dcResult == null) return null;
+
+ // Process address
+ String[] addresses = null;
+ if (!TextUtils.isEmpty(dcResult.addresses)) {
+ addresses = dcResult.addresses.split("\\s+");
+ }
+
+ List<LinkAddress> laList = new ArrayList<>();
+ if (addresses != null) {
+ for (String address : addresses) {
+ address = address.trim();
+ if (address.isEmpty()) continue;
+
+ try {
+ LinkAddress la;
+ // Check if the address contains prefix length. If yes, LinkAddress
+ // can parse that.
+ if (address.split("/").length == 2) {
+ la = new LinkAddress(address);
+ } else {
+ InetAddress ia = NetworkUtils.numericToInetAddress(address);
+ la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128);
+ }
+
+ laList.add(la);
+ } catch (IllegalArgumentException e) {
+ loge("Unknown address: " + address + ", exception = " + e);
+ }
+ }
+ }
+
+ // Process dns
+ String[] dnses = null;
+ if (!TextUtils.isEmpty(dcResult.dnses)) {
+ dnses = dcResult.dnses.split("\\s+");
+ }
+
+ List<InetAddress> dnsList = new ArrayList<>();
+ if (dnses != null) {
+ for (String dns : dnses) {
+ dns = dns.trim();
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(dns);
+ dnsList.add(ia);
+ } catch (IllegalArgumentException e) {
+ loge("Unknown dns: " + dns + ", exception = " + e);
+ }
+ }
+ }
+
+ // Process gateway
+ String[] gateways = null;
+ if (!TextUtils.isEmpty(dcResult.gateways)) {
+ gateways = dcResult.gateways.split("\\s+");
+ }
+
+ List<InetAddress> gatewayList = new ArrayList<>();
+ if (gateways != null) {
+ for (String gateway : gateways) {
+ gateway = gateway.trim();
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(gateway);
+ gatewayList.add(ia);
+ } catch (IllegalArgumentException e) {
+ loge("Unknown gateway: " + gateway + ", exception = " + e);
+ }
+ }
+ }
+
+ return new DataCallResponse(dcResult.status,
+ dcResult.suggestedRetryTime,
+ dcResult.cid,
+ dcResult.active,
+ dcResult.type,
+ dcResult.ifname,
+ laList,
+ dnsList,
+ gatewayList,
+ new ArrayList<>(Arrays.asList(dcResult.pcscf.trim().split("\\s+"))),
+ dcResult.mtu
+ );
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java b/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
deleted file mode 100644
index bc02b97..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved.
- * 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.dataconnection;
-
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkUtils;
-import android.net.RouteInfo;
-import android.os.SystemProperties;
-import android.telephony.Rlog;
-import android.text.TextUtils;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * This is RIL_Data_Call_Response_v5 from ril.h
- */
-public class DataCallResponse {
- private final boolean DBG = true;
- private final String LOG_TAG = "DataCallResponse";
-
- public final int status;
- public final int suggestedRetryTime;
- public final int cid;
- public final int active;
- public final String type;
- public final String ifname;
- public final String [] addresses;
- public final String [] dnses;
- // TODO: Change this to final if possible.
- public String[] gateways;
- public final String [] pcscf;
- public final int mtu;
-
- /**
- * Class returned by onSetupConnectionCompleted.
- */
- public enum SetupResult {
- SUCCESS,
- ERR_BadCommand,
- ERR_UnacceptableParameter,
- ERR_GetLastErrorFromRil,
- ERR_Stale,
- ERR_RilError;
-
- public DcFailCause mFailCause;
-
- SetupResult() {
- mFailCause = DcFailCause.fromInt(0);
- }
-
- @Override
- public String toString() {
- return name() + " SetupResult.mFailCause=" + mFailCause;
- }
- }
-
- public DataCallResponse(int status, int suggestedRetryTime, int cid, int active, String type,
- String ifname, String addresses, String dnses, String gateways,
- String pcscf, int mtu) {
- this.status = status;
- this.suggestedRetryTime = suggestedRetryTime;
- this.cid = cid;
- this.active = active;
- this.type = (type == null) ? "" : type;
- this.ifname = (ifname == null) ? "" : ifname;
- if ((status == DcFailCause.NONE.getErrorCode()) && TextUtils.isEmpty(ifname)) {
- throw new RuntimeException("DataCallResponse, no ifname");
- }
- this.addresses = TextUtils.isEmpty(addresses) ? new String[0] : addresses.split(" ");
- this.dnses = TextUtils.isEmpty(dnses) ? new String[0] : dnses.split(" ");
- this.gateways = TextUtils.isEmpty(gateways) ? new String[0] : gateways.split(" ");
- this.pcscf = TextUtils.isEmpty(pcscf) ? new String[0] : pcscf.split(" ");
- this.mtu = mtu;
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("DataCallResponse: {")
- .append(" status=").append(status)
- .append(" retry=").append(suggestedRetryTime)
- .append(" cid=").append(cid)
- .append(" active=").append(active)
- .append(" type=").append(type)
- .append(" ifname=").append(ifname)
- .append(" mtu=").append(mtu)
- .append(" addresses=[");
- for (String addr : addresses) {
- sb.append(addr);
- sb.append(",");
- }
- if (addresses.length > 0) sb.deleteCharAt(sb.length()-1);
- sb.append("] dnses=[");
- for (String addr : dnses) {
- sb.append(addr);
- sb.append(",");
- }
- if (dnses.length > 0) sb.deleteCharAt(sb.length()-1);
- sb.append("] gateways=[");
- for (String addr : gateways) {
- sb.append(addr);
- sb.append(",");
- }
- if (gateways.length > 0) sb.deleteCharAt(sb.length()-1);
- sb.append("] pcscf=[");
- for (String addr : pcscf) {
- sb.append(addr);
- sb.append(",");
- }
- if (pcscf.length > 0) sb.deleteCharAt(sb.length()-1);
- sb.append("]}");
- return sb.toString();
- }
-
- public SetupResult setLinkProperties(LinkProperties linkProperties,
- boolean okToUseSystemPropertyDns) {
- SetupResult result;
-
- // Start with clean network properties and if we have
- // a failure we'll clear again at the bottom of this code.
- if (linkProperties == null)
- linkProperties = new LinkProperties();
- else
- linkProperties.clear();
-
- if (status == DcFailCause.NONE.getErrorCode()) {
- String propertyPrefix = "net." + ifname + ".";
-
- try {
- // set interface name
- linkProperties.setInterfaceName(ifname);
-
- // set link addresses
- if (addresses != null && addresses.length > 0) {
- for (String addr : addresses) {
- addr = addr.trim();
- if (addr.isEmpty()) continue;
- LinkAddress la;
- int addrPrefixLen;
-
- String [] ap = addr.split("/");
- if (ap.length == 2) {
- addr = ap[0];
- addrPrefixLen = Integer.parseInt(ap[1]);
- } else {
- addrPrefixLen = 0;
- }
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(addr);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric ip addr=" + addr);
- }
- if (! ia.isAnyLocalAddress()) {
- if (addrPrefixLen == 0) {
- // Assume point to point
- addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
- }
- if (DBG) Rlog.d(LOG_TAG, "addr/pl=" + addr + "/" + addrPrefixLen);
- try {
- la = new LinkAddress(ia, addrPrefixLen);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Bad parameter for LinkAddress, ia="
- + ia.getHostAddress() + "/" + addrPrefixLen);
- }
-
- linkProperties.addLinkAddress(la);
- }
- }
- } else {
- throw new UnknownHostException("no address for ifname=" + ifname);
- }
-
- // set dns servers
- if (dnses != null && dnses.length > 0) {
- for (String addr : dnses) {
- addr = addr.trim();
- if (addr.isEmpty()) continue;
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(addr);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric dns addr=" + addr);
- }
- if (! ia.isAnyLocalAddress()) {
- linkProperties.addDnsServer(ia);
- }
- }
- } else if (okToUseSystemPropertyDns){
- String dnsServers[] = new String[2];
- dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
- dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
- for (String dnsAddr : dnsServers) {
- dnsAddr = dnsAddr.trim();
- if (dnsAddr.isEmpty()) continue;
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(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 gateways
- if ((gateways == null) || (gateways.length == 0)) {
- String sysGateways = SystemProperties.get(propertyPrefix + "gw");
- if (sysGateways != null) {
- gateways = sysGateways.split(" ");
- } else {
- gateways = new String[0];
- }
- }
- for (String addr : gateways) {
- addr = addr.trim();
- if (addr.isEmpty()) continue;
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(addr);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric gateway addr=" + addr);
- }
- // Allow 0.0.0.0 or :: as a gateway; this indicates a point-to-point interface.
- linkProperties.addRoute(new RouteInfo(ia));
- }
-
- // set interface MTU
- // this may clobber the setting read from the APN db, but that's ok
- linkProperties.setMtu(mtu);
-
- result = SetupResult.SUCCESS;
- } catch (UnknownHostException e) {
- Rlog.d(LOG_TAG, "setLinkProperties: UnknownHostException " + e);
- e.printStackTrace();
- result = SetupResult.ERR_UnacceptableParameter;
- }
- } else {
- result = SetupResult.ERR_RilError;
- }
-
- // An error occurred so clear properties
- if (result != SetupResult.SUCCESS) {
- if(DBG) {
- Rlog.d(LOG_TAG, "setLinkProperties: error clearing LinkProperties " +
- "status=" + status + " result=" + result);
- }
- linkProperties.clear();
- }
-
- return result;
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 8547bca..00af9fe 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -16,40 +16,58 @@
package com.android.internal.telephony.dataconnection;
+import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+
import android.app.PendingIntent;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.KeepalivePacketData;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
+import android.net.NetworkUtils;
import android.net.ProxyInfo;
+import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.os.AsyncResult;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Rlog;
import android.telephony.ServiceState;
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.text.TextUtils;
+import android.util.LocalLog;
import android.util.Pair;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallTracker;
import com.android.internal.telephony.CarrierSignalAgent;
-import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.DctConstants;
+import com.android.internal.telephony.LinkCapacityEstimate;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
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;
@@ -58,7 +76,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
-import java.util.ArrayList;
+import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
@@ -158,16 +176,19 @@
private DcFailCause mDcFailCause;
private Phone mPhone;
+ private DataServiceManager mDataServiceManager;
private LinkProperties mLinkProperties = new LinkProperties();
private long mCreateTime;
private long mLastFailTime;
private DcFailCause mLastFailCause;
private static final String NULL_IP = "0.0.0.0";
private Object mUserData;
+ private int mSubscriptionOverride;
private int mRilRat = Integer.MAX_VALUE;
private int mDataRegState = Integer.MAX_VALUE;
private NetworkInfo mNetworkInfo;
- private NetworkAgent mNetworkAgent;
+ private DcNetworkAgent mNetworkAgent;
+ private LocalLog mNetCapsLocalLog = new LocalLog(50);
int mTag;
public int mCid;
@@ -179,7 +200,6 @@
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_GET_LAST_FAIL_DONE = BASE + 2;
static final int EVENT_DEACTIVATE_DONE = BASE + 3;
static final int EVENT_DISCONNECT = BASE + 4;
static final int EVENT_RIL_CONNECTED = BASE + 5;
@@ -193,16 +213,22 @@
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_OVERRIDE_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;
private static final int CMD_TO_STRING_COUNT =
- EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE + 1;
+ EVENT_LINK_CAPACITY_CHANGED - 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_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
@@ -219,6 +245,14 @@
"EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
"EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
+ sCmdToString[EVENT_DATA_CONNECTION_OVERRIDE_CHANGED - BASE] =
+ "EVENT_DATA_CONNECTION_OVERRIDE_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";
}
// Convert cmd to string or null if unknown
static String cmdToString(int cmd) {
@@ -242,11 +276,13 @@
* @param id the connection id
* @return DataConnection that was created.
*/
- public static DataConnection makeDataConnection(Phone phone, int id,
- DcTracker dct, DcTesterFailBringUpAll failBringUpAll,
- DcController dcc) {
+ public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct,
+ DataServiceManager dataServiceManager,
+ DcTesterFailBringUpAll failBringUpAll,
+ DcController dcc) {
DataConnection dc = new DataConnection(phone,
- "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc);
+ "DC-" + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager,
+ failBringUpAll, dcc);
dc.start();
if (DBG) dc.log("Made " + dc.getName());
return dc;
@@ -263,10 +299,22 @@
return new LinkProperties(mLinkProperties);
}
- boolean getIsInactive() {
+ boolean isInactive() {
return getCurrentState() == mInactiveState;
}
+ boolean isDisconnecting() {
+ return getCurrentState() == mDisconnectingState;
+ }
+
+ boolean isActive() {
+ return getCurrentState() == mActiveState;
+ }
+
+ boolean isActivating() {
+ return getCurrentState() == mActivatingState;
+ }
+
int getCid() {
return mCid;
}
@@ -280,7 +328,7 @@
}
public static class UpdateLinkPropertyResult {
- public DataCallResponse.SetupResult setupResult = DataCallResponse.SetupResult.SUCCESS;
+ public SetupResult setupResult = SetupResult.SUCCESS;
public LinkProperties oldLp;
public LinkProperties newLp;
public UpdateLinkPropertyResult(LinkProperties curLp) {
@@ -289,6 +337,28 @@
}
}
+ /**
+ * Class returned by onSetupConnectionCompleted.
+ */
+ public enum SetupResult {
+ SUCCESS,
+ ERROR_RADIO_NOT_AVAILABLE,
+ ERROR_INVALID_ARG,
+ ERROR_STALE,
+ ERROR_DATA_SERVICE_SPECIFIC_ERROR;
+
+ public DcFailCause mFailCause;
+
+ SetupResult() {
+ mFailCause = DcFailCause.fromInt(0);
+ }
+
+ @Override
+ public String toString() {
+ return name() + " SetupResult.mFailCause=" + mFailCause;
+ }
+ }
+
public boolean isIpv4Connected() {
boolean ret = false;
Collection <InetAddress> addresses = mLinkProperties.getAddresses();
@@ -323,17 +393,17 @@
return ret;
}
+ @VisibleForTesting
public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
if (newState == null) return result;
- DataCallResponse.SetupResult setupResult;
result.newLp = new LinkProperties();
// set link properties based on data call response
result.setupResult = setLinkProperties(newState, result.newLp);
- if (result.setupResult != DataCallResponse.SetupResult.SUCCESS) {
+ if (result.setupResult != SetupResult.SUCCESS) {
if (DBG) log("updateLinkProperty failed : " + result.setupResult);
return result;
}
@@ -374,9 +444,9 @@
return;
}
- if (apn != null && apn.mtu != PhoneConstants.UNSET_MTU) {
- lp.setMtu(apn.mtu);
- if (DBG) log("MTU set by APN to: " + apn.mtu);
+ if (apn != null && apn.getMtu() != PhoneConstants.UNSET_MTU) {
+ lp.setMtu(apn.getMtu());
+ if (DBG) log("MTU set by APN to: " + apn.getMtu());
return;
}
@@ -390,7 +460,8 @@
//***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
private DataConnection(Phone phone, String name, int id,
- DcTracker dct, DcTesterFailBringUpAll failBringUpAll,
+ DcTracker dct, DataServiceManager dataServiceManager,
+ DcTesterFailBringUpAll failBringUpAll,
DcController dcc) {
super(name, dcc.getHandler());
setLogRecSize(300);
@@ -399,6 +470,7 @@
mPhone = phone;
mDct = dct;
+ mDataServiceManager = dataServiceManager;
mDcTesterFailBringUpAll = failBringUpAll;
mDcController = dcc;
mId = id;
@@ -411,9 +483,6 @@
networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
mNetworkInfo.setRoaming(ss.getDataRoaming());
mNetworkInfo.setIsAvailable(true);
- // The network should be by default metered until we find it has NET_CAPABILITY_NOT_METERED
- // capability.
- mNetworkInfo.setMetered(true);
addState(mDefaultState);
addState(mInactiveState, mDefaultState);
@@ -423,20 +492,23 @@
addState(mDisconnectingErrorCreatingConnection, mDefaultState);
setInitialState(mInactiveState);
- mApnContexts = new HashMap<ApnContext, ConnectionParams>();
+ mApnContexts = new HashMap<>();
}
/**
* Begin setting up a data connection, calls setupDataCall
* and the ConnectionParams will be returned with the
- * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
+ * EVENT_SETUP_DATA_CONNECTION_DONE
*
* @param cp is the connection parameters
*/
private void onConnect(ConnectionParams cp) {
- if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
- + "' APN='" + mApnSetting.apn
- + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
+ if (DBG) {
+ log("onConnect: carrier='" + mApnSetting.getEntryName()
+ + "' APN='" + mApnSetting.getApnName()
+ + "' proxy='" + mApnSetting.getProxyAddressAsString()
+ + "' port='" + mApnSetting.getProxyPort() + "'");
+ }
if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
// Check if we should fake an error.
@@ -444,7 +516,7 @@
DataCallResponse response = new DataCallResponse(
mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause.getErrorCode(),
mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime, 0, 0, "", "",
- "", "", "", "", PhoneConstants.UNSET_MTU);
+ null, null, null, null, PhoneConstants.UNSET_MTU);
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
AsyncResult.forMessage(msg, response, null);
@@ -461,11 +533,10 @@
mLastFailTime = -1;
mLastFailCause = DcFailCause.NONE;
- // msg.obj will be returned in AsyncResult.userObj;
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
- DataProfile dp = new DataProfile(mApnSetting, cp.mProfileId);
+ DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId);
// 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
@@ -478,33 +549,41 @@
boolean allowRoaming = mPhone.getDataRoamingEnabled()
|| (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
- mPhone.mCi.setupDataCall(cp.mRilRat, dp, isModemRoaming, allowRoaming, msg);
+ mDataServiceManager.setupDataCall(
+ ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp, isModemRoaming,
+ allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
+ TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
+ dp.getProfileId(), dp.getApn(), dp.getProtocol());
+ }
+
+ public void onSubscriptionOverride(int overrideMask, int overrideValue) {
+ mSubscriptionOverride = (mSubscriptionOverride & ~overrideMask)
+ | (overrideValue & overrideMask);
+ sendMessage(obtainMessage(EVENT_DATA_CONNECTION_OVERRIDE_CHANGED));
}
/**
* TearDown the data connection when the deactivation is complete a Message with
- * msg.what == EVENT_DEACTIVATE_DONE and msg.obj == AsyncResult with AsyncResult.obj
- * containing the parameter o.
+ * msg.what == EVENT_DEACTIVATE_DONE
*
* @param o is the object returned in the AsyncResult.obj.
*/
private void tearDownData(Object o) {
- int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+ int discReason = DataService.REQUEST_REASON_NORMAL;
ApnContext apnContext = null;
if ((o != null) && (o instanceof DisconnectParams)) {
- DisconnectParams dp = (DisconnectParams)o;
+ DisconnectParams dp = (DisconnectParams) o;
apnContext = dp.mApnContext;
- if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
- discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
- } else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
- discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
+ if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
+ || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
+ discReason = DataService.REQUEST_REASON_SHUTDOWN;
}
}
String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
if (DBG) log(str);
if (apnContext != null) apnContext.requestLog(str);
- mPhone.mCi.deactivateDataCall(mCid, discReason,
+ mDataServiceManager.deactivateDataCall(mCid, discReason,
obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
}
@@ -660,44 +739,39 @@
}
/**
- * Process setup completion.
+ * Process setup data completion result from data service
*
- * @param ar is the result
- * @return SetupResult.
+ * @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 DataCallResponse.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
- DataCallResponse response = (DataCallResponse) ar.result;
- ConnectionParams cp = (ConnectionParams) ar.userObj;
- DataCallResponse.SetupResult result;
+ private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode,
+ DataCallResponse response,
+ ConnectionParams cp) {
+ SetupResult result;
if (cp.mTag != mTag) {
if (DBG) {
log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
}
- result = DataCallResponse.SetupResult.ERR_Stale;
- } else if (ar.exception != null) {
- if (DBG) {
- log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
- " response=" + response);
- }
-
- if (ar.exception instanceof CommandException
- && ((CommandException) (ar.exception)).getCommandError()
- == CommandException.Error.RADIO_NOT_AVAILABLE) {
- result = DataCallResponse.SetupResult.ERR_BadCommand;
+ result = SetupResult.ERROR_STALE;
+ } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) {
+ result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
+ result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
+ } else if (response.getStatus() != 0) {
+ if (response.getStatus() == DcFailCause.RADIO_NOT_AVAILABLE.getErrorCode()) {
+ result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
} else {
- result = DataCallResponse.SetupResult.ERR_RilError;
- result.mFailCause = DcFailCause.fromInt(response.status);
+ result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
+ result.mFailCause = DcFailCause.fromInt(response.getStatus());
}
- } else if (response.status != 0) {
- result = DataCallResponse.SetupResult.ERR_RilError;
- result.mFailCause = DcFailCause.fromInt(response.status);
} else {
if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
- mCid = response.cid;
+ mCid = response.getCallId();
- mPcscfAddr = response.pcscf;
+ mPcscfAddr = response.getPcscfs().toArray(new String[response.getPcscfs().size()]);
result = updateLinkProperty(response).setupResult;
}
@@ -713,12 +787,12 @@
// 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 (!mApnSetting.types[0].equals(PhoneConstants.APN_TYPE_MMS)
- || !isIpAddress(mApnSetting.mmsProxy)) {
+ if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
log(String.format(
- "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
- mApnSetting.types[0], PhoneConstants.APN_TYPE_MMS, mApnSetting.mmsProxy,
- isIpAddress(mApnSetting.mmsProxy)));
+ "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
+ mApnSetting.getApnTypeBitmask(), PhoneConstants.APN_TYPE_MMS,
+ mApnSetting.getMmsProxyAddressAsString(),
+ isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
return false;
}
}
@@ -852,7 +926,7 @@
// Do we need a restricted network to satisfy the request?
// Is this network metered? If not, then don't add restricted
- if (!mApnSetting.isMetered(mPhone)) {
+ if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
return;
}
@@ -865,11 +939,12 @@
result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (mApnSetting != null) {
- ApnSetting securedDunApn = mDct.fetchDunApn();
- for (String type : mApnSetting.types) {
+ final String[] types = ApnSetting.getApnTypesStringFromBitmask(
+ mApnSetting.getApnTypeBitmask()).split(",");
+ for (String type : types) {
if (!mRestrictedNetworkOverride
&& (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
- && ApnSetting.isMeteredApnType(type, mPhone)) {
+ && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
log("Dropped the metered " + type + " for the unmetered data call.");
continue;
}
@@ -882,11 +957,7 @@
result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- // check if this is the DUN apn as well as returned by fetchDunApn().
- // If yes, add DUN capability too.
- if (mApnSetting.equals(securedDunApn)) {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- }
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -902,9 +973,7 @@
break;
}
case PhoneConstants.APN_TYPE_DUN: {
- if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- }
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
case PhoneConstants.APN_TYPE_FOTA: {
@@ -936,12 +1005,10 @@
// 2. The non-restricted data and is intended for unmetered use only.
if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
&& !mRestrictedNetworkOverride)
- || !mApnSetting.isMetered(mPhone)) {
+ || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- mNetworkInfo.setMetered(false);
} else {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- mNetworkInfo.setMetered(true);
}
result.maybeMarkCapabilitiesRestricted();
@@ -978,6 +1045,19 @@
result.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(mPhone.getSubId())));
+ result.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
+ !mPhone.getServiceState().getDataRoaming());
+
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
+
+ // Override values set above when requested by policy
+ if ((mSubscriptionOverride & OVERRIDE_UNMETERED) != 0) {
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+ if ((mSubscriptionOverride & OVERRIDE_CONGESTED) != 0) {
+ result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
+ }
+
return result;
}
@@ -991,18 +1071,95 @@
return InetAddress.isNumeric(address);
}
- private DataCallResponse.SetupResult setLinkProperties(DataCallResponse response,
- LinkProperties lp) {
+ private SetupResult setLinkProperties(DataCallResponse response,
+ LinkProperties linkProperties) {
// Check if system property dns usable
- boolean okToUseSystemPropertyDns = false;
- String propertyPrefix = "net." + response.ifname + ".";
+ String propertyPrefix = "net." + response.getIfname() + ".";
String dnsServers[] = new String[2];
dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
- okToUseSystemPropertyDns = isDnsOk(dnsServers);
+ boolean okToUseSystemPropertyDns = isDnsOk(dnsServers);
- // set link properties based on data call response
- return response.setLinkProperties(lp, okToUseSystemPropertyDns);
+ 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.getStatus() == DcFailCause.NONE.getErrorCode()) {
+ try {
+ // set interface name
+ linkProperties.setInterfaceName(response.getIfname());
+
+ // 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.getNetworkPrefixLength());
+ }
+ linkProperties.addLinkAddress(la);
+ }
+ }
+ } else {
+ throw new UnknownHostException("no address for ifname=" + response.getIfname());
+ }
+
+ // set dns servers
+ if (response.getDnses().size() > 0) {
+ for (InetAddress dns : response.getDnses()) {
+ if (!dns.isAnyLocalAddress()) {
+ linkProperties.addDnsServer(dns);
+ }
+ }
+ } else if (okToUseSystemPropertyDns) {
+ for (String dnsAddr : dnsServers) {
+ dnsAddr = dnsAddr.trim();
+ if (dnsAddr.isEmpty()) continue;
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(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");
+ }
+
+ for (InetAddress gateway : response.getGateways()) {
+ // Allow 0.0.0.0 or :: as a gateway;
+ // this indicates a point-to-point interface.
+ linkProperties.addRoute(new RouteInfo(gateway));
+ }
+
+ // set interface MTU
+ // this may clobber the setting read from the APN db, but that's ok
+ 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.getStatus() + " result=" + result);
+ }
+ linkProperties.clear();
+ }
+
+ return result;
}
/**
@@ -1019,7 +1176,7 @@
// only NOT be set only if we're in DcInactiveState.
mApnSetting = apnContext.getApnSetting();
}
- if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnType())) {
+ if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnTypeBitmask())) {
if (DBG) {
log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
+ " dc=" + DataConnection.this);
@@ -1085,6 +1242,7 @@
mDct = null;
mApnSetting = null;
mPhone = null;
+ mDataServiceManager = null;
mLinkProperties = null;
mLastFailCause = null;
mUserData = null;
@@ -1125,7 +1283,7 @@
break;
}
case DcAsyncChannel.REQ_IS_INACTIVE: {
- boolean val = getIsInactive();
+ boolean val = isInactive();
if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val);
mAc.replyToMessage(msg, DcAsyncChannel.RSP_IS_INACTIVE, val ? 1 : 0);
break;
@@ -1189,12 +1347,11 @@
}
deferMessage(msg);
break;
-
case EVENT_TEAR_DOWN_NOW:
if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
- mPhone.mCi.deactivateDataCall(mCid, 0, null);
+ mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
+ null);
break;
-
case EVENT_LOST_CONNECTION:
if (DBG) {
String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
@@ -1215,26 +1372,31 @@
+ " drs=" + mDataRegState
+ " mRilRat=" + mRilRat);
}
- ServiceState ss = mPhone.getServiceState();
- int networkType = ss.getDataNetworkType();
- mNetworkInfo.setSubtype(networkType,
- TelephonyManager.getNetworkTypeName(networkType));
+ updateNetworkInfo();
+ updateNetworkInfoSuspendState();
if (mNetworkAgent != null) {
- updateNetworkInfoSuspendState();
mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent.sendLinkProperties(mLinkProperties);
}
break;
-
case EVENT_DATA_CONNECTION_ROAM_ON:
- mNetworkInfo.setRoaming(true);
- break;
-
case EVENT_DATA_CONNECTION_ROAM_OFF:
- mNetworkInfo.setRoaming(false);
+ case EVENT_DATA_CONNECTION_OVERRIDE_CHANGED:
+ updateNetworkInfo();
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
break;
-
+ case EVENT_KEEPALIVE_START_REQUEST:
+ case EVENT_KEEPALIVE_STOP_REQUEST:
+ if (mNetworkAgent != null) {
+ mNetworkAgent.onPacketKeepaliveEvent(
+ msg.arg1,
+ ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+ }
+ break;
default:
if (DBG) {
log("DcDefaultState: shouldn't happen but ignore msg.what="
@@ -1247,9 +1409,14 @@
}
}
- private boolean updateNetworkInfoSuspendState() {
- final NetworkInfo.DetailedState oldState = mNetworkInfo.getDetailedState();
+ private void updateNetworkInfo() {
+ final ServiceState state = mPhone.getServiceState();
+ final int subtype = state.getDataNetworkType();
+ mNetworkInfo.setSubtype(subtype, TelephonyManager.getNetworkTypeName(subtype));
+ mNetworkInfo.setRoaming(state.getDataRoaming());
+ }
+ private void updateNetworkInfoSuspendState() {
// this is only called when we are either connected or suspended. Decide which.
if (mNetworkAgent == null) {
Rlog.e(getName(), "Setting suspend state without a NetworkAgent");
@@ -1267,13 +1434,12 @@
if (ct.getState() != PhoneConstants.State.IDLE) {
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null,
mNetworkInfo.getExtraInfo());
- return (oldState != NetworkInfo.DetailedState.SUSPENDED);
+ return;
}
}
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null,
mNetworkInfo.getExtraInfo());
}
- return (oldState != mNetworkInfo.getDetailedState());
}
private DcDefaultState mDefaultState = new DcDefaultState();
@@ -1414,11 +1580,12 @@
break;
case EVENT_SETUP_DATA_CONNECTION_DONE:
- ar = (AsyncResult) msg.obj;
- cp = (ConnectionParams) ar.userObj;
+ cp = (ConnectionParams) msg.obj;
- DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
- if (result != DataCallResponse.SetupResult.ERR_Stale) {
+ 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);
@@ -1437,28 +1604,29 @@
mDcFailCause = DcFailCause.NONE;
transitionTo(mActiveState);
break;
- case ERR_BadCommand:
+ 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);
transitionTo(mInactiveState);
break;
- case ERR_UnacceptableParameter:
+ case ERROR_INVALID_ARG:
// The addresses given from the RIL are bad
tearDownData(cp);
transitionTo(mDisconnectingErrorCreatingConnection);
break;
- case ERR_RilError:
+ 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(ar);
+
+ long delay = getSuggestedRetryDelay(dataCallResponse);
cp.mApnContext.setModemSuggestedDelay(delay);
- String str = "DcActivatingState: ERR_RilError "
+ String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
+ " delay=" + delay
+ " result=" + result
+ " result.isRestartRadioFail=" +
@@ -1475,7 +1643,7 @@
mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
transitionTo(mInactiveState);
break;
- case ERR_Stale:
+ case ERROR_STALE:
loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
+ " tag:" + cp.mTag + " != mTag:" + mTag);
break;
@@ -1484,46 +1652,6 @@
}
retVal = HANDLED;
break;
-
- case EVENT_GET_LAST_FAIL_DONE:
- ar = (AsyncResult) msg.obj;
- cp = (ConnectionParams) ar.userObj;
- if (cp.mTag == mTag) {
- if (mConnectionParams != cp) {
- loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams
- + " != cp:" + cp);
- }
-
- DcFailCause cause = DcFailCause.UNKNOWN;
-
- if (ar.exception == null) {
- int rilFailCause = ((int[]) (ar.result))[0];
- cause = DcFailCause.fromInt(rilFailCause);
- if (cause == DcFailCause.NONE) {
- if (DBG) {
- log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
- + " BAD: error was NONE, change to UNKNOWN");
- }
- cause = DcFailCause.UNKNOWN;
- }
- }
- mDcFailCause = cause;
-
- if (DBG) {
- log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
- + " cause=" + cause + " dc=" + DataConnection.this);
- }
-
- mInactiveState.setEnterNotificationParams(cp, cause);
- transitionTo(mInactiveState);
- } else {
- loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
- + " tag:" + cp.mTag + " != mTag:" + mTag);
- }
-
- retVal = HANDLED;
- break;
-
default:
if (VDBG) {
log("DcActivatingState not handled msg.what=" +
@@ -1541,25 +1669,11 @@
* 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);
- // verify and get updated information in case these things
- // are obsolete
- ServiceState ss = mPhone.getServiceState();
- final int networkType = ss.getDataNetworkType();
- if (mNetworkInfo.getSubtype() != networkType) {
- log("DcActiveState with incorrect subtype (" + mNetworkInfo.getSubtype()
- + ", " + networkType + "), updating.");
- }
- mNetworkInfo.setSubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
- final boolean roaming = ss.getDataRoaming();
- if (roaming != mNetworkInfo.isRoaming()) {
- log("DcActiveState with incorrect roaming (" + mNetworkInfo.isRoaming()
- + ", " + roaming + "), updating.");
- }
-
- mNetworkInfo.setRoaming(roaming);
+ updateNetworkInfo();
// If we were retrying there maybe more than one, otherwise they'll only be one.
notifyAllOfConnected(Phone.REASON_CONNECTED);
@@ -1575,7 +1689,7 @@
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,
mNetworkInfo.getReason(), null);
- mNetworkInfo.setExtraInfo(mApnSetting.apn);
+ mNetworkInfo.setExtraInfo(mApnSetting.getApnName());
updateTcpBufferSizes(mRilRat);
final NetworkMisc misc = new NetworkMisc();
@@ -1588,9 +1702,14 @@
misc.subscriberId = mPhone.getSubscriberId();
setNetworkRestriction();
+ if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride);
mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
"DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
50, misc);
+ mPhone.mCi.registerForNattKeepaliveStatus(
+ getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
+ mPhone.mCi.registerForLceInfo(
+ getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
}
@Override
@@ -1609,6 +1728,8 @@
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
reason, mNetworkInfo.getExtraInfo());
+ mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
+ mPhone.mCi.unregisterForLceInfo(getHandler());
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent = null;
@@ -1687,17 +1808,12 @@
retVal = HANDLED;
break;
}
- case EVENT_DATA_CONNECTION_ROAM_ON: {
- mNetworkInfo.setRoaming(true);
+ case EVENT_DATA_CONNECTION_ROAM_ON:
+ case EVENT_DATA_CONNECTION_ROAM_OFF:
+ case EVENT_DATA_CONNECTION_OVERRIDE_CHANGED: {
+ updateNetworkInfo();
if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
- }
- retVal = HANDLED;
- break;
- }
- case EVENT_DATA_CONNECTION_ROAM_OFF: {
- mNetworkInfo.setRoaming(false);
- if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
}
retVal = HANDLED;
@@ -1708,11 +1824,10 @@
if (ar.exception != null) {
log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
} else {
- final ArrayList<Integer> capInfo = (ArrayList<Integer>)ar.result;
- final int lceBwDownKbps = capInfo.get(0);
+ final LinkCapacityEstimate lce = (LinkCapacityEstimate) ar.result;
NetworkCapabilities nc = getNetworkCapabilities();
if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
- nc.setLinkDownstreamBandwidthKbps(lceBwDownKbps);
+ nc.setLinkDownstreamBandwidthKbps(lce.downlinkCapacityKbps);
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkCapabilities(nc);
}
@@ -1723,13 +1838,130 @@
}
case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: {
- if (updateNetworkInfoSuspendState() && mNetworkAgent != null) {
- // state changed
+ updateNetworkInfo();
+ updateNetworkInfoSuspendState();
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
}
retVal = HANDLED;
break;
}
+ case EVENT_KEEPALIVE_START_REQUEST: {
+ KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
+ int slotId = msg.arg1;
+ int intervalMillis = msg.arg2 * 1000;
+ if (mDataServiceManager.getTransportType()
+ == AccessNetworkConstants.TransportType.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.onPacketKeepaliveEvent(
+ msg.arg1,
+ ConnectivityManager.PacketKeepalive.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 stopPacketKeepalive! " + slotId);
+ 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.onPacketKeepaliveEvent(
+ slot, ConnectivityManager.PacketKeepalive.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 {
+ LinkCapacityEstimate lce = (LinkCapacityEstimate) ar.result;
+ NetworkCapabilities nc = getNetworkCapabilities();
+ if (lce.downlinkCapacityKbps != LinkCapacityEstimate.INVALID) {
+ nc.setLinkDownstreamBandwidthKbps(lce.downlinkCapacityKbps);
+ }
+ if (lce.uplinkCapacityKbps != LinkCapacityEstimate.INVALID) {
+ nc.setLinkUpstreamBandwidthKbps(lce.uplinkCapacityKbps);
+ }
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(nc);
+ }
+ }
+ retVal = HANDLED;
+ break;
+ }
default:
if (VDBG) {
log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -1759,8 +1991,7 @@
break;
case EVENT_DEACTIVATE_DONE:
- AsyncResult ar = (AsyncResult) msg.obj;
- DisconnectParams dp = (DisconnectParams) ar.userObj;
+ DisconnectParams dp = (DisconnectParams) msg.obj;
String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
+ mApnContexts.size();
@@ -1770,7 +2001,7 @@
if (dp.mTag == mTag) {
// Transition to inactive but send notifications after
// we've entered the mInactive state.
- mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
+ mInactiveState.setEnterNotificationParams(dp);
transitionTo(mInactiveState);
} else {
if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
@@ -1802,8 +2033,7 @@
switch (msg.what) {
case EVENT_DEACTIVATE_DONE:
- AsyncResult ar = (AsyncResult) msg.obj;
- ConnectionParams cp = (ConnectionParams) ar.userObj;
+ ConnectionParams cp = (ConnectionParams) msg.obj;
if (cp.mTag == mTag) {
String str = "DcDisconnectionErrorCreatingConnection" +
" msg.what=EVENT_DEACTIVATE_DONE";
@@ -1840,9 +2070,16 @@
private class DcNetworkAgent extends NetworkAgent {
+
+ private NetworkCapabilities mNetworkCapabilities;
+
+ public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
+
public DcNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(l, c, TAG, ni, nc, lp, score, misc);
+ mNetCapsLocalLog.log("New network agent created. capabilities=" + nc);
+ mNetworkCapabilities = nc;
}
@Override
@@ -1885,6 +2122,151 @@
msg.sendToTarget();
}
}
+
+ @Override
+ public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+ if (!networkCapabilities.equals(mNetworkCapabilities)) {
+ String logStr = "Changed from " + mNetworkCapabilities + " to "
+ + networkCapabilities + ", Data RAT="
+ + mPhone.getServiceState().getRilDataRadioTechnology()
+ + ", mApnSetting=" + mApnSetting;
+ mNetCapsLocalLog.log(logStr);
+ log(logStr);
+ mNetworkCapabilities = networkCapabilities;
+ }
+ super.sendNetworkCapabilities(networkCapabilities);
+ }
+
+ @Override
+ protected void startPacketKeepalive(Message msg) {
+ DataConnection.this.obtainMessage(EVENT_KEEPALIVE_START_REQUEST,
+ msg.arg1, msg.arg2, msg.obj).sendToTarget();
+ }
+
+ @Override
+ protected void stopPacketKeepalive(Message msg) {
+ DataConnection.this.obtainMessage(EVENT_KEEPALIVE_STOP_REQUEST,
+ msg.arg1, msg.arg2, msg.obj).sendToTarget();
+ }
+
+ private 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 PacketKeepalive.SUCCESS;
+ case KeepaliveStatus.ERROR_UNSUPPORTED:
+ return PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
+ case KeepaliveStatus.ERROR_NO_RESOURCES:
+ case KeepaliveStatus.ERROR_UNKNOWN:
+ default:
+ return PacketKeepalive.ERROR_HARDWARE_ERROR;
+ }
+ }
+
+ void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
+ switch (ks.statusCode) {
+ case KeepaliveStatus.STATUS_INACTIVE:
+ DcNetworkAgent.this.onPacketKeepaliveEvent(slot,
+ keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
+ break;
+ case KeepaliveStatus.STATUS_ACTIVE:
+ DcNetworkAgent.this.onPacketKeepaliveEvent(
+ slot, PacketKeepalive.SUCCESS);
+ // fall through to add record
+ case KeepaliveStatus.STATUS_PENDING:
+ log("Adding keepalive handle="
+ + ks.sessionHandle + " slot = " + slot);
+ mKeepalives.put(ks.sessionHandle,
+ new KeepaliveRecord(
+ slot, ks.statusCode));
+ break;
+ default:
+ loge("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.
+ log("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:
+ loge("Inactive Keepalive received status!");
+ DcNetworkAgent.this.onPacketKeepaliveEvent(
+ kr.slotId, PacketKeepalive.ERROR_HARDWARE_ERROR);
+ break;
+ case KeepaliveStatus.STATUS_PENDING:
+ switch (ks.statusCode) {
+ case KeepaliveStatus.STATUS_INACTIVE:
+ DcNetworkAgent.this.onPacketKeepaliveEvent(kr.slotId,
+ keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
+ kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
+ mKeepalives.remove(ks.sessionHandle);
+ break;
+ case KeepaliveStatus.STATUS_ACTIVE:
+ log("Pending Keepalive received active status!");
+ kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
+ DcNetworkAgent.this.onPacketKeepaliveEvent(
+ kr.slotId, PacketKeepalive.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:
+ loge("Keepalive received stopped status!");
+ DcNetworkAgent.this.onPacketKeepaliveEvent(
+ kr.slotId, PacketKeepalive.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);
+ }
+ }
+ };
}
// ******* "public" interface
@@ -1900,14 +2282,11 @@
/**
* Using the result of the SETUP_DATA_CALL determine the retry delay.
*
- * @param ar is the result from SETUP_DATA_CALL
+ * @param response The response from setup data call
* @return NO_SUGGESTED_RETRY_DELAY if no retry is needed otherwise the delay to the
* next SETUP_DATA_CALL
*/
- private long getSuggestedRetryDelay(AsyncResult ar) {
-
- DataCallResponse response = (DataCallResponse) ar.result;
-
+ 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.
@@ -1915,19 +2294,19 @@
*/
// The value < 0 means no value is suggested
- if (response.suggestedRetryTime < 0) {
+ if (response.getSuggestedRetryTime() < 0) {
if (DBG) log("No suggested retry delay.");
return RetryManager.NO_SUGGESTED_RETRY_DELAY;
}
// The value of Integer.MAX_VALUE(0x7fffffff) means no retry.
- else if (response.suggestedRetryTime == Integer.MAX_VALUE) {
+ else if (response.getSuggestedRetryTime() == Integer.MAX_VALUE) {
if (DBG) log("Modem suggested not retrying.");
return RetryManager.NO_RETRY;
}
// We need to cast it to long because the value returned from RIL is a 32-bit integer,
// but the time values used in AlarmManager are all 64-bit long.
- return (long) response.suggestedRetryTime;
+ return (long) response.getSuggestedRetryTime();
}
/**
@@ -2093,34 +2472,40 @@
* @param args
*/
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.print("DataConnection ");
super.dump(fd, pw, args);
- pw.println(" mApnContexts.size=" + mApnContexts.size());
- pw.println(" mApnContexts=" + mApnContexts);
pw.flush();
- pw.println(" mDataConnectionTracker=" + mDct);
- pw.println(" mApnSetting=" + mApnSetting);
- pw.println(" mTag=" + mTag);
- pw.println(" mCid=" + mCid);
- pw.println(" mConnectionParams=" + mConnectionParams);
- pw.println(" mDisconnectParams=" + mDisconnectParams);
- pw.println(" mDcFailCause=" + mDcFailCause);
+ pw.increaseIndent();
+ pw.println("mApnContexts.size=" + mApnContexts.size());
+ pw.println("mApnContexts=" + mApnContexts);
+ pw.println("mDataConnectionTracker=" + mDct);
+ pw.println("mApnSetting=" + mApnSetting);
+ pw.println("mTag=" + mTag);
+ pw.println("mCid=" + mCid);
+ pw.println("mConnectionParams=" + mConnectionParams);
+ pw.println("mDisconnectParams=" + mDisconnectParams);
+ pw.println("mDcFailCause=" + mDcFailCause);
+ pw.println("mPhone=" + mPhone);
+ pw.println("mLinkProperties=" + mLinkProperties);
pw.flush();
- pw.println(" mPhone=" + mPhone);
- pw.flush();
- pw.println(" mLinkProperties=" + mLinkProperties);
- pw.flush();
- pw.println(" mDataRegState=" + mDataRegState);
- pw.println(" mRilRat=" + mRilRat);
- pw.println(" mNetworkCapabilities=" + getNetworkCapabilities());
- pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
- pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
- pw.println(" mLastFailCause=" + mLastFailCause);
- pw.flush();
- pw.println(" mUserData=" + mUserData);
- pw.println(" mInstanceNumber=" + mInstanceNumber);
- pw.println(" mAc=" + mAc);
+ pw.println("mDataRegState=" + mDataRegState);
+ pw.println("mRilRat=" + mRilRat);
+ pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
+ pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
+ pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
+ pw.println("mLastFailCause=" + mLastFailCause);
+ pw.println("mUserData=" + mUserData);
+ pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride));
+ pw.println("mInstanceNumber=" + mInstanceNumber);
+ pw.println("mAc=" + mAc);
+ pw.println("Network capabilities changed history:");
+ pw.increaseIndent();
+ mNetCapsLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println();
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index a8bffd3..3b856a7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -17,10 +17,22 @@
package com.android.internal.telephony.dataconnection;
+import android.content.ContentResolver;
import android.os.Handler;
import android.os.RegistrantList;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
import android.util.Pair;
+import com.android.internal.telephony.Phone;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* The class to hold different data enabled/disabled settings. Also it allows clients to register
* for overall data enabled setting changed event.
@@ -28,6 +40,8 @@
*/
public class DataEnabledSettings {
+ private static final String LOG_TAG = "DataEnabledSettings";
+
public static final int REASON_REGISTERED = 0;
public static final int REASON_INTERNAL_DATA_ENABLED = 1;
@@ -45,12 +59,6 @@
private boolean mInternalDataEnabled = true;
/**
- * responds to public (user) API to enable/disable data use independent of
- * mInternalDataEnabled and requests for APN access persisted
- */
- private boolean mUserDataEnabled = true;
-
- /**
* Flag indicating data allowed by network policy manager or not.
*/
private boolean mPolicyDataEnabled = true;
@@ -61,16 +69,29 @@
*/
private boolean mCarrierDataEnabled = true;
+ private Phone mPhone = null;
+ private ContentResolver mResolver = null;
+
private final RegistrantList mDataEnabledChangedRegistrants = new RegistrantList();
+ private final LocalLog mSettingChangeLocalLog = new LocalLog(50);
+
@Override
public String toString() {
- return "[mInternalDataEnabled=" + mInternalDataEnabled + ", mUserDataEnabled="
- + mUserDataEnabled + ", mPolicyDataEnabled=" + mPolicyDataEnabled
+ return "[mInternalDataEnabled=" + mInternalDataEnabled
+ + ", isUserDataEnabled=" + isUserDataEnabled()
+ + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
+ + ", mPolicyDataEnabled=" + mPolicyDataEnabled
+ ", mCarrierDataEnabled=" + mCarrierDataEnabled + "]";
}
+ public DataEnabledSettings(Phone phone) {
+ mPhone = phone;
+ mResolver = mPhone.getContext().getContentResolver();
+ }
+
public synchronized void setInternalDataEnabled(boolean enabled) {
+ localLog("InternalDataEnabled", enabled);
boolean prevDataEnabled = isDataEnabled();
mInternalDataEnabled = enabled;
if (prevDataEnabled != isDataEnabled()) {
@@ -82,17 +103,37 @@
}
public synchronized void setUserDataEnabled(boolean enabled) {
+ localLog("UserDataEnabled", enabled);
boolean prevDataEnabled = isDataEnabled();
- mUserDataEnabled = enabled;
+
+ Settings.Global.putInt(mResolver, getMobileDataSettingName(), enabled ? 1 : 0);
+
if (prevDataEnabled != isDataEnabled()) {
notifyDataEnabledChanged(!prevDataEnabled, REASON_USER_DATA_ENABLED);
}
}
public synchronized boolean isUserDataEnabled() {
- return mUserDataEnabled;
+ boolean defaultVal = "true".equalsIgnoreCase(SystemProperties.get(
+ "ro.com.android.mobiledata", "true"));
+
+ return (Settings.Global.getInt(mResolver, getMobileDataSettingName(),
+ defaultVal ? 1 : 0) != 0);
+ }
+
+ private String getMobileDataSettingName() {
+ // For single SIM phones, this is a per phone property. Or if it's invalid subId, we
+ // read default setting.
+ int subId = mPhone.getSubId();
+ if (TelephonyManager.getDefault().getSimCount() == 1
+ || !SubscriptionManager.isValidSubscriptionId(subId)) {
+ return Settings.Global.MOBILE_DATA;
+ } else {
+ return Settings.Global.MOBILE_DATA + mPhone.getSubId();
+ }
}
public synchronized void setPolicyDataEnabled(boolean enabled) {
+ localLog("PolicyDataEnabled", enabled);
boolean prevDataEnabled = isDataEnabled();
mPolicyDataEnabled = enabled;
if (prevDataEnabled != isDataEnabled()) {
@@ -104,6 +145,7 @@
}
public synchronized void setCarrierDataEnabled(boolean enabled) {
+ localLog("CarrierDataEnabled", enabled);
boolean prevDataEnabled = isDataEnabled();
mCarrierDataEnabled = enabled;
if (prevDataEnabled != isDataEnabled()) {
@@ -115,8 +157,36 @@
}
public synchronized boolean isDataEnabled() {
- return (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled
- && mCarrierDataEnabled);
+ if (isProvisioning()) {
+ return isProvisioningDataEnabled();
+ } else {
+ return mInternalDataEnabled && isUserDataEnabled()
+ && mPolicyDataEnabled && mCarrierDataEnabled;
+ }
+ }
+
+ 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;
}
private void notifyDataEnabledChanged(boolean enabled, int reason) {
@@ -131,4 +201,17 @@
public void unregisterForDataEnabledChanged(Handler h) {
mDataEnabledChangedRegistrants.remove(h);
}
+
+ 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/DataProfile.java b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
deleted file mode 100644
index 48a8107..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
+++ /dev/null
@@ -1,133 +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.dataconnection;
-
-import android.telephony.ServiceState;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.RILConstants;
-
-public class DataProfile {
-
- static final int TYPE_COMMON = 0;
- static final int TYPE_3GPP = 1;
- static final int TYPE_3GPP2 = 2;
-
- //id of the data profile
- public final int profileId;
- //the APN to connect to
- public final String apn;
- //one of the PDP_type values in TS 27.007 section 10.1.1.
- //For example, "IP", "IPV6", "IPV4V6", or "PPP".
- public final String protocol;
- //authentication protocol used for this PDP context
- //(None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3)
- public final int authType;
- //the username for APN, or NULL
- public final String user;
- //the password for APN, or NULL
- public final String password;
- //the profile type, TYPE_COMMON, TYPE_3GPP, TYPE_3GPP2
- public final int type;
- //the period in seconds to limit the maximum connections
- public final int maxConnsTime;
- //the maximum connections during maxConnsTime
- public final int maxConns;
- //the required wait time in seconds after a successful UE initiated
- //disconnect of a given PDN connection before the device can send
- //a new PDN connection request for that given PDN
- public final int waitTime;
- //true to enable the profile, false to disable
- public final boolean enabled;
- //supported APN types bitmap. See RIL_ApnTypes for the value of each bit.
- public final int supportedApnTypesBitmap;
- //one of the PDP_type values in TS 27.007 section 10.1.1 used on roaming network.
- //For example, "IP", "IPV6", "IPV4V6", or "PPP".
- public final String roamingProtocol;
- //The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit.
- public final int bearerBitmap;
- //maximum transmission unit (MTU) size in bytes
- public final int mtu;
- //the MVNO type: possible values are "imsi", "gid", "spn"
- public final String mvnoType;
- //MVNO match data. For example, SPN: A MOBILE, BEN NL, ...
- //IMSI: 302720x94, 2060188, ...
- //GID: 4E, 33, ...
- public final String mvnoMatchData;
- //indicating the data profile was sent to the modem through setDataProfile earlier.
- public final boolean modemCognitive;
-
- DataProfile(int profileId, String apn, String protocol, int authType,
- String user, String password, int type, int maxConnsTime, int maxConns,
- int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
- int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData,
- boolean modemCognitive) {
-
- this.profileId = profileId;
- this.apn = apn;
- this.protocol = protocol;
- if (authType == -1) {
- authType = TextUtils.isEmpty(user) ? RILConstants.SETUP_DATA_AUTH_NONE
- : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
- }
- this.authType = authType;
- this.user = user;
- this.password = password;
- this.type = type;
- this.maxConnsTime = maxConnsTime;
- this.maxConns = maxConns;
- this.waitTime = waitTime;
- this.enabled = enabled;
-
- this.supportedApnTypesBitmap = supportedApnTypesBitmap;
- this.roamingProtocol = roamingProtocol;
- this.bearerBitmap = bearerBitmap;
- this.mtu = mtu;
- this.mvnoType = mvnoType;
- this.mvnoMatchData = mvnoMatchData;
- this.modemCognitive = modemCognitive;
- }
-
- public DataProfile(ApnSetting apn) {
- this(apn, apn.profileId);
- }
-
- public DataProfile(ApnSetting apn, int profileId) {
- this(profileId, apn.apn, apn.protocol,
- apn.authType, apn.user, apn.password, apn.bearerBitmask == 0
- ? TYPE_COMMON : (ServiceState.bearerBitmapHasCdma(apn.bearerBitmask)
- ? TYPE_3GPP2 : TYPE_3GPP),
- apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled, apn.typesBitmap,
- apn.roamingProtocol, apn.bearerBitmask, apn.mtu, apn.mvnoType, apn.mvnoMatchData,
- apn.modemCognitive);
- }
-
- @Override
- public String toString() {
- return "DataProfile=" + profileId + "/" + apn + "/" + protocol + "/" + authType
- + "/" + user + "/" + password + "/" + type + "/" + maxConnsTime
- + "/" + maxConns + "/" + waitTime + "/" + enabled + "/" + supportedApnTypesBitmap
- + "/" + roamingProtocol + "/" + bearerBitmap + "/" + mtu + "/" + mvnoType + "/"
- + mvnoMatchData + "/" + modemCognitive;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof DataProfile == false) return false;
- return (o == this || toString().equals(o.toString()));
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
new file mode 100644
index 0000000..8c3f751
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -0,0 +1,498 @@
+/*
+ * 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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+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.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+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.text.TextUtils;
+
+import com.android.internal.telephony.Phone;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Data service manager manages handling data requests and responses on data services (e.g.
+ * Cellular data service, IWLAN data service).
+ */
+public class DataServiceManager {
+ private static final String TAG = DataServiceManager.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ static final String DATA_CALL_RESPONSE = "data_call_response";
+
+ private final Phone mPhone;
+
+ private final CarrierConfigManager mCarrierConfigManager;
+
+ 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 class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
+
+ private final ComponentName mComponentName;
+
+ DataServiceManagerDeathRecipient(ComponentName name) {
+ mComponentName = name;
+ }
+
+ @Override
+ public void binderDied() {
+ // TODO: try to rebind the service.
+ loge("DataService(" + mComponentName + " transport type " + mTransportType
+ + ") died.");
+ }
+ }
+
+ 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(name);
+ mBound = true;
+
+ try {
+ service.linkToDeath(mDeathRecipient, 0);
+ mIDataService.createDataServiceProvider(mPhone.getPhoneId());
+ mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
+ new CellularDataServiceCallback());
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
+ loge("Remote exception. " + e);
+ return;
+ }
+
+ mServiceBindingChangedRegistrants.notifyResult(true);
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DBG) log("onServiceDisconnected");
+ mIDataService.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ mIDataService = null;
+ mBound = false;
+ mServiceBindingChangedRegistrants.notifyResult(false);
+ }
+ }
+
+ private final class CellularDataServiceCallback extends IDataServiceCallback.Stub {
+ @Override
+ public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode,
+ DataCallResponse response) {
+ if (DBG) {
+ log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
+ + response);
+ }
+ 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);
+ 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 onGetDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
+ List<DataCallResponse> dataCallList) {
+ if (DBG) log("onGetDataCallListComplete. resultCode = " + resultCode);
+ Message msg = mMessageMap.remove(asBinder());
+ sendCompleteMessage(msg, resultCode);
+ }
+
+ @Override
+ public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
+ mDataCallListChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, dataCallList, null));
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param phone The phone object
+ * @param transportType The transport type. Must be a
+ * {@link AccessNetworkConstants.TransportType}.
+ */
+ public DataServiceManager(Phone phone, int transportType) {
+ mPhone = phone;
+ mTransportType = transportType;
+ mBound = false;
+ mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+
+ bindDataService();
+ }
+
+ private void bindDataService() {
+ String packageName = getDataServicePackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ loge("Can't find the binding package");
+ return;
+ }
+
+ try {
+ if (!mPhone.getContext().bindService(
+ new Intent(DataService.DATA_SERVICE_INTERFACE).setPackage(packageName),
+ new CellularDataServiceConnection(),
+ Context.BIND_AUTO_CREATE)) {
+ loge("Cannot bind to the data service.");
+ }
+ } catch (Exception e) {
+ loge("Cannot bind to the data service. Exception: " + e);
+ }
+ }
+
+ private String getDataServicePackageName() {
+ String packageName;
+ int resourceId;
+ String carrierConfig;
+
+ switch (mTransportType) {
+ case AccessNetworkConstants.TransportType.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.TransportType.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="
+ + mTransportType);
+ }
+
+ // Read package name from resource overlay
+ packageName = mPhone.getContext().getResources().getString(resourceId);
+
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ if (b != null) {
+ // If carrier config overrides it, use the one from carrier config
+ packageName = b.getString(carrierConfig, packageName);
+ }
+
+ return packageName;
+ }
+
+ private void sendCompleteMessage(Message msg, 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 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,
+ Message onCompleteMessage) {
+ if (DBG) log("setupDataCall");
+ if (!mBound) {
+ loge("Data service not bound.");
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ return;
+ }
+
+ CellularDataServiceCallback callback = null;
+ if (onCompleteMessage != null) {
+ callback = new CellularDataServiceCallback();
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+ try {
+ mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
+ isRoaming, allowRoaming, reason, linkProperties, callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke setupDataCall on data service.");
+ if (callback != null) {
+ 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 = null;
+ if (onCompleteMessage != null) {
+ callback = new CellularDataServiceCallback();
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+ try {
+ mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke deactivateDataCall on data service.");
+ if (callback != null) {
+ mMessageMap.remove(callback.asBinder());
+ }
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ }
+ }
+
+ /**
+ * 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 = null;
+ if (onCompleteMessage != null) {
+ callback = new CellularDataServiceCallback();
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+ try {
+ mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
+ callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke setInitialAttachApn on data service.");
+ if (callback != null) {
+ 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 = null;
+ if (onCompleteMessage != null) {
+ callback = new CellularDataServiceCallback();
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+ try {
+ mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke setDataProfile on data service.");
+ if (callback != null) {
+ 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 getDataCallList(Message onCompleteMessage) {
+ if (DBG) log("getDataCallList");
+ if (!mBound) {
+ loge("Data service not bound.");
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ return;
+ }
+
+ CellularDataServiceCallback callback = null;
+ if (onCompleteMessage != null) {
+ callback = new CellularDataServiceCallback();
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ }
+ try {
+ mIDataService.getDataCallList(mPhone.getPhoneId(), callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke getDataCallList 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 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 AccessNetworkConstants.TransportType}.
+ *
+ * @return
+ */
+ public int getTransportType() {
+ return mTransportType;
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 67d91d3..06bb7de 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -20,6 +20,7 @@
import android.net.NetworkCapabilities;
import android.net.ProxyInfo;
import android.os.Message;
+import android.telephony.data.ApnSetting;
import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
@@ -80,6 +81,8 @@
sCmdToString[RSP_RESET - BASE] = "RSP_RESET";
}
+ ConnectionParams mLastConnectionParams;
+
// Convert cmd to string or null if unknown
protected static String cmdToString(int cmd) {
cmd -= BASE;
@@ -151,7 +154,7 @@
value = false;
}
} else {
- value = mDc.getIsInactive();
+ value = mDc.isInactive();
}
return value;
}
@@ -377,9 +380,9 @@
log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly
+ " onCompletedMsg=" + onCompletedMsg);
}
- sendMessage(DataConnection.EVENT_CONNECT,
- new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly,
- onCompletedMsg, connectionGeneration));
+ mLastConnectionParams = new ConnectionParams(apnContext, profileId, rilRadioTechnology,
+ unmeteredUseOnly, onCompletedMsg, connectionGeneration);
+ sendMessage(DataConnection.EVENT_CONNECT, mLastConnectionParams);
}
/**
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index 291d6f5..8d05be7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -17,16 +17,20 @@
package com.android.internal.telephony.dataconnection;
import android.content.Context;
+import android.net.INetworkPolicyListener;
import android.net.LinkAddress;
import android.net.LinkProperties.CompareResult;
+import android.net.NetworkPolicyManager;
import android.net.NetworkUtils;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.PhoneStateListener;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
+import android.telephony.data.DataCallResponse;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
@@ -49,14 +53,16 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private Phone mPhone;
- private DcTracker mDct;
- private DcTesterDeactivateAll mDcTesterDeactivateAll;
+ private final Phone mPhone;
+ private final DcTracker mDct;
+ private final DataServiceManager mDataServiceManager;
+ private final DcTesterDeactivateAll mDcTesterDeactivateAll;
// package as its used by Testing code
- ArrayList<DataConnection> mDcListAll = new ArrayList<DataConnection>();
- private HashMap<Integer, DataConnection> mDcListActiveByCid =
- new HashMap<Integer, DataConnection>();
+ // @GuardedBy("mDcListAll")
+ final ArrayList<DataConnection> mDcListAll = new ArrayList<>();
+ // @GuardedBy("mDcListAll")
+ private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>();
/**
* Constants for the data connection activity:
@@ -71,7 +77,9 @@
private DccDefaultState mDccDefaultState = new DccDefaultState();
- TelephonyManager mTelephonyManager;
+ final TelephonyManager mTelephonyManager;
+ final NetworkPolicyManager mNetworkPolicyManager;
+
private PhoneStateListener mPhoneStateListener;
//mExecutingCarrierChange tracks whether the phone is currently executing
@@ -84,15 +92,17 @@
* @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 handler defines the thread/looper to be used with Dcc
*/
private DcController(String name, Phone phone, DcTracker dct,
- Handler handler) {
+ DataServiceManager dataServiceManager, Handler handler) {
super(name, handler);
setLogRecSize(300);
log("E ctor");
mPhone = phone;
mDct = dct;
+ mDataServiceManager = dataServiceManager;
addState(mDccDefaultState);
setInitialState(mDccDefaultState);
log("X ctor");
@@ -104,16 +114,24 @@
}
};
- mTelephonyManager = (TelephonyManager) phone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
- if(mTelephonyManager != null) {
+ mTelephonyManager = (TelephonyManager) phone.getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ mNetworkPolicyManager = (NetworkPolicyManager) phone.getContext()
+ .getSystemService(Context.NETWORK_POLICY_SERVICE);
+
+ mDcTesterDeactivateAll = (Build.IS_DEBUGGABLE)
+ ? new DcTesterDeactivateAll(mPhone, DcController.this, getHandler())
+ : null;
+
+ if (mTelephonyManager != null) {
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
}
}
- public static DcController makeDcc(Phone phone, DcTracker dct, Handler handler) {
- DcController dcc = new DcController("Dcc", phone, dct, handler);
- dcc.start();
+ public static DcController makeDcc(Phone phone, DcTracker dct,
+ DataServiceManager dataServiceManager, Handler handler) {
+ DcController dcc = new DcController("Dcc", phone, dct, dataServiceManager, handler);
return dcc;
}
@@ -124,29 +142,39 @@
}
void addDc(DataConnection dc) {
- mDcListAll.add(dc);
+ synchronized (mDcListAll) {
+ mDcListAll.add(dc);
+ }
}
void removeDc(DataConnection dc) {
- mDcListActiveByCid.remove(dc.mCid);
- mDcListAll.remove(dc);
+ synchronized (mDcListAll) {
+ mDcListActiveByCid.remove(dc.mCid);
+ mDcListAll.remove(dc);
+ }
}
public void addActiveDcByCid(DataConnection dc) {
if (DBG && dc.mCid < 0) {
log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
}
- mDcListActiveByCid.put(dc.mCid, dc);
+ synchronized (mDcListAll) {
+ mDcListActiveByCid.put(dc.mCid, dc);
+ }
}
public DataConnection getActiveDcByCid(int cid) {
- return mDcListActiveByCid.get(cid);
+ synchronized (mDcListAll) {
+ return mDcListActiveByCid.get(cid);
+ }
}
void removeActiveDcByCid(DataConnection dc) {
- DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
- if (DBG && removedDc == null) {
- log("removeActiveDcByCid removedDc=null dc=" + dc);
+ synchronized (mDcListAll) {
+ DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
+ if (DBG && removedDc == null) {
+ log("removeActiveDcByCid removedDc=null dc=" + dc);
+ }
}
}
@@ -154,28 +182,52 @@
return mExecutingCarrierChange;
}
+ private final INetworkPolicyListener mListener = new NetworkPolicyManager.Listener() {
+ @Override
+ public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue) {
+ if (mPhone == null || mPhone.getSubId() != subId) return;
+
+ final HashMap<Integer, DataConnection> dcListActiveByCid;
+ synchronized (mDcListAll) {
+ dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
+ }
+ for (DataConnection dc : dcListActiveByCid.values()) {
+ dc.onSubscriptionOverride(overrideMask, overrideValue);
+ }
+ }
+ };
+
private class DccDefaultState extends State {
@Override
public void enter() {
- mPhone.mCi.registerForRilConnected(getHandler(),
- DataConnection.EVENT_RIL_CONNECTED, null);
- mPhone.mCi.registerForDataCallListChanged(getHandler(),
- DataConnection.EVENT_DATA_STATE_CHANGED, null);
- if (Build.IS_DEBUGGABLE) {
- mDcTesterDeactivateAll =
- new DcTesterDeactivateAll(mPhone, DcController.this, getHandler());
+ if (mPhone != null && mDataServiceManager.getTransportType()
+ == TransportType.WWAN) {
+ mPhone.mCi.registerForRilConnected(getHandler(),
+ DataConnection.EVENT_RIL_CONNECTED, null);
+ }
+
+ mDataServiceManager.registerForDataCallListChanged(getHandler(),
+ DataConnection.EVENT_DATA_STATE_CHANGED);
+
+ if (mNetworkPolicyManager != null) {
+ mNetworkPolicyManager.registerListener(mListener);
}
}
@Override
public void exit() {
- if (mPhone != null) {
+ if (mPhone != null & mDataServiceManager.getTransportType()
+ == TransportType.WWAN) {
mPhone.mCi.unregisterForRilConnected(getHandler());
- mPhone.mCi.unregisterForDataCallListChanged(getHandler());
}
+ mDataServiceManager.unregisterForDataCallListChanged(getHandler());
+
if (mDcTesterDeactivateAll != null) {
mDcTesterDeactivateAll.dispose();
}
+ if (mNetworkPolicyManager != null) {
+ mNetworkPolicyManager.unregisterListener(mListener);
+ }
}
@Override
@@ -213,25 +265,32 @@
* @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
*/
private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
+ final ArrayList<DataConnection> dcListAll;
+ final HashMap<Integer, DataConnection> dcListActiveByCid;
+ synchronized (mDcListAll) {
+ dcListAll = new ArrayList<>(mDcListAll);
+ dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
+ }
+
if (DBG) {
lr("onDataStateChanged: dcsList=" + dcsList
- + " mDcListActiveByCid=" + mDcListActiveByCid);
+ + " dcListActiveByCid=" + dcListActiveByCid);
}
if (VDBG) {
- log("onDataStateChanged: mDcListAll=" + mDcListAll);
+ log("onDataStateChanged: mDcListAll=" + dcListAll);
}
// Create hashmap of cid to DataCallResponse
HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
new HashMap<Integer, DataCallResponse>();
for (DataCallResponse dcs : dcsList) {
- dataCallResponseListByCid.put(dcs.cid, dcs);
+ dataCallResponseListByCid.put(dcs.getCallId(), 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<DataConnection>();
- for (DataConnection dc : mDcListActiveByCid.values()) {
+ for (DataConnection dc : dcListActiveByCid.values()) {
if (dataCallResponseListByCid.get(dc.mCid) == null) {
if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
dcsToRetry.add(dc);
@@ -248,7 +307,7 @@
for (DataCallResponse newState : dcsList) {
- DataConnection dc = mDcListActiveByCid.get(newState.cid);
+ DataConnection dc = dcListActiveByCid.get(newState.getCallId());
if (dc == null) {
// UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
loge("onDataStateChanged: no associated DC yet, ignore");
@@ -260,14 +319,16 @@
} 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.cid
- + " newState=" + newState.toString());
- if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
+ if (DBG) {
+ log("onDataStateChanged: Found ConnId=" + newState.getCallId()
+ + " newState=" + newState.toString());
+ }
+ if (newState.getActive() == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
if (mDct.isCleanupRequired.get()) {
apnsToCleanup.addAll(dc.mApnContexts.keySet());
mDct.isCleanupRequired.set(false);
} else {
- DcFailCause failCause = DcFailCause.fromInt(newState.status);
+ DcFailCause failCause = DcFailCause.fromInt(newState.getStatus());
if (failCause.isRestartRadioFail(mPhone.getContext(),
mPhone.getSubId())) {
if (DBG) {
@@ -352,10 +413,10 @@
}
}
- if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
+ if (newState.getActive() == DATA_CONNECTION_ACTIVE_PH_LINK_UP) {
isAnyDataCallActive = true;
}
- if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
+ if (newState.getActive() == DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT) {
isAnyDataCallDormant = true;
}
}
@@ -434,14 +495,18 @@
@Override
public String toString() {
- return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid;
+ synchronized (mDcListAll) {
+ return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid;
+ }
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
pw.println(" mPhone=" + mPhone);
- pw.println(" mDcListAll=" + mDcListAll);
- pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);
+ synchronized (mDcListAll) {
+ pw.println(" mDcListAll=" + mDcListAll);
+ pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
index 79cfcec..bf2a6af 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
@@ -15,22 +15,10 @@
*/
package com.android.internal.telephony.dataconnection;
-import static com.android.internal.telephony.DctConstants.APN_CBS_ID;
-import static com.android.internal.telephony.DctConstants.APN_DEFAULT_ID;
-import static com.android.internal.telephony.DctConstants.APN_DUN_ID;
-import static com.android.internal.telephony.DctConstants.APN_EMERGENCY_ID;
-import static com.android.internal.telephony.DctConstants.APN_FOTA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_INVALID_ID;
-import static com.android.internal.telephony.DctConstants.APN_MMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_SUPL_ID;
-
import android.content.Context;
-import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
-import android.telephony.Rlog;
+import android.telephony.data.ApnSetting.ApnType;
import java.util.HashMap;
@@ -39,17 +27,17 @@
public final NetworkRequest networkRequest;
public final int priority;
- public final int apnId;
+ public final @ApnType int apnType;
public DcRequest(NetworkRequest nr, Context context) {
initApnPriorities(context);
networkRequest = nr;
- apnId = apnIdForNetworkRequest(networkRequest);
- priority = priorityForApnId(apnId);
+ apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ priority = priorityForApnType(apnType);
}
public String toString() {
- return networkRequest.toString() + ", priority=" + priority + ", apnId=" + apnId;
+ return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType;
}
public int hashCode() {
@@ -67,78 +55,6 @@
return o.priority - priority;
}
- private int apnIdForNetworkRequest(NetworkRequest nr) {
- NetworkCapabilities nc = nr.networkCapabilities;
- // For now, ignore the bandwidth stuff
- if (nc.getTransportTypes().length > 0 &&
- nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
- return APN_INVALID_ID;
- }
-
- // in the near term just do 1-1 matches.
- // TODO - actually try to match the set of capabilities
- int apnId = APN_INVALID_ID;
-
- boolean error = false;
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_DEFAULT_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_MMS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_SUPL_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_DUN_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_FOTA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_IMS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_CBS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_IA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_INVALID_ID;
- loge("RCS APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_INVALID_ID;
- loge("XCAP APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_EMERGENCY_ID;
- }
- 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.
- loge("Multiple apn types specified in request - result is unspecified!");
- }
- if (apnId == APN_INVALID_ID) {
- loge("Unsupported NetworkRequest in Telephony: nr=" + nr);
- }
- return apnId;
- }
-
private static final HashMap<Integer, Integer> sApnPriorityMap =
new HashMap<Integer, Integer>();
@@ -149,19 +65,15 @@
com.android.internal.R.array.networkAttributes);
for (String networkConfigString : networkConfigStrings) {
NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
- final int apnId = ApnContext.apnIdForType(networkConfig.type);
- sApnPriorityMap.put(apnId, networkConfig.priority);
+ final int apnType = ApnContext.getApnTypeFromNetworkType(networkConfig.type);
+ sApnPriorityMap.put(apnType, networkConfig.priority);
}
}
}
}
- private int priorityForApnId(int apnId) {
- Integer priority = sApnPriorityMap.get(apnId);
+ private int priorityForApnType(int apnType) {
+ Integer priority = sApnPriorityMap.get(apnType);
return (priority != null ? priority.intValue() : 0);
}
-
- private void loge(String s) {
- Rlog.e(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
index 009ee59..50130c8 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.dataconnection;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
@@ -34,13 +35,10 @@
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
-import android.net.NetworkInfo;
import android.net.NetworkRequest;
-import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Bundle;
@@ -56,6 +54,7 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Telephony;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.PcoData;
@@ -65,6 +64,8 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
@@ -97,7 +98,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map.Entry;
@@ -122,10 +122,10 @@
private final AlarmManager mAlarmManager;
/* Currently requested APN type (TODO: This should probably be a parameter not a member) */
- private String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ private int mRequestedApnType = ApnSetting.TYPE_DEFAULT;
// All data enabling/disabling related settings
- private final DataEnabledSettings mDataEnabledSettings = new DataEnabledSettings();
+ private final DataEnabledSettings mDataEnabledSettings;
/**
@@ -215,6 +215,9 @@
private AsyncChannel mReplyAc = new AsyncChannel();
+ private final LocalLog mDataRoamingLeakageLog = new LocalLog(50);
+ private final LocalLog mApnSettingsInitializationLog = new LocalLog(50);
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
@Override
public void onReceive(Context context, Intent intent) {
@@ -242,24 +245,6 @@
} else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) {
if (DBG) log("Provisioning apn alarm");
onActionIntentProvisioningApnAlarm(intent);
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- final android.net.NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
- if (DBG) log("NETWORK_STATE_CHANGED_ACTION: mIsWifiConnected=" + mIsWifiConnected);
- } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- if (DBG) log("Wifi state changed");
- final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
- if (!enabled) {
- // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
- // quit and won't report disconnected until next enabling.
- mIsWifiConnected = false;
- }
- if (DBG) {
- log("WIFI_STATE_CHANGED_ACTION: enabled=" + enabled
- + " mIsWifiConnected=" + mIsWifiConnected);
- }
} else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
if (mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded()) {
setDefaultDataRoamingEnabled();
@@ -326,7 +311,7 @@
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix),
- DctConstants.EVENT_ROAMING_ON);
+ DctConstants.EVENT_ROAMING_SETTING_CHANGE);
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
@@ -369,10 +354,21 @@
return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
}
- public void updateTxRxSum() {
+ /**
+ * Get Tcp Tx/Rx packet count from TrafficStats
+ */
+ public void updateTcpTxRxSum() {
this.txPkts = TrafficStats.getMobileTcpTxPackets();
this.rxPkts = TrafficStats.getMobileTcpRxPackets();
}
+
+ /**
+ * Get total Tx/Rx packet count from TrafficStats
+ */
+ public void updateTotalTxRxSum() {
+ this.txPkts = TrafficStats.getMobileTxPackets();
+ this.rxPkts = TrafficStats.getMobileRxPackets();
+ }
}
private void onActionIntentReconnectAlarm(Intent intent) {
@@ -485,9 +481,6 @@
// True when in voice call
private boolean mInVoiceCall = false;
- // wifi connection status will be updated by sticky intent
- private boolean mIsWifiConnected = false;
-
/** Intent sent when the reconnect alarm fires. */
private PendingIntent mReconnectIntent = null;
@@ -522,7 +515,7 @@
private final ConcurrentHashMap<String, ApnContext> mApnContexts =
new ConcurrentHashMap<String, ApnContext>();
- private final SparseArray<ApnContext> mApnContextsById = new SparseArray<ApnContext>();
+ private final SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
private int mDisconnectPendingCount = 0;
@@ -583,14 +576,17 @@
private BroadcastReceiver mProvisionBroadcastReceiver;
private ProgressDialog mProvisioningSpinner;
- public boolean mImsRegistrationState = false;
+ private final DataServiceManager mDataServiceManager;
+
+ private final int mTransportType;
//***** Constructor
- public DcTracker(Phone phone) {
+ public DcTracker(Phone phone, int transportType) {
super();
mPhone = phone;
-
if (DBG) log("DCT.constructor");
+ mTransportType = transportType;
+ mDataServiceManager = new DataServiceManager(phone, transportType);
mResolver = mPhone.getContext().getContentResolver();
mUiccController = UiccController.getInstance();
@@ -604,13 +600,11 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(INTENT_DATA_STALL_ALARM);
filter.addAction(INTENT_PROVISIONING_APN_ALARM);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- // TODO - redundent with update call below?
- mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
+
+ mDataEnabledSettings = new DataEnabledSettings(phone);
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
@@ -623,7 +617,7 @@
HandlerThread dcHandlerThread = new HandlerThread("DcHandlerThread");
dcHandlerThread.start();
Handler dcHandler = new Handler(dcHandlerThread.getLooper());
- mDcc = DcController.makeDcc(mPhone, this, dcHandler);
+ mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler);
mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
mDataConnectionTracker = this;
@@ -661,6 +655,9 @@
mDataConnectionTracker = null;
mProvisionActionName = null;
mSettingsObserver = new SettingsObserver(null, this);
+ mDataEnabledSettings = null;
+ mTransportType = 0;
+ mDataServiceManager = null;
}
public void registerServiceStateTrackerEvents() {
@@ -691,11 +688,13 @@
}
private void registerForAllEvents() {
- mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
- mPhone.mCi.registerForOffOrNotAvailable(this,
- DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
- mPhone.mCi.registerForDataCallListChanged(this,
- DctConstants.EVENT_DATA_STATE_CHANGED, null);
+ if (mTransportType == TransportType.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
@@ -706,12 +705,12 @@
mPhone.getCallTracker().registerForVoiceCallStarted(this,
DctConstants.EVENT_VOICE_CALL_STARTED, null);
registerServiceStateTrackerEvents();
- // SubscriptionManager.registerForDdsSwitch(this,
- // DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
mPhone.getCarrierActionAgent().registerForCarrierAction(
CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
DctConstants.EVENT_SET_CARRIER_DATA_ENABLED, null, false);
+ mDataServiceManager.registerForServiceBindingChanged(this,
+ DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
}
public void dispose() {
@@ -744,7 +743,7 @@
mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
mApnContexts.clear();
- mApnContextsById.clear();
+ mApnContextsByType.clear();
mPrioritySortedApnContexts.clear();
unregisterForAllEvents();
@@ -753,43 +752,30 @@
private void unregisterForAllEvents() {
//Unregister for all events
- mPhone.mCi.unregisterForAvailable(this);
- mPhone.mCi.unregisterForOffOrNotAvailable(this);
+ if (mTransportType == TransportType.WWAN) {
+ mPhone.mCi.unregisterForAvailable(this);
+ mPhone.mCi.unregisterForOffOrNotAvailable(this);
+ mPhone.mCi.unregisterForPcoData(this);
+ }
+
IccRecords r = mIccRecords.get();
if (r != null) {
r.unregisterForRecordsLoaded(this);
mIccRecords.set(null);
}
- mPhone.mCi.unregisterForDataCallListChanged(this);
mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
unregisterServiceStateTrackerEvents();
- //SubscriptionManager.unregisterForDdsSwitch(this);
mPhone.mCi.unregisterForPcoData(this);
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED);
- }
-
- /**
- * Called when EVENT_RESET_DONE is received so goto
- * IDLE state and send notifications to those interested.
- *
- * TODO - currently unused. Needs to be hooked into DataConnection cleanup
- * TODO - needs to pass some notion of which connection is reset..
- */
- private void onResetDone(AsyncResult ar) {
- if (DBG) log("EVENT_RESET_DONE");
- String reason = null;
- if (ar.userObj instanceof String) {
- reason = (String) ar.userObj;
- }
- gotoIdleAndNotifyDataConnection(reason);
+ mDataServiceManager.unregisterForServiceBindingChanged(this);
}
/**
* Modify {@link android.provider.Settings.Global#MOBILE_DATA} value.
*/
- public void setDataEnabled(boolean enable) {
+ public void setUserDataEnabled(boolean enable) {
Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
msg.arg1 = enable ? 1 : 0;
if (DBG) log("setDataEnabled: sendMessage: enable=" + enable);
@@ -797,36 +783,26 @@
}
private void onSetUserDataEnabled(boolean enabled) {
- synchronized (mDataEnabledSettings) {
- if (mDataEnabledSettings.isUserDataEnabled() != enabled) {
- mDataEnabledSettings.setUserDataEnabled(enabled);
-
- //TODO: We should move the followings into DataEnabledSettings class.
- // For single SIM phones, this is a per phone property.
- if (TelephonyManager.getDefault().getSimCount() == 1) {
- Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
- } else {
- int phoneSubId = mPhone.getSubId();
- Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA + phoneSubId,
- enabled ? 1 : 0);
- }
- if (!getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
- if (enabled) {
- notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
- } else {
- notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
- }
- }
-
- // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
- // handle the rest from there.
+ if (mDataEnabledSettings.isUserDataEnabled() != enabled) {
+ mDataEnabledSettings.setUserDataEnabled(enabled);
+ if (!getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
if (enabled) {
- reevaluateDataConnections();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
+ notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
} else {
- onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
+ notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
}
}
+
+ mPhone.notifyUserMobileDataStateChanged(enabled);
+
+ // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+ // handle the rest from there.
+ if (enabled) {
+ reevaluateDataConnections();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
+ }
}
}
@@ -860,7 +836,7 @@
// data connection.
apnContext.setReason(Phone.REASON_DATA_ENABLED);
cleanUpConnection(true, apnContext);
- } else if (apnContext.getApnSetting().isMetered(mPhone)
+ } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone)
&& (netCaps != null && netCaps.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
if (DBG) {
@@ -880,12 +856,10 @@
}
private void onDeviceProvisionedChange() {
- if (getDataEnabled()) {
- mDataEnabledSettings.setUserDataEnabled(true);
+ if (isDataEnabled()) {
reevaluateDataConnections();
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
- mDataEnabledSettings.setUserDataEnabled(false);
onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
}
}
@@ -906,40 +880,19 @@
}
public void requestNetwork(NetworkRequest networkRequest, LocalLog log) {
- final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsById.get(apnId);
+ final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ final ApnContext apnContext = mApnContextsByType.get(apnType);
log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext);
if (apnContext != null) apnContext.requestNetwork(networkRequest, log);
}
public void releaseNetwork(NetworkRequest networkRequest, LocalLog log) {
- final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsById.get(apnId);
+ final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ final ApnContext apnContext = mApnContextsByType.get(apnType);
log.log("DcTracker.releaseNetwork for " + networkRequest + " found " + apnContext);
if (apnContext != null) apnContext.releaseNetwork(networkRequest, log);
}
- public boolean isApnSupported(String name) {
- if (name == null) {
- loge("isApnSupported: name=null");
- return false;
- }
- ApnContext apnContext = mApnContexts.get(name);
- if (apnContext == null) {
- loge("Request for unsupported mobile name: " + name);
- return false;
- }
- return true;
- }
-
- public int getApnPriority(String name) {
- ApnContext apnContext = mApnContexts.get(name);
- if (apnContext == null) {
- loge("Request for unsupported mobile name: " + name);
- }
- return apnContext.priority;
- }
-
// Turn telephony radio on or off.
private void setRadio(boolean on) {
final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
@@ -1010,7 +963,7 @@
private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this);
mApnContexts.put(type, apnContext);
- mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext);
+ mApnContextsByType.put(ApnSetting.getApnTypesBitmaskFromString(type), apnContext);
mPrioritySortedApnContexts.add(apnContext);
return apnContext;
}
@@ -1114,19 +1067,37 @@
if (apnContext != null) {
ApnSetting apnSetting = apnContext.getApnSetting();
if (apnSetting != null) {
- return apnSetting.apn;
+ return apnSetting.getApnName();
}
}
return null;
}
- // Return state of specific apn type
+ /**
+ * 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.
+ */
public DctConstants.State getState(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext != null) {
- return apnContext.getState();
+ final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
+ for (DataConnection dc : mDataConnections.values()) {
+ ApnSetting apnSetting = dc.getApnSetting();
+ if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) {
+ if (dc.isActive()) {
+ return DctConstants.State.CONNECTED;
+ } else if (dc.isActivating()) {
+ return DctConstants.State.CONNECTING;
+ } else if (dc.isInactive()) {
+ return DctConstants.State.IDLE;
+ } else if (dc.isDisconnecting()) {
+ return DctConstants.State.DISCONNECTING;
+ }
+ }
}
- return DctConstants.State.FAILED;
+
+ return DctConstants.State.IDLE;
}
// Return if apn type is a provisioning apn.
@@ -1185,6 +1156,10 @@
}
}
+ /**
+ * Whether data is enabled. This does not only check isUserDataEnabled(), but also
+ * others like CarrierDataEnabled and internalDataEnabled.
+ */
@VisibleForTesting
public boolean isDataEnabled() {
return mDataEnabledSettings.isDataEnabled();
@@ -1274,7 +1249,7 @@
SubscriptionManager.getDefaultDataSubscriptionId());
boolean isMeteredApnType = apnContext == null
- || ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone);
+ || ApnSettingUtils.isMeteredApnType(apnContext.getApnType(), mPhone);
PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
// Note this is explicitly not using mPhone.getState. See b/19090488.
@@ -1371,11 +1346,11 @@
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.
+ // If the request is restricted and there are only disallowed reasons due to data
+ // disabled, we should allow the data.
if (apnContext != null
&& !apnContext.hasNoRestrictedRequests(true)
- && !reasons.allowed()) {
+ && reasons.contains(DataDisallowedReasonType.DATA_DISABLED)) {
reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
}
@@ -1569,22 +1544,29 @@
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);
+ reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN) ||
+ reason.equals(Phone.REASON_PDP_RESET);
}
for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.isDisconnected() == false) didDisconnect = true;
if (disableMeteredOnly) {
// Use ApnSetting to decide metered or non-metered.
// Tear down all metered data connections.
ApnSetting apnSetting = apnContext.getApnSetting();
- if (apnSetting != null && apnSetting.isMetered(mPhone)) {
+ if (apnSetting != null && ApnSettingUtils.isMetered(apnSetting, mPhone)) {
+ if (apnContext.isDisconnected() == false) didDisconnect = true;
if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
}
} else {
+ // Exclude the IMS APN from single DataConenction case.
+ if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
+ && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ continue;
+ }
// TODO - only do cleanup if not disconnected
+ if (apnContext.isDisconnected() == false) didDisconnect = true;
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
}
@@ -1594,7 +1576,7 @@
stopDataStallAlarm();
// TODO: Do we need mRequestedApnType?
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ mRequestedApnType = ApnSetting.TYPE_DEFAULT;
log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
if (tearDown && mDisconnectPendingCount == 0) {
@@ -1716,27 +1698,27 @@
}
/**
- * Fetch dun apn
- * @return ApnSetting to be used for dun
+ * Fetch the DUN apns
+ * @return a list of DUN ApnSetting objects
*/
@VisibleForTesting
- public ApnSetting fetchDunApn() {
+ public @NonNull ArrayList<ApnSetting> fetchDunApns() {
if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
- log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
- return null;
+ log("fetchDunApns: net.tethering.noprovisioning=true ret: empty list");
+ return new ArrayList<ApnSetting>(0);
}
int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
IccRecords r = mIccRecords.get();
String operator = (r != null) ? r.getOperatorNumeric() : "";
ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
- ApnSetting retDunSetting = null;
+ ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
// Places to look for tether APN in order: TETHER_DUN_APN setting (to be deprecated soon),
// APN database, and config_tether_apndata resource (to be deprecated soon).
String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
if (!TextUtils.isEmpty(apnData)) {
dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
- if (VDBG) log("fetchDunApn: dunCandidates from Setting: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from Setting: " + dunCandidates);
}
// todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
@@ -1751,45 +1733,60 @@
// apn may be null if apnString isn't valid or has error parsing
if (apn != null) dunCandidates.add(apn);
}
- if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from resource: " + dunCandidates);
}
}
if (dunCandidates.isEmpty()) {
if (!ArrayUtils.isEmpty(mAllApnSettings)) {
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+ if (apn.canHandleType(ApnSetting.TYPE_DUN)) {
dunCandidates.add(apn);
}
}
- if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates);
}
}
for (ApnSetting dunSetting : dunCandidates) {
- if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
- if (dunSetting.numeric.equals(operator)) {
+ if (!ServiceState.bitmaskHasTech(dunSetting.getNetworkTypeBitmask(),
+ ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
+ continue;
+ }
+ if (dunSetting.getOperatorNumeric().equals(operator)) {
if (dunSetting.hasMvnoParams()) {
- if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
- dunSetting.mvnoMatchData)) {
- retDunSetting = dunSetting;
- break;
+ if (r != null && ApnSettingUtils.mvnoMatches(r, dunSetting.getMvnoType(),
+ dunSetting.getMvnoMatchData())) {
+ retDunSettings.add(dunSetting);
}
} else if (mMvnoMatched == false) {
- retDunSetting = dunSetting;
- break;
+ retDunSettings.add(dunSetting);
}
}
}
- if (VDBG) log("fetchDunApn: dunSetting=" + retDunSetting);
- return retDunSetting;
+ if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
+ return retDunSettings;
+ }
+
+ private int getPreferredApnSetId() {
+ 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.getCount() < 1) {
+ loge("getPreferredApnSetId: no APNs found");
+ return Telephony.Carriers.NO_SET_SET;
+ } else {
+ c.moveToFirst();
+ return c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
+ }
}
public boolean hasMatchedTetherApnSetting() {
- ApnSetting matched = fetchDunApn();
- log("hasMatchedTetherApnSetting: APN=" + matched);
- return matched != null;
+ ArrayList<ApnSetting> matches = fetchDunApns();
+ log("hasMatchedTetherApnSetting: APNs=" + matches);
+ return matches.size() > 0;
}
/**
@@ -1800,7 +1797,8 @@
final int rilRat = mPhone.getServiceState().getRilDataRadioTechnology();
if (ServiceState.isCdma(rilRat)) return true;
- return (fetchDunApn() != null);
+ ArrayList<ApnSetting> apns = fetchDunApns();
+ return apns.size() > 0;
}
/**
@@ -1821,70 +1819,11 @@
}
}
- /**
- * @param types comma delimited list of APN types
- * @return array of APN types
- */
- private String[] parseTypes(String types) {
- String[] result;
- // If unset, set to DEFAULT.
- if (types == null || types.equals("")) {
- result = new String[1];
- result[0] = PhoneConstants.APN_TYPE_ALL;
- } else {
- result = types.split(",");
- }
- return result;
- }
-
boolean isPermanentFailure(DcFailCause dcFailCause) {
return (dcFailCause.isPermanentFailure(mPhone.getContext(), mPhone.getSubId()) &&
(mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
}
- private ApnSetting makeApnSetting(Cursor cursor) {
- String[] types = parseTypes(
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
- ApnSetting apn = new ApnSetting(
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
- types,
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
- cursor.getString(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.ROAMING_PROTOCOL)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.CARRIER_ENABLED)) == 1,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.MODEM_COGNITIVE)) == 1,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.WAIT_TIME)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
- return apn;
- }
-
private ArrayList<ApnSetting> createApnList(Cursor cursor) {
ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
@@ -1892,13 +1831,14 @@
if (cursor.moveToFirst()) {
do {
- ApnSetting apn = makeApnSetting(cursor);
+ ApnSetting apn = ApnSetting.makeApnSetting(cursor);
if (apn == null) {
continue;
}
if (apn.hasMvnoParams()) {
- if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+ if (r != null && ApnSettingUtils.mvnoMatches(r, apn.getMvnoType(),
+ apn.getMvnoMatchData())) {
mvnoApns.add(apn);
}
} else {
@@ -1972,7 +1912,7 @@
return false;
}
- int profileId = apnSetting.profileId;
+ int profileId = apnSetting.getProfileId();
if (profileId == 0) {
profileId = getApnProfileID(apnContext.getApnType());
}
@@ -1981,8 +1921,8 @@
// 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 (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
- teardownForDun() == false) {
+ if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DUN)
+ || ServiceState.isGsm(mPhone.getServiceState().getRilDataRadioTechnology())) {
dcac = checkForCompatibleConnectedApnContext(apnContext);
if (dcac != null) {
// Get the dcacApnSetting for the connection we want to share.
@@ -2002,15 +1942,18 @@
return false;
}
- // 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 (cleanUpAllConnections(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;
+ if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ // 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 (cleanUpAllConnections(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
@@ -2054,7 +1997,7 @@
log("setInitialApn: E mPreferredApn=" + mPreferredApn);
- if (mPreferredApn != null && mPreferredApn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+ if (mPreferredApn != null && mPreferredApn.canHandleType(ApnSetting.TYPE_IA)) {
iaApnSetting = mPreferredApn;
} else if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
firstApnSetting = mAllApnSettings.get(0);
@@ -2062,13 +2005,13 @@
// Search for Initial APN setting and the first apn that can handle default
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+ if (apn.canHandleType(ApnSetting.TYPE_IA)) {
// The Initial Attach APN is highest priority so use it if there is one
log("setInitialApn: iaApnSetting=" + apn);
iaApnSetting = apn;
break;
} else if ((defaultApnSetting == null)
- && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
+ && (apn.canHandleType(ApnSetting.TYPE_DEFAULT))) {
// Use the first default apn if no better choice
log("setInitialApn: defaultApnSetting=" + apn);
defaultApnSetting = apn;
@@ -2102,7 +2045,7 @@
} else {
if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
- mPhone.mCi.setInitialAttachApn(new DataProfile(initialAttachApnSetting),
+ mDataServiceManager.setInitialAttachApn(createDataProfile(initialAttachApnSetting),
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
}
@@ -2146,19 +2089,20 @@
return null;
}
- // TODO: For multiple Active APNs not exactly sure how to do this.
- private void gotoIdleAndNotifyDataConnection(String reason) {
- if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
- notifyDataConnection(reason);
- }
-
/**
* "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(PhoneConstants.APN_TYPE_IMS)) {
+ return false;
+ }
+
for (ApnContext otherContext : mPrioritySortedApnContexts) {
+ if (otherContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ continue;
+ }
if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
return true;
@@ -2308,29 +2252,27 @@
Rlog.e(LOG_TAG, "CarrierDataEnable exception: " + ar.exception);
return;
}
- synchronized (mDataEnabledSettings) {
- boolean enabled = (boolean) ar.result;
- if (enabled != mDataEnabledSettings.isCarrierDataEnabled()) {
- if (DBG) {
- log("carrier Action: set metered apns enabled: " + enabled);
- }
+ boolean enabled = (boolean) ar.result;
+ if (enabled != mDataEnabledSettings.isCarrierDataEnabled()) {
+ if (DBG) {
+ log("carrier Action: set metered apns enabled: " + enabled);
+ }
- // Disable/enable all metered apns
- mDataEnabledSettings.setCarrierDataEnabled(enabled);
+ // Disable/enable all metered apns
+ mDataEnabledSettings.setCarrierDataEnabled(enabled);
- if (!enabled) {
- // Send otasp_sim_unprovisioned so that SuW is able to proceed and notify users
- mPhone.notifyOtaspChanged(TelephonyManager.OTASP_SIM_UNPROVISIONED);
- // Tear down all metered apns
- cleanUpAllConnections(true, Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
- } else {
- // Re-evaluate Otasp state
- int otaspState = mPhone.getServiceStateTracker().getOtasp();
- mPhone.notifyOtaspChanged(otaspState);
+ if (!enabled) {
+ // Send otasp_sim_unprovisioned so that SuW is able to proceed and notify users
+ mPhone.notifyOtaspChanged(TelephonyManager.OTASP_SIM_UNPROVISIONED);
+ // Tear down all metered apns
+ cleanUpAllConnections(true, Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
+ } else {
+ // Re-evaluate Otasp state
+ int otaspState = mPhone.getServiceStateTracker().getOtasp();
+ mPhone.notifyOtaspChanged(otaspState);
- reevaluateDataConnections();
- setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
- }
+ reevaluateDataConnections();
+ setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
}
}
}
@@ -2345,24 +2287,6 @@
mAutoAttachOnCreation.set(false);
}
- private void onSetDependencyMet(String apnType, boolean met) {
- // don't allow users to tweak hipri to work around default dependency not met
- if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
-
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext == null) {
- loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
- apnType + ", " + met + ")");
- return;
- }
- applyNewState(apnContext, apnContext.isEnabled(), met);
- if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
- // tie actions on default to similar actions on HIPRI regarding dependencyMet
- apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
- if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
- }
- }
-
public void setPolicyDataEnabled(boolean enabled) {
if (DBG) log("setPolicyDataEnabled: " + enabled);
Message msg = obtainMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE);
@@ -2371,19 +2295,17 @@
}
private void onSetPolicyDataEnabled(boolean enabled) {
- synchronized (mDataEnabledSettings) {
- final boolean prevEnabled = isDataEnabled();
- if (mDataEnabledSettings.isPolicyDataEnabled() != enabled) {
- mDataEnabledSettings.setPolicyDataEnabled(enabled);
- // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
- // handle the rest from there.
- if (prevEnabled != isDataEnabled()) {
- if (!prevEnabled) {
- reevaluateDataConnections();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
- }
+ final boolean prevEnabled = isDataEnabled();
+ if (mDataEnabledSettings.isPolicyDataEnabled() != enabled) {
+ mDataEnabledSettings.setPolicyDataEnabled(enabled);
+ // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+ // handle the rest from there.
+ if (prevEnabled != isDataEnabled()) {
+ if (!prevEnabled) {
+ reevaluateDataConnections();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
}
}
}
@@ -2469,11 +2391,11 @@
}
private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
- String apnType = apnContext.getApnType();
- ApnSetting dunSetting = null;
+ int apnType = apnContext.getApnTypeBitmask();
+ ArrayList<ApnSetting> dunSettings = null;
- if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
- dunSetting = fetchDunApn();
+ if (ApnSetting.TYPE_DUN == apnType) {
+ dunSettings = sortApnListByPreferred(fetchDunApns());
}
if (DBG) {
log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
@@ -2486,23 +2408,26 @@
if (curDcac != null) {
ApnSetting apnSetting = curApnCtx.getApnSetting();
log("apnSetting: " + apnSetting);
- if (dunSetting != null) {
- if (dunSetting.equals(apnSetting)) {
- switch (curApnCtx.getState()) {
- case CONNECTED:
- if (DBG) {
- log("checkForCompatibleConnectedApnContext:"
- + " found dun conn=" + curDcac
- + " curApnCtx=" + curApnCtx);
- }
- return curDcac;
- case RETRYING:
- case CONNECTING:
- potentialDcac = curDcac;
- potentialApnCtx = curApnCtx;
- default:
- // Not connected, potential unchanged
- break;
+ if (dunSettings != null && dunSettings.size() > 0) {
+ for (ApnSetting dunSetting : dunSettings) {
+ if (dunSetting.equals(apnSetting)) {
+ switch (curApnCtx.getState()) {
+ case CONNECTED:
+ if (DBG) {
+ log("checkForCompatibleConnectedApnContext:"
+ + " found dun conn=" + curDcac
+ + " curApnCtx=" + curApnCtx);
+ }
+ return curDcac;
+ case RETRYING:
+ case CONNECTING:
+ potentialDcac = curDcac;
+ potentialApnCtx = curApnCtx;
+ break;
+ default:
+ // Not connected, potential unchanged
+ break;
+ }
}
}
} else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
@@ -2518,6 +2443,7 @@
case CONNECTING:
potentialDcac = curDcac;
potentialApnCtx = curApnCtx;
+ break;
default:
// Not connected, potential unchanged
break;
@@ -2541,17 +2467,17 @@
return null;
}
- public void setEnabled(int id, boolean enable) {
+ public void setEnabled(int apnType, boolean enable) {
Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
- msg.arg1 = id;
+ msg.arg1 = apnType;
msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
sendMessage(msg);
}
- private void onEnableApn(int apnId, int enabled) {
- ApnContext apnContext = mApnContextsById.get(apnId);
+ private void onEnableApn(int apnType, int enabled) {
+ ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext == null) {
- loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
+ loge("onEnableApn(" + apnType + ", " + enabled + "): NO ApnContext");
return;
}
// TODO change our retry manager to use the appropriate numbers for the new APN
@@ -2582,47 +2508,16 @@
}
/**
- * Return current {@link android.provider.Settings.Global#MOBILE_DATA} value.
+ * 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.
*/
- //TODO: Merge this into DataSettings. And probably should rename to getUserDataEnabled().
- public boolean getDataEnabled() {
- final int device_provisioned =
- Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0);
-
- boolean retVal = "true".equalsIgnoreCase(SystemProperties.get(
- "ro.com.android.mobiledata", "true"));
- if (TelephonyManager.getDefault().getSimCount() == 1) {
- retVal = Settings.Global.getInt(mResolver, Settings.Global.MOBILE_DATA,
- retVal ? 1 : 0) != 0;
+ public boolean isUserDataEnabled() {
+ if (mDataEnabledSettings.isProvisioning()) {
+ return mDataEnabledSettings.isProvisioningDataEnabled();
} else {
- int phoneSubId = mPhone.getSubId();
- try {
- retVal = TelephonyManager.getIntWithSubId(mResolver,
- Settings.Global.MOBILE_DATA, phoneSubId) != 0;
- } catch (SettingNotFoundException e) {
- // use existing retVal
- }
+ return mDataEnabledSettings.isUserDataEnabled();
}
- if (VDBG) log("getDataEnabled: retVal=" + retVal);
- if (device_provisioned == 0) {
- // device is still getting provisioned - use whatever setting they
- // want during this process
- //
- // use the normal data_enabled setting (retVal, determined above)
- // as the default if nothing else is set
- final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata",
- retVal ? "true" : "false");
- 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;
}
/**
@@ -2663,19 +2558,17 @@
boolean isDataRoamingEnabled;
final int phoneSubId = mPhone.getSubId();
- try {
- // For single SIM phones, this is a per phone property.
- if (TelephonyManager.getDefault().getSimCount() == 1) {
- isDataRoamingEnabled = Settings.Global.getInt(mResolver,
- Settings.Global.DATA_ROAMING, getDefaultDataRoamingEnabled() ? 1 : 0) != 0;
- } else {
- isDataRoamingEnabled = TelephonyManager.getIntWithSubId(mResolver,
- Settings.Global.DATA_ROAMING, phoneSubId) != 0;
- }
- } catch (SettingNotFoundException snfe) {
- if (DBG) log("getDataRoamingEnabled: SettingNofFoundException snfe=" + snfe);
- isDataRoamingEnabled = getDefaultDataRoamingEnabled();
+ // For single SIM phones, this is a per phone property.
+ if (TelephonyManager.getDefault().getSimCount() == 1) {
+ isDataRoamingEnabled = Settings.Global.getInt(mResolver,
+ Settings.Global.DATA_ROAMING,
+ getDefaultDataRoamingEnabled() ? 1 : 0) != 0;
+ } else {
+ isDataRoamingEnabled = Settings.Global.getInt(mResolver,
+ Settings.Global.DATA_ROAMING + phoneSubId,
+ getDefaultDataRoamingEnabled() ? 1 : 0) != 0;
}
+
if (VDBG) {
log("getDataRoamingEnabled: phoneSubId=" + phoneSubId
+ " isDataRoamingEnabled=" + isDataRoamingEnabled);
@@ -2772,8 +2665,10 @@
// 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() {
+ 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()) {
@@ -2781,6 +2676,8 @@
return;
}
+ checkDataRoamingStatus(settingChanged);
+
if (getDataRoamingEnabled()) {
if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
@@ -2796,6 +2693,22 @@
}
}
+ // 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.getDcAc() != null
+ ? apnContext.getDcAc().mLastConnectionParams : ""));
+ }
+ }
+ }
+ }
+
private void onRadioAvailable() {
if (DBG) log("onRadioAvailable");
if (mPhone.getSimulatedRadioControl() != null) {
@@ -2909,23 +2822,25 @@
} else {
ApnSetting apn = apnContext.getApnSetting();
if (DBG) {
- log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
+ log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
+ : apn.getApnName()));
}
- if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
+ if (apn != null && !TextUtils.isEmpty(apn.getProxyAddressAsString())) {
try {
- String port = apn.port;
- if (TextUtils.isEmpty(port)) port = "8080";
- ProxyInfo proxy = new ProxyInfo(apn.proxy,
- Integer.parseInt(port), null);
+ int port = apn.getProxyPort();
+ if (port == -1) {
+ port = 8080;
+ }
+ ProxyInfo proxy = new ProxyInfo(apn.getProxyAddressAsString(), port, null);
dcac.setLinkPropertiesHttpProxySync(proxy);
} catch (NumberFormatException e) {
- loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
- apn.port + "): " + e);
+ loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
+ + apn.getProxyPort() + "): " + e);
}
}
// everything is setup
- if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
+ if (TextUtils.equals(apnContext.getApnType(), PhoneConstants.APN_TYPE_DEFAULT)) {
try {
SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
} catch (RuntimeException ex) {
@@ -2935,7 +2850,7 @@
if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
mPreferredApn = apn;
if (mPreferredApn != null) {
- setPreferredApn(mPreferredApn.id);
+ setPreferredApn(mPreferredApn.getId());
}
}
} else {
@@ -2949,6 +2864,8 @@
// A connection is setup
apnContext.setState(DctConstants.State.CONNECTED);
+ checkDataRoamingStatus(false);
+
boolean isProvApn = apnContext.isProvisioningApn();
final ConnectivityManager cm = ConnectivityManager.from(mPhone.getContext());
if (mProvisionBroadcastReceiver != null) {
@@ -3016,7 +2933,7 @@
if (DBG) {
ApnSetting apn = apnContext.getApnSetting();
log(String.format("onDataSetupComplete: error apn=%s cause=%s",
- (apn == null ? "unknown" : apn.apn), cause));
+ (apn == null ? "unknown" : apn.getApnName()), cause));
}
if (cause.isEventLoggable()) {
// Log this failure to the Event Logs.
@@ -3026,7 +2943,8 @@
}
ApnSetting apn = apnContext.getApnSetting();
mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
- apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());
+ apnContext.getApnType(), apn != null ? apn.getApnName()
+ : "unknown", cause.toString());
// Compose broadcast intent send to the specific carrier signaling receivers
Intent intent = new Intent(TelephonyIntents
@@ -3254,9 +3172,9 @@
setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
}
- private void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
+ private void onCleanUpConnection(boolean tearDown, int apnType, String reason) {
if (DBG) log("onCleanUpConnection");
- ApnContext apnContext = mApnContextsById.get(apnId);
+ ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext != null) {
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
@@ -3302,15 +3220,15 @@
if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
for (ApnSetting apn : mAllApnSettings) {
- if (apn.modemCognitive) {
- DataProfile dp = new DataProfile(apn);
+ if (apn.getModemCognitive()) {
+ DataProfile dp = createDataProfile(apn);
if (!dps.contains(dp)) {
dps.add(dp);
}
}
}
if (dps.size() > 0) {
- mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]),
+ mDataServiceManager.setDataProfile(dps,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
}
@@ -3334,13 +3252,20 @@
// ORDER BY Telephony.Carriers._ID ("_id")
Cursor cursor = mPhone.getContext().getContentResolver().query(
- Telephony.Carriers.CONTENT_URI, null, selection, null, Telephony.Carriers._ID);
+ Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "filtered"),
+ null, selection, null, Telephony.Carriers._ID);
if (cursor != null) {
if (cursor.getCount() > 0) {
mAllApnSettings = createApnList(cursor);
+ } else {
+ if (DBG) log("createAllApnList: cursor count is 0");
+ mApnSettingsInitializationLog.log("no APN in db for carrier: " + operator);
}
cursor.close();
+ } else {
+ if (DBG) log("createAllApnList: cursor is null");
+ mApnSettingsInitializationLog.log("cursor is null for carrier: " + operator);
}
}
@@ -3350,12 +3275,13 @@
if (mAllApnSettings.isEmpty()) {
if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
+ mApnSettingsInitializationLog.log("no APN found for carrier: " + operator);
mPreferredApn = null;
// TODO: What is the right behavior?
//notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
} else {
mPreferredApn = getPreferredApn();
- if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
+ if (mPreferredApn != null && !mPreferredApn.getOperatorNumeric().equals(operator)) {
mPreferredApn = null;
setPreferredApn(-1);
}
@@ -3392,30 +3318,33 @@
}
private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
- int id = dest.id;
- ArrayList<String> resultTypes = new ArrayList<String>();
- resultTypes.addAll(Arrays.asList(dest.types));
- for (String srcType : src.types) {
- if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
- if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
+ int id = dest.getId();
+ if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
+ id = src.getId();
}
- String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
- String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
- String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
- String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
- String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
- String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
- String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
- dest.roamingProtocol;
- int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
- 0 : (dest.bearerBitmask | src.bearerBitmask);
+ 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(id, dest.numeric, dest.carrier, dest.apn,
- proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
- dest.authType, resultTypes.toArray(new String[0]), protocol,
- roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
- (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
- dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
+ return ApnSetting.makeApnSetting(id, dest.getOperatorNumeric(), dest.getEntryName(),
+ dest.getApnName(), proxy, port, mmsc, mmsProxy, mmsPort, dest.getUser(),
+ dest.getPassword(), dest.getAuthType(), resultApnType, protocol, roamingProtocol,
+ dest.isEnabled(), networkTypeBitmask, dest.getProfileId(),
+ (dest.getModemCognitive() || src.getModemCognitive()), dest.getMaxConns(),
+ dest.getWaitTime(), dest.getMaxConnsTime(), dest.getMtu(), dest.getMvnoType(),
+ dest.getMvnoMatchData(), dest.getApnSetId());
}
/** Return the DC AsyncChannel for the new data connection */
@@ -3423,8 +3352,8 @@
if (DBG) log("createDataConnection E");
int id = mUniqueIdGenerator.getAndIncrement();
- DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
- this, mDcTesterFailBringUpAll, mDcc);
+ DataConnection conn = DataConnection.makeDataConnection(mPhone, id, this,
+ mDataServiceManager, mDcTesterFailBringUpAll, mDcc);
mDataConnections.put(id, conn);
DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
@@ -3458,12 +3387,15 @@
if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
- if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
- ApnSetting dun = fetchDunApn();
- if (dun != null) {
- apnList.add(dun);
- if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
- return apnList;
+ int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
+ 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 sortApnListByPreferred(apnList);
}
}
@@ -3495,14 +3427,16 @@
}
if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
- mPreferredApn.canHandleType(requestedApnType)) {
+ mPreferredApn.canHandleType(requestedApnTypeBitmask)) {
if (DBG) {
log("buildWaitingApns: Preferred APN:" + operator + ":"
- + mPreferredApn.numeric + ":" + mPreferredApn);
+ + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn);
}
- if (mPreferredApn.numeric.equals(operator)) {
- if (ServiceState.bitmaskHasTech(mPreferredApn.bearerBitmask, radioTech)) {
+ if (mPreferredApn.getOperatorNumeric().equals(operator)) {
+ if (ServiceState.bitmaskHasTech(mPreferredApn.getNetworkTypeBitmask(),
+ ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
apnList.add(mPreferredApn);
+ apnList = sortApnListByPreferred(apnList);
if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
return apnList;
} else {
@@ -3519,14 +3453,16 @@
if (mAllApnSettings != null) {
if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(requestedApnType)) {
- if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
+ if (apn.canHandleType(requestedApnTypeBitmask)) {
+ if (ServiceState.bitmaskHasTech(apn.getNetworkTypeBitmask(),
+ ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
if (DBG) log("buildWaitingApns: adding apn=" + apn);
apnList.add(apn);
} else {
if (DBG) {
- log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +
- "not include radioTech:" + radioTech);
+ log("buildWaitingApns: networkTypeBitmask:"
+ + apn.getNetworkTypeBitmask()
+ + "do not include radioTech:" + radioTech);
}
}
} else if (DBG) {
@@ -3537,10 +3473,45 @@
} else {
loge("mAllApnSettings is null!");
}
+
+ apnList = sortApnListByPreferred(apnList);
if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
return apnList;
}
+ /**
+ * Sort a list of ApnSetting objects, with the preferred APNs at the front of the list
+ *
+ * e.g. if the preferred APN set = 2 and we have
+ * 1. APN with apn_set_id = 0 = Carriers.NO_SET_SET (no set is set)
+ * 2. APN with apn_set_id = 1 (not preferred set)
+ * 3. APN with apn_set_id = 2 (preferred set)
+ * Then the return order should be (3, 1, 2) or (3, 2, 1)
+ *
+ * e.g. if the preferred APN set = Carriers.NO_SET_SET (no preferred set) then the
+ * return order can be anything
+ */
+ @VisibleForTesting
+ public ArrayList<ApnSetting> sortApnListByPreferred(ArrayList<ApnSetting> list) {
+ if (list == null || list.size() <= 1) return list;
+ int preferredApnSetId = getPreferredApnSetId();
+ if (preferredApnSetId != Telephony.Carriers.NO_SET_SET) {
+ list.sort(new Comparator<ApnSetting>() {
+ @Override
+ public int compare(ApnSetting apn1, ApnSetting apn2) {
+ if (apn1.getApnSetId() == preferredApnSetId) {
+ return -1;
+ }
+ if (apn2.getApnSetId() == preferredApnSetId) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ }
+ return list;
+ }
+
private String apnListToString (ArrayList<ApnSetting> apns) {
StringBuilder result = new StringBuilder();
for (int i = 0, size = apns.size(); i < size; i++) {
@@ -3597,7 +3568,7 @@
pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
for(ApnSetting p : mAllApnSettings) {
log("getPreferredApn: apnSetting=" + p);
- if (p.id == pos && p.canHandleType(mRequestedApnType)) {
+ if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
log("getPreferredApn: X found apnSetting" + p);
cursor.close();
return p;
@@ -3674,7 +3645,7 @@
cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
mReregisterOnReconnectFailure = false;
}
- ApnContext apnContext = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+ ApnContext apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
if (apnContext != null) {
apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
trySetupData(apnContext);
@@ -3760,7 +3731,8 @@
break;
case DctConstants.EVENT_ROAMING_ON:
- onDataRoamingOnOrSettingsChanged();
+ case DctConstants.EVENT_ROAMING_SETTING_CHANGE:
+ onDataRoamingOnOrSettingsChanged(msg.what);
break;
case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
@@ -3805,31 +3777,12 @@
case DctConstants.EVENT_VOICE_CALL_ENDED:
onVoiceCallEnded();
break;
-
- case DctConstants.EVENT_RESET_DONE: {
- if (DBG) log("EVENT_RESET_DONE");
- onResetDone((AsyncResult) msg.obj);
- break;
- }
case DctConstants.CMD_SET_USER_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
onSetUserDataEnabled(enabled);
break;
}
- // TODO - remove
- case DctConstants.CMD_SET_DEPENDENCY_MET: {
- boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
- if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
- Bundle bundle = msg.getData();
- if (bundle != null) {
- String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
- if (apnType != null) {
- onSetDependencyMet(apnType, met);
- }
- }
- break;
- }
case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
onSetPolicyDataEnabled(enabled);
@@ -3895,7 +3848,7 @@
}
case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
- ApnContext apnCtx = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+ ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
if (mProvisioningApnAlarmTag == msg.arg1) {
if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
@@ -3955,11 +3908,6 @@
}
break;
}
- case DctConstants.EVENT_DATA_STATE_CHANGED: {
- // no longer do anything, but still registered - clean up log
- // TODO - why are we still registering?
- break;
- }
case DctConstants.EVENT_PCO_DATA_RECEIVED: {
handlePcoData((AsyncResult)msg.obj);
break;
@@ -3970,6 +3918,9 @@
case DctConstants.EVENT_DATA_RECONNECT:
onDataReconnect(msg.getData());
break;
+ case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
+ onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
+ break;
default:
Rlog.e("DcTracker", "Unhandled event=" + msg);
break;
@@ -4044,7 +3995,6 @@
log("update(): Active DDS, register for all events now!");
onUpdateIcc();
- mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
mAutoAttachOnCreation.set(false);
((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
@@ -4106,24 +4056,22 @@
}
private void onSetInternalDataEnabled(boolean enabled, Message onCompleteMsg) {
- synchronized (mDataEnabledSettings) {
- if (DBG) log("onSetInternalDataEnabled: enabled=" + enabled);
- boolean sendOnComplete = true;
+ if (DBG) log("onSetInternalDataEnabled: enabled=" + enabled);
+ boolean sendOnComplete = true;
- mDataEnabledSettings.setInternalDataEnabled(enabled);
- if (enabled) {
- log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- sendOnComplete = false;
- log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
- cleanUpAllConnections(Phone.REASON_DATA_DISABLED, onCompleteMsg);
- }
+ mDataEnabledSettings.setInternalDataEnabled(enabled);
+ if (enabled) {
+ log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ sendOnComplete = false;
+ log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
+ cleanUpAllConnections(Phone.REASON_DATA_DISABLED, onCompleteMsg);
+ }
- if (sendOnComplete) {
- if (onCompleteMsg != null) {
- onCompleteMsg.sendToTarget();
- }
+ if (sendOnComplete) {
+ if (onCompleteMsg != null) {
+ onCompleteMsg.sendToTarget();
}
}
}
@@ -4169,11 +4117,14 @@
pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
pw.println(" mResolver=" + mResolver);
- pw.println(" mIsWifiConnected=" + mIsWifiConnected);
pw.println(" mReconnectIntent=" + mReconnectIntent);
pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
pw.println(" mIsScreenOn=" + mIsScreenOn);
pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
+ pw.println(" mDataRoamingLeakageLog= ");
+ mDataRoamingLeakageLog.dump(fd, pw, args);
+ pw.println(" mApnSettingsInitializationLog= ");
+ mApnSettingsInitializationLog.dump(fd, pw, args);
pw.flush();
pw.println(" ***************************************");
DcController dcc = mDcc;
@@ -4240,6 +4191,7 @@
pw.println(" getOverallState=" + getOverallState());
pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
pw.println(" mAttached=" + mAttached.get());
+ mDataEnabledSettings.dump(fd, pw, args);
pw.flush();
}
@@ -4253,9 +4205,9 @@
}
if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
- apnContext = mApnContextsById.get(DctConstants.APN_EMERGENCY_ID);
+ apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
} else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
- apnContext = mApnContextsById.get(DctConstants.APN_IMS_ID);
+ apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
} else {
log("apnType is invalid, return null");
return null;
@@ -4272,8 +4224,10 @@
if (dcac != null) {
result = dcac.getPcscfAddr();
- for (int i = 0; i < result.length; i++) {
- log("Pcscf[" + i + "]: " + result[i]);
+ if (result != null) {
+ for (int i = 0; i < result.length; i++) {
+ log("Pcscf[" + i + "]: " + result[i]);
+ }
}
return result;
}
@@ -4292,12 +4246,13 @@
// DB will contain only one entry for Emergency APN
String selection = "type=\"emergency\"";
Cursor cursor = mPhone.getContext().getContentResolver().query(
- Telephony.Carriers.CONTENT_URI, null, selection, null, null);
+ Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "filtered"),
+ null, selection, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
- mEmergencyApn = makeApnSetting(cursor);
+ mEmergencyApn = ApnSetting.makeApnSetting(cursor);
}
}
cursor.close();
@@ -4314,7 +4269,7 @@
} else {
boolean hasEmergencyApn = false;
for (ApnSetting apn : mAllApnSettings) {
- if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
+ if ((apn.getApnTypeBitmask() & ApnSetting.TYPE_EMERGENCY) > 0) {
hasEmergencyApn = true;
break;
}
@@ -4386,7 +4341,7 @@
stopDataStallAlarm();
}
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ mRequestedApnType = ApnSetting.TYPE_DEFAULT;
if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
if (tearDown && mDisconnectPendingCount == 0) {
@@ -4465,7 +4420,7 @@
TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
TxRxSum curTxRxSum = new TxRxSum();
- curTxRxSum.updateTxRxSum();
+ curTxRxSum.updateTotalTxRxSum();
mTxPkts = curTxRxSum.txPkts;
mRxPkts = curTxRxSum.rxPkts;
@@ -4589,36 +4544,37 @@
final int recoveryAction = getRecoveryAction();
TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
switch (recoveryAction) {
- case RecoveryAction.GET_DATA_CALL_LIST:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
- mSentSinceLastRecv);
- if (DBG) log("doRecovery() get data call list");
- mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
- putRecoveryAction(RecoveryAction.CLEANUP);
- break;
- case RecoveryAction.CLEANUP:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
- if (DBG) log("doRecovery() cleanup all connections");
- cleanUpAllConnections(Phone.REASON_PDP_RESET);
- putRecoveryAction(RecoveryAction.REREGISTER);
- break;
- case RecoveryAction.REREGISTER:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
- mSentSinceLastRecv);
- if (DBG) log("doRecovery() re-register");
- mPhone.getServiceStateTracker().reRegisterNetwork(null);
- putRecoveryAction(RecoveryAction.RADIO_RESTART);
- break;
- case RecoveryAction.RADIO_RESTART:
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
- mSentSinceLastRecv);
- if (DBG) log("restarting radio");
- restartRadio();
- putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
- break;
- default:
- throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
- recoveryAction);
+ case RecoveryAction.GET_DATA_CALL_LIST:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() get data call list");
+ mDataServiceManager.getDataCallList(obtainMessage());
+ putRecoveryAction(RecoveryAction.CLEANUP);
+ break;
+ case RecoveryAction.CLEANUP:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() cleanup all connections");
+ cleanUpAllConnections(Phone.REASON_PDP_RESET);
+ putRecoveryAction(RecoveryAction.REREGISTER);
+ break;
+ case RecoveryAction.REREGISTER:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() re-register");
+ mPhone.getServiceStateTracker().reRegisterNetwork(null);
+ putRecoveryAction(RecoveryAction.RADIO_RESTART);
+ break;
+ case RecoveryAction.RADIO_RESTART:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
+ mSentSinceLastRecv);
+ if (DBG) log("restarting radio");
+ restartRadio();
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ break;
+ default:
+ throw new RuntimeException("doRecovery: Invalid recoveryAction="
+ + recoveryAction);
}
mSentSinceLastRecv = 0;
}
@@ -4628,7 +4584,7 @@
long sent, received;
TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
- mDataStallTxRxSum.updateTxRxSum();
+ mDataStallTxRxSum.updateTcpTxRxSum();
if (VDBG_STALL) {
log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
@@ -4819,4 +4775,40 @@
}
}
+ private static DataProfile createDataProfile(ApnSetting apn) {
+ return createDataProfile(apn, apn.getProfileId());
+ }
+
+ @VisibleForTesting
+ public static DataProfile createDataProfile(ApnSetting apn, int profileId) {
+ int profileType;
+
+ int bearerBitmap = 0;
+ bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
+ apn.getNetworkTypeBitmask());
+
+ if (bearerBitmap == 0) {
+ profileType = DataProfile.TYPE_COMMON;
+ } else if (ServiceState.bearerBitmapHasCdma(bearerBitmap)) {
+ profileType = DataProfile.TYPE_3GPP2;
+ } else {
+ profileType = DataProfile.TYPE_3GPP;
+ }
+
+ return new DataProfile(profileId, apn.getApnName(),
+ ApnSetting.getProtocolStringFromInt(apn.getProtocol()), apn.getAuthType(),
+ apn.getUser(), apn.getPassword(), profileType, apn.getMaxConnsTime(),
+ apn.getMaxConns(), apn.getWaitTime(), apn.isEnabled(), apn.getApnTypeBitmask(),
+ ApnSetting.getProtocolStringFromInt(apn.getRoamingProtocol()), bearerBitmap,
+ apn.getMtu(), ApnSetting.getMvnoTypeStringFromInt(apn.getMvnoType()),
+ apn.getMvnoMatchData(), apn.getModemCognitive());
+ }
+
+ private void onDataServiceBindingChanged(boolean bound) {
+ if (bound) {
+ mDcc.start();
+ } else {
+ mDcc.dispose();
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/KeepaliveStatus.java b/src/java/com/android/internal/telephony/dataconnection/KeepaliveStatus.java
new file mode 100644
index 0000000..8b0ec4f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/KeepaliveStatus.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class serves to pass around the parameters of Keepalive session
+ * status within the telephony framework.
+ *
+ * {@hide}
+ */
+public class KeepaliveStatus implements Parcelable {
+ private static final String LOG_TAG = "KeepaliveStatus";
+
+ /** This should match the HAL Radio::1_1::KeepaliveStatusCode */
+ public static final int STATUS_ACTIVE = 0;
+ public static final int STATUS_INACTIVE = 1;
+ public static final int STATUS_PENDING = 2;
+
+ public static final int ERROR_NONE = 0;
+ public static final int ERROR_UNSUPPORTED = 1;
+ public static final int ERROR_NO_RESOURCES = 2;
+ public static final int ERROR_UNKNOWN = 3;
+
+ public static final int INVALID_HANDLE = Integer.MAX_VALUE;
+
+ /** An opaque value that identifies this Keepalive status to the modem */
+ public final int sessionHandle;
+
+ /**
+ * A status code indicating whether this Keepalive session is
+ * active, inactive, or pending activation
+ */
+ public final int statusCode;
+
+ /** An error code indicating a lower layer failure, if any */
+ public final int errorCode;
+
+ public KeepaliveStatus(int error) {
+ sessionHandle = INVALID_HANDLE;
+ statusCode = STATUS_INACTIVE;
+ errorCode = error;
+ }
+
+ public KeepaliveStatus(int handle, int code) {
+ sessionHandle = handle;
+ statusCode = code;
+ errorCode = ERROR_NONE;
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("{errorCode=%d, sessionHandle=%d, statusCode=%d}",
+ errorCode, sessionHandle, statusCode);
+ }
+
+ // Parcelable Implementation
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(errorCode);
+ dest.writeInt(sessionHandle);
+ dest.writeInt(statusCode);
+ }
+
+ private KeepaliveStatus(Parcel p) {
+ errorCode = p.readInt();
+ sessionHandle = p.readInt();
+ statusCode = p.readInt();
+ }
+
+ public static final Parcelable.Creator<KeepaliveStatus> CREATOR =
+ new Parcelable.Creator<KeepaliveStatus>() {
+ @Override
+ public KeepaliveStatus createFromParcel(Parcel source) {
+ return new KeepaliveStatus(source);
+ }
+
+ @Override
+ public KeepaliveStatus[] newArray(int size) {
+ return new KeepaliveStatus[size];
+ }
+ };
+}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
new file mode 100644
index 0000000..d3c59a3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -0,0 +1,1086 @@
+/*
+ * 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.euicc;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ComponentInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.service.euicc.EuiccProfileInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager;
+import android.telephony.euicc.EuiccNotification;
+import android.telephony.euicc.EuiccRulesAuthTable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccSlot;
+import com.android.internal.telephony.uicc.euicc.EuiccCard;
+import com.android.internal.telephony.uicc.euicc.EuiccCardErrorException;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Backing implementation of {@link EuiccCardManager}. */
+public class EuiccCardController extends IEuiccCardController.Stub {
+ private static final String TAG = "EuiccCardController";
+ private static final String KEY_LAST_BOOT_COUNT = "last_boot_count";
+
+ private final Context mContext;
+ private AppOpsManager mAppOps;
+ private String mCallingPackage;
+ private ComponentInfo mBestComponent;
+ private Handler mEuiccMainThreadHandler;
+ private SimSlotStatusChangedBroadcastReceiver mSimSlotStatusChangeReceiver;
+ private EuiccController mEuiccController;
+ private UiccController mUiccController;
+
+ private static EuiccCardController sInstance;
+
+ private class SimSlotStatusChangedBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED.equals(intent.getAction())) {
+ if (isEmbeddedSlotActivated()) {
+ mEuiccController.startOtaUpdatingIfNecessary();
+ }
+ mContext.unregisterReceiver(mSimSlotStatusChangeReceiver);
+ }
+ }
+ }
+
+ /** Initialize the instance. Should only be called once. */
+ public static EuiccCardController init(Context context) {
+ synchronized (EuiccCardController.class) {
+ if (sInstance == null) {
+ sInstance = new EuiccCardController(context);
+ } else {
+ Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ }
+ return sInstance;
+ }
+
+ /** Get an instance. Assumes one has already been initialized with {@link #init}. */
+ public static EuiccCardController get() {
+ if (sInstance == null) {
+ synchronized (EuiccCardController.class) {
+ if (sInstance == null) {
+ throw new IllegalStateException("get() called before init()");
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ private EuiccCardController(Context context) {
+ this(context, new Handler(), EuiccController.get(), UiccController.getInstance());
+ ServiceManager.addService("euicc_card_controller", this);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public EuiccCardController(
+ Context context,
+ Handler handler,
+ EuiccController euiccController,
+ UiccController uiccController) {
+ mContext = context;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+ mEuiccMainThreadHandler = handler;
+ mUiccController = uiccController;
+ mEuiccController = euiccController;
+
+ if (isBootUp(mContext)) {
+ mSimSlotStatusChangeReceiver = new SimSlotStatusChangedBroadcastReceiver();
+ mContext.registerReceiver(
+ mSimSlotStatusChangeReceiver,
+ new IntentFilter(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED));
+ }
+ }
+
+ /**
+ * Check whether the restored boot count is the same as current one. If not, update the restored
+ * one.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static boolean isBootUp(Context context) {
+ int bootCount = Settings.Global.getInt(
+ context.getContentResolver(), Settings.Global.BOOT_COUNT, -1);
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ int lastBootCount = sp.getInt(KEY_LAST_BOOT_COUNT, -1);
+ if (bootCount == -1 || lastBootCount == -1 || bootCount != lastBootCount) {
+ sp.edit().putInt(KEY_LAST_BOOT_COUNT, bootCount).apply();
+ return true;
+ }
+ return false;
+ }
+
+ /** Whether embedded slot is activated or not. */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public boolean isEmbeddedSlotActivated() {
+ UiccSlot[] slots = mUiccController.getUiccSlots();
+ if (slots == null) {
+ return false;
+ }
+ for (int i = 0; i < slots.length; ++i) {
+ UiccSlot slotInfo = slots[i];
+ if (slotInfo.isEuicc() && slotInfo.isActive()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkCallingPackage(String callingPackage) {
+ // Check the caller is LPA.
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mCallingPackage = callingPackage;
+ mBestComponent = EuiccConnector.findBestComponent(mContext.getPackageManager());
+ if (mBestComponent == null
+ || !TextUtils.equals(mCallingPackage, mBestComponent.packageName)) {
+ throw new SecurityException("The calling package can only be LPA.");
+ }
+ }
+
+ private EuiccCard getEuiccCard(String cardId) {
+ UiccController controller = UiccController.getInstance();
+ int slotId = controller.getUiccSlotForCardId(cardId);
+ if (slotId != UiccController.INVALID_SLOT_ID) {
+ UiccSlot slot = controller.getUiccSlot(slotId);
+ if (slot.isEuicc()) {
+ return (EuiccCard) controller.getUiccCardForSlot(slotId);
+ }
+ }
+ loge("EuiccCard is null. CardId : " + cardId);
+ return null;
+ }
+
+ private int getResultCode(Throwable e) {
+ if (e instanceof EuiccCardErrorException) {
+ return ((EuiccCardErrorException) e).getErrorCode();
+ }
+ return EuiccCardManager.RESULT_UNKNOWN_ERROR;
+ }
+
+ @Override
+ public void getAllProfiles(String callingPackage, String cardId,
+ IGetAllProfilesCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getAllProfiles callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccProfileInfo[]> cardCb =
+ new AsyncResultCallback<EuiccProfileInfo[]>() {
+ @Override
+ public void onResult(EuiccProfileInfo[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getAllProfiles callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getAllProfiles callback failure.", exception);
+ }
+ }
+ };
+
+ card.getAllProfiles(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getProfile(String callingPackage, String cardId, String iccid,
+ IGetProfileCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getProfile callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccProfileInfo> cardCb = new AsyncResultCallback<EuiccProfileInfo>() {
+ @Override
+ public void onResult(EuiccProfileInfo result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getProfile callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getProfile callback failure.", exception);
+ }
+ }
+ };
+
+ card.getProfile(iccid, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+ IDisableProfileCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("disableProfile callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("disableProfile callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("disableProfile callback failure.", exception);
+ }
+ }
+ };
+
+ card.disableProfile(iccid, refresh, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+ ISwitchToProfileCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("switchToProfile callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccProfileInfo> profileCb =
+ new AsyncResultCallback<EuiccProfileInfo>() {
+ @Override
+ public void onResult(EuiccProfileInfo profile) {
+ AsyncResultCallback<Void> switchCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, profile);
+ } catch (RemoteException exception) {
+ loge("switchToProfile callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), profile);
+ } catch (RemoteException exception) {
+ loge("switchToProfile callback failure.", exception);
+ }
+ }
+ };
+
+ card.switchToProfile(iccid, refresh, switchCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("switchToProfile callback failure.", exception);
+ }
+ }
+ };
+
+ card.getProfile(iccid, profileCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void setNickname(String callingPackage, String cardId, String iccid, String nickname,
+ ISetNicknameCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("setNickname callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("setNickname callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("setNickname callback failure.", exception);
+ }
+ }
+ };
+
+ card.setNickname(iccid, nickname, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void deleteProfile(String callingPackage, String cardId, String iccid,
+ IDeleteProfileCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("deleteProfile callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("deleteProfile callback failure.", exception);
+ }
+ });
+ };
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("deleteProfile callback failure.", exception);
+ }
+ }
+ };
+
+ card.deleteProfile(iccid, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void resetMemory(String callingPackage, String cardId,
+ @EuiccCardManager.ResetOption int options, IResetMemoryCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("resetMemory callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("resetMemory callback failure.", exception);
+ }
+ });
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("resetMemory callback failure.", exception);
+ }
+ }
+ };
+
+ card.resetMemory(options, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getDefaultSmdpAddress(String callingPackage, String cardId,
+ IGetDefaultSmdpAddressCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getDefaultSmdpAddress callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<String> cardCb = new AsyncResultCallback<String>() {
+ @Override
+ public void onResult(String result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getDefaultSmdpAddress callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getDefaultSmdpAddress callback failure.", exception);
+ }
+ }
+ };
+
+ card.getDefaultSmdpAddress(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getSmdsAddress(String callingPackage, String cardId,
+ IGetSmdsAddressCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getSmdsAddress callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<String> cardCb = new AsyncResultCallback<String>() {
+ @Override
+ public void onResult(String result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getSmdsAddress callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getSmdsAddress callback failure.", exception);
+ }
+ }
+ };
+
+ card.getSmdsAddress(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void setDefaultSmdpAddress(String callingPackage, String cardId, String address,
+ ISetDefaultSmdpAddressCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("setDefaultSmdpAddress callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("setDefaultSmdpAddress callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("setDefaultSmdpAddress callback failure.", exception);
+ }
+ }
+ };
+
+ card.setDefaultSmdpAddress(address, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getRulesAuthTable(String callingPackage, String cardId,
+ IGetRulesAuthTableCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getRulesAuthTable callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccRulesAuthTable> cardCb =
+ new AsyncResultCallback<EuiccRulesAuthTable>() {
+ @Override
+ public void onResult(EuiccRulesAuthTable result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getRulesAuthTable callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getRulesAuthTable callback failure.", exception);
+ }
+ }
+ };
+
+ card.getRulesAuthTable(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getEuiccChallenge(String callingPackage, String cardId,
+ IGetEuiccChallengeCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getEuiccChallenge callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getEuiccChallenge callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getEuiccChallenge callback failure.", exception);
+ }
+ }
+ };
+
+ card.getEuiccChallenge(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getEuiccInfo1(String callingPackage, String cardId,
+ IGetEuiccInfo1Callback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo1 callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo1 callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo1 callback failure.", exception);
+ }
+ }
+ };
+
+ card.getEuiccInfo1(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void getEuiccInfo2(String callingPackage, String cardId,
+ IGetEuiccInfo2Callback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo2 callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo2 callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("getEuiccInfo2 callback failure.", exception);
+ }
+ }
+ };
+
+ card.getEuiccInfo2(cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void authenticateServer(String callingPackage, String cardId, String matchingId,
+ byte[] serverSigned1, byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed,
+ byte[] serverCertificate, IAuthenticateServerCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("authenticateServer callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("authenticateServer callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("authenticateServer callback failure.", exception);
+ }
+ }
+ };
+
+ card.authenticateServer(matchingId, serverSigned1, serverSignature1, euiccCiPkIdToBeUsed,
+ serverCertificate, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void prepareDownload(String callingPackage, String cardId, @Nullable byte[] hashCc,
+ byte[] smdpSigned2, byte[] smdpSignature2, byte[] smdpCertificate,
+ IPrepareDownloadCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("prepareDownload callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("prepareDownload callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("prepareDownload callback failure.", exception);
+ }
+ }
+ };
+
+ card.prepareDownload(hashCc, smdpSigned2, smdpSignature2, smdpCertificate, cardCb,
+ mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void loadBoundProfilePackage(String callingPackage, String cardId,
+ byte[] boundProfilePackage, ILoadBoundProfilePackageCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("loadBoundProfilePackage callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("loadBoundProfilePackage callback failure.", exception);
+ }
+ });
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("loadBoundProfilePackage callback failure.", exception);
+ }
+ }
+ };
+
+ card.loadBoundProfilePackage(boundProfilePackage, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void cancelSession(String callingPackage, String cardId, byte[] transactionId,
+ @EuiccCardManager.CancelReason int reason, ICancelSessionCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("cancelSession callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("cancelSession callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("cancelSession callback failure.", exception);
+ }
+ }
+ };
+
+ card.cancelSession(transactionId, reason, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void listNotifications(String callingPackage, String cardId,
+ @EuiccNotification.Event int events, IListNotificationsCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("listNotifications callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccNotification[]> cardCb =
+ new AsyncResultCallback<EuiccNotification[]>() {
+ @Override
+ public void onResult(EuiccNotification[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("listNotifications callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("listNotifications callback failure.", exception);
+ }
+ }
+ };
+
+ card.listNotifications(events, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void retrieveNotificationList(String callingPackage, String cardId,
+ @EuiccNotification.Event int events, IRetrieveNotificationListCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("retrieveNotificationList callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccNotification[]> cardCb =
+ new AsyncResultCallback<EuiccNotification[]>() {
+ @Override
+ public void onResult(EuiccNotification[] result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("retrieveNotificationList callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("retrieveNotificationList callback failure.", exception);
+ }
+ }
+ };
+
+ card.retrieveNotificationList(events, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void retrieveNotification(String callingPackage, String cardId, int seqNumber,
+ IRetrieveNotificationCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
+ } catch (RemoteException exception) {
+ loge("retrieveNotification callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<EuiccNotification> cardCb =
+ new AsyncResultCallback<EuiccNotification>() {
+ @Override
+ public void onResult(EuiccNotification result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("retrieveNotification callback failure.", exception);
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e), null);
+ } catch (RemoteException exception) {
+ loge("retrieveNotification callback failure.", exception);
+ }
+ }
+ };
+
+ card.retrieveNotification(seqNumber, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void removeNotificationFromList(String callingPackage, String cardId, int seqNumber,
+ IRemoveNotificationFromListCallback callback) {
+ checkCallingPackage(callingPackage);
+
+ EuiccCard card = getEuiccCard(cardId);
+ if (card == null) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
+ } catch (RemoteException exception) {
+ loge("removeNotificationFromList callback failure.", exception);
+ }
+ return;
+ }
+
+ AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
+ @Override
+ public void onResult(Void result) {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("removeNotificationFromList callback failure.", exception);
+ }
+
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ try {
+ callback.onComplete(getResultCode(e));
+ } catch (RemoteException exception) {
+ loge("removeNotificationFromList callback failure.", exception);
+ }
+ }
+ };
+
+ card.removeNotificationFromList(seqNumber, cardCb, mEuiccMainThreadHandler);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
+ final long token = Binder.clearCallingIdentity();
+
+ super.dump(fd, pw, args);
+ // TODO(b/38206971): dump more information.
+ pw.println("mCallingPackage=" + mCallingPackage);
+ pw.println("mBestComponent=" + mBestComponent);
+
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private static void loge(String message) {
+ Log.e(TAG, message);
+ }
+
+ private static void loge(String message, Throwable tr) {
+ Log.e(TAG, message, tr);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 5e07eed..54e5691 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -33,6 +33,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.service.euicc.EuiccService;
import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
@@ -46,12 +47,16 @@
import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
+import android.service.euicc.IGetOtaStatusCallback;
+import android.service.euicc.IOtaStatusChangedCallback;
import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager;
+import android.telephony.euicc.EuiccManager.OtaStatus;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -132,6 +137,8 @@
private static final int CMD_UPDATE_SUBSCRIPTION_NICKNAME = 108;
private static final int CMD_ERASE_SUBSCRIPTIONS = 109;
private static final int CMD_RETAIN_SUBSCRIPTIONS = 110;
+ private static final int CMD_GET_OTA_STATUS = 111;
+ private static final int CMD_START_OTA_IF_NECESSARY = 112;
private static boolean isEuiccCommand(int what) {
return what >= CMD_GET_EID;
@@ -185,6 +192,21 @@
void onGetEidComplete(String eid);
}
+ /** Callback class for {@link #getOtaStatus}. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public interface GetOtaStatusCommandCallback extends BaseEuiccCommandCallback {
+ /** Called when the getting OTA status lookup has completed. */
+ void onGetOtaStatusComplete(@OtaStatus int status);
+ }
+
+ /** Callback class for {@link #startOtaIfNecessary}. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public interface OtaStatusChangedCallback extends BaseEuiccCommandCallback {
+ /**
+ * Called when OTA status is changed to {@link EuiccM}. */
+ void onOtaStatusChanged(int status);
+ }
+
static class GetMetadataRequest {
DownloadableSubscription mSubscription;
boolean mForceDeactivateSim;
@@ -373,6 +395,18 @@
sendMessage(CMD_GET_EID, callback);
}
+ /** Asynchronously get OTA status. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public void getOtaStatus(GetOtaStatusCommandCallback callback) {
+ sendMessage(CMD_GET_OTA_STATUS, callback);
+ }
+
+ /** Asynchronously perform OTA update. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public void startOtaIfNecessary(OtaStatusChangedCallback callback) {
+ sendMessage(CMD_START_OTA_IF_NECESSARY, callback);
+ }
+
/** Asynchronously fetch metadata for the given downloadable subscription. */
@VisibleForTesting(visibility = PACKAGE)
public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
@@ -809,6 +843,42 @@
});
break;
}
+ case CMD_GET_OTA_STATUS: {
+ mEuiccService.getOtaStatus(slotId,
+ new IGetOtaStatusCallback.Stub() {
+ @Override
+ public void onSuccess(@OtaStatus int status) {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((GetOtaStatusCommandCallback) callback)
+ .onGetOtaStatusComplete(status);
+ onCommandEnd(callback);
+ });
+ }
+ });
+ break;
+ }
+ case CMD_START_OTA_IF_NECESSARY: {
+ mEuiccService.startOtaIfNecessary(slotId,
+ new IOtaStatusChangedCallback.Stub() {
+ @Override
+ public void onOtaStatusChanged(int status)
+ throws RemoteException {
+ if (status == EuiccManager.EUICC_OTA_IN_PROGRESS) {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((OtaStatusChangedCallback) callback)
+ .onOtaStatusChanged(status);
+ });
+ } else {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((OtaStatusChangedCallback) callback)
+ .onOtaStatusChanged(status);
+ onCommandEnd(callback);
+ });
+ }
+ }
+ });
+ break;
+ }
default: {
Log.wtf(TAG, "Unimplemented eUICC command: " + message.what);
callback.onEuiccServiceUnavailable();
@@ -851,6 +921,8 @@
case CMD_GET_EUICC_INFO:
case CMD_ERASE_SUBSCRIPTIONS:
case CMD_RETAIN_SUBSCRIPTIONS:
+ case CMD_GET_OTA_STATUS:
+ case CMD_START_OTA_IF_NECESSARY:
return (BaseEuiccCommandCallback) message.obj;
case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
return ((GetMetadataRequest) message.obj).mCallback;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 0d58d80..8c1b81e 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -15,7 +15,10 @@
*/
package com.android.internal.telephony.euicc;
+import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE;
+
import android.Manifest;
+import android.Manifest.permission;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -38,10 +41,13 @@
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
+import android.telephony.euicc.EuiccManager.OtaStatus;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -63,6 +69,8 @@
EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
private static final int ERROR =
EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR;
+ private static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION;
private static EuiccController sInstance;
@@ -168,6 +176,45 @@
}
}
+ /**
+ * Return the current status of OTA update.
+ *
+ * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
+ * that IPC should generally be fast.
+ */
+ @Override
+ public @OtaStatus int getOtaStatus() {
+ if (!callerCanWriteEmbeddedSubscriptions()) {
+ throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get OTA status");
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ return blockingGetOtaStatusFromEuiccService();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
+ /**
+ * Start eUICC OTA update if current eUICC OS is not the latest one. When OTA is started or
+ * finished, the broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} will be sent.
+ *
+ * This function will only be called from phone process and isn't exposed to the other apps.
+ */
+ public void startOtaUpdatingIfNecessary() {
+ mConnector.startOtaIfNecessary(
+ new OtaStatusChangedCallback() {
+ @Override
+ public void onOtaStatusChanged(int status) {
+ sendOtaStatusChangedBroadcast();
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {}
+ });
+ }
+
@Override
public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
String callingPackage, PendingIntent callbackIntent) {
@@ -214,25 +261,26 @@
GetDownloadableSubscriptionMetadataResult result) {
Intent extrasIntent = new Intent();
final int resultCode;
- switch (result.result) {
+ switch (result.getResult()) {
case EuiccService.RESULT_OK:
resultCode = OK;
extrasIntent.putExtra(
- EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
- result.subscription);
+ EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
+ result.getDownloadableSubscription());
break;
case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
resultCode = RESOLVABLE_ERROR;
addResolutionIntent(extrasIntent,
EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
mCallingPackage,
+ false /* confirmationCodeRetried */,
getOperationForDeactivateSim());
break;
default:
resultCode = ERROR;
extrasIntent.putExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
- result.result);
+ result.getResult());
break;
}
@@ -300,12 +348,13 @@
@Override
public void onGetMetadataComplete(
GetDownloadableSubscriptionMetadataResult result) {
- if (result.result == EuiccService.RESULT_MUST_DEACTIVATE_SIM) {
+ if (result.getResult() == EuiccService.RESULT_MUST_DEACTIVATE_SIM) {
// If we need to deactivate the current SIM to even check permissions, go ahead and
// require that the user resolve the stronger permission dialog.
Intent extrasIntent = new Intent();
addResolutionIntent(extrasIntent, EuiccService.ACTION_RESOLVE_NO_PRIVILEGES,
mCallingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forDownloadNoPrivileges(
mCallingToken, mSubscription, mSwitchAfterDownload,
mCallingPackage));
@@ -313,14 +362,18 @@
return;
}
- if (result.result != EuiccService.RESULT_OK) {
+ if (result.getResult() != EuiccService.RESULT_OK) {
// Just propagate the error as normal.
super.onGetMetadataComplete(result);
return;
}
- DownloadableSubscription subscription = result.subscription;
- UiccAccessRule[] rules = subscription.getAccessRules();
+ DownloadableSubscription subscription = result.getDownloadableSubscription();
+ UiccAccessRule[] rules = null;
+ List<UiccAccessRule> rulesList = subscription.getAccessRules();
+ if (rulesList != null) {
+ rules = rulesList.toArray(new UiccAccessRule[rulesList.size()]);
+ }
if (rules == null) {
Log.e(TAG, "No access rules but caller is unprivileged");
sendResult(mCallbackIntent, ERROR, null /* extrasIntent */);
@@ -354,6 +407,7 @@
Intent extrasIntent = new Intent();
addResolutionIntent(extrasIntent, EuiccService.ACTION_RESOLVE_NO_PRIVILEGES,
mCallingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forDownloadNoPrivileges(
mCallingToken, subscription, mSwitchAfterDownload,
mCallingPackage));
@@ -394,6 +448,9 @@
mContext.getContentResolver(),
Settings.Global.EUICC_PROVISIONED,
1);
+ extrasIntent.putExtra(
+ EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
+ subscription);
if (!switchAfterDownload) {
// Since we're not switching, nothing will trigger a
// subscription list refresh on its own, so request one here.
@@ -407,10 +464,25 @@
addResolutionIntent(extrasIntent,
EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
callingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forDownloadDeactivateSim(
callingToken, subscription, switchAfterDownload,
callingPackage));
break;
+ case EuiccService.RESULT_NEED_CONFIRMATION_CODE:
+ resultCode = RESOLVABLE_ERROR;
+ boolean retried = false;
+ if (!TextUtils.isEmpty(subscription.getConfirmationCode())) {
+ retried = true;
+ }
+ addResolutionIntent(extrasIntent,
+ EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE,
+ callingPackage,
+ retried /* confirmationCodeRetried */,
+ EuiccOperation.forDownloadConfirmationCode(
+ callingToken, subscription, switchAfterDownload,
+ callingPackage));
+ break;
default:
resultCode = ERROR;
extrasIntent.putExtra(
@@ -499,18 +571,22 @@
public void onGetDefaultListComplete(GetDefaultDownloadableSubscriptionListResult result) {
Intent extrasIntent = new Intent();
final int resultCode;
- switch (result.result) {
+ switch (result.getResult()) {
case EuiccService.RESULT_OK:
resultCode = OK;
- extrasIntent.putExtra(
- EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS,
- result.subscriptions);
+ List<DownloadableSubscription> list = result.getDownloadableSubscriptions();
+ if (list != null && list.size() > 0) {
+ extrasIntent.putExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS,
+ list.toArray(new DownloadableSubscription[list.size()]));
+ }
break;
case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
resultCode = RESOLVABLE_ERROR;
addResolutionIntent(extrasIntent,
EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
mCallingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forGetDefaultListDeactivateSim(
mCallingToken, mCallingPackage));
break;
@@ -518,7 +594,7 @@
resultCode = ERROR;
extrasIntent.putExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
- result.result);
+ result.getResult());
break;
}
@@ -647,7 +723,7 @@
return;
}
if (!callerCanWriteEmbeddedSubscriptions
- && !sub.canManageSubscription(mContext, callingPackage)) {
+ && !mSubscriptionManager.canManageSubscription(sub, callingPackage)) {
Log.e(TAG, "Not permitted to switch to subscription: " + subscriptionId);
sendResult(callbackIntent, ERROR, null /* extrasIntent */);
return;
@@ -662,6 +738,7 @@
addResolutionIntent(extrasIntent,
EuiccService.ACTION_RESOLVE_NO_PRIVILEGES,
callingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forSwitchNoPrivileges(
token, subscriptionId, callingPackage));
sendResult(callbackIntent, RESOLVABLE_ERROR, extrasIntent);
@@ -707,6 +784,7 @@
addResolutionIntent(extrasIntent,
EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
callingPackage,
+ false /* confirmationCodeRetried */,
EuiccOperation.forSwitchDeactivateSim(
callingToken, subscriptionId, callingPackage));
break;
@@ -874,11 +952,13 @@
/** Add a resolution intent to the given extras intent. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void addResolutionIntent(Intent extrasIntent, String resolutionAction,
- String callingPackage, EuiccOperation op) {
+ String callingPackage, boolean confirmationCodeRetried, EuiccOperation op) {
Intent intent = new Intent(EuiccManager.ACTION_RESOLVE_ERROR);
intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION,
resolutionAction);
intent.putExtra(EuiccService.EXTRA_RESOLUTION_CALLING_PACKAGE, callingPackage);
+ intent.putExtra(EuiccService.EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED,
+ confirmationCodeRetried);
intent.putExtra(EXTRA_OPERATION, op);
PendingIntent resolutionIntent = PendingIntent.getActivity(
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_ONE_SHOT);
@@ -897,6 +977,16 @@
}
}
+ /**
+ * Send broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} for OTA status
+ * changed.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void sendOtaStatusChangedBroadcast() {
+ Intent intent = new Intent(EuiccManager.ACTION_OTA_STATUS_CHANGED);
+ mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+ }
+
@Nullable
private SubscriptionInfo getSubscriptionForSubscriptionId(int subscriptionId) {
List<SubscriptionInfo> subs = mSubscriptionManager.getAvailableSubscriptionInfoList();
@@ -929,6 +1019,25 @@
return awaitResult(latch, eidRef);
}
+ private @OtaStatus int blockingGetOtaStatusFromEuiccService() {
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference<Integer> statusRef =
+ new AtomicReference<>(EUICC_OTA_STATUS_UNAVAILABLE);
+ mConnector.getOtaStatus(new EuiccConnector.GetOtaStatusCommandCallback() {
+ @Override
+ public void onGetOtaStatusComplete(@OtaStatus int status) {
+ statusRef.set(status);
+ latch.countDown();
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ latch.countDown();
+ }
+ });
+ return awaitResult(latch, statusRef);
+ }
+
@Nullable
private EuiccInfo blockingGetEuiccInfoFromEuiccService() {
CountDownLatch latch = new CountDownLatch(1);
@@ -966,7 +1075,9 @@
int size = subInfoList.size();
for (int subIndex = 0; subIndex < size; subIndex++) {
SubscriptionInfo subInfo = subInfoList.get(subIndex);
- if (subInfo.isEmbedded() && subInfo.canManageSubscription(mContext, callingPackage)) {
+
+ if (subInfo.isEmbedded()
+ && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) {
return true;
}
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccOperation.java b/src/java/com/android/internal/telephony/euicc/EuiccOperation.java
index 3b0dbc5..b0a18a3 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccOperation.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccOperation.java
@@ -25,6 +25,7 @@
import android.service.euicc.EuiccService;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -60,6 +61,7 @@
ACTION_GET_METADATA_DEACTIVATE_SIM,
ACTION_DOWNLOAD_DEACTIVATE_SIM,
ACTION_DOWNLOAD_NO_PRIVILEGES,
+ ACTION_DOWNLOAD_CONFIRMATION_CODE,
})
@interface Action {}
@@ -75,6 +77,8 @@
static final int ACTION_SWITCH_DEACTIVATE_SIM = 5;
@VisibleForTesting
static final int ACTION_SWITCH_NO_PRIVILEGES = 6;
+ @VisibleForTesting
+ static final int ACTION_DOWNLOAD_CONFIRMATION_CODE = 7;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final @Action int mAction;
@@ -123,6 +127,17 @@
subscription, 0 /* subscriptionId */, switchAfterDownload, callingPackage);
}
+ /**
+ * {@link EuiccManager#downloadSubscription} failed with
+ * {@link EuiccService#RESULT_NEED_CONFIRMATION_CODE} error.
+ */
+ public static EuiccOperation forDownloadConfirmationCode(long callingToken,
+ DownloadableSubscription subscription, boolean switchAfterDownload,
+ String callingPackage) {
+ return new EuiccOperation(ACTION_DOWNLOAD_CONFIRMATION_CODE, callingToken,
+ subscription, 0 /* subscriptionId */, switchAfterDownload, callingPackage);
+ }
+
static EuiccOperation forGetDefaultListDeactivateSim(long callingToken, String callingPackage) {
return new EuiccOperation(ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM, callingToken,
null /* downloadableSubscription */, 0 /* subscriptionId */,
@@ -192,32 +207,37 @@
switch (mAction) {
case ACTION_GET_METADATA_DEACTIVATE_SIM:
resolvedGetMetadataDeactivateSim(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
callbackIntent);
break;
case ACTION_DOWNLOAD_DEACTIVATE_SIM:
resolvedDownloadDeactivateSim(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
callbackIntent);
break;
case ACTION_DOWNLOAD_NO_PRIVILEGES:
resolvedDownloadNoPrivileges(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
+ callbackIntent);
+ break;
+ case ACTION_DOWNLOAD_CONFIRMATION_CODE:
+ resolvedDownloadConfirmationCode(
+ resolutionExtras.getString(EuiccService.EXTRA_RESOLUTION_CONFIRMATION_CODE),
callbackIntent);
break;
case ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM:
resolvedGetDefaultListDeactivateSim(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
callbackIntent);
break;
case ACTION_SWITCH_DEACTIVATE_SIM:
resolvedSwitchDeactivateSim(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
callbackIntent);
break;
case ACTION_SWITCH_NO_PRIVILEGES:
resolvedSwitchNoPrivileges(
- resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
+ resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT),
callbackIntent);
break;
default:
@@ -284,6 +304,22 @@
}
}
+ private void resolvedDownloadConfirmationCode(String confirmationCode,
+ PendingIntent callbackIntent) {
+ if (TextUtils.isEmpty(confirmationCode)) {
+ fail(callbackIntent);
+ } else {
+ mDownloadableSubscription.setConfirmationCode(confirmationCode);
+ EuiccController.get()
+ .downloadSubscription(
+ mDownloadableSubscription,
+ mSwitchAfterDownload,
+ mCallingPackage,
+ true /* forceDeactivateSim */,
+ callbackIntent);
+ }
+ }
+
private void resolvedGetDefaultListDeactivateSim(
boolean consent, PendingIntent callbackIntent) {
if (consent) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index b9a07f9..2a6c848 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -236,7 +236,6 @@
ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
- ret.mCallbackReceiver = wrappedCallback;
if(ret.mDialingNumber != null &&
ret.mDialingNumber.endsWith("#") &&
@@ -268,6 +267,10 @@
ret.mDialingNumber = dialString;
}
+ if (ret != null) {
+ ret.mCallbackReceiver = wrappedCallback;
+ }
+
return ret;
}
@@ -863,6 +866,13 @@
return mIsSsInfo;
}
+ public static boolean isVoiceUnconditionalForwarding(int reason, int serviceClass) {
+ return (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL)
+ || (reason == CommandsInterface.CF_REASON_ALL))
+ && (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0)
+ || (serviceClass == CommandsInterface.SERVICE_CLASS_NONE)));
+ }
+
/** Process a MMI code or short code...anything that isn't a dialing number */
public void
processCode() throws CallStateException {
@@ -933,12 +943,6 @@
throw new RuntimeException ("invalid action");
}
- int isSettingUnconditionalVoice =
- (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) ||
- (reason == CommandsInterface.CF_REASON_ALL)) &&
- (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) ||
- (serviceClass == CommandsInterface.SERVICE_CLASS_NONE))) ? 1 : 0;
-
int isEnableDesired =
((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
(cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
@@ -947,7 +951,7 @@
mPhone.mCi.setCallForward(cfAction, reason, serviceClass,
dialingNumber, time, obtainMessage(
EVENT_SET_CFF_COMPLETE,
- isSettingUnconditionalVoice,
+ isVoiceUnconditionalForwarding(reason, serviceClass) ? 1 : 0,
isEnableDesired, this));
}
} else if (isServiceCodeCallBarring(mSc)) {
@@ -1251,6 +1255,9 @@
} else if (err == CommandException.Error.SS_MODIFIED_TO_SS) {
Rlog.i(LOG_TAG, "SS_MODIFIED_TO_SS");
return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
+ } 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);
}
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 8f18c61..99082ee 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -16,40 +16,32 @@
package com.android.internal.telephony.gsm;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
-import android.net.Uri;
import android.os.AsyncResult;
import android.os.Message;
-import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.util.Pair;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsDispatchersController;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsUsageMonitor;
+import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.SMSDispatcherUtil;
import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public final class GsmSMSDispatcher extends SMSDispatcher {
private static final String TAG = "GsmSMSDispatcher";
- private static final boolean VDBG = false;
protected UiccController mUiccController = null;
private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
private AtomicReference<UiccCardApplication> mUiccApplication =
@@ -59,10 +51,9 @@
/** Status report received */
private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
- public GsmSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
- ImsSMSDispatcher imsSMSDispatcher,
+ public GsmSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController,
GsmInboundSmsHandler gsmInboundSmsHandler) {
- super(phone, usageMonitor, imsSMSDispatcher);
+ super(phone, smsDispatchersController);
mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
mGsmInboundSmsHandler = gsmInboundSmsHandler;
mUiccController = UiccController.getInstance();
@@ -109,6 +100,32 @@
}
}
+ @Override
+ protected boolean shouldBlockSmsForEcbm() {
+ // There is no such thing as ECBM for GSM. This only applies to CDMA.
+ return false;
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+ int validityPeriod) {
+ return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+ validityPeriod);
+ }
+
+ @Override
+ protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested) {
+ return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message,
+ statusReportRequested);
+ }
+
+ @Override
+ protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+ return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly);
+ }
+
/**
* Called when a status report is received. This should correspond to
* a previously successful SEND.
@@ -121,25 +138,17 @@
SmsMessage sms = SmsMessage.newFromCDS(pdu);
if (sms != null) {
- int tpStatus = sms.getStatus();
int messageRef = sms.mMessageRef;
for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
SmsTracker tracker = deliveryPendingList.get(i);
if (tracker.mMessageRef == messageRef) {
- // Found it. Remove from list and broadcast.
- if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
- deliveryPendingList.remove(i);
- // Update the message status (COMPLETE or FAILED)
- tracker.updateSentMessageStatus(mContext, tpStatus);
+ Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
+ tracker,
+ getFormat(),
+ pdu);
+ if (result.second) {
+ deliveryPendingList.remove(i);
}
- PendingIntent intent = tracker.mDeliveryIntent;
- Intent fillIn = new Intent();
- fillIn.putExtra("pdu", pdu);
- fillIn.putExtra("format", getFormat());
- try {
- intent.send(mContext, Activity.RESULT_OK, fillIn);
- } catch (CanceledException ex) {}
-
// Only expect to see one tracker matching this messageref
break;
}
@@ -150,101 +159,6 @@
/** {@inheritDoc} */
@Override
- protected void sendData(String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
- scAddr, destAddr, destPort, data, (deliveryIntent != null));
- if (pdu != null) {
- HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
- SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
- false /*isText*/, true /*persistMessage*/);
-
- String carrierPackage = getCarrierAppPackageName();
- if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
- DataSmsSender smsSender = new DataSmsSender(tracker);
- smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
- } else {
- Rlog.v(TAG, "No carrier package.");
- sendRawPdu(tracker);
- }
- } else {
- Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
- }
- }
-
- /** {@inheritDoc} */
- @VisibleForTesting
- @Override
- public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
- boolean persistMessage) {
- SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
- scAddr, destAddr, text, (deliveryIntent != null));
- if (pdu != null) {
- HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
- SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
- persistMessage);
-
- String carrierPackage = getCarrierAppPackageName();
- if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
- TextSmsSender smsSender = new TextSmsSender(tracker);
- smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
- } else {
- Rlog.v(TAG, "No carrier package.");
- sendRawPdu(tracker);
- }
- } else {
- Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
- }
-
- /** {@inheritDoc} */
- @Override
- protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
- boolean use7bitOnly) {
- return SmsMessage.calculateLength(messageBody, use7bitOnly);
- }
-
- /** {@inheritDoc} */
- @Override
- protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
- String message, SmsHeader smsHeader, int encoding,
- PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
- AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
- String fullMessageText) {
- SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
- message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
- encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
- if (pdu != null) {
- HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
- message, pdu);
- return getSmsTracker(map, sentIntent,
- deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
- smsHeader, !lastPart, fullMessageText, true /*isText*/,
- false /*persistMessage*/);
- } else {
- Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
- return null;
- }
- }
-
- @Override
- protected void sendSubmitPdu(SmsTracker tracker) {
- sendRawPdu(tracker);
- }
-
- /** {@inheritDoc} */
- @Override
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.getData();
@@ -269,14 +183,9 @@
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + mPhone.getServiceState().getState());
- sendSmsByPstn(tracker);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void sendSmsByPstn(SmsTracker tracker) {
int ss = mPhone.getServiceState().getState();
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
@@ -284,26 +193,17 @@
return;
}
- HashMap<String, Object> map = tracker.getData();
-
byte smsc[] = (byte[]) map.get("smsc");
- byte[] pdu = (byte[]) map.get("pdu");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
// sms over gsm is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
- // indicated by mImsRetry > 0
- if (0 == tracker.mImsRetry && !isIms()) {
- if (tracker.mRetryCount > 0) {
- // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
- // TP-RD (bit 2) is 1 for retry
- // and TP-MR is set to previously failed sms TP-MR
- if (((0x01 & pdu[0]) == 0x01)) {
- pdu[0] |= 0x04; // TP-RD
- pdu[1] = (byte) tracker.mMessageRef; // TP-MR
- }
- }
+ // indicated by mImsRetry > 0 OR
+ // this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
+ // this message because the ImsSmsDispatcher has indicated that the message needs to
+ // fall back to sending over CS.
+ if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
diff --git a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
index 8b64ade..41e76ae 100644
--- a/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
+++ b/src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java
@@ -24,7 +24,7 @@
* {@hide}
*/
public class SuppServiceNotification {
- /** Type of notification: 0 = MO; 1 = MT */
+ /** Type of notification: 0 = code1; 1 = code2 */
public int notificationType;
/** TS 27.007 7.17 "code1" or "code2" */
public int code;
@@ -38,27 +38,160 @@
/** List of forwarded numbers, if any */
public String[] history;
- static public final int MO_CODE_UNCONDITIONAL_CF_ACTIVE = 0;
- static public final int MO_CODE_SOME_CF_ACTIVE = 1;
- static public final int MO_CODE_CALL_FORWARDED = 2;
- static public final int MO_CODE_CALL_IS_WAITING = 3;
- static public final int MO_CODE_CUG_CALL = 4;
- static public final int MO_CODE_OUTGOING_CALLS_BARRED = 5;
- static public final int MO_CODE_INCOMING_CALLS_BARRED = 6;
- static public final int MO_CODE_CLIR_SUPPRESSION_REJECTED = 7;
- static public final int MO_CODE_CALL_DEFLECTED = 8;
+ /**
+ * Notification type is from the "code 1" group (per TS 27.007 7.17).
+ * This means the {@link #code} will be a code such as {@link #CODE_1_CALL_FORWARDED}.
+ */
+ public static final int NOTIFICATION_TYPE_CODE_1 = 0;
- static public final int MT_CODE_FORWARDED_CALL = 0;
- static public final int MT_CODE_CUG_CALL = 1;
- static public final int MT_CODE_CALL_ON_HOLD = 2;
- static public final int MT_CODE_CALL_RETRIEVED = 3;
- static public final int MT_CODE_MULTI_PARTY_CALL = 4;
- static public final int MT_CODE_ON_HOLD_CALL_RELEASED = 5;
- static public final int MT_CODE_FORWARD_CHECK_RECEIVED = 6;
- static public final int MT_CODE_CALL_CONNECTING_ECT = 7;
- static public final int MT_CODE_CALL_CONNECTED_ECT = 8;
- static public final int MT_CODE_DEFLECTED_CALL = 9;
- static public final int MT_CODE_ADDITIONAL_CALL_FORWARDED = 10;
+ /**
+ * Notification type is from the "code 2" group (per TS 27.007 7.17).
+ * This means the {@link #code} will be a code such as {@link #CODE_2_CALL_ON_HOLD}.
+ */
+ public static final int NOTIFICATION_TYPE_CODE_2 = 1;
+
+ /**
+ * Indicates that unconditional call forwarding is active.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_UNCONDITIONAL_CF_ACTIVE = 0;
+
+ /**
+ * Indicates that some conditional call forwarding options are active.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_SOME_CF_ACTIVE = 1;
+
+ /**
+ * Indicates that an outgoing call has been forwarded to another number.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_CALL_FORWARDED = 2;
+
+ /**
+ * Indicates that an outgoing call is waiting. This means that the called party is already in
+ * another call and is hearing the call waiting tone.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_CALL_IS_WAITING = 3;
+
+ /**
+ * Indicates that an outgoing call is to a number in a closed user group.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_CUG_CALL = 4;
+
+ /**
+ * Indicates that outgoing calls are barred.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_OUTGOING_CALLS_BARRED = 5;
+
+ /**
+ * Indicates that incoming calls are barred.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_INCOMING_CALLS_BARRED = 6;
+
+ /**
+ * Indicates that CLIR suppression has been rejected for an outgoing call.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_CLIR_SUPPRESSION_REJECTED = 7;
+
+ /**
+ * Indicates that an outgoing call bas been deflected to another number.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_1}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_1_CALL_DEFLECTED = 8;
+
+ /**
+ * Indicates that an incoming call is a forwarded call.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_FORWARDED_CALL = 0;
+
+ /**
+ * Indicates that an incoming call is from a member of a closed user group.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_CUG_CALL = 1;
+
+ /**
+ * Indicates that a call has been remotely put on hold.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_CALL_ON_HOLD = 2;
+
+ /**
+ * Indicates that a call has been remotely resumed (retrieved).
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_CALL_RETRIEVED = 3;
+
+ /**
+ * Indicates that a conference call has been entered.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_MULTI_PARTY_CALL = 4;
+
+ /**
+ * Indicates that an ongoing call on hold has been released.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_ON_HOLD_CALL_RELEASED = 5;
+
+ /**
+ * Indicates that a forward check message was received.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_FORWARD_CHECK_RECEIVED = 6;
+
+ /**
+ * Indicates that a call is being connected (alerting) with another party as a result of an
+ * explicit call transfer operation.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_CALL_CONNECTING_ECT = 7;
+
+ /**
+ * Indicates that a call has been connected with another party as a result of an explicit call
+ * transfer operation.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_CALL_CONNECTED_ECT = 8;
+
+ /**
+ * Indicates that an outgoing call has been deflected to another number.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_DEFLECTED_CALL = 9;
+
+ /**
+ * Indicates that an additional incoming call has been forwarded.
+ * Valid {@link #code} when {@link #type} is {@link #NOTIFICATION_TYPE_CODE_2}.
+ * See TS 27.007 7.17.
+ */
+ public static final int CODE_2_ADDITIONAL_CALL_FORWARDED = 10;
@Override
public String toString()
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index 00ab0f4..6489014 100755
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -523,6 +523,7 @@
switch(msg.what) {
case EVENT_PBR_LOAD_DONE:
+ log("Loading PBR records done");
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
createPbrFile((ArrayList<byte[]>)ar.result);
diff --git a/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java b/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java
new file mode 100644
index 0000000..90d415e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsConfigCompatAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ims;
+
+import android.os.RemoteException;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsConfig;
+
+public class ImsConfigCompatAdapter extends ImsConfigImplBase {
+
+ private static final String TAG = "ImsConfigCompatAdapter";
+
+ private final IImsConfig mOldConfigInterface;
+
+ // Compat constants
+ public static final int UNKNOWN = -1;
+ public static final int SUCCESS = 0;
+ public static final int FAILED = 1;
+
+ public ImsConfigCompatAdapter(IImsConfig config) {
+ mOldConfigInterface = config;
+ }
+
+ @Override
+ public int setConfig(int item, int value) {
+ try {
+ if (mOldConfigInterface.setProvisionedValue(item, value) == SUCCESS) {
+ return CONFIG_RESULT_SUCCESS;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "setConfig: item=" + item + " value=" + value + "failed: "
+ + e.getMessage());
+ }
+ return CONFIG_RESULT_FAILED;
+ }
+
+ @Override
+ public int setConfig(int item, String value) {
+ try {
+ if (mOldConfigInterface.setProvisionedStringValue(item, value) == SUCCESS) {
+ return CONFIG_RESULT_SUCCESS;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "setConfig: item=" + item + " value=" + value + "failed: "
+ + e.getMessage());
+ }
+ return CONFIG_RESULT_FAILED;
+ }
+
+ @Override
+ public int getConfigInt(int item) {
+ try {
+ int value = mOldConfigInterface.getProvisionedValue(item);
+ if (value != UNKNOWN) {
+ return value;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "getConfigInt: item=" + item + "failed: " + e.getMessage());
+ }
+ return CONFIG_RESULT_UNKNOWN;
+ }
+
+ @Override
+ public String getConfigString(int item) {
+ try {
+ return mOldConfigInterface.getProvisionedStringValue(item);
+ } catch (RemoteException e) {
+ Log.w(TAG, "getConfigInt: item=" + item + "failed: " + e.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
new file mode 100644
index 0000000..5a51fd7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ims;
+
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArrayMap;
+
+import com.android.ims.internal.IImsRegistrationListener;
+
+import java.util.Map;
+
+public class ImsRegistrationCompatAdapter extends ImsRegistrationImplBase {
+
+ // Maps "RAT" based radio technologies to ImsRegistrationImplBase definitions.
+ private static final Map<Integer, Integer> RADIO_TECH_MAPPER = new ArrayMap<>(2);
+ static {
+ RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_LTE, REGISTRATION_TECH_LTE);
+ RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_IWLAN, REGISTRATION_TECH_IWLAN);
+ }
+
+ // Trampolines "old" listener events to the new interface.
+ private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() {
+ @Override
+ public void registrationConnected() throws RemoteException {
+ onRegistered(REGISTRATION_TECH_NONE);
+ }
+
+ @Override
+ public void registrationProgressing() throws RemoteException {
+ onRegistering(REGISTRATION_TECH_NONE);
+ }
+
+ @Override
+ public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+ onRegistered(RADIO_TECH_MAPPER.getOrDefault(imsRadioTech, REGISTRATION_TECH_NONE));
+ }
+
+ @Override
+ public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+ onRegistering(RADIO_TECH_MAPPER.getOrDefault(imsRadioTech, REGISTRATION_TECH_NONE));
+ }
+
+ @Override
+ public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+ onDeregistered(imsReasonInfo);
+ }
+
+ @Override
+ public void registrationResumed() throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationSuspended() throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationServiceCapabilityChanged(int serviceClass, int event)
+ throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+ int[] disabledFeatures) throws RemoteException {
+ // Implemented in the MMTel Adapter
+ }
+
+ @Override
+ public void voiceMessageCountUpdate(int count) throws RemoteException {
+ // Implemented in the MMTel Adapter
+ }
+
+ @Override
+ public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+ onSubscriberAssociatedUriChanged(uris);
+ }
+
+ @Override
+ public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ onTechnologyChangeFailed(RADIO_TECH_MAPPER.getOrDefault(targetAccessTech,
+ REGISTRATION_TECH_NONE), imsReasonInfo);
+ }
+ };
+
+ /**
+ * Need access to the listener in order to register for events in MMTelFeature adapter
+ */
+ public IImsRegistrationListener getRegistrationListener() {
+ return mListener;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 2f790b7..f0aee01 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.ims;
import android.Manifest;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -27,27 +28,33 @@
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -69,11 +76,14 @@
private static final String TAG = "ImsResolver";
- public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
"android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
+ // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
+ // ImsService that is connecting to the platform.
+ // This should ONLY be used for testing and should not be used in production ImsServices.
+ private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
// Based on updates from PackageManager
private static final int HANDLER_ADD_PACKAGE = 0;
@@ -81,6 +91,16 @@
private static final int HANDLER_REMOVE_PACKAGE = 1;
// Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
private static final int HANDLER_CONFIG_CHANGED = 2;
+ // A query has been started for an ImsService to relay the features they support.
+ private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
+ // A query to request ImsService features has completed or the ImsService has updated features.
+ private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
+ // Testing: Overrides the current configuration for ImsService binding
+ private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
+
+ // Delay between dynamic ImsService queries.
+ private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
+
/**
* Stores information about an ImsService, including the package name, class name, and features
@@ -89,7 +109,35 @@
@VisibleForTesting
public static class ImsServiceInfo {
public ComponentName name;
- public Set<Integer> supportedFeatures;
+ // Determines if features were created from metadata in the manifest or through dynamic
+ // query.
+ public boolean featureFromMetadata = true;
+ public ImsServiceControllerFactory controllerFactory;
+
+ // Map slotId->Feature
+ private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
+ private final int mNumSlots;
+
+ public ImsServiceInfo(int numSlots) {
+ mNumSlots = numSlots;
+ mSupportedFeatures = new HashSet<>();
+ }
+
+ void addFeatureForAllSlots(int feature) {
+ for (int i = 0; i < mNumSlots; i++) {
+ mSupportedFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(i, feature));
+ }
+ }
+
+ void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) {
+ mSupportedFeatures.clear();
+ mSupportedFeatures.addAll(newFeatures);
+ }
+
+ @VisibleForTesting
+ public HashSet<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
+ return mSupportedFeatures;
+ }
@Override
public boolean equals(Object o) {
@@ -99,17 +147,37 @@
ImsServiceInfo that = (ImsServiceInfo) o;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
- return supportedFeatures != null ? supportedFeatures.equals(that.supportedFeatures)
- : that.supportedFeatures == null;
-
+ if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
+ return false;
+ }
+ return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
+ : that.controllerFactory == null;
}
@Override
public int hashCode() {
+ // We do not include mSupportedFeatures in hashcode because the internal structure
+ // changes after adding.
int result = name != null ? name.hashCode() : 0;
- result = 31 * result + (supportedFeatures != null ? supportedFeatures.hashCode() : 0);
+ result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
return result;
}
+
+ @Override
+ public String toString() {
+ StringBuilder res = new StringBuilder();
+ res.append("[ImsServiceInfo] name=");
+ res.append(name);
+ res.append(", supportedFeatures=[ ");
+ for (ImsFeatureConfiguration.FeatureSlotPair feature : mSupportedFeatures) {
+ res.append("(");
+ res.append(feature.slotId);
+ res.append(",");
+ res.append(feature.featureType);
+ res.append(") ");
+ }
+ return res.toString();
+ }
}
// Receives broadcasts from the system involving changes to the installed applications. If
@@ -122,6 +190,8 @@
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
// intentional fall-through
+ case Intent.ACTION_PACKAGE_REPLACED:
+ // intentional fall-through
case Intent.ACTION_PACKAGE_CHANGED:
mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
break;
@@ -140,17 +210,17 @@
@Override
public void onReceive(Context context, Intent intent) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- Log.i(TAG, "Received SIM change for invalid sub id.");
+ if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.i(TAG, "Received SIM change for invalid slot id.");
return;
}
- Log.i(TAG, "Received Carrier Config Changed for SubId: " + subId);
+ Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId);
- mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, subId).sendToTarget();
+ mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget();
}
};
@@ -192,14 +262,71 @@
@VisibleForTesting
public interface ImsServiceControllerFactory {
/**
- * Returns the ImsServiceController created usiing the context and componentName supplied.
- * Used for DI when testing.
+ * @return the Service Interface String used for binding the ImsService.
*/
- ImsServiceController get(Context context, ComponentName componentName);
+ String getServiceInterface();
+ /**
+ * @return the ImsServiceController created using the context and componentName supplied.
+ */
+ ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks);
}
- private ImsServiceControllerFactory mImsServiceControllerFactory = (context, componentName) ->
- new ImsServiceController(context, componentName, this);
+ private ImsServiceControllerFactory mImsServiceControllerFactory =
+ new ImsServiceControllerFactory() {
+
+ @Override
+ public String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ return new ImsServiceController(context, componentName, callbacks);
+ }
+ };
+
+ /**
+ * Used for testing.
+ */
+ @VisibleForTesting
+ public interface ImsDynamicQueryManagerFactory {
+ ImsServiceFeatureQueryManager create(Context context,
+ ImsServiceFeatureQueryManager.Listener listener);
+ }
+
+ private ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
+ new ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ return android.telephony.ims.compat.ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ return new ImsServiceControllerCompat(context, componentName, callbacks);
+ }
+ };
+
+ private ImsServiceControllerFactory mImsServiceControllerFactoryStaticBindingCompat =
+ new ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ // The static method of binding does not use service interfaces.
+ return null;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ return new ImsServiceControllerStaticCompat(context, componentName, callbacks);
+ }
+ };
+
+ private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory =
+ ImsServiceFeatureQueryManager::new;
private final CarrierConfigManager mCarrierConfigManager;
private final Context mContext;
@@ -207,6 +334,9 @@
// ImsServiceController callbacks.
private final Object mBoundServicesLock = new Object();
private final int mNumSlots;
+ private final boolean mIsDynamicBinding;
+ // Package name of the default device service.
+ private String mDeviceService;
// Synchronize all messages on a handler to ensure that the cache includes the most recent
// version of the installed ImsServices.
@@ -223,8 +353,52 @@
break;
}
case HANDLER_CONFIG_CHANGED: {
- int subId = (Integer) msg.obj;
- maybeRebindService(subId);
+ int slotId = (Integer) msg.obj;
+ carrierConfigChanged(slotId);
+ break;
+ }
+ case HANDLER_START_DYNAMIC_FEATURE_QUERY: {
+ ImsServiceInfo info = (ImsServiceInfo) msg.obj;
+ startDynamicQuery(info);
+ break;
+ }
+ case HANDLER_DYNAMIC_FEATURE_CHANGE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ ComponentName name = (ComponentName) args.arg1;
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features =
+ (Set<ImsFeatureConfiguration.FeatureSlotPair>) args.arg2;
+ args.recycle();
+ dynamicQueryComplete(name, features);
+ break;
+ }
+ case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
+ int slotId = msg.arg1;
+ // arg2 will be equal to 1 if it is a carrier service.
+ boolean isCarrierImsService = (msg.arg2 == 1);
+ String packageName = (String) msg.obj;
+ if (isCarrierImsService) {
+ Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
+ + packageName);
+ maybeRebindService(slotId, packageName);
+ } else {
+ Log.i(TAG, "overriding device ImsService - packageName=" + packageName);
+ if (packageName == null || packageName.isEmpty()) {
+ unbindImsService(getImsServiceInfoFromCache(mDeviceService));
+ }
+ mDeviceService = packageName;
+ ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService);
+ if (deviceInfo == null) {
+ // The package name is either "" or does not exist on the device.
+ break;
+ }
+ if (deviceInfo.featureFromMetadata) {
+ bindImsService(deviceInfo);
+ } else {
+ // newly added ImsServiceInfo that has not had features queried yet. Start
+ // async bind and query features.
+ scheduleQueryForFeatures(deviceInfo);
+ }
+ }
break;
}
default:
@@ -233,38 +407,69 @@
return true;
});
- // Package name of the default device service.
- private String mDeviceService;
+ // Results from dynamic queries to ImsService regarding the features they support.
+ private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
+ new ImsServiceFeatureQueryManager.Listener() {
+
+ @Override
+ public void onComplete(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ Log.d(TAG, "onComplete called for name: " + name + "features:"
+ + printFeatures(features));
+ handleFeaturesChanged(name, features);
+ }
+
+ @Override
+ public void onError(ComponentName name) {
+ Log.w(TAG, "onError: " + name + "returned with an error result");
+ scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS);
+ }
+ };
+
// Array index corresponds to slot Id associated with the service package name.
private String[] mCarrierServices;
// List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
// Locked on mBoundServicesLock
private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
// not locked, only accessed on a handler thread.
- private Set<ImsServiceInfo> mInstalledServicesCache = new ArraySet<>();
+ private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
// not locked, only accessed on a handler thread.
- private Set<ImsServiceController> mActiveControllers = new ArraySet<>();
+ private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
+ // Only used as the Component name for legacy ImsServices that did not use dynamic binding.
+ private final ComponentName mStaticComponent;
+ private ImsServiceFeatureQueryManager mFeatureQueryManager;
- public ImsResolver(Context context, String defaultImsPackageName, int numSlots) {
+ public ImsResolver(Context context, String defaultImsPackageName, int numSlots,
+ boolean isDynamicBinding) {
mContext = context;
mDeviceService = defaultImsPackageName;
mNumSlots = numSlots;
+ mIsDynamicBinding = isDynamicBinding;
+ mStaticComponent = new ComponentName(mContext, ImsResolver.class);
+ if (!mIsDynamicBinding) {
+ Log.i(TAG, "ImsResolver initialized with static binding.");
+ mDeviceService = mStaticComponent.getPackageName();
+ }
mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
Context.CARRIER_CONFIG_SERVICE);
mCarrierServices = new String[numSlots];
mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
.limit(mNumSlots).collect(Collectors.toList());
- IntentFilter appChangedFilter = new IntentFilter();
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- appChangedFilter.addDataScheme("package");
- context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter, null,
- null);
+ // Only register for Package/CarrierConfig updates if dynamic binding.
+ if(mIsDynamicBinding) {
+ IntentFilter appChangedFilter = new IntentFilter();
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ appChangedFilter.addDataScheme("package");
+ context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter,
+ null,
+ null);
- context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
+ CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ }
}
@VisibleForTesting
@@ -282,34 +487,112 @@
return mHandler;
}
+ @VisibleForTesting
+ public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) {
+ mDynamicQueryManagerFactory = m;
+ }
+
/**
* Needs to be called after the constructor to first populate the cache and possibly bind to
* ImsServices.
*/
- public void populateCacheAndStartBind() {
+ public void initPopulateCacheAndStartBind() {
Log.i(TAG, "Initializing cache and binding.");
+ mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
// Populates the CarrierConfig override package names for each slot
- mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, -1).sendToTarget();
+ mHandler.obtainMessage(HANDLER_CONFIG_CHANGED,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX).sendToTarget();
// Starts first bind to the system.
mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget();
}
/**
- * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS
- * feature or {@link null} if the service is not available. If an ImsServiceController is
- * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for
- * feature updates.
- * @param slotId The SIM slot that we are requesting the {@link IImsServiceController} for.
- * @param feature The IMS Feature we are requesting.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsServiceController} interface for the feature specified or {@link null} if
- * it is unavailable.
+ * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
+ * trigger ImsFeature status updates.
*/
- public IImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
- IImsServiceFeatureListener callback) {
- if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
- || feature >= ImsFeature.MAX) {
+ public void enableIms(int slotId) {
+ SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
+ if (controllers != null) {
+ for (int i = 0; i < controllers.size(); i++) {
+ int key = controllers.keyAt(i);
+ controllers.get(key).enableIms(slotId);
+ }
+ }
+ }
+
+ /**
+ * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
+ * trigger ImsFeature capability status to become false.
+ */
+ public void disableIms(int slotId) {
+ SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
+ if (controllers != null) {
+ for (int i = 0; i < controllers.size(); i++) {
+ int key = controllers.keyAt(i);
+ controllers.get(key).disableIms(slotId);
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if
+ * the service is not available. If an IImsMMTelFeature is available, the
+ * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+ * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
+ * @param callback Listener that will send updates to ImsManager when there are updates to
+ * the feature.
+ * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable.
+ */
+ public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
+ IImsServiceFeatureCallback callback) {
+ ImsServiceController controller = getImsServiceControllerAndListen(slotId,
+ ImsFeature.FEATURE_MMTEL, callback);
+ return (controller != null) ? controller.getMmTelFeature(slotId) : null;
+ }
+
+ /**
+ * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency
+ * calling or {@link null} if the service is not available. If an IImsMMTelFeature is
+ * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for
+ * feature updates.
+ * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for.
+ * @param callback listener that will send updates to ImsManager when there are updates to
+ * the feature.
+ * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable.
+ */
+ public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
+ ImsServiceController controller = getImsServiceControllerAndListen(slotId,
+ ImsFeature.FEATURE_RCS, callback);
+ return (controller != null) ? controller.getRcsFeature(slotId) : null;
+ }
+
+ /**
+ * Returns the ImsRegistration structure associated with the slotId and feature specified.
+ */
+ public @Nullable IImsRegistration getImsRegistration(int slotId, int feature)
+ throws RemoteException {
+ ImsServiceController controller = getImsServiceController(slotId, feature);
+ if (controller != null) {
+ return controller.getRegistration(slotId);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the ImsConfig structure associated with the slotId and feature specified.
+ */
+ public @Nullable IImsConfig getImsConfig(int slotId, int feature)
+ throws RemoteException {
+ ImsServiceController controller = getImsServiceController(slotId, feature);
+ if (controller != null) {
+ return controller.getConfig(slotId);
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ public ImsServiceController getImsServiceController(int slotId, int feature) {
+ if (slotId < 0 || slotId >= mNumSlots) {
return null;
}
ImsServiceController controller;
@@ -320,16 +603,67 @@
}
controller = services.get(feature);
}
+ return controller;
+ }
+
+ private SparseArray<ImsServiceController> getImsServiceControllers(int slotId) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ return null;
+ }
+ synchronized (mBoundServicesLock) {
+ SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
+ if (services == null) {
+ return null;
+ }
+ return services;
+ }
+ }
+
+ @VisibleForTesting
+ public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
+ IImsServiceFeatureCallback callback) {
+ ImsServiceController controller = getImsServiceController(slotId, feature);
+
if (controller != null) {
- controller.addImsServiceFeatureListener(callback);
- return controller.getImsServiceController();
+ controller.addImsServiceFeatureCallback(callback);
+ return controller;
}
return null;
}
+ // Used for testing only.
+ public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
+ String packageName) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
+ return false;
+ }
+
+ if (packageName == null) {
+ Log.w(TAG, "overrideImsServiceConfiguration: null packageName!");
+ return false;
+ }
+
+ // encode boolean to int for Message.
+ int carrierService = isCarrierService ? 1 : 0;
+ Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, slotId, carrierService,
+ packageName).sendToTarget();
+ return true;
+ }
+
+ // used for testing only.
+ public String getImsServiceConfiguration(int slotId, boolean isCarrierService) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
+ return "";
+ }
+
+ return isCarrierService ? mCarrierServices[slotId] : mDeviceService;
+ }
+
private void putImsController(int slotId, int feature, ImsServiceController controller) {
- if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
- || feature >= ImsFeature.MAX) {
+ if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
+ || feature >= ImsFeature.FEATURE_MAX) {
Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
+ ", feature: " + feature);
return;
@@ -347,8 +681,8 @@
}
private ImsServiceController removeImsController(int slotId, int feature) {
- if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
- || feature >= ImsFeature.MAX) {
+ if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
+ || feature >= ImsFeature.FEATURE_MAX) {
Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
+ ", feature: " + feature);
return null;
@@ -368,7 +702,6 @@
}
}
-
// Update the current cache with the new ImsService(s) if it has been added or update the
// supported IMS features if they have changed.
// Called from the handler ONLY
@@ -379,19 +712,32 @@
for (ImsServiceInfo info : infos) {
// Checking to see if the ComponentName is the same, so we can update the supported
// features. Will only be one (if it exists), since it is a set.
- Optional<ImsServiceInfo> match = getInfoByComponentName(mInstalledServicesCache,
- info.name);
- if (match.isPresent()) {
- // update features in the cache
- Log.i(TAG, "Updating features in cached ImsService: " + info.name);
- Log.d(TAG, "Updating features - Old features: " + match.get().supportedFeatures
- + " new features: " + info.supportedFeatures);
- match.get().supportedFeatures = info.supportedFeatures;
- updateImsServiceFeatures(info);
+ ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
+ if (match != null) {
+ // for dynamic query the new "info" will have no supported features yet. Don't wipe
+ // out the cache for the existing features or update yet. Instead start a query
+ // for features dynamically.
+ if (info.featureFromMetadata) {
+ // update features in the cache
+ Log.i(TAG, "Updating features in cached ImsService: " + info.name);
+ Log.d(TAG, "Updating features - Old features: " + match + " new features: "
+ + info);
+ match.replaceFeatures(info.getSupportedFeatures());
+ updateImsServiceFeatures(info);
+ } else {
+ // start a query to get ImsService features
+ scheduleQueryForFeatures(info);
+ }
} else {
Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
- mInstalledServicesCache.add(info);
- newlyAddedInfos.add(info);
+ mInstalledServicesCache.put(info.name, info);
+ if (info.featureFromMetadata) {
+ newlyAddedInfos.add(info);
+ } else {
+ // newly added ImsServiceInfo that has not had features queried yet. Start async
+ // bind and query features.
+ scheduleQueryForFeatures(info);
+ }
}
}
// Loop through the newly created ServiceInfos in a separate loop to make sure the cache
@@ -400,12 +746,12 @@
if (isActiveCarrierService(info)) {
// New ImsService is registered to active carrier services and must be newly
// bound.
- bindNewImsService(info);
+ bindImsService(info);
// Update existing device service features
updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
} else if (isDeviceService(info)) {
// New ImsService is registered as device default and must be newly bound.
- bindNewImsService(info);
+ bindImsService(info);
}
}
}
@@ -414,11 +760,11 @@
// killed.
// Called from the handler ONLY
private boolean maybeRemovedImsService(String packageName) {
- Optional<ImsServiceInfo> match = getInfoByPackageName(mInstalledServicesCache, packageName);
- if (match.isPresent()) {
- mInstalledServicesCache.remove(match.get());
- Log.i(TAG, "Removing ImsService: " + match.get().name);
- unbindImsService(match.get());
+ ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (match != null) {
+ mInstalledServicesCache.remove(match.name);
+ Log.i(TAG, "Removing ImsService: " + match.name);
+ unbindImsService(match);
updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
return true;
}
@@ -447,25 +793,26 @@
return i;
}
}
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
}
- private Optional<ImsServiceController> getControllerByServiceInfo(
- Set<ImsServiceController> searchSet, ImsServiceInfo matchValue) {
- return searchSet.stream()
- .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)).findFirst();
+ private ImsServiceController getControllerByServiceInfo(
+ Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) {
+ return searchMap.values().stream()
+ .filter(c -> Objects.equals(c.getComponentName(), matchValue.name))
+ .findFirst().orElse(null);
}
- private Optional<ImsServiceInfo> getInfoByPackageName(Set<ImsServiceInfo> searchSet,
+ private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap,
String matchValue) {
- return searchSet.stream()
- .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)).findFirst();
+ return searchMap.values().stream()
+ .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue))
+ .findFirst().orElse(null);
}
- private Optional<ImsServiceInfo> getInfoByComponentName(Set<ImsServiceInfo> searchSet,
- ComponentName matchValue) {
- return searchSet.stream()
- .filter((i) -> Objects.equals(i.name, matchValue)).findFirst();
+ private ImsServiceInfo getInfoByComponentName(
+ Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) {
+ return searchMap.get(matchValue);
}
// Creates new features in active ImsServices and removes obsolete cached features. If
@@ -475,48 +822,71 @@
if (newInfo == null) {
return;
}
- Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers,
- newInfo);
- if (o.isPresent()) {
- Log.i(TAG, "Updating features for ImsService: " + o.get().getComponentName());
- HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(newInfo);
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, newInfo);
+ // Will return zero if these features are overridden or it should not currently have any
+ // features because it is not carrier/device.
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features =
+ calculateFeaturesToCreate(newInfo);
+ if (shouldFeaturesCauseBind(features)) {
try {
- if (features.size() > 0) {
+ if (controller != null) {
+ Log.i(TAG, "Updating features for ImsService: "
+ + controller.getComponentName());
Log.d(TAG, "Updating Features - New Features: " + features);
- o.get().changeImsServiceFeatures(features);
-
- // If the carrier service features have changed, the device features will also
- // need to be recalculated.
- if (isActiveCarrierService(newInfo)
- // Prevent infinite recursion from bad behavior
- && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
- Log.i(TAG, "Updating device default");
- updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
- }
+ controller.changeImsServiceFeatures(features);
} else {
- Log.i(TAG, "Unbinding: features = 0 for ImsService: "
- + o.get().getComponentName());
- o.get().unbind();
+ Log.i(TAG, "updateImsServiceFeatures: unbound with active features, rebinding");
+ bindImsServiceWithFeatures(newInfo, features);
+ }
+ // If the carrier service features have changed, the device features will also
+ // need to be recalculated.
+ if (isActiveCarrierService(newInfo)
+ // Prevent infinite recursion from bad behavior
+ && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
+ Log.i(TAG, "Updating device default");
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
}
} catch (RemoteException e) {
Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
}
+ // Don't stay bound if the ImsService is providing no features.
+ } else if (controller != null) {
+ Log.i(TAG, "Unbinding: features = 0 for ImsService: " + controller.getComponentName());
+ unbindImsService(newInfo);
}
}
- // Bind to a new ImsService and wait for the service to be connected to create ImsFeatures.
- private void bindNewImsService(ImsServiceInfo info) {
+ // Bind to an ImsService and wait for the service to be connected to create ImsFeatures.
+ private void bindImsService(ImsServiceInfo info) {
if (info == null) {
return;
}
- ImsServiceController controller = mImsServiceControllerFactory.get(mContext, info.name);
- HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(info);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+ bindImsServiceWithFeatures(info, features);
+ }
+
+ private void bindImsServiceWithFeatures(ImsServiceInfo info,
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
// Only bind if there are features that will be created by the service.
- if (features.size() > 0) {
- Log.i(TAG, "Binding ImsService: " + controller.getComponentName() + " with features: "
- + features);
- controller.bind(features);
- mActiveControllers.add(controller);
+ if (shouldFeaturesCauseBind(features)) {
+ // Check to see if an active controller already exists
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
+ if (controller != null) {
+ Log.i(TAG, "ImsService connection exists, updating features " + features);
+ try {
+ controller.changeImsServiceFeatures(features);
+ // Features have been set, there was an error adding/removing. When the
+ // controller recovers, it will add/remove again.
+ } catch (RemoteException e) {
+ Log.w(TAG, "bindImsService: error=" + e.getMessage());
+ }
+ } else {
+ controller = info.controllerFactory.create(mContext, info.name, this);
+ Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
+ + " with features: " + features);
+ controller.bind(features);
+ }
+ mActiveControllers.put(info.name, controller);
}
}
@@ -525,16 +895,16 @@
if (info == null) {
return;
}
- Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers, info);
- if (o.isPresent()) {
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
+ if (controller != null) {
// Calls imsServiceFeatureRemoved on all features in the controller
try {
- Log.i(TAG, "Unbinding ImsService: " + o.get().getComponentName());
- o.get().unbind();
+ Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName());
+ controller.unbind();
} catch (RemoteException e) {
Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
}
- mActiveControllers.remove(o.get());
+ mActiveControllers.remove(info.name);
}
}
@@ -542,13 +912,16 @@
// ImsServiceController, it will be granted all of the features it requests on the associated
// slot. If it is the device ImsService, it will get all of the features not covered by the
// carrier implementation.
- private HashSet<Pair<Integer, Integer>> calculateFeaturesToCreate(ImsServiceInfo info) {
- HashSet<Pair<Integer, Integer>> imsFeaturesBySlot = new HashSet<>();
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate(
+ ImsServiceInfo info) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>();
// Check if the info is a carrier service
int slotId = getSlotForActiveCarrierService(info);
- if (slotId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
- feature -> new Pair<>(slotId, feature)).collect(Collectors.toList()));
+ if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
+ // Match slotId with feature slotId.
+ .filter(feature -> slotId == feature.slotId)
+ .collect(Collectors.toList()));
} else if (isDeviceService(info)) {
// For all slots that are not currently using a carrier ImsService, enable all features
// for the device default.
@@ -556,17 +929,19 @@
final int currSlotId = i;
ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
if (carrierImsInfo == null) {
- // No Carrier override, add all features
- imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
- feature -> new Pair<>(currSlotId, feature)).collect(
- Collectors.toList()));
+ // No Carrier override, add all features for this slot
+ imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
+ .filter(feature -> currSlotId == feature.slotId)
+ .collect(Collectors.toList()));
} else {
// Add all features to the device service that are not currently covered by
// the carrier ImsService.
- Set<Integer> deviceFeatures = new HashSet<>(info.supportedFeatures);
- deviceFeatures.removeAll(carrierImsInfo.supportedFeatures);
- imsFeaturesBySlot.addAll(deviceFeatures.stream().map(
- feature -> new Pair<>(currSlotId, feature)).collect(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures =
+ new HashSet<>(info.getSupportedFeatures());
+ deviceFeatures.removeAll(carrierImsInfo.getSupportedFeatures());
+ // only add features for current slot
+ imsFeaturesBySlot.addAll(deviceFeatures.stream()
+ .filter(feature -> currSlotId == feature.slotId).collect(
Collectors.toList()));
}
}
@@ -592,28 +967,64 @@
removeImsController(slotId, feature);
}
+ /**
+ * Implementation of
+ * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which
+ * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService.
+ */
+ public void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+ ImsServiceController controller) {
+ if (controller == null || config == null) {
+ return;
+ }
+ Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures()
+ + ", ComponentName=" + controller.getComponentName());
+ handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
+ }
+
+ /**
+ * Determines if the features specified should cause a bind or keep a binding active to an
+ * ImsService.
+ * @return true if MMTEL or RCS features are present, false if they are not or only
+ * EMERGENCY_MMTEL is specified.
+ */
+ private boolean shouldFeaturesCauseBind(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ long bindableFeatures = features.stream()
+ // remove all emergency features
+ .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
+ return bindableFeatures > 0;
+ }
+
// Possibly rebind to another ImsService if currently installed ImsServices were changed or if
// the SIM card has changed.
// Called from the handler ONLY
- private void maybeRebindService(int subId) {
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ private void maybeRebindService(int slotId, String newPackageName) {
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
// not specified, replace package on all slots.
for (int i = 0; i < mNumSlots; i++) {
- // get Sub id from Slot Id
- subId = mSubscriptionManagerProxy.getSubId(i);
- updateBoundCarrierServices(subId);
+ updateBoundCarrierServices(i, newPackageName);
}
} else {
- updateBoundCarrierServices(subId);
+ updateBoundCarrierServices(slotId, newPackageName);
}
}
- private void updateBoundCarrierServices(int subId) {
- int slotId = mSubscriptionManagerProxy.getSlotIndex(subId);
- String newPackageName = mCarrierConfigManager.getConfigForSubId(subId).getString(
- CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
- if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
+ private void carrierConfigChanged(int slotId) {
+ int subId = mSubscriptionManagerProxy.getSubId(slotId);
+ PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ if (config != null) {
+ String newPackageName = config.getString(
+ CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+ maybeRebindService(slotId, newPackageName);
+ } else {
+ Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!");
+ }
+ }
+
+ private void updateBoundCarrierServices(int slotId, String newPackageName) {
+ if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
String oldPackageName = mCarrierServices[slotId];
mCarrierServices[slotId] = newPackageName;
if (!TextUtils.equals(newPackageName, oldPackageName)) {
@@ -622,14 +1033,132 @@
// ImsService is retrieved from the cache. If the cache hasn't been populated yet,
// the calls to unbind/bind will fail (intended during initial start up).
unbindImsService(getImsServiceInfoFromCache(oldPackageName));
- bindNewImsService(getImsServiceInfoFromCache(newPackageName));
- // Recalculate the device ImsService features to reflect changes.
- updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ ImsServiceInfo newInfo = getImsServiceInfoFromCache(newPackageName);
+ // if there is no carrier ImsService, newInfo is null. This we still want to update
+ // bindings for device ImsService to pick up the missing features.
+ if (newInfo == null || newInfo.featureFromMetadata) {
+ bindImsService(newInfo);
+ // Recalculate the device ImsService features to reflect changes.
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ } else {
+ // ImsServiceInfo that has not had features queried yet. Start async
+ // bind and query features.
+ scheduleQueryForFeatures(newInfo);
+ }
}
}
}
/**
+ * Schedules a query for dynamic ImsService features.
+ */
+ private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) {
+ // if not current device/carrier service, don't perform query. If this changes, this method
+ // will be called again.
+ if (!isDeviceService(service) && getSlotForActiveCarrierService(service)
+ == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
+ + " set as carrier/device ImsService.");
+ return;
+ }
+ Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service);
+ if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) {
+ Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name
+ + " already scheduled");
+ return;
+ }
+ Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name
+ + " in " + delayMs + "ms.");
+ mHandler.sendMessageDelayed(msg, delayMs);
+ }
+
+ private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
+ ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ if (service == null) {
+ Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
+ return;
+ }
+ scheduleQueryForFeatures(service, delayMs);
+ }
+
+ private void scheduleQueryForFeatures(ImsServiceInfo service) {
+ scheduleQueryForFeatures(service, 0);
+ }
+
+ /**
+ * Schedules the processing of a completed query.
+ */
+ private void handleFeaturesChanged(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = features;
+ mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget();
+ }
+
+ // Starts a dynamic query. Called from handler ONLY.
+ private void startDynamicQuery(ImsServiceInfo service) {
+ boolean queryStarted = mFeatureQueryManager.startQuery(service.name,
+ service.controllerFactory.getServiceInterface());
+ if (!queryStarted) {
+ Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
+ scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS);
+ } else {
+ Log.d(TAG, "startDynamicQuery: Service queried, waiting for response.");
+ }
+ }
+
+ // process complete dynamic query. Called from handler ONLY.
+ private void dynamicQueryComplete(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ if (service == null) {
+ Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: "
+ + name);
+ return;
+ }
+ // Add features to service
+ service.replaceFeatures(features);
+ if (isActiveCarrierService(service)) {
+ // New ImsService is registered to active carrier services and must be newly
+ // bound.
+ bindImsService(service);
+ // Update existing device service features
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ } else if (isDeviceService(service)) {
+ // New ImsService is registered as device default and must be newly bound.
+ bindImsService(service);
+ }
+ }
+
+ /**
+ * @return true if the ImsResolver is in the process of resolving a dynamic query and should not
+ * be considered available, false if the ImsResolver is idle.
+ */
+ public boolean isResolvingBinding() {
+ return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY)
+ // We haven't processed this message yet, so it is still resolving.
+ || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)
+ || mFeatureQueryManager.isQueryInProgress();
+ }
+
+ private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ StringBuilder featureString = new StringBuilder();
+ featureString.append("features: [");
+ if (features != null) {
+ for (ImsFeatureConfiguration.FeatureSlotPair feature : features) {
+ featureString.append("{");
+ featureString.append(feature.slotId);
+ featureString.append(",");
+ featureString.append(feature.featureType);
+ featureString.append("} ");
+ }
+ featureString.append("]");
+ }
+ return featureString.toString();
+ }
+
+ /**
* Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
* the ImsService caching functionality.
*/
@@ -638,10 +1167,9 @@
if (TextUtils.isEmpty(packageName)) {
return null;
}
- Optional<ImsServiceInfo> infoFilter = getInfoByPackageName(mInstalledServicesCache,
- packageName);
- if (infoFilter.isPresent()) {
- return infoFilter.get();
+ ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (infoFilter != null) {
+ return infoFilter;
} else {
return null;
}
@@ -651,8 +1179,35 @@
// get all packages that support ImsServices.
private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
List<ImsServiceInfo> infos = new ArrayList<>();
+ if (!mIsDynamicBinding) {
+ // always return the same ImsService info.
+ infos.addAll(getStaticImsService());
+ } else {
+ // Search for Current ImsService implementations
+ infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
+ // Search for compat ImsService Implementations
+ infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
+ }
+ return infos;
+ }
- Intent serviceIntent = new Intent(SERVICE_INTERFACE);
+ private List<ImsServiceInfo> getStaticImsService() {
+ List<ImsServiceInfo> infos = new ArrayList<>();
+
+ ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
+ info.name = mStaticComponent;
+ info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
+ infos.add(info);
+ return infos;
+ }
+
+ private List<ImsServiceInfo> searchForImsServices(String packageName,
+ ImsServiceControllerFactory controllerFactory) {
+ List<ImsServiceInfo> infos = new ArrayList<>();
+
+ Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
serviceIntent.setPackage(packageName);
PackageManager packageManager = mContext.getPackageManager();
@@ -663,30 +1218,51 @@
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
- ImsServiceInfo info = new ImsServiceInfo();
+ ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- info.supportedFeatures = new HashSet<>(ImsFeature.MAX);
- // Add all supported features
- if (serviceInfo.metaData != null) {
- if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, false)) {
- info.supportedFeatures.add(ImsFeature.EMERGENCY_MMTEL);
+ info.controllerFactory = controllerFactory;
+
+ // we will allow the manifest method of declaring manifest features in two cases:
+ // 1) it is the device overlay "default" ImsService, where the features do not
+ // change (the new method can still be used if the default does not define manifest
+ // entries).
+ // 2) using the "compat" ImsService, which only supports manifest query.
+ if (isDeviceService(info)
+ || mImsServiceControllerFactoryCompat == controllerFactory) {
+ if (serviceInfo.metaData != null) {
+ if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
+ false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ }
+ if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
+ }
+ if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_RCS);
+ }
}
- if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
- info.supportedFeatures.add(ImsFeature.MMTEL);
+ // Only dynamic query if we are not a compat version of ImsService and the
+ // default service.
+ if (mImsServiceControllerFactoryCompat != controllerFactory
+ && info.getSupportedFeatures().isEmpty()) {
+ // metadata empty, try dynamic query instead
+ info.featureFromMetadata = false;
}
- if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
- info.supportedFeatures.add(ImsFeature.RCS);
- }
+ } else {
+ // We are a carrier service and not using the compat version of ImsService.
+ info.featureFromMetadata = false;
}
+ Log.i(TAG, "service name: " + info.name + ", manifest query: "
+ + info.featureFromMetadata);
// Check manifest permission to be sure that the service declares the correct
- // permissions.
- if (TextUtils.equals(serviceInfo.permission,
- Manifest.permission.BIND_IMS_SERVICE)) {
- Log.d(TAG, "ImsService added to cache: " + info.name + " with features: "
- + info.supportedFeatures);
+ // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
+ // true.
+ // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
+ if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
+ || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
infos.add(info);
} else {
- Log.w(TAG, "ImsService does not have BIND_IMS_SERVICE permission: "
+ Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
+ info.name);
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 5257dad..60f11c6 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -24,34 +24,42 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.Log;
-import android.util.Pair;
import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ExponentialBackoff;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
* ImsService will support.
*
- * When the ImsService is first bound, {@link IImsServiceController#createImsFeature} will be
- * called
+ * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and
+ * {@link ImsService#createRcsFeature(int)} will be called
* on each feature that the service supports. For each ImsFeature that is created,
* {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the
* listener that the ImsService now supports that feature.
*
* When {@link #changeImsServiceFeatures} is called with a set of features that is different from
- * the original set, {@link IImsServiceController#createImsFeature} and
- * {@link IImsServiceController#removeImsFeature} will be called for each feature that is
- * created/removed.
+ * the original set, create and {@link IImsServiceController#removeImsFeature} will be called for
+ * each feature that is created/removed.
*/
public class ImsServiceController {
@@ -66,7 +74,11 @@
@Override
public void binderDied() {
Log.e(LOG_TAG, "ImsService(" + mComponentName + ") died. Restarting...");
- notifyAllFeaturesRemoved();
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
+ }
+ cleanupAllFeatures();
cleanUpService();
startDelayedRebindToService();
}
@@ -76,10 +88,10 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ mBackoff.stop();
synchronized (mLock) {
mIsBound = true;
mIsBinding = false;
- grantPermissionsToService();
Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
+ service);
if (service != null) {
@@ -87,9 +99,10 @@
try {
service.linkToDeath(mImsDeathRecipient, 0);
mImsServiceControllerBinder = service;
- mIImsServiceController = IImsServiceController.Stub.asInterface(service);
+ setServiceController(service);
+ notifyImsServiceReady();
// create all associated features in the ImsService
- for (Pair<Integer, Integer> i : mImsFeatures) {
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
addImsServiceFeature(i);
}
} catch (RemoteException e) {
@@ -111,29 +124,61 @@
synchronized (mLock) {
mIsBinding = false;
}
- if (mIImsServiceController != null) {
- mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
+ cleanupConnection();
+ Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting...");
+ // Service disconnected, but we are still technically bound. Waiting for reconnect.
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
}
- notifyAllFeaturesRemoved();
- cleanUpService();
- Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Rebinding...");
+ cleanupConnection();
+ Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
startDelayedRebindToService();
}
+
+ private void cleanupConnection() {
+ if (isServiceControllerAvailable()) {
+ mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
+ }
+ cleanupAllFeatures();
+ cleanUpService();
+ }
}
+ private ImsService.Listener mFeatureChangedListener = new ImsService.Listener() {
+ @Override
+ public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.imsServiceFeaturesChanged(c, ImsServiceController.this);
+ }
+ };
+
/**
* Defines callbacks that are used by the ImsServiceController to notify when an ImsService
* has created or removed a new feature as well as the associated ImsServiceController.
*/
public interface ImsServiceControllerCallbacks {
/**
- * Called by ImsServiceController when a new feature has been created.
+ * Called by ImsServiceController when a new MMTEL or RCS feature has been created.
*/
void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
/**
- * Called by ImsServiceController when a new feature has been removed.
+ * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
*/
void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
+
+ /**
+ * Called by the ImsServiceController when the ImsService has notified the framework that
+ * its features have changed.
+ */
+ void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+ ImsServiceController controller);
}
/**
@@ -142,35 +187,81 @@
@VisibleForTesting
public interface RebindRetry {
/**
- * Return a long in ms indiciating how long the ImsServiceController should wait before
- * rebinding.
+ * Returns a long in ms indicating how long the ImsServiceController should wait before
+ * rebinding for the first time.
*/
- long getRetryTimeout();
+ long getStartDelay();
+
+ /**
+ * Returns a long in ms indicating the maximum time the ImsServiceController should wait
+ * before rebinding.
+ */
+ long getMaximumDelay();
}
private static final String LOG_TAG = "ImsServiceController";
- private static final int REBIND_RETRY_TIME = 5000;
- private final Context mContext;
+ private static final int REBIND_START_DELAY_MS = 2 * 1000; // 2 seconds
+ private static final int REBIND_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
private final ComponentName mComponentName;
- private final Object mLock = new Object();
private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
private final IPackageManager mPackageManager;
private ImsServiceControllerCallbacks mCallbacks;
- private Handler mHandler;
+ private ExponentialBackoff mBackoff;
private boolean mIsBound = false;
private boolean mIsBinding = false;
// Set of a pair of slotId->feature
- private HashSet<Pair<Integer, Integer>> mImsFeatures;
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
+ // Binder interfaces to the features set in mImsFeatures;
+ private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>();
private IImsServiceController mIImsServiceController;
- // Easier for testing.
private IBinder mImsServiceControllerBinder;
private ImsServiceConnection mImsServiceConnection;
private ImsDeathRecipient mImsDeathRecipient;
- private Set<IImsServiceFeatureListener> mImsStatusCallbacks = new HashSet<>();
+ private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = ConcurrentHashMap.newKeySet();
// Only added or removed, never accessed on purpose.
private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
+ protected final Object mLock = new Object();
+ protected final Context mContext;
+
+ private class ImsFeatureContainer {
+ public int slotId;
+ public int featureType;
+ private IInterface mBinder;
+
+ ImsFeatureContainer(int slotId, int featureType, IInterface binder) {
+ this.slotId = slotId;
+ this.featureType = featureType;
+ this.mBinder = binder;
+ }
+
+ // Casts the IInterface into the binder class we are looking for.
+ public <T extends IInterface> T resolve(Class<T> className) {
+ return className.cast(mBinder);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ImsFeatureContainer that = (ImsFeatureContainer) o;
+
+ if (slotId != that.slotId) return false;
+ if (featureType != that.featureType) return false;
+ return mBinder != null ? mBinder.equals(that.mBinder) : that.mBinder == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = slotId;
+ result = 31 * result + featureType;
+ result = 31 * result + (mBinder != null ? mBinder.hashCode() : 0);
+ return result;
+ }
+ }
+
/**
* Container class for the IImsFeatureStatusCallback callback implementation. This class is
* never used directly, but we need to keep track of the IImsFeatureStatusCallback
@@ -213,17 +304,17 @@
}
};
- private RebindRetry mRebindRetry = () -> REBIND_RETRY_TIME;
+ private RebindRetry mRebindRetry = new RebindRetry() {
+ @Override
+ public long getStartDelay() {
+ return REBIND_START_DELAY_MS;
+ }
- @VisibleForTesting
- public void setRebindRetryTime(RebindRetry retry) {
- mRebindRetry = retry;
- }
-
- @VisibleForTesting
- public Handler getHandler() {
- return mHandler;
- }
+ @Override
+ public long getMaximumDelay() {
+ return REBIND_MAXIMUM_DELAY_MS;
+ }
+ };
public ImsServiceController(Context context, ComponentName componentName,
ImsServiceControllerCallbacks callbacks) {
@@ -231,7 +322,12 @@
mComponentName = componentName;
mCallbacks = callbacks;
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mBackoff = new ExponentialBackoff(
+ mRebindRetry.getStartDelay(),
+ mRebindRetry.getMaximumDelay(),
+ 2, /* multiplier */
+ mHandlerThread.getLooper(),
+ mRestartImsServiceRunnable);
mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
@@ -239,42 +335,53 @@
// Creating a new HandlerThread and background handler for each test causes a segfault, so for
// testing, use a handler supplied by the testing system.
public ImsServiceController(Context context, ComponentName componentName,
- ImsServiceControllerCallbacks callbacks, Handler testHandler) {
+ ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry) {
mContext = context;
mComponentName = componentName;
mCallbacks = callbacks;
- mHandler = testHandler;
+ mBackoff = new ExponentialBackoff(
+ rebindRetry.getStartDelay(),
+ rebindRetry.getMaximumDelay(),
+ 2, /* multiplier */
+ handler,
+ mRestartImsServiceRunnable);
mPackageManager = null;
}
/**
- * Sends request to bind to ImsService designated by the {@ComponentName} with the feature set
- * imsFeatureSet
+ * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature
+ * set imsFeatureSet.
*
* @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be
* created once the service is bound.
* @return {@link true} if the service is in the process of being bound, {@link false} if it
* has failed.
*/
- public boolean bind(HashSet<Pair<Integer, Integer>> imsFeatureSet) {
+ public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) {
synchronized (mLock) {
- // Remove pending rebind retry
- mHandler.removeCallbacks(mRestartImsServiceRunnable);
if (!mIsBound && !mIsBinding) {
mIsBinding = true;
mImsFeatures = imsFeatureSet;
- Intent imsServiceIntent = new Intent(ImsResolver.SERVICE_INTERFACE).setComponent(
+ grantPermissionsToService();
+ Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
mComponentName);
mImsServiceConnection = new ImsServiceConnection();
int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_IMPORTANT;
Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
try {
- return mContext.bindService(imsServiceIntent, mImsServiceConnection,
- serviceFlags);
+ boolean bindSucceeded = startBindToService(imsServiceIntent,
+ mImsServiceConnection, serviceFlags);
+ if (!bindSucceeded) {
+ mIsBinding = false;
+ mBackoff.notifyFailed();
+ }
+ return bindSucceeded;
} catch (Exception e) {
+ mBackoff.notifyFailed();
Log.e(LOG_TAG, "Error binding (" + mComponentName + ") with exception: "
- + e.getMessage());
+ + e.getMessage() + ", rebinding in " + mBackoff.getCurrentDelay()
+ + " ms");
return false;
}
} else {
@@ -284,19 +391,27 @@
}
/**
+ * Starts the bind to the ImsService. Overridden by subclasses that need to access the service
+ * in a different fashion.
+ */
+ protected boolean startBindToService(Intent intent, ImsServiceConnection connection,
+ int flags) {
+ return mContext.bindService(intent, connection, flags);
+ }
+
+ /**
* Calls {@link IImsServiceController#removeImsFeature} on all features that the
* ImsService supports and then unbinds the service.
*/
public void unbind() throws RemoteException {
synchronized (mLock) {
- // Remove pending rebind retry
- mHandler.removeCallbacks(mRestartImsServiceRunnable);
+ mBackoff.stop();
if (mImsServiceConnection == null || mImsDeathRecipient == null) {
return;
}
// Clean up all features
changeImsServiceFeatures(new HashSet<>());
- removeImsServiceFeatureListener();
+ removeImsServiceFeatureCallbacks();
mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
mContext.unbindService(mImsServiceConnection);
@@ -305,31 +420,35 @@
}
/**
- * Finds the difference between the set of features that the ImsService has active and the new
- * set defined in newImsFeatures. For every feature that is added,
- * {@link IImsServiceController#createImsFeature} is called on the service. For every ImsFeature
- * that is removed, {@link IImsServiceController#removeImsFeature} is called.
+ * For every feature that is added, the service calls the associated create. For every
+ * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called.
*/
- public void changeImsServiceFeatures(HashSet<Pair<Integer, Integer>> newImsFeatures)
+ public void changeImsServiceFeatures(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures)
throws RemoteException {
synchronized (mLock) {
+ Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for "
+ + "ImsService: " + mComponentName);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures =
+ new HashSet<>(mImsFeatures);
+ // Set features first in case we lose binding and need to rebind later.
+ mImsFeatures = newImsFeatures;
if (mIsBound) {
// add features to service.
- HashSet<Pair<Integer, Integer>> newFeatures = new HashSet<>(newImsFeatures);
- newFeatures.removeAll(mImsFeatures);
- for (Pair<Integer, Integer> i : newFeatures) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures =
+ new HashSet<>(mImsFeatures);
+ newFeatures.removeAll(oldImsFeatures);
+ for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) {
addImsServiceFeature(i);
}
// remove old features
- HashSet<Pair<Integer, Integer>> oldFeatures = new HashSet<>(mImsFeatures);
- oldFeatures.removeAll(newImsFeatures);
- for (Pair<Integer, Integer> i : oldFeatures) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures =
+ new HashSet<>(oldImsFeatures);
+ oldFeatures.removeAll(mImsFeatures);
+ for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) {
removeImsServiceFeature(i);
}
}
- Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for "
- + "ImsService: " + mComponentName);
- mImsFeatures = newImsFeatures;
}
}
@@ -343,6 +462,11 @@
return mImsServiceControllerBinder;
}
+ @VisibleForTesting
+ public long getRebindDelay() {
+ return mBackoff.getCurrentDelay();
+ }
+
public ComponentName getComponentName() {
return mComponentName;
}
@@ -350,23 +474,145 @@
/**
* Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle.
*/
- public void addImsServiceFeatureListener(IImsServiceFeatureListener callback) {
+ public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
+ mImsStatusCallbacks.add(callback);
synchronized (mLock) {
- mImsStatusCallbacks.add(callback);
+ if (mImsFeatures == null || mImsFeatures.isEmpty()) {
+ return;
+ }
+ // notify the new status callback of the features that are available.
+ try {
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
+ callback.imsFeatureCreated(i.slotId, i.featureType);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
+ }
}
}
- private void removeImsServiceFeatureListener() {
- synchronized (mLock) {
- mImsStatusCallbacks.clear();
+ public void enableIms(int slotId) {
+ try {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ mIImsServiceController.enableIms(slotId);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
}
}
+ public void disableIms(int slotId) {
+ try {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ mIImsServiceController.disableIms(slotId);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Return the {@Link MMTelFeature} binder on the slot associated with the slotId.
+ * Used for normal calling.
+ */
+ public IImsMmTelFeature getMmTelFeature(int slotId) {
+ synchronized (mLock) {
+ ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_MMTEL);
+ if (f == null) {
+ Log.w(LOG_TAG, "Requested null MMTelFeature on slot " + slotId);
+ return null;
+ }
+ return f.resolve(IImsMmTelFeature.class);
+ }
+ }
+
+ /**
+ * Return the {@Link RcsFeature} binder on the slot associated with the slotId.
+ */
+ public IImsRcsFeature getRcsFeature(int slotId) {
+ synchronized (mLock) {
+ ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_RCS);
+ if (f == null) {
+ Log.w(LOG_TAG, "Requested null RcsFeature on slot " + slotId);
+ return null;
+ }
+ return f.resolve(IImsRcsFeature.class);
+ }
+ }
+
+ /**
+ * @return the IImsRegistration that corresponds to the slot id specified.
+ */
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ synchronized (mLock) {
+ return isServiceControllerAvailable()
+ ? mIImsServiceController.getRegistration(slotId) : null;
+ }
+ }
+
+ /**
+ * @return the IImsConfig that corresponds to the slot id specified.
+ */
+ public IImsConfig getConfig(int slotId) throws RemoteException {
+ synchronized (mLock) {
+ return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null;
+ }
+ }
+
+ /**
+ * notify the ImsService that the ImsService is ready for feature creation.
+ */
+ protected void notifyImsServiceReady() throws RemoteException {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ Log.d(LOG_TAG, "notifyImsServiceReady");
+ mIImsServiceController.setListener(mFeatureChangedListener);
+ mIImsServiceController.notifyImsServiceReadyForFeatureCreation();
+ }
+ }
+ }
+
+ protected String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ /**
+ * Sets the IImsServiceController instance. Overridden by compat layers to set compatibility
+ * versions of this service controller.
+ */
+ protected void setServiceController(IBinder serviceController) {
+ mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+ }
+
+ /**
+ * @return true if the controller is currently bound.
+ */
+ public boolean isBound() {
+ synchronized (mLock) {
+ return mIsBound;
+ }
+ }
+
+ /**
+ * Check to see if the service controller is available, overridden for compat versions,
+ * @return true if available, false otherwise;
+ */
+ protected boolean isServiceControllerAvailable() {
+ return mIImsServiceController != null;
+ }
+
+ @VisibleForTesting
+ public void removeImsServiceFeatureCallbacks() {
+ mImsStatusCallbacks.clear();
+ }
+
// Only add a new rebind if there are no pending rebinds waiting.
private void startDelayedRebindToService() {
- if (!mHandler.hasCallbacks(mRestartImsServiceRunnable)) {
- mHandler.postDelayed(mRestartImsServiceRunnable, mRebindRetry.getRetryTimeout());
- }
+ mBackoff.start();
}
// Grant runtime permissions to ImsService. PackageManager ensures that the ImsService is
@@ -385,108 +631,164 @@
}
private void sendImsFeatureCreatedCallback(int slot, int feature) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureListener callbacks = i.next();
- try {
- callbacks.imsFeatureCreated(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsFeatureCreated(slot, feature);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
private void sendImsFeatureRemovedCallback(int slot, int feature) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureListener callbacks = i.next();
- try {
- callbacks.imsFeatureRemoved(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsFeatureRemoved(slot, feature);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
private void sendImsFeatureStatusChanged(int slot, int feature, int status) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureListener callbacks = i.next();
- try {
- callbacks.imsStatusChanged(slot, feature, status);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsStatusChanged(slot, feature, status);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
// This method should only be called when synchronized on mLock
- private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException {
- if (mIImsServiceController == null || mCallbacks == null) {
+ private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair)
+ throws RemoteException {
+ if (!isServiceControllerAvailable() || mCallbacks == null) {
Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
return;
}
- ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.first,
- featurePair.second);
- mFeatureStatusCallbacks.add(c);
- mIImsServiceController.createImsFeature(featurePair.first, featurePair.second,
- c.getCallback());
- // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
- mCallbacks.imsServiceFeatureCreated(featurePair.first, featurePair.second, this);
- // Send callback to ImsServiceProxy to change supported ImsFeatures
- sendImsFeatureCreatedCallback(featurePair.first, featurePair.second);
+ if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
+ ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId,
+ featurePair.featureType);
+ mFeatureStatusCallbacks.add(c);
+ IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType,
+ c.getCallback());
+ addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f);
+ // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+ mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
+ } else {
+ // Don't update ImsService for emergency MMTEL feature.
+ Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
+ }
+ // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency
+ // MMTEL state.
+ sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType);
}
// This method should only be called when synchronized on mLock
- private void removeImsServiceFeature(Pair<Integer, Integer> featurePair)
- throws RemoteException {
- if (mIImsServiceController == null || mCallbacks == null) {
+ private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) {
+ if (!isServiceControllerAvailable() || mCallbacks == null) {
Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
return;
}
- ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
- c.mSlotId == featurePair.first && c.mFeatureType == featurePair.second)
- .findFirst().orElse(null);
- // Remove status callbacks from list.
- if (callbackToRemove != null) {
- mFeatureStatusCallbacks.remove(callbackToRemove);
+ if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
+ ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
+ c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType)
+ .findFirst().orElse(null);
+ // Remove status callbacks from list.
+ if (callbackToRemove != null) {
+ mFeatureStatusCallbacks.remove(callbackToRemove);
+ }
+ removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
+ // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+ mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
+ try {
+ removeImsFeature(featurePair.slotId, featurePair.featureType,
+ (callbackToRemove != null ? callbackToRemove.getCallback() : null));
+ } catch (RemoteException e) {
+ // The connection to this ImsService doesn't exist. This may happen if the service
+ // has died and we are removing features.
+ Log.i(LOG_TAG, "Couldn't remove feature {" + featurePair.featureType
+ + "}, connection is down: " + e.getMessage());
+ }
+ } else {
+ // Don't update ImsService for emergency MMTEL feature.
+ Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId);
}
- mIImsServiceController.removeImsFeature(featurePair.first, featurePair.second,
- (callbackToRemove != null ? callbackToRemove.getCallback() : null));
- // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
- mCallbacks.imsServiceFeatureRemoved(featurePair.first, featurePair.second, this);
// Send callback to ImsServiceProxy to change supported ImsFeatures
// Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an
// ImsManager requests the ImsService while it is being removed in ImsResolver, this
// callback will clean it up after.
- sendImsFeatureRemovedCallback(featurePair.first, featurePair.second);
+ sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType);
}
- private void notifyAllFeaturesRemoved() {
- if (mCallbacks == null) {
- Log.w(LOG_TAG, "notifyAllFeaturesRemoved called with invalid callbacks.");
- return;
- }
- synchronized (mLock) {
- for (Pair<Integer, Integer> feature : mImsFeatures) {
- mCallbacks.imsServiceFeatureRemoved(feature.first, feature.second, this);
- sendImsFeatureRemovedCallback(feature.first, feature.second);
+ // This method should only be called when already synchronized on mLock.
+ // overridden by compat layer to create features
+ protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ switch (featureType) {
+ case ImsFeature.FEATURE_MMTEL: {
+ return mIImsServiceController.createMmTelFeature(slotId, c);
}
+ case ImsFeature.FEATURE_RCS: {
+ return mIImsServiceController.createRcsFeature(slotId, c);
+ }
+ default:
+ return null;
+ }
+ }
+
+ // overridden by compat layer to remove features
+ protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ mIImsServiceController.removeImsFeature(slotId, featureType, c);
+ }
+
+ // This method should only be called when synchronized on mLock
+ private void addImsFeatureBinder(int slotId, int featureType, IInterface b) {
+ mImsFeatureBinders.add(new ImsFeatureContainer(slotId, featureType, b));
+ }
+
+ // This method should only be called when synchronized on mLock
+ private void removeImsFeatureBinder(int slotId, int featureType) {
+ ImsFeatureContainer container = mImsFeatureBinders.stream()
+ .filter(f-> (f.slotId == slotId && f.featureType == featureType))
+ .findFirst().orElse(null);
+ if (container != null) {
+ mImsFeatureBinders.remove(container);
+ }
+ }
+
+ private ImsFeatureContainer getImsFeatureContainer(int slotId, int featureType) {
+ return mImsFeatureBinders.stream()
+ .filter(f-> (f.slotId == slotId && f.featureType == featureType))
+ .findFirst().orElse(null);
+ }
+
+ private void cleanupAllFeatures() {
+ synchronized (mLock) {
+ // Remove all features and clean up all associated Binders.
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
+ removeImsServiceFeature(i);
+ }
+ // remove all MmTelFeatureConnection callbacks, since we have already sent removed
+ // callback.
+ removeImsServiceFeatureCallbacks();
}
}
@@ -495,8 +797,7 @@
mImsDeathRecipient = null;
mImsServiceConnection = null;
mImsServiceControllerBinder = null;
- mIImsServiceController = null;
- mIsBound = false;
+ setServiceController(null);
}
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
new file mode 100644
index 0000000..4e0aea0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -0,0 +1,202 @@
+/*
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.compat.ImsService;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsServiceController;
+
+/**
+ * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
+ * ImsService will support.
+ *
+ * Compatibility interface for interacting with older implementations of ImsService. The older
+ * ImsService implementation is contained within the android.telephony.ims.compat.* namspace.
+ * Newer implementations of ImsService should use the current APIs contained in
+ * android.telephony.ims.compat.*.
+ */
+public class ImsServiceControllerCompat extends ImsServiceController {
+
+ private static final String TAG = "ImsSCCompat";
+
+ private IImsServiceController mServiceController;
+
+ private final SparseArray<MmTelFeatureCompatAdapter> mMmTelCompatAdapters = new SparseArray<>();
+ private final SparseArray<ImsConfigCompatAdapter> mConfigCompatAdapters = new SparseArray<>();
+ private final SparseArray<ImsRegistrationCompatAdapter> mRegCompatAdapters =
+ new SparseArray<>();
+
+ public ImsServiceControllerCompat(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ super(context, componentName, callbacks);
+ }
+
+ @Override
+ protected final String getServiceInterface() {
+ // Return compatibility version of String.
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ /**
+ * Converts the new command to {@link MMTelFeature#turnOnIms()}.
+ */
+ @Override
+ public final void enableIms(int slotId) {
+ MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
+ if (adapter == null) {
+ Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
+ return;
+ }
+ try {
+ adapter.enableIms();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Couldn't enable IMS: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Converts the new command to {@link MMTelFeature#turnOffIms()}.
+ */
+ @Override
+ public final void disableIms(int slotId) {
+ MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
+ if (adapter == null) {
+ Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
+ return;
+ }
+ try {
+ adapter.disableIms();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Couldn't enable IMS: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @return the IImsRegistration that corresponds to the slot id specified.
+ */
+ @Override
+ public final IImsRegistration getRegistration(int slotId) {
+ ImsRegistrationCompatAdapter adapter = mRegCompatAdapters.get(slotId);
+ if (adapter == null) {
+ Log.w(TAG, "getRegistration: Registration does not exist for slot " + slotId);
+ return null;
+ }
+ return adapter.getBinder();
+ }
+
+ /**
+ * @return the IImsConfig that corresponds to the slot id specified.
+ */
+ @Override
+ public final IImsConfig getConfig(int slotId) {
+ ImsConfigCompatAdapter adapter = mConfigCompatAdapters.get(slotId);
+ if (adapter == null) {
+ Log.w(TAG, "getConfig: Config does not exist for slot " + slotId);
+ return null;
+ }
+ return adapter.getIImsConfig();
+ }
+
+ @Override
+ protected final void notifyImsServiceReady() {
+ Log.d(TAG, "notifyImsServiceReady");
+ // don't do anything for compat impl.
+ }
+
+ @Override
+ protected final IInterface createImsFeature(int slotId, int featureType,
+ IImsFeatureStatusCallback c)
+ throws RemoteException {
+ switch (featureType) {
+ case ImsFeature.MMTEL: {
+ return createMMTelCompat(slotId, c);
+ }
+ case ImsFeature.RCS: {
+ return createRcsFeature(slotId, c);
+ }
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected final void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ if (featureType == ImsFeature.MMTEL) {
+ mMmTelCompatAdapters.remove(slotId);
+ mRegCompatAdapters.remove(slotId);
+ mConfigCompatAdapters.remove(slotId);
+ }
+ if (mServiceController != null) {
+ mServiceController.removeImsFeature(slotId, featureType, c);
+ }
+ }
+
+ @Override
+ protected void setServiceController(IBinder serviceController) {
+ mServiceController = IImsServiceController.Stub.asInterface(serviceController);
+ }
+
+ @Override
+ protected boolean isServiceControllerAvailable() {
+ return mServiceController != null;
+ }
+
+ protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ IImsMMTelFeature feature = mServiceController.createMMTelFeature(slotId, c);
+ if (feature == null) {
+ Log.w(TAG, "createMMTelCompat: createMMTelFeature returned null.");
+ return null;
+ }
+ return new MmTelInterfaceAdapter(slotId, feature.asBinder());
+ }
+
+ private IImsMmTelFeature createMMTelCompat(int slotId, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId, c);
+ MmTelFeatureCompatAdapter mmTelAdapter = new MmTelFeatureCompatAdapter(mContext, slotId,
+ interfaceAdapter);
+ mMmTelCompatAdapters.put(slotId, mmTelAdapter);
+ ImsRegistrationCompatAdapter regAdapter = new ImsRegistrationCompatAdapter();
+ mmTelAdapter.addRegistrationAdapter(regAdapter);
+ mRegCompatAdapters.put(slotId, regAdapter);
+ mConfigCompatAdapters.put(slotId, new ImsConfigCompatAdapter(
+ mmTelAdapter.getOldConfigInterface()));
+ return mmTelAdapter.getBinder();
+ }
+
+ private IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ // Return non-null if there is a custom RCS implementation that needs a compatability layer.
+ return null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
new file mode 100644
index 0000000..e39aa52
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
@@ -0,0 +1,81 @@
+/*
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsService;
+
+/**
+ * A compat layer for communicating with older devices that still used the ServiceManager to get
+ * the ImsService.
+ */
+
+public class ImsServiceControllerStaticCompat extends ImsServiceControllerCompat {
+
+ private static final String TAG = "ImsSCStaticCompat";
+
+ private static final String IMS_SERVICE_NAME = "ims";
+
+ private IImsService mImsServiceCompat = null;
+
+ public ImsServiceControllerStaticCompat(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ super(context, componentName, callbacks);
+ }
+
+ @Override
+ public boolean startBindToService(Intent intent, ImsServiceConnection connection, int flags) {
+ IBinder binder = ServiceManager.checkService(IMS_SERVICE_NAME);
+
+ if (binder == null) {
+ return false;
+ }
+ // This is a little hacky, but we are going to call the onServiceConnected to "pretend" like
+ // bindService has completed here, which will pass the binder to setServiceController and
+ // set up all supporting structures.
+ connection.onServiceConnected(new ComponentName(mContext,
+ ImsServiceControllerStaticCompat.class), binder);
+ return true;
+ }
+
+ @Override
+ protected void setServiceController(IBinder serviceController) {
+ mImsServiceCompat = IImsService.Stub.asInterface(serviceController);
+ }
+
+ @Override
+ // used for add/remove features and cleanup in ImsServiceController.
+ protected boolean isServiceControllerAvailable() {
+ return mImsServiceCompat != null;
+ }
+
+ @Override
+ protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c) {
+ if (mImsServiceCompat == null) {
+ Log.w(TAG, "getInterface: IImsService returned null.");
+ return null;
+ }
+ return new ImsServiceInterfaceAdapter(slotId, mImsServiceCompat.asBinder());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
new file mode 100644
index 0000000..24ec0be
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -0,0 +1,160 @@
+/*
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures
+ * they support.
+ */
+
+public class ImsServiceFeatureQueryManager {
+
+ private final class ImsServiceFeatureQuery implements ServiceConnection {
+
+ private static final String LOG_TAG = "ImsServiceFeatureQuery";
+
+ private final ComponentName mName;
+ private final String mIntentFilter;
+
+ ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
+ mName = name;
+ mIntentFilter = intentFilter;
+ }
+
+ /**
+ * Starts the bind to the ImsService specified ComponentName.
+ * @return true if binding started, false if it failed and will not recover.
+ */
+ public boolean start() {
+ Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName);
+ Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName);
+ int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_IMPORTANT;
+ boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags);
+ if (!bindStarted) {
+ // Docs say to unbind if this fails.
+ cleanup();
+ }
+ return bindStarted;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(LOG_TAG, "onServiceConnected for component: " + name);
+ if (service != null) {
+ queryImsFeatures(IImsServiceController.Stub.asInterface(service));
+ } else {
+ Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up.");
+ cleanup();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.w(LOG_TAG, "onServiceDisconnected for component: " + name);
+ }
+
+ private void queryImsFeatures(IImsServiceController controller) {
+ ImsFeatureConfiguration config;
+ try {
+ config = controller.querySupportedImsFeatures();
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "queryImsFeatures - error: " + e);
+ cleanup();
+ mListener.onError(mName);
+ return;
+ }
+ Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures();
+ // Complete, remove from active queries and notify.
+ cleanup();
+ mListener.onComplete(mName, servicePairs);
+ }
+
+ private void cleanup() {
+ mContext.unbindService(this);
+ synchronized (mLock) {
+ mActiveQueries.remove(mName);
+ }
+ }
+ }
+
+ public interface Listener {
+ /**
+ * Called when a query has completed.
+ * @param name The Package Name of the query
+ * @param features A Set of slotid->feature pairs that the ImsService supports.
+ */
+ void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features);
+
+ /**
+ * Called when a query has failed and should be retried.
+ */
+ void onError(ComponentName name);
+ }
+
+ // Maps an active ImsService query (by Package Name String) its query.
+ private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>();
+ private final Context mContext;
+ private final Listener mListener;
+ private final Object mLock = new Object();
+
+ public ImsServiceFeatureQueryManager(Context context, Listener listener) {
+ mContext = context;
+ mListener = listener;
+ }
+
+ /**
+ * Starts an ImsService feature query for the ComponentName and Intent specified.
+ * @param name The ComponentName of the ImsService being queried.
+ * @param intentFilter The Intent filter that the ImsService specified.
+ * @return true if the query started, false if it was unable to start.
+ */
+ public boolean startQuery(ComponentName name, String intentFilter) {
+ synchronized (mLock) {
+ if (mActiveQueries.containsKey(name)) {
+ // We already have an active query, wait for it to return.
+ return true;
+ }
+ ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter);
+ mActiveQueries.put(name, query);
+ return query.start();
+ }
+ }
+
+ /**
+ * @return true if there are any active queries, false if the manager is idle.
+ */
+ public boolean isQueryInProgress() {
+ synchronized (mLock) {
+ return !mActiveQueries.isEmpty();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java b/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
new file mode 100644
index 0000000..f554e6f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.compat.feature.ImsFeature;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Compatibility layer for IImsService implementations of IMS. Converts "generic" MMTel commands
+ * to implementation.
+ */
+
+public class ImsServiceInterfaceAdapter extends MmTelInterfaceAdapter {
+
+ private static final int SERVICE_ID = ImsFeature.MMTEL;
+
+ public ImsServiceInterfaceAdapter(int slotId, IBinder binder) {
+ super(slotId, binder);
+ }
+
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ return getInterface().open(mSlotId, ImsFeature.MMTEL, incomingCallIntent, listener);
+ }
+
+ public void endSession(int sessionId) throws RemoteException {
+ getInterface().close(sessionId);
+ }
+
+ public boolean isConnected(int callSessionType, int callType) throws RemoteException {
+ return getInterface().isConnected(SERVICE_ID, callSessionType, callType);
+ }
+
+ public boolean isOpened() throws RemoteException {
+ return getInterface().isOpened(SERVICE_ID);
+ }
+
+ public int getFeatureState() throws RemoteException {
+ return ImsFeature.STATE_READY;
+ }
+
+ public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException {
+ getInterface().addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
+ }
+
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ // Not Implemented in the old ImsService. If the registration listener becomes invalid, the
+ // ImsService will remove it.
+ }
+
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+ throws RemoteException {
+ return getInterface().createCallProfile(sessionId, callSessionType, callType);
+ }
+
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+ throws RemoteException {
+ return getInterface().createCallSession(sessionId, profile, null);
+ }
+
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ return getInterface().getPendingCallSession(sessionId, callId);
+ }
+
+ public IImsUt getUtInterface() throws RemoteException {
+ return getInterface().getUtInterface(SERVICE_ID);
+ }
+
+ public IImsConfig getConfigInterface() throws RemoteException {
+ return getInterface().getConfigInterface(mSlotId);
+ }
+
+ public void turnOnIms() throws RemoteException {
+ getInterface().turnOnIms(mSlotId);
+ }
+
+ public void turnOffIms() throws RemoteException {
+ getInterface().turnOffIms(mSlotId);
+ }
+
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ return getInterface().getEcbmInterface(SERVICE_ID);
+ }
+
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+ getInterface().setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
+ }
+
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ return getInterface().getMultiEndpointInterface(SERVICE_ID);
+ }
+
+ private IImsService getInterface() throws RemoteException {
+ IImsService feature = IImsService.Stub.asInterface(mBinder);
+ if (feature == null) {
+ throw new RemoteException("Binder not Available");
+ }
+ return feature;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
new file mode 100644
index 0000000..7b0619b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -0,0 +1,551 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.ImsConfigListener;
+import com.android.ims.ImsManager;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MmTelFeatureCompatAdapter extends MmTelFeature {
+
+ private static final String TAG = "MmTelFeatureCompat";
+
+ public static final String ACTION_IMS_INCOMING_CALL = "com.android.ims.IMS_INCOMING_CALL";
+
+ private static final int WAIT_TIMEOUT_MS = 2000;
+
+ private final MmTelInterfaceAdapter mCompatFeature;
+ private ImsRegistrationCompatAdapter mRegCompatAdapter;
+ private int mSessionId = -1;
+
+ private static final Map<Integer, Integer> REG_TECH_TO_NET_TYPE = new HashMap<>(2);
+
+ static {
+ REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ TelephonyManager.NETWORK_TYPE_IWLAN);
+ }
+
+ // Feature Type for compatibility with old "feature" updates
+ public static final int FEATURE_TYPE_UNKNOWN = -1;
+ public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
+ public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
+ public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
+ public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
+ public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
+ public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
+
+ public static final int FEATURE_UNKNOWN = -1;
+ public static final int FEATURE_DISABLED = 0;
+ public static final int FEATURE_ENABLED = 1;
+
+ private static class ConfigListener extends ImsConfigListener.Stub {
+
+ private final int mCapability;
+ private final int mTech;
+ private final CountDownLatch mLatch;
+
+ public ConfigListener(int capability, int tech, CountDownLatch latch) {
+ mCapability = capability;
+ mTech = tech;
+ mLatch = latch;
+ }
+
+ @Override
+ public void onGetFeatureResponse(int feature, int network, int value, int status)
+ throws RemoteException {
+ if (feature == mCapability && network == mTech) {
+ mLatch.countDown();
+ getFeatureValueReceived(value);
+ } else {
+ Log.i(TAG, "onGetFeatureResponse: response different than requested: feature="
+ + feature + " and network=" + network);
+ }
+ }
+
+ @Override
+ public void onSetFeatureResponse(int feature, int network, int value, int status)
+ throws RemoteException {
+ if (feature == mCapability && network == mTech) {
+ mLatch.countDown();
+ setFeatureValueReceived(value);
+ } else {
+ Log.i(TAG, "onSetFeatureResponse: response different than requested: feature="
+ + feature + " and network=" + network);
+ }
+ }
+
+ @Override
+ public void onGetVideoQuality(int status, int quality) throws RemoteException {
+ }
+
+ @Override
+ public void onSetVideoQuality(int status) throws RemoteException {
+ }
+
+ public void getFeatureValueReceived(int value) {
+ }
+
+ public void setFeatureValueReceived(int value) {
+ }
+ }
+
+ // Trampolines "old" listener events to the new interface.
+ private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() {
+ @Override
+ public void registrationConnected() throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationProgressing() throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+ // At de-registration, notify the framework that no IMS capabilities are currently
+ // available.
+ Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities.");
+ notifyCapabilitiesStatusChanged(new MmTelCapabilities());
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationResumed() throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationSuspended() throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationServiceCapabilityChanged(int serviceClass, int event)
+ throws RemoteException {
+ // Don't care
+ }
+
+ @Override
+ public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+ int[] disabledFeatures) throws RemoteException {
+ notifyCapabilitiesStatusChanged(convertCapabilities(enabledFeatures));
+ }
+
+ @Override
+ public void voiceMessageCountUpdate(int count) throws RemoteException {
+ notifyVoiceMessageCountUpdate(count);
+ }
+
+ @Override
+ public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+
+ @Override
+ public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ // Implemented in the Registration Adapter
+ }
+ };
+
+ /**
+ * Stub implementation of the "old" Registration listener interface that provides no
+ * functionality. Instead, it is used to ensure compatibility with older devices that require
+ * a listener on startSession. The actual Registration Listener Interface is added separately
+ * in ImsRegistration.
+ */
+ private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub {
+
+ @Override
+ public void registrationConnected() throws RemoteException {
+ }
+
+ @Override
+ public void registrationProgressing() throws RemoteException {
+ }
+
+ @Override
+ public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
+ }
+
+ @Override
+ public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
+ }
+
+ @Override
+ public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+ }
+
+ @Override
+ public void registrationResumed() throws RemoteException {
+ }
+
+ @Override
+ public void registrationSuspended() throws RemoteException {
+ }
+
+ @Override
+ public void registrationServiceCapabilityChanged(int serviceClass, int event)
+ throws RemoteException {
+ }
+
+ @Override
+ public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
+ int[] disabledFeatures) throws RemoteException {
+ }
+
+ @Override
+ public void voiceMessageCountUpdate(int count) throws RemoteException {
+ }
+
+ @Override
+ public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
+ }
+
+ @Override
+ public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ }
+ }
+
+ // Handle Incoming Call as PendingIntent, the old method
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "onReceive");
+ if (intent.getAction().equals(ACTION_IMS_INCOMING_CALL)) {
+ Log.i(TAG, "onReceive : incoming call intent.");
+
+ String callId = intent.getStringExtra("android:imsCallID");
+ try {
+ IImsCallSession session = mCompatFeature.getPendingCallSession(mSessionId,
+ callId);
+ notifyIncomingCallSession(session, intent.getExtras());
+ } catch (RemoteException e) {
+ Log.w(TAG, "onReceive: Couldn't get Incoming call session.");
+ }
+ }
+ }
+ };
+
+ public MmTelFeatureCompatAdapter(Context context, int slotId,
+ MmTelInterfaceAdapter compatFeature) {
+ initialize(context, slotId);
+ mCompatFeature = compatFeature;
+ }
+
+ @Override
+ public boolean queryCapabilityConfiguration(int capability, int radioTech) {
+ int capConverted = convertCapability(capability, radioTech);
+ // Wait for the result from the ImsService
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] returnValue = new int[1];
+ returnValue[0] = FEATURE_UNKNOWN;
+ int regTech = REG_TECH_TO_NET_TYPE.getOrDefault(radioTech,
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ try {
+ mCompatFeature.getConfigInterface().getFeatureValue(capConverted, regTech,
+ new ConfigListener(capConverted, regTech, latch) {
+ @Override
+ public void getFeatureValueReceived(int value) {
+ returnValue[0] = value;
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG, "queryCapabilityConfiguration");
+ }
+ try {
+ latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "queryCapabilityConfiguration - error waiting: " + e.getMessage());
+ }
+ return returnValue[0] == FEATURE_ENABLED;
+ }
+
+ @Override
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ if (request == null) {
+ return;
+ }
+ try {
+ IImsConfig imsConfig = mCompatFeature.getConfigInterface();
+ // Disable Capabilities
+ for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToDisable()) {
+ CountDownLatch latch = new CountDownLatch(1);
+ int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
+ int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
+ + radioTechConverted + " disabled");
+ imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_DISABLED,
+ new ConfigListener(capConverted, radioTechConverted, latch) {
+ @Override
+ public void setFeatureValueReceived(int value) {
+ if (value != FEATURE_DISABLED) {
+ if (c == null) {
+ return;
+ }
+ c.onChangeCapabilityConfigurationError(cap.getCapability(),
+ cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
+ }
+ Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived"
+ + " with value " + value);
+ }
+ });
+ latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ // Enable Capabilities
+ for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToEnable()) {
+ CountDownLatch latch = new CountDownLatch(1);
+ int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
+ int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
+ + radioTechConverted + " enabled");
+ imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_ENABLED,
+ new ConfigListener(capConverted, radioTechConverted, latch) {
+ @Override
+ public void setFeatureValueReceived(int value) {
+ if (value != FEATURE_ENABLED) {
+ if (c == null) {
+ return;
+ }
+ c.onChangeCapabilityConfigurationError(cap.getCapability(),
+ cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
+ }
+ Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived"
+ + " with value " + value);
+ }
+ });
+ latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ } catch (RemoteException | InterruptedException e) {
+ Log.w(TAG, "changeEnabledCapabilities: Error processing: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ try {
+ return mCompatFeature.createCallProfile(mSessionId, callSessionType, callType);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+ throws RemoteException {
+ return mCompatFeature.createCallSession(mSessionId, profile);
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ return mCompatFeature.getUtInterface();
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ return mCompatFeature.getEcbmInterface();
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ return mCompatFeature.getMultiEndpointInterface();
+ }
+
+ @Override
+ public int getFeatureState() {
+ try {
+ return mCompatFeature.getFeatureState();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ @Override
+ public void setUiTtyMode(int mode, Message onCompleteMessage) {
+ try {
+ mCompatFeature.setUiTTYMode(mode, onCompleteMessage);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+
+ @Override
+ public void onFeatureRemoved() {
+ mContext.unregisterReceiver(mReceiver);
+ try {
+ mCompatFeature.endSession(mSessionId);
+ mCompatFeature.removeRegistrationListener(mListener);
+ if (mRegCompatAdapter != null) {
+ mCompatFeature.removeRegistrationListener(
+ mRegCompatAdapter.getRegistrationListener());
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "onFeatureRemoved: Couldn't end session: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void onFeatureReady() {
+ Log.i(TAG, "onFeatureReady called!");
+ // This gets called when MmTelFeature.setListener is called. We need to use this time to
+ // call openSession on the old MMTelFeature implementation.
+ IntentFilter intentFilter = new IntentFilter(ImsManager.ACTION_IMS_INCOMING_CALL);
+ mContext.registerReceiver(mReceiver, intentFilter);
+ try {
+ mSessionId = mCompatFeature.startSession(createIncomingCallPendingIntent(),
+ new ImsRegistrationListenerBase());
+ mCompatFeature.addRegistrationListener(mListener);
+ mCompatFeature.addRegistrationListener(mRegCompatAdapter.getRegistrationListener());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't start compat feature: " + e.getMessage());
+ }
+ }
+
+ public void enableIms() throws RemoteException {
+ mCompatFeature.turnOnIms();
+ }
+
+ public void disableIms() throws RemoteException {
+ mCompatFeature.turnOffIms();
+ }
+
+ public IImsConfig getOldConfigInterface() {
+ try {
+ return mCompatFeature.getConfigInterface();
+ } catch (RemoteException e) {
+ Log.w(TAG, "getOldConfigInterface(): " + e.getMessage());
+ return null;
+ }
+ }
+
+ public void addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat)
+ throws RemoteException {
+ mRegCompatAdapter = regCompat;
+ }
+
+ private MmTelCapabilities convertCapabilities(int[] enabledFeatures) {
+ boolean[] featuresEnabled = new boolean[enabledFeatures.length];
+ for (int i = FEATURE_TYPE_VOICE_OVER_LTE; i <= FEATURE_TYPE_UT_OVER_WIFI
+ && i < enabledFeatures.length; i++) {
+ if (enabledFeatures[i] == i) {
+ featuresEnabled[i] = true;
+ } else if (enabledFeatures[i] == FEATURE_TYPE_UNKNOWN) {
+ // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
+ featuresEnabled[i] = false;
+ }
+ }
+ MmTelCapabilities capabilities = new MmTelCapabilities();
+ if (featuresEnabled[FEATURE_TYPE_VOICE_OVER_LTE]
+ || featuresEnabled[FEATURE_TYPE_VOICE_OVER_WIFI]) {
+ // voice is enabled
+ capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ }
+ if (featuresEnabled[FEATURE_TYPE_VIDEO_OVER_LTE]
+ || featuresEnabled[FEATURE_TYPE_VIDEO_OVER_WIFI]) {
+ // video is enabled
+ capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ }
+ if (featuresEnabled[FEATURE_TYPE_UT_OVER_LTE]
+ || featuresEnabled[FEATURE_TYPE_UT_OVER_WIFI]) {
+ // ut is enabled
+ capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_UT);
+ }
+ Log.i(TAG, "convertCapabilities - capabilities: " + capabilities);
+ return capabilities;
+ }
+
+ private PendingIntent createIncomingCallPendingIntent() {
+ Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
+ intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private int convertCapability(int capability, int radioTech) {
+ int capConverted = FEATURE_TYPE_UNKNOWN;
+ if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+ switch (capability) {
+ case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+ capConverted = FEATURE_TYPE_VOICE_OVER_LTE;
+ break;
+ case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+ capConverted = FEATURE_TYPE_VIDEO_OVER_LTE;
+ break;
+ case MmTelCapabilities.CAPABILITY_TYPE_UT:
+ capConverted = FEATURE_TYPE_UT_OVER_LTE;
+ break;
+ }
+ } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+ switch (capability) {
+ case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+ capConverted = FEATURE_TYPE_VOICE_OVER_WIFI;
+ break;
+ case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+ capConverted = FEATURE_TYPE_VIDEO_OVER_WIFI;
+ break;
+ case MmTelCapabilities.CAPABILITY_TYPE_UT:
+ capConverted = FEATURE_TYPE_UT_OVER_WIFI;
+ break;
+ }
+ }
+ return capConverted;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java
new file mode 100644
index 0000000..ef08133
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/MmTelInterfaceAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Defines "generic" MmTel commands and provides a concrete implementation for compatibility
+ * purposes.
+ */
+
+public class MmTelInterfaceAdapter {
+
+ protected IBinder mBinder;
+ protected int mSlotId;
+
+ public MmTelInterfaceAdapter(int slotId, IBinder binder) {
+ mBinder = binder;
+ mSlotId = slotId;
+ }
+
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ return getInterface().startSession(incomingCallIntent, listener);
+ }
+
+ public void endSession(int sessionId) throws RemoteException {
+ getInterface().endSession(sessionId);
+ }
+
+ public boolean isConnected(int callSessionType, int callType) throws RemoteException {
+ return getInterface().isConnected(callSessionType, callType);
+ }
+
+ public boolean isOpened() throws RemoteException {
+ return getInterface().isOpened();
+ }
+
+ public int getFeatureState() throws RemoteException {
+ return getInterface().getFeatureStatus();
+ }
+
+ public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException {
+ getInterface().addRegistrationListener(listener);
+ }
+
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ getInterface().removeRegistrationListener(listener);
+ }
+
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+ throws RemoteException {
+ return getInterface().createCallProfile(sessionId, callSessionType, callType);
+ }
+
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+ throws RemoteException {
+ return getInterface().createCallSession(sessionId, profile);
+ }
+
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ return getInterface().getPendingCallSession(sessionId, callId);
+ }
+
+ public IImsUt getUtInterface() throws RemoteException {
+ return getInterface().getUtInterface();
+ }
+
+ public IImsConfig getConfigInterface() throws RemoteException {
+ return getInterface().getConfigInterface();
+ }
+
+ public void turnOnIms() throws RemoteException {
+ getInterface().turnOnIms();
+ }
+
+ public void turnOffIms() throws RemoteException {
+ getInterface().turnOffIms();
+ }
+
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ return getInterface().getEcbmInterface();
+ }
+
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+ getInterface().setUiTTYMode(uiTtyMode, onComplete);
+ }
+
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ return getInterface().getMultiEndpointInterface();
+ }
+
+ private IImsMMTelFeature getInterface() throws RemoteException {
+ IImsMMTelFeature feature = IImsMMTelFeature.Stub.asInterface(mBinder);
+ if (feature == null) {
+ throw new RemoteException("Binder not Available");
+ }
+ return feature;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
index b833533..03ea1c8 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.imsphone;
+import android.telephony.ims.ImsExternalCallState;
+
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
@@ -25,7 +27,7 @@
/**
* Companion class for {@link ImsExternalConnection}; represents an external call which was
- * received via {@link com.android.ims.ImsExternalCallState} info.
+ * received via {@link ImsExternalCallState} info.
*/
public class ImsExternalCall extends Call {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
index 4bea73d..3227cfa 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalCallTracker.java
@@ -16,8 +16,8 @@
package com.android.internal.telephony.imsphone;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsExternalCallState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsExternalCallState;
import com.android.ims.ImsExternalCallStateListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -31,7 +31,6 @@
import android.os.Message;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
-import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
index 071aebb..6de4e80 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
@@ -28,11 +28,9 @@
import android.net.Uri;
import android.telecom.PhoneAccount;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.util.Log;
+import android.telephony.ims.ImsExternalCallState;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,7 +40,7 @@
* Package.
*
* Dialog event package information is received from the IMS framework via
- * {@link com.android.ims.ImsExternalCallState} instances.
+ * {@link ImsExternalCallState} instances.
*
* @hide
*/
@@ -129,6 +127,12 @@
}
@Override
+ public void deflect(String number) throws CallStateException {
+ // Deflect is not supported for external calls.
+ throw new CallStateException ("Deflect is not supported for external calls");
+ }
+
+ @Override
public void separate() throws CallStateException {
// No-op - Separate is not supported for external calls.
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 5a60f8e..0a86867 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -59,7 +59,6 @@
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.telecom.VideoProfile;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
@@ -67,16 +66,16 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UssdResponse;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsInfo;
import android.text.TextUtils;
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsCallProfile;
import com.android.ims.ImsEcbm;
import com.android.ims.ImsEcbmStateListener;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsInfo;
import com.android.ims.ImsUtInterface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -94,7 +93,7 @@
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.util.NotificationChannelController;
@@ -128,6 +127,59 @@
// Default Emergency Callback Mode exit timer
private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
+ public static class ImsDialArgs extends DialArgs {
+ public static class Builder extends DialArgs.Builder<ImsDialArgs.Builder> {
+ private android.telecom.Connection.RttTextStream mRttTextStream;
+ private int mClirMode = CommandsInterface.CLIR_DEFAULT;
+
+ public static ImsDialArgs.Builder from(DialArgs dialArgs) {
+ return new ImsDialArgs.Builder()
+ .setUusInfo(dialArgs.uusInfo)
+ .setVideoState(dialArgs.videoState)
+ .setIntentExtras(dialArgs.intentExtras);
+ }
+
+ public static ImsDialArgs.Builder from(ImsDialArgs dialArgs) {
+ return new ImsDialArgs.Builder()
+ .setUusInfo(dialArgs.uusInfo)
+ .setVideoState(dialArgs.videoState)
+ .setIntentExtras(dialArgs.intentExtras)
+ .setRttTextStream(dialArgs.rttTextStream)
+ .setClirMode(dialArgs.clirMode);
+ }
+
+ public ImsDialArgs.Builder setRttTextStream(
+ android.telecom.Connection.RttTextStream s) {
+ mRttTextStream = s;
+ return this;
+ }
+
+ public ImsDialArgs.Builder setClirMode(int clirMode) {
+ this.mClirMode = clirMode;
+ return this;
+ }
+
+ public ImsDialArgs build() {
+ return new ImsDialArgs(this);
+ }
+ }
+
+ /**
+ * The RTT text stream. If non-null, indicates that connection supports RTT
+ * communication with the in-call app.
+ */
+ public final android.telecom.Connection.RttTextStream rttTextStream;
+
+ /** The CLIR mode to use */
+ public final int clirMode;
+
+ private ImsDialArgs(ImsDialArgs.Builder b) {
+ super(b);
+ this.rttTextStream = b.mRttTextStream;
+ this.clirMode = b.mClirMode;
+ }
+ }
+
// Instance Variables
Phone mDefaultPhone;
ImsPhoneCallTracker mCT;
@@ -235,7 +287,7 @@
//todo: get rid of this function. It is not needed since parentPhone obj never changes
@Override
public void dispose() {
- Rlog.d(LOG_TAG, "dispose");
+ logd("dispose");
// Nothing to dispose in Phone
//super.dispose();
mPendingMMIs.clear();
@@ -365,7 +417,7 @@
}
if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall");
+ if (DBG) logd("MmiCode 0: rejectCall");
try {
mCT.rejectCall();
} catch (CallStateException e) {
@@ -373,7 +425,7 @@
notifySuppServiceFailed(Phone.SuppService.REJECT);
}
} else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground");
+ if (DBG) logd("MmiCode 0: hangupWaitingOrBackground");
try {
mCT.hangup(getBackgroundCall());
} catch (CallStateException e) {
@@ -398,13 +450,13 @@
throws CallStateException {
if (mPendingMMIs.size() > 0) {
// There are MMI codes in progress; fail attempt now.
- Rlog.i(LOG_TAG, "handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
+ logi("handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
wrappedCallback );
return true;
}
try {
- dialInternal(ussdRequest, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
+ dialInternal(ussdRequest, new ImsDialArgs.Builder().build(), wrappedCallback);
} catch (CallStateException cse) {
if (CS_FALLBACK.equals(cse.getMessage())) {
throw cse;
@@ -434,14 +486,14 @@
try {
if (len > 1) {
- if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND");
+ if (DBG) logd("not support 1X SEND");
notifySuppServiceFailed(Phone.SuppService.HANGUP);
} else {
if (call.getState() != ImsPhoneCall.State.IDLE) {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground");
+ if (DBG) logd("MmiCode 1: hangup foreground");
mCT.hangup(call);
} else {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive");
+ if (DBG) logd("MmiCode 1: switchWaitingOrHoldingAndActive");
mCT.switchWaitingOrHoldingAndActive();
}
}
@@ -461,15 +513,15 @@
}
if (len > 1) {
- if (DBG) Rlog.d(LOG_TAG, "separate not supported");
+ if (DBG) logd("separate not supported");
notifySuppServiceFailed(Phone.SuppService.SEPARATE);
} else {
try {
if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call");
+ if (DBG) logd("MmiCode 2: accept ringing call");
mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
} else {
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive");
+ if (DBG) logd("MmiCode 2: switchWaitingOrHoldingAndActive");
mCT.switchWaitingOrHoldingAndActive();
}
} catch (CallStateException e) {
@@ -487,7 +539,7 @@
return false;
}
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls");
+ if (DBG) logd("MmiCode 3: merge calls");
conference();
return true;
}
@@ -500,7 +552,7 @@
return false;
}
- if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer");
+ if (DBG) logd("MmiCode 4: not support explicit call transfer");
notifySuppServiceFailed(Phone.SuppService.TRANSFER);
return true;
}
@@ -510,14 +562,14 @@
return false;
}
- Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!");
+ logi("MmiCode 5: CCBS not supported!");
// Treat it as an "unknown" service.
notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
return true;
}
public void notifySuppSvcNotification(SuppServiceNotification suppSvc) {
- Rlog.d(LOG_TAG, "notifySuppSvcNotification: suppSvc = " + suppSvc);
+ logd("notifySuppSvcNotification: suppSvc = " + suppSvc);
AsyncResult ar = new AsyncResult(null, suppSvc, null);
mSsnRegistrants.notifyRegistrants(ar);
@@ -598,27 +650,14 @@
}
@Override
- public Connection
- dial(String dialString, int videoState) throws CallStateException {
- return dialInternal(dialString, videoState, null, null);
+ public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException {
+ return dialInternal(dialString, dialArgs, null);
}
- @Override
- public Connection
- dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ private Connection dialInternal(String dialString, DialArgs dialArgs,
+ ResultReceiver wrappedCallback)
throws CallStateException {
- // ignore UUSInfo
- return dialInternal (dialString, videoState, intentExtras, null);
- }
- protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
- throws CallStateException {
- return dialInternal(dialString, videoState, intentExtras, null);
- }
-
- private Connection dialInternal(String dialString, int videoState,
- Bundle intentExtras, ResultReceiver wrappedCallback)
- throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -627,28 +666,37 @@
return null;
}
+ ImsDialArgs.Builder imsDialArgsBuilder;
+ // Get the CLIR info if needed
+ if (!(dialArgs instanceof ImsDialArgs)) {
+ imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
+ } else {
+ imsDialArgsBuilder = ImsDialArgs.Builder.from((ImsDialArgs) dialArgs);
+ }
+ imsDialArgsBuilder.setClirMode(mCT.getClirMode());
+
if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- return mCT.dial(dialString, videoState, intentExtras);
+ return mCT.dial(dialString, imsDialArgsBuilder.build());
}
// Only look at the Network portion for mmi
String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
ImsPhoneMmiCode mmi =
ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
- if (DBG) Rlog.d(LOG_TAG,
- "dialInternal: dialing w/ mmi '" + mmi + "'...");
+ if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
- return mCT.dial(dialString, videoState, intentExtras);
+ return mCT.dial(dialString, imsDialArgsBuilder.build());
} else if (mmi.isTemporaryModeCLIR()) {
- return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState, intentExtras);
+ imsDialArgsBuilder.setClirMode(mmi.getCLIRMode());
+ return mCT.dial(mmi.getDialingNumber(), imsDialArgsBuilder.build());
} else if (!mmi.isSupportedOverImsPhone()) {
// If the mmi is not supported by IMS service,
// try to initiate dialing with default phone
// Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
// causes it to return true even though the "processCode" method ultimately throws the
// exception.
- Rlog.i(LOG_TAG, "dialInternal: USSD not supported by IMS; fallback to CS.");
+ logi("dialInternal: USSD not supported by IMS; fallback to CS.");
throw new CallStateException(CS_FALLBACK);
} else {
mPendingMMIs.add(mmi);
@@ -658,7 +706,7 @@
mmi.processCode();
} catch (CallStateException cse) {
if (CS_FALLBACK.equals(cse.getMessage())) {
- Rlog.i(LOG_TAG, "dialInternal: fallback to GSM required.");
+ logi("dialInternal: fallback to GSM required.");
// Make sure we remove from the list of pending MMIs since it will handover to
// GSM.
mPendingMMIs.remove(mmi);
@@ -674,8 +722,7 @@
public void
sendDtmf(char c) {
if (!PhoneNumberUtils.is12Key(c)) {
- Rlog.e(LOG_TAG,
- "sendDtmf called with invalid character '" + c + "'");
+ loge("sendDtmf called with invalid character '" + c + "'");
} else {
if (mCT.getState() == PhoneConstants.State.OFFHOOK) {
mCT.sendDtmf(c, null);
@@ -687,8 +734,7 @@
public void
startDtmf(char c) {
if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) {
- Rlog.e(LOG_TAG,
- "startDtmf called with invalid character '" + c + "'");
+ loge("startDtmf called with invalid character '" + c + "'");
} else {
mCT.startDtmf(c);
}
@@ -701,7 +747,7 @@
}
public void notifyIncomingRing() {
- if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing");
+ if (DBG) logd("notifyIncomingRing");
AsyncResult ar = new AsyncResult(null, null, null);
sendMessage(obtainMessage(EVENT_CALL_RING, ar));
}
@@ -806,7 +852,7 @@
@Override
public void getOutgoingCallerIdDisplay(Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "getCLIR");
+ if (DBG) logd("getCLIR");
Message resp;
resp = obtainMessage(EVENT_GET_CLIR_DONE, onComplete);
@@ -820,7 +866,7 @@
@Override
public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "setCLIR action= " + clirMode);
+ if (DBG) logd("setCLIR action= " + clirMode);
Message resp;
// Packing CLIR value in the message. This will be required for
// SharedPreference caching, if the message comes back as part of
@@ -837,9 +883,9 @@
@Override
public void getCallForwardingOption(int commandInterfaceCFReason,
Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason);
+ if (DBG) logd("getCallForwardingOption reason=" + commandInterfaceCFReason);
if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
- if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query.");
+ if (DBG) logd("requesting call forwarding query.");
Message resp;
resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
@@ -870,14 +916,15 @@
int serviceClass,
int timerSeconds,
Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
- + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
+ if (DBG) {
+ logd("setCallForwardingOption action=" + commandInterfaceCFAction
+ + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
+ }
if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
(isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
Message resp;
- Cf cf = new Cf(dialingNumber,
- (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false),
- onComplete);
+ Cf cf = new Cf(dialingNumber, GsmMmiCode.isVoiceUnconditionalForwarding(
+ commandInterfaceCFReason, serviceClass), onComplete);
resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
@@ -899,7 +946,7 @@
@Override
public void getCallWaiting(Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "getCallWaiting");
+ if (DBG) logd("getCallWaiting");
Message resp;
resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
@@ -917,7 +964,7 @@
}
public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
+ if (DBG) logd("setCallWaiting enable=" + enable);
Message resp;
resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
@@ -952,22 +999,42 @@
}
public void getCallBarring(String facility, Message onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility);
+ getCallBarring(facility, onComplete, CommandsInterface.SERVICE_CLASS_NONE);
+ }
+
+ public void getCallBarring(String facility, Message onComplete, int serviceClass) {
+ getCallBarring(facility, "", onComplete, serviceClass);
+ }
+
+ @Override
+ public void getCallBarring(String facility, String password, Message onComplete,
+ int serviceClass) {
+ if (DBG) logd("getCallBarring facility=" + facility + ", serviceClass = " + serviceClass);
Message resp;
resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
try {
ImsUtInterface ut = mCT.getUtInterface();
- ut.queryCallBarring(getCBTypeFromFacility(facility), resp);
+ // password is not required with Ut interface
+ ut.queryCallBarring(getCBTypeFromFacility(facility), resp, serviceClass);
} catch (ImsException e) {
sendErrorResponse(onComplete, e);
}
}
- public void setCallBarring(String facility, boolean lockState, String password, Message
- onComplete) {
- if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility
- + ", lockState=" + lockState);
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete) {
+ setCallBarring(facility, lockState, password, onComplete,
+ CommandsInterface.SERVICE_CLASS_NONE);
+ }
+
+ @Override
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete, int serviceClass) {
+ if (DBG) {
+ logd("setCallBarring facility=" + facility
+ + ", lockState=" + lockState + ", serviceClass = " + serviceClass);
+ }
Message resp;
resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
@@ -982,7 +1049,8 @@
try {
ImsUtInterface ut = mCT.getUtInterface();
// password is not required with Ut interface
- ut.updateCallBarring(getCBTypeFromFacility(facility), action, resp, null);
+ ut.updateCallBarring(getCBTypeFromFacility(facility), action,
+ resp, null, serviceClass);
} catch (ImsException e) {
sendErrorResponse(onComplete, e);
}
@@ -990,7 +1058,7 @@
@Override
public void sendUssdResponse(String ussdMessge) {
- Rlog.d(LOG_TAG, "sendUssdResponse");
+ logd("sendUssdResponse");
ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
@@ -1007,7 +1075,7 @@
}
private void sendErrorResponse(Message onComplete) {
- Rlog.d(LOG_TAG, "sendErrorResponse");
+ logd("sendErrorResponse");
if (onComplete != null) {
AsyncResult.forMessage(onComplete, null,
new CommandException(CommandException.Error.GENERIC_FAILURE));
@@ -1017,7 +1085,7 @@
@VisibleForTesting
public void sendErrorResponse(Message onComplete, Throwable e) {
- Rlog.d(LOG_TAG, "sendErrorResponse");
+ logd("sendErrorResponse");
if (onComplete != null) {
AsyncResult.forMessage(onComplete, null, getCommandException(e));
onComplete.sendToTarget();
@@ -1025,8 +1093,7 @@
}
private CommandException getCommandException(int code, String errorString) {
- Rlog.d(LOG_TAG, "getCommandException code= " + code
- + ", errorString= " + errorString);
+ logd("getCommandException code= " + code + ", errorString= " + errorString);
CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
switch(code) {
@@ -1041,6 +1108,19 @@
break;
case ImsReasonInfo.CODE_FDN_BLOCKED:
error = CommandException.Error.FDN_CHECK_FAILURE;
+ break;
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
+ error = CommandException.Error.SS_MODIFIED_TO_DIAL;
+ break;
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
+ error = CommandException.Error.SS_MODIFIED_TO_USSD;
+ break;
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
+ error = CommandException.Error.SS_MODIFIED_TO_SS;
+ break;
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
+ error = CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO;
+ break;
default:
break;
}
@@ -1054,7 +1134,7 @@
if (e instanceof ImsException) {
ex = getCommandException(((ImsException)e).getCode(), e.getMessage());
} else {
- Rlog.d(LOG_TAG, "getCommandException generic failure");
+ logd("getCommandException generic failure");
ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
}
return ex;
@@ -1062,14 +1142,14 @@
private void
onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
- Rlog.d(LOG_TAG, "onNetworkInitiatedUssd");
+ logd("onNetworkInitiatedUssd");
mMmiCompleteRegistrants.notifyRegistrants(
new AsyncResult(null, mmi, null));
}
/* package */
void onIncomingUSSD(int ussdMode, String ussdMessage) {
- if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode);
+ if (DBG) logd("onIncomingUSSD ussdMode=" + ussdMode);
boolean isUssdError;
boolean isUssdRequest;
@@ -1121,8 +1201,8 @@
* The exception is cancellation of an incoming USSD-REQUEST, which is
* not on the list.
*/
- Rlog.d(LOG_TAG, "onMMIDone: mmi=" + mmi);
- if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
+ logd("onMMIDone: mmi=" + mmi);
+ if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest() || mmi.isSsInfo()) {
ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
if (receiverCallback != null) {
int returnCode = (mmi.getState() == MmiCode.State.COMPLETE) ?
@@ -1130,7 +1210,7 @@
sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
receiverCallback );
} else {
- Rlog.v(LOG_TAG, "onMMIDone: notifyRegistrants");
+ logv("onMMIDone: notifyRegistrants");
mMmiCompleteRegistrants.notifyRegistrants(
new AsyncResult(null, mmi, null));
}
@@ -1199,16 +1279,20 @@
private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
CallForwardInfo cfInfo = new CallForwardInfo();
- cfInfo.status = info.mStatus;
- cfInfo.reason = getCFReasonFromCondition(info.mCondition);
+ cfInfo.status = info.getStatus();
+ cfInfo.reason = getCFReasonFromCondition(info.getCondition());
cfInfo.serviceClass = SERVICE_CLASS_VOICE;
- cfInfo.toa = info.mToA;
- cfInfo.number = info.mNumber;
- cfInfo.timeSeconds = info.mTimeSeconds;
+ cfInfo.toa = info.getToA();
+ cfInfo.number = info.getNumber();
+ cfInfo.timeSeconds = info.getTimeSeconds();
return cfInfo;
}
- private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
+ /**
+ * Used to Convert ImsCallForwardInfo[] to CallForwardInfo[].
+ * Update received call forward status to default IccRecords.
+ */
+ public CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
CallForwardInfo[] cfInfos = null;
if (infos != null && infos.length != 0) {
@@ -1224,10 +1308,10 @@
}
} else {
for (int i = 0, s = infos.length; i < s; i++) {
- if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
+ if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
if (r != null) {
- setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1),
- infos[i].mNumber);
+ setVoiceCallForwardingFlag(r, 1, (infos[i].getStatus() == 1),
+ infos[i].getNumber());
}
}
cfInfos[i] = getCallForwardInfo(infos[i]);
@@ -1241,7 +1325,7 @@
int[] cbInfos = new int[1];
cbInfos[0] = SERVICE_CLASS_NONE;
- if (infos[0].mStatus == 1) {
+ if (infos[0].getStatus() == 1) {
cbInfos[0] = SERVICE_CLASS_VOICE;
}
@@ -1252,7 +1336,7 @@
int[] cwInfos = new int[2];
cwInfos[0] = 0;
- if (infos[0].mStatus == 1) {
+ if (infos[0].getStatus() == 1) {
cwInfos[0] = 1;
cwInfos[1] = SERVICE_CLASS_VOICE;
}
@@ -1278,7 +1362,7 @@
ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
mSS.setDataRegState(ss.getDataRegState());
mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology());
- Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
+ logd("updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
}
}
@@ -1286,7 +1370,7 @@
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
- if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what);
+ if (DBG) logd("handleMessage what=" + msg.what);
switch (msg.what) {
case EVENT_SET_CALL_FORWARD_DONE:
IccRecords r = mDefaultPhone.getIccRecords();
@@ -1338,27 +1422,33 @@
break;
case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
- if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
+ if (DBG) logd("EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
updateDataServiceState();
break;
case EVENT_SERVICE_STATE_CHANGED:
- if (VDBG) Rlog.d(LOG_TAG, "EVENT_SERVICE_STATE_CHANGED");
+ if (VDBG) logd("EVENT_SERVICE_STATE_CHANGED");
ar = (AsyncResult) msg.obj;
ServiceState newServiceState = (ServiceState) ar.result;
// only update if roaming status changed
if (mRoaming != newServiceState.getRoaming()) {
- if (DBG) Rlog.d(LOG_TAG, "Roaming state changed");
- updateRoamingState(newServiceState.getRoaming());
+ if (DBG) logd("Roaming state changed - " + mRoaming);
+ // Update WFC mode only if voice or data is in service.
+ // The STATE_IN_SERVICE is checked to prevent wifi calling mode change
+ // when phone moves from roaming to no service.
+ boolean isInService =
+ (newServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE ||
+ newServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
+ updateRoamingState(newServiceState.getRoaming(), isInService);
}
break;
case EVENT_VOICE_CALL_ENDED:
- if (DBG) Rlog.d(LOG_TAG, "Voice call ended. Handle pending updateRoamingState.");
+ if (DBG) logd("Voice call ended. Handle pending updateRoamingState.");
mCT.unregisterForVoiceCallEnded(this);
// only update if roaming status changed
boolean newRoaming = getCurrentRoaming();
if (mRoaming != newRoaming) {
- updateRoamingState(newRoaming);
+ updateRoamingState(newRoaming, true);
}
break;
@@ -1375,13 +1465,13 @@
new ImsEcbmStateListener() {
@Override
public void onECBMEntered() {
- if (DBG) Rlog.d(LOG_TAG, "onECBMEntered");
+ if (DBG) logd("onECBMEntered");
handleEnterEmergencyCallbackMode();
}
@Override
public void onECBMExited() {
- if (DBG) Rlog.d(LOG_TAG, "onECBMExited");
+ if (DBG) logd("onECBMExited");
handleExitEmergencyCallbackMode();
}
};
@@ -1402,7 +1492,7 @@
intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
- if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
+ if (DBG) logd("sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
}
@Override
@@ -1410,7 +1500,7 @@
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
- if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()");
+ if (DBG) logd("exitEmergencyCallbackMode()");
// Send a message which will invoke handleExitEmergencyCallbackMode
ImsEcbm ecbm;
@@ -1423,10 +1513,7 @@
}
private void handleEnterEmergencyCallbackMode() {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
- + isInEcm());
- }
+ if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm());
// if phone is not in Ecm mode, and it's changed to Ecm mode
if (!isInEcm()) {
setIsInEcm(true);
@@ -1445,10 +1532,7 @@
@Override
protected void handleExitEmergencyCallbackMode() {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = "
- + isInEcm());
- }
+ if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm());
if (isInEcm()) {
setIsInEcm(false);
@@ -1468,6 +1552,7 @@
// send an Intent
sendEmergencyCallbackModeChange();
+ ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(false);
}
/**
@@ -1488,7 +1573,7 @@
((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
break;
default:
- Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
+ loge("handleTimerInEmergencyCallbackMode, unsupported action " + action);
}
}
@@ -1522,6 +1607,11 @@
}
@Override
+ public int getImsRegistrationTech() {
+ return mCT.getImsRegistrationTech();
+ }
+
+ @Override
public Phone getDefaultPhone() {
return mDefaultPhone;
}
@@ -1595,7 +1685,7 @@
if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
&& imsReasonInfo.mExtraMessage != null) {
// Suppress WFC Registration notifications if WFC is not enabled by the user.
- if (ImsManager.isWfcEnabledByUser(mContext)) {
+ if (ImsManager.getInstance(mContext, mPhoneId).isWfcEnabledByUser()) {
processWfcDisconnectForNotification(imsReasonInfo);
}
}
@@ -1607,12 +1697,12 @@
CarrierConfigManager configManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) {
- Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
+ loge("processDisconnectReason: CarrierConfigManager is not ready");
return;
}
PersistableBundle pb = configManager.getConfigForSubId(getSubId());
if (pb == null) {
- Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
+ loge("processDisconnectReason: no config for subId " + getSubId());
return;
}
final String[] wfcOperatorErrorCodes =
@@ -1633,7 +1723,7 @@
for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
String[] codes = wfcOperatorErrorCodes[i].split("\\|");
if (codes.length != 2) {
- Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
+ loge("Invalid carrier config: " + wfcOperatorErrorCodes[i]);
continue;
}
@@ -1664,7 +1754,7 @@
if (idx < 0
|| idx >= wfcOperatorErrorAlertMessages.length
|| idx >= wfcOperatorErrorNotificationMessages.length) {
- Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
+ loge("Invalid index: " + wfcOperatorErrorCodes[i]);
continue;
}
String messageAlert = imsReasonInfo.mExtraMessage;
@@ -1680,9 +1770,6 @@
imsReasonInfo.mExtraMessage); // Fill IMS error code into notification
}
- // UX requirement is to disable WFC in case of "permanent" registration failures.
- ImsManager.setWfcSetting(mContext, false);
-
// If WfcSettings are active then alert will be shown
// otherwise notification will be added.
Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
@@ -1723,14 +1810,18 @@
return mCT.getVtDataUsage(perUidStats);
}
- private void updateRoamingState(boolean newRoaming) {
+ private void updateRoamingState(boolean newRoaming, boolean isInService) {
if (mCT.getState() == PhoneConstants.State.IDLE) {
- if (DBG) Rlog.d(LOG_TAG, "updateRoamingState now: " + newRoaming);
+ if (DBG) logd("updateRoamingState now: " + newRoaming);
mRoaming = newRoaming;
- ImsManager.setWfcMode(mContext,
- ImsManager.getWfcMode(mContext, newRoaming), newRoaming);
+ if (isInService) {
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
+ } else {
+ if (DBG) Rlog.d(LOG_TAG, "updateRoamingState service state is OUT_OF_SERVICE");
+ }
} else {
- if (DBG) Rlog.d(LOG_TAG, "updateRoamingState postponed: " + newRoaming);
+ if (DBG) logd("updateRoamingState postponed: " + newRoaming);
mCT.registerForVoiceCallEnded(this,
EVENT_VOICE_CALL_ENDED, null);
}
@@ -1739,7 +1830,7 @@
private boolean getCurrentRoaming() {
TelephonyManager tm = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
- return tm.isNetworkRoaming();
+ return tm.isNetworkRoaming(getSubId());
}
@Override
@@ -1762,4 +1853,20 @@
pw.println(" mSsnRegistrants = " + mSsnRegistrants);
pw.flush();
}
+
+ private void logi(String s) {
+ Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private void logv(String s) {
+ Rlog.v(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private void logd(String s) {
+ Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 87b96d8..e573fbd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -32,6 +32,7 @@
import android.telephony.SignalStrength;
import android.util.Pair;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.IccCard;
@@ -104,7 +105,8 @@
*
* @param cn The connection.
*/
- protected void startOnHoldTone(Connection cn) {
+ @VisibleForTesting
+ public void startOnHoldTone(Connection cn) {
Pair<Connection, Boolean> result = new Pair<Connection, Boolean>(cn, Boolean.TRUE);
mOnHoldRegistrants.notifyRegistrants(new AsyncResult(null, result, null));
}
@@ -444,10 +446,6 @@
Message response) {
}
- @Override
- public void getDataCallList(Message response) {
- }
-
public List<DataConnection> getCurrentDataConnectionList () {
return null;
}
@@ -474,12 +472,17 @@
}
@Override
- public boolean getDataEnabled() {
+ public boolean isUserDataEnabled() {
return false;
}
@Override
- public void setDataEnabled(boolean enable) {
+ public boolean isDataEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setUserDataEnabled(boolean enable) {
}
@@ -539,6 +542,16 @@
}
@Override
+ public void getCallBarring(String facility, String password, Message onComplete,
+ int serviceClass) {
+ }
+
+ @Override
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete, int serviceClass) {
+ }
+
+ @Override
protected void onUpdateIccAvailability() {
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index 52636f5..cb5917d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -28,7 +28,7 @@
import com.android.internal.telephony.Phone;
import com.android.ims.ImsCall;
import com.android.ims.ImsException;
-import com.android.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import java.util.List;
@@ -237,8 +237,8 @@
}
}
- /*package*/ ImsPhoneConnection
- getFirstConnection() {
+ @VisibleForTesting
+ public ImsPhoneConnection getFirstConnection() {
if (mConnections.size() == 0) return null;
return (ImsPhoneConnection) mConnections.get(0);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 163ee0c..1117a4c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -16,7 +16,8 @@
package com.android.internal.telephony.imsphone;
-import android.app.PendingIntent;
+import static com.android.internal.telephony.Phone.CS_FALLBACK;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -50,8 +51,14 @@
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsServiceProxy;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -59,18 +66,14 @@
import android.util.SparseIntArray;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
import com.android.ims.ImsConfig;
import com.android.ims.ImsConfigListener;
-import com.android.ims.ImsConnectionStateListener;
import com.android.ims.ImsEcbm;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.ImsMultiEndpoint;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsServiceClass;
-import com.android.ims.ImsSuppServiceNotification;
import com.android.ims.ImsUtInterface;
+import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
import com.android.ims.internal.VideoPauseTracker;
@@ -84,6 +87,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.gsm.SuppServiceNotification;
@@ -117,6 +121,10 @@
SharedPreferences getDefaultSharedPreferences(Context context);
}
+ public interface PhoneNumberUtilsProxy {
+ boolean isEmergencyNumber(String number);
+ }
+
private static final boolean DBG = true;
// When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -126,90 +134,100 @@
private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING ||
Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE);
- //Indices map to ImsConfig.FeatureConstants
- private boolean[] mImsFeatureEnabled = {false, false, false, false, false, false};
- private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi",
- "UTLTE", "UTWiFi"};
+ private MmTelFeature.MmTelCapabilities mMmTelCapabilities =
+ new MmTelFeature.MmTelCapabilities();
private TelephonyMetrics mMetrics;
private boolean mCarrierConfigLoaded = false;
+ private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
+ private class MmTelFeatureListener extends MmTelFeature.Listener {
+ @Override
+ public void onIncomingCall(IImsCallSession c, Bundle extras) {
+ if (DBG) log("onReceive : incoming call intent");
+
+ if (mImsManager == null) return;
+
+ try {
+ // Network initiated USSD will be treated by mImsUssdListener
+ boolean isUssd = extras.getBoolean(ImsManager.EXTRA_USSD, false);
+ if (isUssd) {
+ if (DBG) log("onReceive : USSD");
+ mUssdSession = mImsManager.takeCall(c, extras, mImsUssdListener);
+ if (mUssdSession != null) {
+ mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
+ }
+ return;
+ }
+
+ boolean isUnknown = extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
+ if (DBG) {
+ log("onReceive : isUnknown = " + isUnknown
+ + " fg = " + mForegroundCall.getState()
+ + " bg = " + mBackgroundCall.getState());
+ }
+
+ // Normal MT/Unknown call
+ ImsCall imsCall = mImsManager.takeCall(c, extras, mImsCallListener);
+ ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
+ ImsPhoneCallTracker.this,
+ (isUnknown ? mForegroundCall : mRingingCall), isUnknown);
+
+ // If there is an active call.
+ if (mForegroundCall.hasConnections()) {
+ ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
+ if (activeCall != null && imsCall != null) {
+ // activeCall could be null if the foreground call is in a disconnected
+ // state. If either of the calls is null there is no need to check if
+ // one will be disconnected on answer.
+ boolean answeringWillDisconnect =
+ shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
+ conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
+ }
+ }
+ conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
+ addConnection(conn);
+
+ setVideoCallProvider(conn, imsCall);
+
+ TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
+ imsCall.getSession());
+
+ if (isUnknown) {
+ mPhone.notifyUnknownConnection(conn);
+ } else {
+ if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE)
+ || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
+ conn.update(imsCall, ImsPhoneCall.State.WAITING);
+ }
+
+ mPhone.notifyNewRingingConnection(conn);
+ mPhone.notifyIncomingRing();
+ }
+
+ updatePhoneState();
+ mPhone.notifyPreciseCallStateChanged();
+ } catch (ImsException e) {
+ loge("onReceive : exception " + e);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void onVoiceMessageCountUpdate(int count) {
+ if (mPhone != null && mPhone.mDefaultPhone != null) {
+ if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
+ mPhone.mDefaultPhone.setVoiceMessageCount(count);
+ } else {
+ loge("onVoiceMessageCountUpdate: null phone");
+ }
+ }
+ }
+
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
- if (DBG) log("onReceive : incoming call intent");
-
- if (mImsManager == null) return;
-
- if (mServiceId < 0) return;
-
- try {
- // Network initiated USSD will be treated by mImsUssdListener
- boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
- if (isUssd) {
- if (DBG) log("onReceive : USSD");
- mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
- if (mUssdSession != null) {
- mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
- }
- return;
- }
-
- boolean isUnknown = intent.getBooleanExtra(ImsManager.EXTRA_IS_UNKNOWN_CALL,
- false);
- if (DBG) {
- log("onReceive : isUnknown = " + isUnknown +
- " fg = " + mForegroundCall.getState() +
- " bg = " + mBackgroundCall.getState());
- }
-
- // Normal MT/Unknown call
- ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
- ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
- ImsPhoneCallTracker.this,
- (isUnknown? mForegroundCall: mRingingCall), isUnknown);
-
- // If there is an active call.
- if (mForegroundCall.hasConnections()) {
- ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
- if (activeCall != null && imsCall != null) {
- // activeCall could be null if the foreground call is in a disconnected
- // state. If either of the calls is null there is no need to check if
- // one will be disconnected on answer.
- boolean answeringWillDisconnect =
- shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
- conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
- }
- }
- conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
- addConnection(conn);
-
- setVideoCallProvider(conn, imsCall);
-
- TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
- imsCall.getSession());
-
- if (isUnknown) {
- mPhone.notifyUnknownConnection(conn);
- } else {
- if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
- (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
- conn.update(imsCall, ImsPhoneCall.State.WAITING);
- }
-
- mPhone.notifyNewRingingConnection(conn);
- mPhone.notifyIncomingRing();
- }
-
- updatePhoneState();
- mPhone.notifyPreciseCallStateChanged();
- } catch (ImsException e) {
- loge("onReceive : exception " + e);
- } catch (RemoteException e) {
- }
- } else if (intent.getAction().equals(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+ if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (subId == mPhone.getSubId()) {
@@ -217,7 +235,6 @@
log("onReceive : Updating mAllowEmergencyVideoCalls = " +
mAllowEmergencyVideoCalls);
}
- mCarrierConfigLoaded = true;
} else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
@@ -236,19 +253,12 @@
private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21;
private static final int EVENT_VT_DATA_USAGE_UPDATE = 22;
private static final int EVENT_DATA_ENABLED_CHANGED = 23;
- private static final int EVENT_GET_IMS_SERVICE = 24;
private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
+ private static final int EVENT_SUPP_SERVICE_INDICATION = 27;
private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
- // Initial condition for ims connection retry.
- private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
- // Ceiling bitshift amount for service query timeout, calculated as:
- // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
- // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
- private static final int CEILING_SERVICE_RETRY_COUNT = 6;
-
private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms
//***** Instance Variables
@@ -287,9 +297,8 @@
private PhoneConstants.State mState = PhoneConstants.State.IDLE;
- private int mImsServiceRetryCount;
private ImsManager mImsManager;
- private int mServiceId = -1;
+ private ImsUtInterface mUtInterface;
private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
@@ -305,6 +314,7 @@
private boolean mAllowEmergencyVideoCalls = false;
private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
private boolean mIsViLteDataMetered = false;
+ private boolean mAlwaysPlayRemoteHoldTone = false;
/**
* Listeners to changes in the phone state. Intended for use by other interested IMS components
@@ -537,6 +547,8 @@
PreciseDisconnectCause.ACCESS_CLASS_BLOCKED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH,
PreciseDisconnectCause.NETWORK_DETACH);
+ PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER,
+ PreciseDisconnectCause.UNOBTAINABLE_NUMBER);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1,
PreciseDisconnectCause.OEM_CAUSE_1);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2,
@@ -596,7 +608,7 @@
/**
* TODO: Remove this code; it is a workaround.
- * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(Context, int, boolean)} to
+ * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(boolean)} to
* be called when an ongoing video call is disconnected. In some cases, where video pause is
* supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
* has been disabled we will pause the video rather than disconnecting the call. When this
@@ -612,50 +624,16 @@
return PreferenceManager.getDefaultSharedPreferences(context);
};
- // Callback fires when ImsManager MMTel Feature changes state
- private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> {
- try {
- int status = mImsManager.getImsServiceStatus();
- log("Status Changed: " + status);
- switch(status) {
- case ImsFeature.STATE_READY: {
- startListeningForCalls();
- break;
- }
- case ImsFeature.STATE_INITIALIZING:
- // fall through
- case ImsFeature.STATE_NOT_AVAILABLE: {
- stopListeningForCalls();
- break;
- }
- default: {
- Log.w(LOG_TAG, "Unexpected State!");
- }
- }
- } catch (ImsException e) {
- // Could not get the ImsService, retry!
- retryGetImsService();
- }
- };
-
- @VisibleForTesting
- public interface IRetryTimeout {
- int get();
- }
-
/**
- * Default implementation of interface that calculates the ImsService retry timeout.
- * Override-able for testing.
+ * Default implementation for determining if a number is an emergency number. Uses the real
+ * PhoneNumberUtils.
*/
- @VisibleForTesting
- public IRetryTimeout mRetryTimeout = () -> {
- int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
- if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
- mImsServiceRetryCount++;
- }
- return timeout;
+ private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = (String string) -> {
+ return PhoneNumberUtils.isEmergencyNumber(string);
};
+ private final ImsManager.Connector mImsManagerConnector;
+
//***** Events
@@ -667,7 +645,6 @@
mMetrics = TelephonyMetrics.getInstance();
IntentFilter intentfilter = new IntentFilter();
- intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
mPhone.getContext().registerReceiver(mReceiver, intentfilter);
@@ -676,8 +653,6 @@
mPhone.getDefaultPhone().registerForDataEnabledChanged(
this, EVENT_DATA_ENABLED_CHANGED, null);
- mImsServiceRetryCount = 0;
-
final TelecomManager telecomManager =
(TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
mDefaultDialerUid.set(
@@ -687,9 +662,20 @@
mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
- // Send a message to connect to the Ims Service and open a connection through
- // getImsService().
- sendEmptyMessage(EVENT_GET_IMS_SERVICE);
+ mImsManagerConnector = new ImsManager.Connector(phone.getContext(), phone.getPhoneId(),
+ new ImsManager.Connector.Listener() {
+ @Override
+ public void connectionReady(ImsManager manager) throws ImsException {
+ mImsManager = manager;
+ startListeningForCalls();
+ }
+
+ @Override
+ public void connectionUnavailable() {
+ stopListeningForCalls();
+ }
+ });
+ mImsManagerConnector.connect();
}
/**
@@ -702,6 +688,23 @@
mSharedPreferenceProxy = sharedPreferenceProxy;
}
+ /**
+ * Test-only method used to mock out access to the phone number utils class.
+ * @param phoneNumberUtilsProxy
+ */
+ @VisibleForTesting
+ public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy phoneNumberUtilsProxy) {
+ mPhoneNumberUtilsProxy = phoneNumberUtilsProxy;
+ }
+
+ /**
+ * Test-only method used to set the ImsService retry timeout.
+ */
+ @VisibleForTesting
+ public void setRetryTimeout(ImsManager.Connector.RetryTimeout retryTimeout) {
+ mImsManagerConnector.mRetryTimeout = retryTimeout;
+ }
+
private int getPackageUid(Context context, String pkg) {
if (pkg == null) {
return NetworkStats.UID_ALL;
@@ -718,31 +721,15 @@
return uid;
}
- private PendingIntent createIncomingCallPendingIntent() {
- Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void getImsService() throws ImsException {
- if (DBG) log("getImsService");
- mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
- // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
- // this method will throw an ImsException.
- mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
- // Wait for ImsService.STATE_READY to start listening for calls.
- // Call the callback right away for compatibility with older devices that do not use states.
- mNotifyStatusChangedCallback.notifyStatusChanged();
- }
-
private void startListeningForCalls() throws ImsException {
- mImsServiceRetryCount = 0;
- mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
- createIncomingCallPendingIntent(),
- mImsConnectionStateListener);
+ log("startListeningForCalls");
+ mImsManager.open(mMmTelFeatureListener);
+ mImsManager.addRegistrationCallback(mImsRegistrationCallback);
+ mImsManager.addCapabilitiesCallback(mImsCapabilityCallback);
- mImsManager.setImsConfigListener(mImsConfigListener);
+ mImsManager.setConfigListener(mImsConfigListener);
+
+ mImsManager.getConfigInterface().addConfigCallback(mConfigCallback);
// Get the ECBM interface and set IMSPhone's listener object for notifications
getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener());
@@ -762,22 +749,29 @@
mPhone.getExternalCallTracker().getExternalCallStateListener());
}
+ //Set UT interface listener to receive UT indications.
+ mUtInterface = getUtInterface();
+ if (mUtInterface != null) {
+ mUtInterface.registerForSuppServiceIndication(this,
+ EVENT_SUPP_SERVICE_INDICATION, null);
+ }
+
if (mCarrierConfigLoaded) {
- ImsManager.updateImsServiceConfig(mPhone.getContext(),
- mPhone.getPhoneId(), true);
+ mImsManager.updateImsServiceConfig(true);
}
}
private void stopListeningForCalls() {
- try {
- resetImsCapabilities();
- // Only close on valid session.
- if (mImsManager != null && mServiceId > 0) {
- mImsManager.close(mServiceId);
- mServiceId = -1;
+ log("stopListeningForCalls");
+ resetImsCapabilities();
+ // Only close on valid session.
+ if (mImsManager != null) {
+ try {
+ mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback);
+ } catch (ImsException e) {
+ Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback.");
}
- } catch (ImsException e) {
- // If the binder is unavailable, then the ImsService doesn't need to close.
+ mImsManager.close();
}
}
@@ -789,9 +783,12 @@
mHandoverCall.dispose();
clearDisconnected();
+ if (mUtInterface != null) {
+ mUtInterface.unregisterForSuppServiceIndication(this);
+ }
mPhone.getContext().unregisterReceiver(mReceiver);
mPhone.getDefaultPhone().unregisterForDataEnabledChanged(this);
- removeMessages(EVENT_GET_IMS_SERVICE);
+ mImsManagerConnector.disconnect();
}
@Override
@@ -824,31 +821,46 @@
mVoiceCallEndedRegistrants.remove(h);
}
- public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
- CallStateException {
- int oirMode;
+ public int getClirMode() {
if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) {
SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences(
mPhone.getContext());
- oirMode = sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(),
+ return sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(),
CommandsInterface.CLIR_DEFAULT);
} else {
loge("dial; could not get default CLIR mode.");
- oirMode = CommandsInterface.CLIR_DEFAULT;
+ return CommandsInterface.CLIR_DEFAULT;
}
- return dial(dialString, oirMode, videoState, intentExtras);
}
- /**
- * oirMode is one of the CLIR_ constants
- */
- synchronized Connection
- dial(String dialString, int clirMode, int videoState, Bundle intentExtras)
+ public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
+ CallStateException {
+ ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
+ .setIntentExtras(intentExtras)
+ .setVideoState(videoState)
+ .setClirMode(getClirMode())
+ .build();
+ return dial(dialString, dialArgs);
+ }
+
+ public synchronized Connection dial(String dialString, ImsPhone.ImsDialArgs dialArgs)
throws CallStateException {
boolean isPhoneInEcmMode = isPhoneInEcbMode();
- boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);
+ boolean isEmergencyNumber = mPhoneNumberUtilsProxy.isEmergencyNumber(dialString);
+
+ if (!shouldNumberBePlacedOnIms(isEmergencyNumber, dialString)) {
+ Rlog.i(LOG_TAG, "dial: shouldNumberBePlacedOnIms = false");
+ throw new CallStateException(CS_FALLBACK);
+ }
+
+ int clirMode = dialArgs.clirMode;
+ int videoState = dialArgs.videoState;
if (DBG) log("dial clirMode=" + clirMode);
+ if (isEmergencyNumber) {
+ clirMode = CommandsInterface.CLIR_SUPPRESSION;
+ if (DBG) log("dial emergency call, set clirModIe=" + clirMode);
+ }
// note that this triggers call state changed notif
clearDisconnected();
@@ -887,7 +899,7 @@
holdBeforeDial = true;
// Cache the video state for pending MO call.
mPendingCallVideoState = videoState;
- mPendingIntentExtras = intentExtras;
+ mPendingIntentExtras = dialArgs.intentExtras;
switchWaitingOrHoldingAndActive();
}
@@ -916,12 +928,16 @@
checkForTestEmergencyNumber(dialString), this, mForegroundCall,
isEmergencyNumber);
mPendingMO.setVideoState(videoState);
+ if (dialArgs.rttTextStream != null) {
+ log("dial: setting RTT stream on mPendingMO");
+ mPendingMO.setCurrentRttTextStream(dialArgs.rttTextStream);
+ }
}
addConnection(mPendingMO);
if (!holdBeforeDial) {
if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
- dialInternal(mPendingMO, clirMode, videoState, intentExtras);
+ dialInternal(mPendingMO, clirMode, videoState, dialArgs.intentExtras);
} else {
try {
getEcbmInterface().exitEmergencyCallbackMode();
@@ -947,7 +963,39 @@
return false;
}
- return mImsManager.isServiceAvailable();
+ return mImsManager.isServiceReady();
+ }
+
+ private boolean shouldNumberBePlacedOnIms(boolean isEmergency, String number) {
+ int processCallResult;
+ try {
+ if (mImsManager != null) {
+ processCallResult = mImsManager.shouldProcessCall(isEmergency,
+ new String[]{number});
+ Rlog.i(LOG_TAG, "shouldProcessCall: number: " + Rlog.pii(LOG_TAG, number)
+ + ", result: " + processCallResult);
+ } else {
+ Rlog.w(LOG_TAG, "ImsManager unavailable, shouldProcessCall returning false.");
+ return false;
+ }
+ } catch (ImsException e) {
+ Rlog.w(LOG_TAG, "ImsService unavailable, shouldProcessCall returning false.");
+ return false;
+ }
+ switch(processCallResult) {
+ case MmTelFeature.PROCESS_CALL_IMS: {
+ // The ImsService wishes to place the call over IMS
+ return true;
+ }
+ case MmTelFeature.PROCESS_CALL_CSFB: {
+ Rlog.i(LOG_TAG, "shouldProcessCall: place over CSFB instead.");
+ return false;
+ }
+ default: {
+ Rlog.w(LOG_TAG, "shouldProcessCall returned unknown result.");
+ return false;
+ }
+ }
}
/**
@@ -958,16 +1006,21 @@
private void cacheCarrierConfiguration(int subId) {
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (carrierConfigManager == null) {
- loge("cacheCarrierConfiguration: No carrier config service found.");
+ if (carrierConfigManager == null
+ || !SubscriptionController.getInstance().isActiveSubId(subId)) {
+ loge("cacheCarrierConfiguration: No carrier config service found" + " "
+ + "or not active subId = " + subId);
+ mCarrierConfigLoaded = false;
return;
}
PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
if (carrierConfig == null) {
loge("cacheCarrierConfiguration: Empty carrier config.");
+ mCarrierConfigLoaded = false;
return;
}
+ mCarrierConfigLoaded = true;
mAllowEmergencyVideoCalls =
carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
@@ -992,6 +1045,8 @@
CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL);
mSupportPauseVideo = carrierConfig.getBoolean(
CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
+ mAlwaysPlayRemoteHoldTone = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL);
String[] mappings = carrierConfig
.getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
@@ -1054,7 +1109,7 @@
// Always unmute when initiating a new call
setMute(false);
- int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ?
+ int serviceType = mPhoneNumberUtilsProxy.isEmergencyNumber(conn.getAddress()) ?
ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
//TODO(vt): Is this sufficient? At what point do we know the video state of the call?
@@ -1062,8 +1117,7 @@
try {
String[] callees = new String[] { conn.getAddress() };
- ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
- serviceType, callType);
+ ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType);
profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
// Translate call subject intent-extra from Telecom-specific extra key to the
@@ -1076,6 +1130,10 @@
);
}
+ if (conn.hasRttTextStream()) {
+ profile.mMediaProfile.mRttMode = ImsStreamMediaProfile.RTT_MODE_FULL;
+ }
+
if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) {
profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL,
intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL));
@@ -1094,8 +1152,7 @@
// being sent to the lower layers/to the network.
}
- ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,
- callees, mImsCallListener);
+ ImsCall imsCall = mImsManager.makeCall(profile, callees, mImsCallListener);
conn.setImsCall(imsCall);
mMetrics.writeOnImsCallStart(mPhone.getPhoneId(),
@@ -1480,6 +1537,16 @@
return mDesiredMute;
}
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ * @param result the result message to send when done. If non-null, the {@link Message} must
+ * contain a valid {@link android.os.Messenger} in the {@link Message#replyTo} field,
+ * since this can be used across IPC boundaries.
+ */
public void sendDtmf(char c, Message result) {
if (DBG) log("sendDtmf");
@@ -1657,13 +1724,12 @@
}
String[] callees = new String[] { ussdString };
- ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
+ ImsCallProfile profile = mImsManager.createCallProfile(
ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
ImsCallProfile.DIALSTRING_USSD);
- mUssdSession = mImsManager.makeCall(mServiceId, profile,
- callees, mImsUssdListener);
+ mUssdSession = mImsManager.makeCall(profile, callees, mImsUssdListener);
} catch (ImsException e) {
loge("sendUSSD : " + e);
mPhone.sendErrorResponse(response, e);
@@ -1844,6 +1910,8 @@
int code = maybeRemapReasonCode(reasonInfo);
switch (code) {
+ case ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL:
+ return DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL;
case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
return DisconnectCause.NUMBER_UNREACHABLE;
@@ -1909,6 +1977,9 @@
}
}
+ case ImsReasonInfo.CODE_CALL_BARRED:
+ return DisconnectCause.CALL_BARRED;
+
case ImsReasonInfo.CODE_FDN_BLOCKED:
return DisconnectCause.FDN_BLOCKED;
@@ -1942,6 +2013,33 @@
case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE:
return DisconnectCause.EMERGENCY_PERM_FAILURE;
+ case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD:
+ return DisconnectCause.DIAL_MODIFIED_TO_USSD;
+
+ case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS:
+ return DisconnectCause.DIAL_MODIFIED_TO_SS;
+
+ case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL:
+ return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
+
+ case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO:
+ return DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO;
+
+ case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL:
+ return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL;
+
+ case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
+ return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO;
+
+ case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS:
+ return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS;
+
+ case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD:
+ return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD;
+
+ case ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER:
+ return DisconnectCause.UNOBTAINABLE_NUMBER;
+
default:
}
@@ -1957,7 +2055,7 @@
* @return true if the phone is in Emergency Callback mode, otherwise false
*/
private boolean isPhoneInEcbMode() {
- return mPhone.isInEcm();
+ return mPhone != null && mPhone.isInEcm();
}
/**
@@ -2024,6 +2122,7 @@
}
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
+ if (DBG) log("onCallUpdated: profile is " + imsCall.getCallProfile());
processCallStateChange(imsCall, conn.getCall().mState,
DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
mMetrics.writeImsCallState(mPhone.getPhoneId(),
@@ -2169,7 +2268,8 @@
if (mRingingCall.getState().isRinging()) {
// Drop pending MO. We should address incoming call first
mPendingMO = null;
- } else if (mPendingMO != null) {
+ } else if (mPendingMO != null
+ && mPendingMO.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
sendEmptyMessage(EVENT_DIAL_PENDINGMO);
}
}
@@ -2206,7 +2306,9 @@
if (mShouldUpdateImsConfigOnDisconnect) {
// Ensure we update the IMS config when the call is disconnected; we delayed this
// because a video call was paused.
- ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+ if (mImsManager != null) {
+ mImsManager.updateImsServiceConfig(true);
+ }
mShouldUpdateImsConfigOnDisconnect = false;
}
}
@@ -2282,6 +2384,9 @@
mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
}
+ if (imsCall != mCallExpectedToResume) {
+ mCallExpectedToResume = null;
+ }
}
mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
}
@@ -2374,45 +2479,14 @@
// Type of notification: 0 = MO; 1 = MT
// Refer SuppServiceNotification class documentation.
supp.notificationType = 1;
- supp.code = SuppServiceNotification.MT_CODE_CALL_RETRIEVED;
+ supp.code = SuppServiceNotification.CODE_2_CALL_RETRIEVED;
mPhone.notifySuppSvcNotification(supp);
mMetrics.writeOnImsCallResumeReceived(mPhone.getPhoneId(), imsCall.getCallSession());
}
@Override
public void onCallHoldReceived(ImsCall imsCall) {
- if (DBG) log("onCallHoldReceived");
-
- ImsPhoneConnection conn = findConnection(imsCall);
- if (conn != null) {
- if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall) &&
- conn.getState() == ImsPhoneCall.State.ACTIVE) {
- mPhone.startOnHoldTone(conn);
- mOnHoldToneStarted = true;
- mOnHoldToneId = System.identityHashCode(conn);
- }
- conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
-
- boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useVideoPauseWorkaround);
- if (useVideoPauseWorkaround && mSupportPauseVideo &&
- VideoProfile.isVideo(conn.getVideoState())) {
- // If we are using the video pause workaround, the vendor IMS code has issues
- // with video pause signalling. In this case, when a call is remotely
- // held, the modem does not reliably change the video state of the call to be
- // paused.
- // As a workaround, we will turn on that bit now.
- conn.changeToPausedState();
- }
- }
-
- SuppServiceNotification supp = new SuppServiceNotification();
- // Type of notification: 0 = MO; 1 = MT
- // Refer SuppServiceNotification class documentation.
- supp.notificationType = 1;
- supp.code = SuppServiceNotification.MT_CODE_CALL_ON_HOLD;
- mPhone.notifySuppSvcNotification(supp);
- mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession());
+ ImsPhoneCallTracker.this.onCallHoldReceived(imsCall);
}
@Override
@@ -2543,10 +2617,18 @@
&& targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
&& targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
if (isHandoverFromWifi && imsCall.isVideoCall()) {
- if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
- log("onCallHandover :: notifying of WIFI to LTE handover.");
- conn.onConnectionEvent(
- TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
+ if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
+ if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+ log("onCallHandover :: notifying of WIFI to LTE handover.");
+ conn.onConnectionEvent(
+ TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
+ } else {
+ // Call has already had a disconnect request issued by the user or is
+ // in the process of disconnecting; do not inform the UI of this as it
+ // is not relevant.
+ log("onCallHandover :: skip notify of WIFI to LTE handover for "
+ + "disconnected call.");
+ }
}
if (!mIsDataEnabled && mIsViLteDataMetered) {
@@ -2604,10 +2686,6 @@
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
conn.onRttModifyResponseReceived(status);
- if (status ==
- android.telecom.Connection.RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
- conn.startRttTextProcessing();
- }
}
}
@@ -2701,83 +2779,59 @@
}
};
- /**
- * Listen to the IMS service state change
- *
- */
- private ImsConnectionStateListener mImsConnectionStateListener =
- new ImsConnectionStateListener() {
- @Override
- public void onImsConnected(int imsRadioTech) {
- if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
- mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
- mPhone.setImsRegistered(true);
- mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
- ImsConnectionState.State.CONNECTED, null);
- }
+ private final ImsRegistrationImplBase.Callback mImsRegistrationCallback =
+ new ImsRegistrationImplBase.Callback() {
- @Override
- public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
- if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
- resetImsCapabilities();
- mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mPhone.setImsRegistered(false);
- mPhone.processDisconnectReason(imsReasonInfo);
- mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
- ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
- }
+ @Override
+ public void onRegistered(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
+ mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
+ mPhone.setImsRegistered(true);
+ mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+ ImsConnectionState.State.CONNECTED, null);
+ }
- @Override
- public void onImsProgressing(int imsRadioTech) {
- if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
- mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mPhone.setImsRegistered(false);
- mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
- ImsConnectionState.State.PROGRESSING, null);
- }
+ @Override
+ public void onRegistering(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
+ mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ mPhone.setImsRegistered(false);
+ mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+ ImsConnectionState.State.PROGRESSING, null);
+ }
- @Override
- public void onImsResumed() {
- if (DBG) log("onImsResumed");
- mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
- mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
- ImsConnectionState.State.RESUMED, null);
- }
+ @Override
+ public void onDeregistered(ImsReasonInfo imsReasonInfo) {
+ if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
+ mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ mPhone.setImsRegistered(false);
+ mPhone.processDisconnectReason(imsReasonInfo);
+ mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+ ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
+ }
- @Override
- public void onImsSuspended() {
- if (DBG) log("onImsSuspended");
- mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
- ImsConnectionState.State.SUSPENDED, null);
+ @Override
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (DBG) log("registrationAssociatedUriChanged");
+ mPhone.setCurrentSubscriberUris(uris);
+ }
+ };
- }
-
- @Override
- public void onFeatureCapabilityChanged(int serviceClass,
- int[] enabledFeatures, int[] disabledFeatures) {
- if (DBG) log("onFeatureCapabilityChanged");
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = serviceClass;
- args.arg1 = enabledFeatures;
- args.arg2 = disabledFeatures;
- // Remove any pending updates; they're already stale, so no need to process them.
- removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
- obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
- }
-
- @Override
- public void onVoiceMessageCountChanged(int count) {
- if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
- mPhone.mDefaultPhone.setVoiceMessageCount(count);
- }
-
- @Override
- public void registrationAssociatedUriChanged(Uri[] uris) {
- if (DBG) log("registrationAssociatedUriChanged");
- mPhone.setCurrentSubscriberUris(uris);
- }
- };
+ private final ImsFeature.CapabilityCallback mImsCapabilityCallback =
+ new ImsFeature.CapabilityCallback() {
+ @Override
+ public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+ if (DBG) log("onCapabilitiesStatusChanged: " + config);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = config;
+ // Remove any pending updates; they're already stale, so no need to process
+ // them.
+ removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
+ obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
+ }
+ };
private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() {
@Override
@@ -2785,8 +2839,7 @@
@Override
public void onSetFeatureResponse(int feature, int network, int value, int status) {
- mMetrics.writeImsSetFeatureValue(
- mPhone.getPhoneId(), feature, network, value, status);
+ mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value);
}
@Override
@@ -2797,6 +2850,30 @@
};
+ private final ImsConfigImplBase.Callback mConfigCallback = new ImsConfigImplBase.Callback() {
+ @Override
+ public void onConfigChanged(int item, int value) {
+ sendConfigChangedIntent(item, Integer.toString(value));
+ }
+
+ @Override
+ public void onConfigChanged(int item, String value) {
+ sendConfigChangedIntent(item, value);
+ }
+
+ // send IMS_CONFIG_CHANGED intent for older services that do not implement the new callback
+ // interface.
+ private void sendConfigChangedIntent(int item, String value) {
+ log("sendConfigChangedIntent - [" + item + ", " + value + "]");
+ Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+ if (mPhone != null && mPhone.getContext() != null) {
+ mPhone.getContext().sendBroadcast(configChangedIntent);
+ }
+ }
+ };
+
public ImsUtInterface getUtInterface() throws ImsException {
if (mImsManager == null) {
throw getImsManagerIsNullException();
@@ -2923,14 +3000,6 @@
onDataEnabledChanged(p.first, p.second);
}
break;
- case EVENT_GET_IMS_SERVICE:
- try {
- getImsService();
- } catch (ImsException e) {
- loge("getImsService: " + e);
- retryGetImsService();
- }
- break;
case EVENT_CHECK_FOR_WIFI_HANDOVER:
if (msg.obj instanceof ImsCall) {
ImsCall imsCall = (ImsCall) msg.obj;
@@ -2946,15 +3015,24 @@
case EVENT_ON_FEATURE_CAPABILITY_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- int serviceClass = args.argi1;
- int[] enabledFeatures = (int[]) args.arg1;
- int[] disabledFeatures = (int[]) args.arg2;
- handleFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
+ ImsFeature.Capabilities capabilities = (ImsFeature.Capabilities) args.arg1;
+ handleFeatureCapabilityChanged(capabilities);
} finally {
args.recycle();
}
break;
}
+ case EVENT_SUPP_SERVICE_INDICATION: {
+ ar = (AsyncResult) msg.obj;
+ ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone);
+ try {
+ mmiCode.setIsSsInfo(true);
+ mmiCode.processImsSsData(ar);
+ } catch (ImsException e) {
+ Rlog.e(LOG_TAG, "Exception in parsing SS Data: " + e);
+ }
+ break;
+ }
}
}
@@ -2986,7 +3064,8 @@
// Uid -1 indicates this is for the overall device data usage.
vtDataUsageSnapshot.combineValues(new NetworkStats.Entry(
NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND,
- NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 0, delta / 2, 0, 0));
+ NetworkStats.TAG_NONE, NetworkStats.METERED_YES, isRoaming,
+ NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
mVtDataUsageSnapshot = vtDataUsageSnapshot;
// Create the snapshot of video call data usage per dialer. combineValues will create
@@ -3008,18 +3087,18 @@
// the only thing we can do here is splitting the usage into half rx and half tx.
vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(),
- NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, 1, isRoaming, delta / 2,
- 0, delta / 2, 0, 0));
+ NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, NetworkStats.METERED_YES,
+ isRoaming, NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot;
}
@Override
protected void log(String msg) {
- Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
protected void loge(String msg) {
- Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
+ Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
/**
@@ -3064,10 +3143,7 @@
pw.println(" mPhone=" + mPhone);
pw.println(" mDesiredMute=" + mDesiredMute);
pw.println(" mState=" + mState);
- for (int i = 0; i < mImsFeatureEnabled.length; i++) {
- pw.println(" " + mImsFeatureStrings[i] + ": "
- + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled"));
- }
+ pw.println(" mMmTelCapabilities=" + mMmTelCapabilities);
pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
@@ -3101,7 +3177,7 @@
throw getImsManagerIsNullException();
}
- ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId);
+ ImsEcbm ecbm = mImsManager.getEcbmInterface();
return ecbm;
}
@@ -3112,7 +3188,7 @@
}
try {
- return mImsManager.getMultiEndpointInterface(mServiceId);
+ return mImsManager.getMultiEndpointInterface();
} catch (ImsException e) {
if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) {
return null;
@@ -3128,16 +3204,21 @@
}
public boolean isVolteEnabled() {
- return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE];
+ boolean isRadioTechLte = getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+ return isRadioTechLte && mMmTelCapabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
}
public boolean isVowifiEnabled() {
- return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI];
+ boolean isRadioTechIwlan = getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+ return isRadioTechIwlan && mMmTelCapabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
}
public boolean isVideoCallEnabled() {
- return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE]
- || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]);
+ return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
}
@Override
@@ -3145,17 +3226,20 @@
return mState;
}
+ public int getImsRegistrationTech() {
+ if (mImsManager != null) {
+ return mImsManager.getRegistrationTech();
+ }
+ return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ }
+
private void retryGetImsService() {
// The binder connection is already up. Do not try to get it again.
if (mImsManager.isServiceAvailable()) {
return;
}
- //Leave mImsManager as null, then CallStateException will be thrown when dialing
- mImsManager = null;
- // Exponential backoff during retry, limited to 32 seconds.
- loge("getImsService: Retrying getting ImsService...");
- removeMessages(EVENT_GET_IMS_SERVICE);
- sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());
+
+ mImsManagerConnector.connect();
}
private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
@@ -3180,8 +3264,7 @@
}
public boolean isUtEnabled() {
- return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE]
- || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI]);
+ return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
}
/**
@@ -3307,8 +3390,8 @@
boolean isActiveCallVideo = activeCall.isVideoCall() ||
(mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall());
boolean isActiveCallOnWifi = activeCall.isWifiCall();
- boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform(mPhone.getContext()) &&
- mImsManager.isWfcEnabledByUser(mPhone.getContext());
+ boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform()
+ && mImsManager.isWfcEnabledByUser();
boolean isIncomingCallAudio = !incomingCall.isVideoCall();
log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo +
" isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" +
@@ -3388,7 +3471,6 @@
log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
- ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
mIsDataEnabled = enabled;
if (!mIsViLteDataMetered) {
@@ -3422,10 +3504,12 @@
// We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
// the carrier config has loaded and will deregister IMS.
if (!mShouldUpdateImsConfigOnDisconnect
- && reason != DataEnabledSettings.REASON_REGISTERED) {
+ && reason != DataEnabledSettings.REASON_REGISTERED && mCarrierConfigLoaded) {
// This will call into updateVideoCallFeatureValue and eventually all clients will be
// asynchronously notified that the availability of VT over LTE has changed.
- ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+ if (mImsManager != null) {
+ mImsManager.updateImsServiceConfig(true);
+ }
}
}
@@ -3507,7 +3591,8 @@
if (imsCall != null) {
if (conn.hasCapabilities(
Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
- Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
+ Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)
+ && !mSupportPauseVideo) {
// If the carrier supports downgrading to voice, then we can simply issue a
// downgrade to voice instead of terminating the call.
@@ -3530,9 +3615,7 @@
private void resetImsCapabilities() {
log("Resetting Capabilities...");
- for (int i = 0; i < mImsFeatureEnabled.length; i++) {
- mImsFeatureEnabled[i] = false;
- }
+ mMmTelCapabilities = new MmTelFeature.MmTelCapabilities();
}
/**
@@ -3557,70 +3640,86 @@
return mSupportDowngradeVtToAudio;
}
- private void handleFeatureCapabilityChanged(int serviceClass,
- int[] enabledFeatures, int[] disabledFeatures) {
- if (serviceClass == ImsServiceClass.MMTEL) {
- boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
- // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
- StringBuilder sb;
- if (DBG) {
- sb = new StringBuilder(120);
- sb.append("handleFeatureCapabilityChanged: ");
- }
- for (int i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
- i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI &&
- i < enabledFeatures.length; i++) {
- if (enabledFeatures[i] == i) {
- // If the feature is set to its own integer value it is enabled.
- if (DBG) {
- sb.append(mImsFeatureStrings[i]);
- sb.append(":true ");
- }
+ @VisibleForTesting
+ public void setDataEnabled(boolean isDataEnabled) {
+ mIsDataEnabled = isDataEnabled;
+ }
- mImsFeatureEnabled[i] = true;
- } else if (enabledFeatures[i]
- == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
- // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
- if (DBG) {
- sb.append(mImsFeatureStrings[i]);
- sb.append(":false ");
- }
+ private void handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities) {
+ boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
+ // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+ StringBuilder sb;
+ if (DBG) {
+ sb = new StringBuilder(120);
+ sb.append("handleFeatureCapabilityChanged: ");
+ }
+ sb.append(capabilities);
+ mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(capabilities);
+ boolean isVideoEnabled = isVideoCallEnabled();
+ boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
+ if (DBG) {
+ sb.append(" isVideoEnabledStateChanged=");
+ sb.append(isVideoEnabledStatechanged);
+ }
- mImsFeatureEnabled[i] = false;
- } else {
- // Feature has unknown state; it is not its own value or -1.
- if (DBG) {
- loge("handleFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i]
- + "): unexpectedValue=" + enabledFeatures[i]);
- }
- }
- }
- boolean isVideoEnabled = isVideoCallEnabled();
- boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
- if (DBG) {
- sb.append(" isVideoEnabledStateChanged=");
- sb.append(isVideoEnabledStatechanged);
- }
+ if (isVideoEnabledStatechanged) {
+ log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged="
+ + isVideoEnabled);
+ mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
+ }
- if (isVideoEnabledStatechanged) {
- log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" +
- isVideoEnabled);
- mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
- }
+ if (DBG) log(sb.toString());
- if (DBG) {
- log(sb.toString());
- }
-
- if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+ if (DBG) {
+ log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+ ", isVideoCallEnabled=" + isVideoCallEnabled()
+ ", isVowifiEnabled=" + isVowifiEnabled()
+ ", isUtEnabled=" + isUtEnabled());
-
- mPhone.onFeatureCapabilityChanged();
-
- mMetrics.writeOnImsCapabilities(
- mPhone.getPhoneId(), mImsFeatureEnabled);
}
+
+ mPhone.onFeatureCapabilityChanged();
+
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(),
+ mMmTelCapabilities);
+ }
+
+ @VisibleForTesting
+ public void onCallHoldReceived(ImsCall imsCall) {
+ if (DBG) log("onCallHoldReceived");
+
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ if (!mOnHoldToneStarted && (ImsPhoneCall.isLocalTone(imsCall)
+ || mAlwaysPlayRemoteHoldTone) &&
+ conn.getState() == ImsPhoneCall.State.ACTIVE) {
+ mPhone.startOnHoldTone(conn);
+ mOnHoldToneStarted = true;
+ mOnHoldToneId = System.identityHashCode(conn);
+ }
+ conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+
+ boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_useVideoPauseWorkaround);
+ if (useVideoPauseWorkaround && mSupportPauseVideo &&
+ VideoProfile.isVideo(conn.getVideoState())) {
+ // If we are using the video pause workaround, the vendor IMS code has issues
+ // with video pause signalling. In this case, when a call is remotely
+ // held, the modem does not reliably change the video state of the call to be
+ // paused.
+ // As a workaround, we will turn on that bit now.
+ conn.changeToPausedState();
+ }
+ }
+
+ SuppServiceNotification supp = new SuppServiceNotification();
+ supp.notificationType = SuppServiceNotification.NOTIFICATION_TYPE_CODE_2;
+ supp.code = SuppServiceNotification.CODE_2_CALL_ON_HOLD;
+ mPhone.notifySuppSvcNotification(supp);
+ mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession());
+ }
+
+ @VisibleForTesting
+ public void setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone) {
+ mAlwaysPlayRemoteHoldTone = shouldPlayRemoteHoldTone;
}
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 60a50b9..93a6d3e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -17,18 +17,20 @@
package com.android.internal.telephony.imsphone;
import android.content.Context;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
import android.service.carrier.CarrierIdentifier;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.data.DataProfile;
import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import java.util.List;
@@ -50,6 +52,14 @@
}
@Override
+ public void getIccSlotsStatus(Message result) {
+ }
+
+ @Override
+ public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
+ }
+
+ @Override
public void supplyIccPin(String pin, Message result) {
}
@@ -260,8 +270,9 @@
}
@Override
- public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result) {
}
@Override
@@ -548,10 +559,6 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message response) {
- }
-
- @Override
public void requestIccSimAuthentication(int authContext, String data, String aid, Message response) {
}
@@ -646,6 +653,26 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
+
+ @Override
+ public void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
+ }
+
+ @Override
+ public void stopNattKeepalive(int sessionHandle, Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 9800a44..d63b9c2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -23,6 +23,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Registrant;
@@ -33,12 +34,12 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.text.TextUtils;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
import com.android.ims.ImsException;
-import com.android.ims.ImsStreamMediaProfile;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
@@ -79,6 +80,7 @@
private UUSInfo mUusInfo;
private Handler mHandler;
+ private Messenger mHandlerMessenger;
private PowerManager.WakeLock mPartialWakeLock;
@@ -105,6 +107,8 @@
private ImsRttTextHandler mRttTextHandler;
private android.telecom.Connection.RttTextStream mRttTextStream;
+ // This reflects the RTT status as reported to us by the IMS stack via the media profile.
+ private boolean mIsRttEnabledForCall = false;
/**
* Used to indicate that this call is in the midst of being merged into a conference.
@@ -169,6 +173,7 @@
mOwner = ct;
mHandler = new MyHandler(mOwner.getLooper());
+ mHandlerMessenger = new Messenger(mHandler);
mImsCall = imsCall;
if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
@@ -343,6 +348,23 @@
}
@Override
+ public void deflect(String number) throws CallStateException {
+ if (mParent.getState().isRinging()) {
+ try {
+ if (mImsCall != null) {
+ mImsCall.deflect(number);
+ } else {
+ throw new CallStateException("no valid ims call to deflect");
+ }
+ } catch (ImsException e) {
+ throw new CallStateException("cannot deflect call");
+ }
+ } else {
+ throw new CallStateException("phone not ringing");
+ }
+ }
+
+ @Override
public void hangup() throws CallStateException {
if (!mDisconnected) {
mOwner.hangup(this);
@@ -428,6 +450,7 @@
mDisconnected = true;
mOwner.mPhone.notifyDisconnect(this);
+ notifyDisconnect(mCause);
if (mParent != null) {
changed = mParent.connectionDisconnected(this);
@@ -475,7 +498,9 @@
private boolean
processPostDialChar(char c) {
if (PhoneNumberUtils.is12Key(c)) {
- mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
+ Message dtmfComplete = mHandler.obtainMessage(EVENT_DTMF_DONE);
+ dtmfComplete.replyTo = mHandlerMessenger;
+ mOwner.sendDtmf(c, dtmfComplete);
} else if (c == PhoneNumberUtils.PAUSE) {
// From TS 22.101:
// It continues...
@@ -781,8 +806,8 @@
callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
if (Phone.DEBUG_PHONE) {
Rlog.d(LOG_TAG, "updateAddressDisplay: callId = " + getTelecomCallId()
- + " address = " + Rlog.pii(LOG_TAG, address) + " name = " + name
- + " nump = " + nump + " namep = " + namep);
+ + " address = " + Rlog.pii(LOG_TAG, address) + " name = "
+ + Rlog.pii(LOG_TAG, name) + " nump = " + nump + " namep = " + namep);
}
if (!mIsMergeInProcess) {
// Only process changes to the name and address when a merge is not in process.
@@ -873,6 +898,25 @@
mShouldIgnoreVideoStateChanges = true;
}
}
+
+ if (negotiatedCallProfile.mMediaProfile != null) {
+ mIsRttEnabledForCall = negotiatedCallProfile.mMediaProfile.isRttCall();
+
+ if (mIsRttEnabledForCall && mRttTextHandler == null) {
+ Rlog.d(LOG_TAG, "updateMediaCapabilities -- turning RTT on, profile="
+ + negotiatedCallProfile);
+ startRttTextProcessing();
+ onRttInitiated();
+ changed = true;
+ } else if (!mIsRttEnabledForCall && mRttTextHandler != null) {
+ Rlog.d(LOG_TAG, "updateMediaCapabilities -- turning RTT off, profile="
+ + negotiatedCallProfile);
+ mRttTextHandler.tearDown();
+ mRttTextHandler = null;
+ onRttTerminated();
+ changed = true;
+ }
+ }
}
// Check for a change in the capabilities for the call and update
@@ -946,31 +990,64 @@
imsCall.sendRttModifyResponse(accept);
if (accept) {
setCurrentRttTextStream(textStream);
- startRttTextProcessing();
} else {
Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
}
}
public void onRttMessageReceived(String message) {
- getOrCreateRttTextHandler().sendToInCall(message);
+ synchronized (this) {
+ if (mRttTextHandler == null) {
+ Rlog.w(LOG_TAG, "onRttMessageReceived: RTT text handler not available."
+ + " Attempting to create one.");
+ if (mRttTextStream == null) {
+ Rlog.e(LOG_TAG, "onRttMessageReceived:"
+ + " Unable to process incoming message. No textstream available");
+ return;
+ }
+ createRttTextHandler();
+ }
+ }
+ mRttTextHandler.sendToInCall(message);
}
public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
- mRttTextStream = rttTextStream;
+ synchronized (this) {
+ mRttTextStream = rttTextStream;
+ if (mRttTextHandler == null && mIsRttEnabledForCall) {
+ Rlog.i(LOG_TAG, "setCurrentRttTextStream: Creating a text handler");
+ createRttTextHandler();
+ }
+ }
+ }
+
+ public boolean hasRttTextStream() {
+ return mRttTextStream != null;
+ }
+
+ public boolean isRttEnabledForCall() {
+ return mIsRttEnabledForCall;
}
public void startRttTextProcessing() {
- getOrCreateRttTextHandler().initialize(mRttTextStream);
+ synchronized (this) {
+ if (mRttTextStream == null) {
+ Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
+ return;
+ }
+ if (mRttTextHandler != null) {
+ Rlog.w(LOG_TAG, "startRttTextProcessing: RTT text handler already exists");
+ return;
+ }
+ createRttTextHandler();
+ }
}
- private ImsRttTextHandler getOrCreateRttTextHandler() {
- if (mRttTextHandler != null) {
- return mRttTextHandler;
- }
+ // Make sure to synchronize on ImsPhoneConnection.this before calling.
+ private void createRttTextHandler() {
mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
(message) -> getImsCall().sendRttMessage(message));
- return mRttTextHandler;
+ mRttTextHandler.initialize(mRttTextStream);
}
/**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 46c43c9..87e51ec 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -36,12 +36,13 @@
import android.os.ResultReceiver;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import com.android.ims.ImsException;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsInfo;
import com.android.ims.ImsUtInterface;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
@@ -185,6 +186,7 @@
private boolean mIsCallFwdReg;
private State mState = State.PENDING;
private CharSequence mMessage;
+ private boolean mIsSsInfo = false;
//resgister/erasure of ICB (Specific DN)
static final String IcbDnMmi = "Specific Incoming Call Barring";
//ICB (Anonymous)
@@ -496,7 +498,7 @@
//***** Constructor
- ImsPhoneMmiCode(ImsPhone phone) {
+ public ImsPhoneMmiCode(ImsPhone phone) {
// The telephony unit-test cases may create ImsPhoneMmiCode's
// in secondary threads
super(phone.getHandler().getLooper());
@@ -723,7 +725,6 @@
boolean
isSupportedOverImsPhone() {
if (isShortCode()) return true;
- else if (mDialingNumber != null) return false;
else if (isServiceCodeCallForwarding(mSc)
|| isServiceCodeCallBarring(mSc)
|| (mSc != null && mSc.equals(SC_WAIT))
@@ -737,7 +738,9 @@
try {
int serviceClass = siToServiceClass(mSib);
if (serviceClass != SERVICE_CLASS_NONE
- && serviceClass != SERVICE_CLASS_VOICE) {
+ && serviceClass != SERVICE_CLASS_VOICE
+ && serviceClass != (SERVICE_CLASS_PACKET
+ + SERVICE_CLASS_DATA_SYNC)) {
return false;
}
return true;
@@ -846,13 +849,14 @@
String password = mSia;
String facility = scToBarringFacility(mSc);
+ int serviceClass = siToServiceClass(mSib);
if (isInterrogate()) {
mPhone.getCallBarring(facility,
- obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
+ obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this), serviceClass);
} else if (isActivate() || isDeactivate()) {
mPhone.setCallBarring(facility, isActivate(), password,
- obtainMessage(EVENT_SET_COMPLETE, this));
+ obtainMessage(EVENT_SET_COMPLETE, this), serviceClass);
} else {
throw new RuntimeException ("Invalid or Unsupported MMI Code");
}
@@ -1174,15 +1178,42 @@
}
private CharSequence getErrorMessage(AsyncResult ar) {
- if (ar.exception instanceof CommandException) {
- CommandException.Error err = ((CommandException) (ar.exception)).getCommandError();
- if (err == CommandException.Error.FDN_CHECK_FAILURE) {
- Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
+ CharSequence errorMessage;
+ return ((errorMessage = getMmiErrorMessage(ar)) != null) ? errorMessage :
+ mContext.getText(com.android.internal.R.string.mmiError);
+ }
+
+ private CharSequence getMmiErrorMessage(AsyncResult ar) {
+ if (ar.exception instanceof ImsException) {
+ switch (((ImsException) ar.exception).getCode()) {
+ case ImsReasonInfo.CODE_FDN_BLOCKED:
+ return mContext.getText(com.android.internal.R.string.mmiFdnError);
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial);
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd);
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
+ case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
+ default:
+ return null;
+ }
+ } else if (ar.exception instanceof CommandException) {
+ CommandException err = (CommandException) ar.exception;
+ if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
return mContext.getText(com.android.internal.R.string.mmiFdnError);
+ } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL) {
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial);
+ } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_USSD) {
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd);
+ } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_SS) {
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
+ } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO) {
+ return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
}
}
-
- return mContext.getText(com.android.internal.R.string.mmiError);
+ return null;
}
private CharSequence getScString() {
@@ -1223,11 +1254,12 @@
if (ar.exception instanceof CommandException) {
CommandException err = (CommandException) ar.exception;
+ CharSequence errorMessage;
if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
sb.append(mContext.getText(
com.android.internal.R.string.passwordIncorrect));
- } else if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
- sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
+ } else if ((errorMessage = getMmiErrorMessage(ar)) != null) {
+ sb.append(errorMessage);
} else if (err.getMessage() != null) {
sb.append(err.getMessage());
} else {
@@ -1245,10 +1277,18 @@
sb.append(mContext.getText(
com.android.internal.R.string.serviceEnabled));
}
+ // Record CLIR setting
+ if (mSc.equals(SC_CLIR)) {
+ mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION);
+ }
} else if (isDeactivate()) {
mState = State.COMPLETE;
sb.append(mContext.getText(
com.android.internal.R.string.serviceDisabled));
+ // Record CLIR setting
+ if (mSc.equals(SC_CLIR)) {
+ mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION);
+ }
} else if (isRegister()) {
mState = State.COMPLETE;
sb.append(mContext.getText(
@@ -1377,7 +1417,7 @@
infos = (CallForwardInfo[]) ar.result;
- if (infos.length == 0) {
+ if (infos == null || infos.length == 0) {
// Assume the default is not active
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
@@ -1439,11 +1479,11 @@
ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
if (ssInfo != null) {
Rlog.d(LOG_TAG,
- "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.mStatus);
- if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
+ "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus());
+ if (ssInfo.getStatus() == ImsSsInfo.DISABLED) {
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
mState = State.COMPLETE;
- } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
+ } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) {
sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
mState = State.COMPLETE;
} else {
@@ -1491,10 +1531,10 @@
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
} else {
for (int i = 0, s = infos.length; i < s ; i++) {
- if (infos[i].mIcbNum !=null) {
- sb.append("Num: " + infos[i].mIcbNum + " status: "
- + infos[i].mStatus + "\n");
- } else if (infos[i].mStatus == 1) {
+ if (infos[i].getIcbNum() != null) {
+ sb.append("Num: " + infos[i].getIcbNum() + " status: "
+ + infos[i].getStatus() + "\n");
+ } else if (infos[i].getStatus() == 1) {
sb.append(mContext.getText(com.android.internal
.R.string.serviceEnabled));
} else {
@@ -1657,8 +1697,9 @@
private CharSequence getImsErrorMessage(AsyncResult ar) {
ImsException error = (ImsException) ar.exception;
- if (error.getCode() == ImsReasonInfo.CODE_FDN_BLOCKED) {
- return mContext.getText(com.android.internal.R.string.mmiFdnError);
+ CharSequence errorMessage;
+ if ((errorMessage = getMmiErrorMessage(ar)) != null) {
+ return errorMessage;
} else if (error.getMessage() != null) {
return error.getMessage();
} else {
@@ -1671,6 +1712,165 @@
return this.mCallbackReceiver;
}
+ /**
+ * Process IMS SS Data received.
+ */
+ public void processImsSsData(AsyncResult data) throws ImsException {
+ try {
+ ImsSsData ssData = (ImsSsData) data.result;
+ parseSsData(ssData);
+ } catch (ClassCastException | NullPointerException ex) {
+ throw new ImsException("Exception in parsing SS Data", 0);
+ }
+ }
+
+ void parseSsData(ImsSsData ssData) {
+ ImsException ex = (ssData.result != ImsSsData.RESULT_SUCCESS)
+ ? new ImsException(null, ssData.result) : null;
+ mSc = getScStringFromScType(ssData.serviceType);
+ mAction = getActionStringFromReqType(ssData.requestType);
+ Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex);
+
+ switch (ssData.requestType) {
+ case ImsSsData.SS_ACTIVATION:
+ case ImsSsData.SS_DEACTIVATION:
+ case ImsSsData.SS_REGISTRATION:
+ case ImsSsData.SS_ERASURE:
+ if ((ssData.result == ImsSsData.RESULT_SUCCESS)
+ && ssData.isTypeUnConditional()) {
+ /*
+ * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and
+ * ssData.requestType is activate/register and
+ * ServiceClass is Voice/Video/None, turn on voice call forwarding.
+ */
+ boolean cffEnabled = ((ssData.requestType == ImsSsData.SS_ACTIVATION
+ || ssData.requestType == ImsSsData.SS_REGISTRATION)
+ && isServiceClassVoiceVideoOrNone(ssData.serviceClass));
+
+ Rlog.d(LOG_TAG, "setCallForwardingFlag cffEnabled: " + cffEnabled);
+ if (mIccRecords != null) {
+ Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info.");
+ //Only CF status is set here as part of activation/registration,
+ //number is not available until interrogation.
+ mPhone.setVoiceCallForwardingFlag(1, cffEnabled, null);
+ } else {
+ Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null.");
+ }
+ }
+ onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex));
+ break;
+ case ImsSsData.SS_INTERROGATION:
+ if (ssData.isTypeClir()) {
+ Rlog.d(LOG_TAG, "CLIR INTERROGATION");
+ Bundle clirInfo = new Bundle();
+ clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfo());
+ onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
+ } else if (ssData.isTypeCF()) {
+ Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
+ onQueryCfComplete(new AsyncResult(null, mPhone
+ .handleCfQueryResult(ssData.getCallForwardInfo()), ex));
+ } else if (ssData.isTypeBarring()) {
+ onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
+ } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
+ int[] suppServiceInfo = ssData.getSuppServiceInfo();
+ ImsSsInfo ssInfo = new ImsSsInfo(suppServiceInfo[0], null);
+ Bundle clInfo = new Bundle();
+ clInfo.putParcelable(UT_BUNDLE_KEY_SSINFO, ssInfo);
+ onSuppSvcQueryComplete(new AsyncResult(null, clInfo, ex));
+ } else if (ssData.isTypeIcb()) {
+ onIcbQueryComplete(new AsyncResult(null, ssData.getImsSpecificSuppServiceInfo(),
+ ex));
+ } else {
+ onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
+ }
+ break;
+ default:
+ Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.requestType);
+ break;
+ }
+ }
+
+ private String getScStringFromScType(int serviceType) {
+ switch (serviceType) {
+ case ImsSsData.SS_CFU:
+ return SC_CFU;
+ case ImsSsData.SS_CF_BUSY:
+ return SC_CFB;
+ case ImsSsData.SS_CF_NO_REPLY:
+ return SC_CFNRy;
+ case ImsSsData.SS_CF_NOT_REACHABLE:
+ return SC_CFNR;
+ case ImsSsData.SS_CF_ALL:
+ return SC_CF_All;
+ case ImsSsData.SS_CF_ALL_CONDITIONAL:
+ return SC_CF_All_Conditional;
+ case ImsSsData.SS_CLIP:
+ return SC_CLIP;
+ case ImsSsData.SS_CLIR:
+ return SC_CLIR;
+ case ImsSsData.SS_COLP:
+ return SC_COLP;
+ case ImsSsData.SS_COLR:
+ return SC_COLR;
+ case ImsSsData.SS_CNAP:
+ return SC_CNAP;
+ case ImsSsData.SS_WAIT:
+ return SC_WAIT;
+ case ImsSsData.SS_BAOC:
+ return SC_BAOC;
+ case ImsSsData.SS_BAOIC:
+ return SC_BAOIC;
+ case ImsSsData.SS_BAOIC_EXC_HOME:
+ return SC_BAOICxH;
+ case ImsSsData.SS_BAIC:
+ return SC_BAIC;
+ case ImsSsData.SS_BAIC_ROAMING:
+ return SC_BAICr;
+ case ImsSsData.SS_ALL_BARRING:
+ return SC_BA_ALL;
+ case ImsSsData.SS_OUTGOING_BARRING:
+ return SC_BA_MO;
+ case ImsSsData.SS_INCOMING_BARRING:
+ return SC_BA_MT;
+ case ImsSsData.SS_INCOMING_BARRING_DN:
+ return SC_BS_MT;
+ case ImsSsData.SS_INCOMING_BARRING_ANONYMOUS:
+ return SC_BAICa;
+ default:
+ return null;
+ }
+ }
+
+ private String getActionStringFromReqType(int requestType) {
+ switch (requestType) {
+ case ImsSsData.SS_ACTIVATION:
+ return ACTION_ACTIVATE;
+ case ImsSsData.SS_DEACTIVATION:
+ return ACTION_DEACTIVATE;
+ case ImsSsData.SS_INTERROGATION:
+ return ACTION_INTERROGATE;
+ case ImsSsData.SS_REGISTRATION:
+ return ACTION_REGISTER;
+ case ImsSsData.SS_ERASURE:
+ return ACTION_ERASURE;
+ default:
+ return null;
+ }
+ }
+
+ private boolean isServiceClassVoiceVideoOrNone(int serviceClass) {
+ return ((serviceClass == SERVICE_CLASS_NONE) || (serviceClass == SERVICE_CLASS_VOICE)
+ || (serviceClass == (SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC)));
+ }
+
+ public boolean isSsInfo() {
+ return mIsSsInfo;
+ }
+
+ public void setIsSsInfo(boolean isSsInfo) {
+ mIsSsInfo = isSsInfo;
+ }
+
/***
* 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/imsphone/ImsPullCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
index dbb4036..3bf304e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPullCall.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony.imsphone;
-import com.android.ims.ImsCallProfile;
-
/**
* Interface implemented by modules which are capable of performing a pull of an external call.
* This is used to break the dependency between {@link ImsExternalCallTracker} and
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
index 68a832b..3f9e8a8 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
@@ -22,7 +22,10 @@
import android.telecom.Connection;
import android.telephony.Rlog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
public class ImsRttTextHandler extends Handler {
public interface NetworkWriter {
@@ -57,9 +60,11 @@
// limiter. msg.arg1 should be set to N.
private static final int EXPIRE_SENT_CODEPOINT_COUNT = 5;
// Indicates that the call is over and we should teardown everything we have set up.
- private static final int TEARDOWN = 6;
+ private static final int TEARDOWN = 9999;
private Connection.RttTextStream mRttTextStream;
+ // For synchronization during testing
+ private CountDownLatch mReadNotifier;
private class InCallReaderThread extends Thread {
private final Connection.RttTextStream mReaderThreadRttTextStream;
@@ -95,6 +100,9 @@
}
obtainMessage(APPEND_TO_NETWORK_BUFFER, charsReceived)
.sendToTarget();
+ if (mReadNotifier != null) {
+ mReadNotifier.countDown();
+ }
}
}
}
@@ -200,4 +208,13 @@
public void tearDown() {
obtainMessage(TEARDOWN).sendToTarget();
}
+
+ @VisibleForTesting
+ public void setReadNotifier(CountDownLatch latch) {
+ mReadNotifier = latch;
+ }
+
+ public String getNetworkBufferText() {
+ return mBufferedTextToNetwork.toString();
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
new file mode 100644
index 0000000..8001eb6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
@@ -0,0 +1,81 @@
+/*
+ * 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.metrics;
+
+import android.os.BatteryStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.connectivity.CellularBatteryStats;
+import android.text.format.DateUtils;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
+
+/**
+ * ModemPowerMetrics holds the modem power metrics and converts them to ModemPowerStats proto buf.
+ * This proto buf is included in the Telephony proto buf.
+ */
+public class ModemPowerMetrics {
+
+ /* BatteryStats API */
+ private final IBatteryStats mBatteryStats;
+
+ public ModemPowerMetrics() {
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
+ }
+
+ /**
+ * Build ModemPowerStats proto
+ * @return ModemPowerStats
+ */
+ public ModemPowerStats buildProto() {
+ ModemPowerStats m = new ModemPowerStats();
+ CellularBatteryStats stats = getStats();
+ if (stats != null) {
+ m.loggingDurationMs = stats.getLoggingDurationMs();
+ m.energyConsumedMah = stats.getEnergyConsumedMaMs()
+ / ((double) DateUtils.HOUR_IN_MILLIS);
+ m.numPacketsTx = stats.getNumPacketsTx();
+ m.cellularKernelActiveTimeMs = stats.getKernelActiveTimeMs();
+ if (stats.getTimeInRxSignalStrengthLevelMs() != null
+ && stats.getTimeInRxSignalStrengthLevelMs().length > 0) {
+ m.timeInVeryPoorRxSignalLevelMs = stats.getTimeInRxSignalStrengthLevelMs()[0];
+ }
+ m.sleepTimeMs = stats.getSleepTimeMs();
+ m.idleTimeMs = stats.getIdleTimeMs();
+ m.rxTimeMs = stats.getRxTimeMs();
+ long[] t = stats.getTxTimeMs();
+ m.txTimeMs = new long[t.length];
+ for (int i = 0; i < t.length; i++) {
+ m.txTimeMs[i] = t[i];
+ }
+ }
+ return m;
+ }
+
+ /**
+ * Get cellular stats from batterystats
+ * @return CellularBatteryStats
+ */
+ private CellularBatteryStats getStats() {
+ try {
+ return mBatteryStats.getCellularBatteryStats();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index 530be1d..5004ce7 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -88,4 +88,9 @@
mEvent.format = format;
return this;
}
+
+ public SmsSessionEventBuilder setCellBroadcastMessage(SmsSession.Event.CBMessage msg) {
+ mEvent.cellBroadcastMessage = msg;
+ return this;
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
index 6530802..d28c035 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -20,6 +20,7 @@
import static com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import static com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching;
import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
@@ -116,4 +117,13 @@
mEvent.modemRestart = modemRestart;
return this;
}
+
+ /**
+ * Set and build Carrier Id Matching event
+ */
+ public TelephonyEventBuilder setCarrierIdMatching(CarrierIdMatching carrierIdMatching) {
+ mEvent.type = TelephonyEvent.Type.CARRIER_ID_MATCHING;
+ mEvent.carrierIdMatching = carrierIdMatching;
+ return this;
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 4c642c0..8b9d580 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -23,8 +23,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
-import static com.android.internal.telephony.RILConstants
- .RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
@@ -36,25 +35,29 @@
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN;
+import android.hardware.radio.V1_0.SetupDataCallResult;
import android.os.Build;
import android.os.SystemClock;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyHistogram;
+import android.telephony.TelephonyManager;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataService;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Base64;
import android.util.SparseArray;
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsCallSession;
import com.android.internal.telephony.GsmCdmaConnection;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.nano.TelephonyProto;
import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
@@ -66,13 +69,16 @@
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse
- .RilDataCallFailCause;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
+import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
@@ -230,6 +236,8 @@
return "DATA_STALL_ACTION";
case TelephonyEvent.Type.MODEM_RESTART:
return "MODEM_RESTART";
+ case TelephonyEvent.Type.CARRIER_ID_MATCHING:
+ return "CARRIER_ID_MATCHING";
default:
return Integer.toString(event);
}
@@ -407,6 +415,21 @@
}
pw.decreaseIndent();
+ pw.println("Modem power stats:");
+ pw.increaseIndent();
+ ModemPowerStats s = new ModemPowerMetrics().buildProto();
+ pw.println("Power log duration (battery time) (ms): " + s.loggingDurationMs);
+ pw.println("Energy consumed by modem (mAh): " + s.energyConsumedMah);
+ pw.println("Number of packets sent (tx): " + s.numPacketsTx);
+ pw.println("Amount of time kernel is active because of cellular data (ms): " +
+ s.cellularKernelActiveTimeMs);
+ pw.println("Amount of time spent in very poor rx signal level (ms): " +
+ s.timeInVeryPoorRxSignalLevelMs);
+ pw.println("Amount of time modem is in sleep (ms): " + s.sleepTimeMs);
+ pw.println("Amount of time modem is in idle (ms): " + s.idleTimeMs);
+ pw.println("Amount of time modem is in rx (ms): " + s.rxTimeMs);
+ pw.println("Amount of time modem is in tx (ms): " + Arrays.toString(s.txTimeMs));
+ pw.decreaseIndent();
}
/**
@@ -500,6 +523,9 @@
histogramProto.bucketCounters = rilHistogram.getBucketCounters();
}
+ // Build modem power metrics
+ log.modemPowerStats = new ModemPowerMetrics().buildProto();
+
// Log the starting system time
log.startTime = new TelephonyProto.Time();
log.startTime.systemTimestampMillis = mStartSystemTimeMs;
@@ -832,26 +858,30 @@
* @param feature IMS feature
* @param network The IMS network type
* @param value The settings. 0 indicates disabled, otherwise enabled.
- * @param status IMS operation status. See OperationStatusConstants for details.
*/
- public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value,
- int status) {
+ public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value) {
TelephonySettings s = new TelephonySettings();
- switch (feature) {
- case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE:
- s.isEnhanced4GLteModeEnabled = (value != 0);
- break;
- case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI:
- s.isWifiCallingEnabled = (value != 0);
- break;
- case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE:
- s.isVtOverLteEnabled = (value != 0);
- break;
- case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI:
- s.isVtOverWifiEnabled = (value != 0);
- break;
+ if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+ switch (feature) {
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+ s.isEnhanced4GLteModeEnabled = (value != 0);
+ break;
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+ s.isVtOverLteEnabled = (value != 0);
+ break;
+ }
+ } else if (network == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+ switch (feature) {
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+ s.isWifiCallingEnabled = (value != 0);
+ break;
+ case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
+ s.isVtOverWifiEnabled = (value != 0);
+ break;
+ }
}
+
// If the settings don't change, we don't log the event.
if (mLastSettings.get(phoneId) != null &&
Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
@@ -948,15 +978,27 @@
* @param phoneId Phone id
* @param capabilities IMS capabilities array
*/
- public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) {
+ public synchronized void writeOnImsCapabilities(int phoneId,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech,
+ MmTelFeature.MmTelCapabilities capabilities) {
ImsCapabilities cap = new ImsCapabilities();
- cap.voiceOverLte = capabilities[0];
- cap.videoOverLte = capabilities[1];
- cap.voiceOverWifi = capabilities[2];
- cap.videoOverWifi = capabilities[3];
- cap.utOverLte = capabilities[4];
- cap.utOverWifi = capabilities[5];
+ if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+ cap.voiceOverLte = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ cap.videoOverLte = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ cap.utOverLte = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+
+ } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+ cap.voiceOverWifi = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ cap.videoOverWifi = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ cap.utOverWifi = capabilities.isCapable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+ }
TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
@@ -1005,19 +1047,17 @@
* Write setup data call event
*
* @param phoneId Phone id
- * @param rilSerial RIL request serial number
* @param radioTechnology The data call RAT
- * @param profile Data profile
+ * @param profileId Data profile id
* @param apn APN in string
- * @param authType Authentication type
* @param protocol Data connection protocol
*/
- public void writeRilSetupDataCall(int phoneId, int rilSerial, int radioTechnology, int profile,
- String apn, int authType, String protocol) {
+ public void writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn,
+ String protocol) {
RilSetupDataCall setupDataCall = new RilSetupDataCall();
setupDataCall.rat = radioTechnology;
- setupDataCall.dataProfile = profile + 1; // off by 1 between proto and RIL constants.
+ setupDataCall.dataProfile = profileId + 1; // off by 1 between proto and RIL constants.
if (apn != null) {
setupDataCall.apn = apn;
}
@@ -1041,7 +1081,19 @@
RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
deactivateDataCall.cid = cid;
- deactivateDataCall.reason = reason + 1;
+ switch (reason) {
+ case DataService.REQUEST_REASON_NORMAL:
+ deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_NONE;
+ break;
+ case DataService.REQUEST_REASON_SHUTDOWN:
+ deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_RADIO_OFF;
+ break;
+ case DataService.REQUEST_REASON_HANDOVER:
+ deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_HANDOVER;
+ break;
+ default:
+ deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_UNKNOWN;
+ }
addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
deactivateDataCall).build());
@@ -1059,12 +1111,12 @@
for (int i = 0; i < dcsList.size(); i++) {
dataCalls[i] = new RilDataCall();
- dataCalls[i].cid = dcsList.get(i).cid;
- if (!TextUtils.isEmpty(dcsList.get(i).ifname)) {
- dataCalls[i].iframe = dcsList.get(i).ifname;
+ dataCalls[i].cid = dcsList.get(i).getCallId();
+ if (!TextUtils.isEmpty(dcsList.get(i).getIfname())) {
+ dataCalls[i].iframe = dcsList.get(i).getIfname();
}
- if (!TextUtils.isEmpty(dcsList.get(i).type)) {
- dataCalls[i].type = toPdpType(dcsList.get(i).type);
+ if (!TextUtils.isEmpty(dcsList.get(i).getType())) {
+ dataCalls[i].type = toPdpType(dcsList.get(i).getType());
}
}
@@ -1298,26 +1350,26 @@
* @param rilSerial RIL request serial number
* @param rilError RIL error
* @param rilRequest RIL request
- * @param response Data call response
+ * @param result Data call result
*/
private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError,
- int rilRequest, DataCallResponse response) {
+ int rilRequest, SetupDataCallResult result) {
RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse();
RilDataCall dataCall = new RilDataCall();
- if (response != null) {
+ if (result != null) {
setupDataCallResponse.status =
- (response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
- setupDataCallResponse.suggestedRetryTimeMillis = response.suggestedRetryTime;
+ (result.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : result.status);
+ setupDataCallResponse.suggestedRetryTimeMillis = result.suggestedRetryTime;
- dataCall.cid = response.cid;
- if (!TextUtils.isEmpty(response.type)) {
- dataCall.type = toPdpType(response.type);
+ dataCall.cid = result.cid;
+ if (!TextUtils.isEmpty(result.type)) {
+ dataCall.type = toPdpType(result.type);
}
- if (!TextUtils.isEmpty(response.ifname)) {
- dataCall.iframe = response.ifname;
+ if (!TextUtils.isEmpty(result.ifname)) {
+ dataCall.iframe = result.ifname;
}
}
setupDataCallResponse.call = dataCall;
@@ -1405,8 +1457,8 @@
int rilRequest, Object ret) {
switch (rilRequest) {
case RIL_REQUEST_SETUP_DATA_CALL:
- DataCallResponse dataCall = (DataCallResponse) ret;
- writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, dataCall);
+ SetupDataCallResult result = (SetupDataCallResult) ret;
+ writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, result);
break;
case RIL_REQUEST_DEACTIVATE_DATA_CALL:
writeOnDeactivateDataCallResponse(phoneId, rilError);
@@ -1653,7 +1705,7 @@
* @param tech SMS RAT
* @param format SMS format. Either 3GPP or 3GPP2.
*/
- public void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
+ public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
@@ -1672,7 +1724,7 @@
* @param tech SMS RAT
* @param format SMS format. Either 3GPP or 3GPP2.
*/
- public void writeRilNewSms(int phoneId, int tech, int format) {
+ public synchronized void writeRilNewSms(int phoneId, int tech, int format) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
@@ -1684,6 +1736,42 @@
}
/**
+ * Write incoming Broadcast SMS event
+ *
+ * @param phoneId Phone id
+ * @param format CB msg format
+ * @param priority CB msg priority
+ * @param isCMAS true if msg is CMAS
+ * @param isETWS true if msg is ETWS
+ * @param serviceCategory Service category of CB msg
+ */
+ public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS,
+ boolean isETWS, int serviceCategory) {
+ InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
+
+ int type;
+ if (isCMAS) {
+ type = SmsSession.Event.CBMessageType.CMAS;
+ } else if (isETWS) {
+ type = SmsSession.Event.CBMessageType.ETWS;
+ } else {
+ type = SmsSession.Event.CBMessageType.OTHER;
+ }
+
+ SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage();
+ cbm.msgFormat = format;
+ cbm.msgPriority = priority + 1;
+ cbm.msgType = type;
+ cbm.serviceCategory = serviceCategory;
+
+ smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED)
+ .setCellBroadcastMessage(cbm)
+ );
+
+ finishSmsSessionIfNeeded(smsSession);
+ }
+
+ /**
* Write NITZ event
*
* @param phoneId Phone id
@@ -1715,6 +1803,43 @@
addTelephonyEvent(event);
}
+ /**
+ * Write carrier identification matching event
+ *
+ * @param phoneId Phone id
+ * @param version Carrier table version
+ * @param cid Unique Carrier Id
+ * @param mccmnc MCC and MNC that map to this carrier
+ * @param gid1 Group id level 1
+ */
+ public void writeCarrierIdMatchingEvent(int phoneId, int version, int cid,
+ String mccmnc, String gid1) {
+ final CarrierIdMatching carrierIdMatching = new CarrierIdMatching();
+ final CarrierIdMatchingResult carrierIdMatchingResult = new CarrierIdMatchingResult();
+
+ if (cid != TelephonyManager.UNKNOWN_CARRIER_ID) {
+ // Successful matching event if result only has carrierId
+ carrierIdMatchingResult.carrierId = cid;
+ // Unknown Gid1 event if result only has carrierId, gid1 and mccmnc
+ if (gid1 != null) {
+ carrierIdMatchingResult.mccmnc = mccmnc;
+ carrierIdMatchingResult.gid1 = gid1;
+ }
+ } else {
+ // Unknown mccmnc event if result only has mccmnc
+ if (mccmnc != null) {
+ carrierIdMatchingResult.mccmnc = mccmnc;
+ }
+ }
+
+ carrierIdMatching.cidTableVersion = version;
+ carrierIdMatching.result = carrierIdMatchingResult;
+
+ TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierIdMatching(
+ carrierIdMatching).build();
+ addTelephonyEvent(event);
+ }
+
//TODO: Expand the proto in the future
public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}
@@ -1729,4 +1854,4 @@
public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session,
ImsReasonInfo reasonInfo) {}
public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {}
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index fe1d7c5..396fd4b 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -17,17 +17,19 @@
package com.android.internal.telephony.sip;
import android.content.Context;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
import android.service.carrier.CarrierIdentifier;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.data.DataProfile;
import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import java.util.List;
@@ -49,6 +51,14 @@
}
@Override
+ public void getIccSlotsStatus(Message result) {
+ }
+
+ @Override
+ public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
+ }
+
+ @Override
public void supplyIccPin(String pin, Message result) {
}
@@ -261,8 +271,9 @@
}
@Override
- public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result) {
}
@Override
@@ -549,10 +560,6 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message response) {
- }
-
- @Override
public void requestIccSimAuthentication(int authContext, String data, String aid, Message response) {
}
@@ -648,6 +655,26 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
+
+ @Override
+ public void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
+ }
+
+ @Override
+ public void stopNattKeepalive(int sessionHandle, Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index 9a4df0c..ff9e5a7 100644
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -183,9 +183,9 @@
}
@Override
- public Connection dial(String dialString, int videoState) throws CallStateException {
+ public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException {
synchronized (SipPhone.class) {
- return dialInternal(dialString, videoState);
+ return dialInternal(dialString, dialArgs.videoState);
}
}
@@ -1006,6 +1006,12 @@
}
}
+ @Override
+ public void deflect(String number) throws CallStateException {
+ //Deflect is not supported.
+ throw new CallStateException ("deflect is not supported for SipPhone");
+ }
+
private void log(String s) {
Rlog.d(SCN_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 7bdfad8..c72ea86 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -44,7 +44,6 @@
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.uicc.IccFileHandler;
import java.util.ArrayList;
@@ -69,13 +68,6 @@
@Override
public abstract Call getRingingCall();
- @Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
- throws CallStateException {
- // ignore UUSInfo
- return dial(dialString, videoState);
- }
-
void migrateFrom(SipPhoneBase from) {
super.migrateFrom(from);
migrate(mRingbackRegistrants, from.mRingbackRegistrants);
@@ -416,14 +408,6 @@
}
@Override
- public void getDataCallList(Message response) {
- }
-
- public List<DataConnection> getCurrentDataConnectionList () {
- return null;
- }
-
- @Override
public void updateServiceLocation() {
}
@@ -445,12 +429,17 @@
}
@Override
- public boolean getDataEnabled() {
+ public boolean isUserDataEnabled() {
return false;
}
@Override
- public void setDataEnabled(boolean enable) {
+ public boolean isDataEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setUserDataEnabled(boolean enable) {
}
public boolean enableDataConnectivity() {
@@ -547,4 +536,14 @@
@Override
public void setBroadcastEmergencyCallStateChanges(boolean broadcast) {
}
+
+ @Override
+ public void getCallBarring(String facility, String password, Message onComplete,
+ int serviceClass) {
+ }
+
+ @Override
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete, int serviceClass) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 0de2bec..1fc9a26 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -17,7 +17,10 @@
package com.android.internal.telephony.test;
import android.hardware.radio.V1_0.DataRegStateResult;
+import android.hardware.radio.V1_0.SetupDataCallResult;
import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
@@ -31,10 +34,13 @@
import android.telephony.CellInfoGsm;
import android.telephony.IccOpenLogicalChannelResponse;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkRegistrationState;
import android.telephony.NetworkScanRequest;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.BaseCommands;
@@ -48,12 +54,11 @@
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccSlotStatus;
import java.util.ArrayList;
import java.util.List;
@@ -114,14 +119,22 @@
int mNetworkType;
String mPin2Code;
boolean mSsnNotifyOn = false;
- private int mVoiceRegState = ServiceState.RIL_REG_STATE_HOME;
+ private int mVoiceRegState = NetworkRegistrationState.REG_STATE_HOME;
private int mVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
- private int mDataRegState = ServiceState.RIL_REG_STATE_HOME;
+ private int mDataRegState = NetworkRegistrationState.REG_STATE_HOME;
private int mDataRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+ public boolean mCssSupported;
+ public int mRoamingIndicator;
+ public int mSystemIsInPrl;
+ public int mDefaultRoamingIndicator;
+ public int mReasonForDenial;
+ public int mMaxDataCalls;
+
private SignalStrength mSignalStrength;
private List<CellInfo> mCellInfoList;
private int[] mImsRegState;
private IccCardStatus mIccCardStatus;
+ private IccSlotStatus mIccSlotStatus;
private IccIoResult mIccIoResultForApduLogicalChannel;
private int mChannelId = IccOpenLogicalChannelResponse.INVALID_CHANNEL;
@@ -131,7 +144,8 @@
int mNextCallFailCause = CallFailCause.NORMAL_CLEARING;
private boolean mDcSuccess = true;
- private DataCallResponse mDcResponse;
+ private SetupDataCallResult mSetupDataCallResult;
+ private boolean mIsRadioPowerFailResponse = false;
//***** Constructor
public
@@ -166,13 +180,34 @@
@Override
public void getIccCardStatus(Message result) {
- if(mIccCardStatus!=null) {
+ SimulatedCommandsVerifier.getInstance().getIccCardStatus(result);
+ if (mIccCardStatus != null) {
resultSuccess(result, mIccCardStatus);
} else {
resultFail(result, null, new RuntimeException("IccCardStatus not set"));
}
}
+ public void setIccSlotStatus(IccSlotStatus iccSlotStatus) {
+ mIccSlotStatus = iccSlotStatus;
+ }
+
+ @Override
+ public void getIccSlotsStatus(Message result) {
+ SimulatedCommandsVerifier.getInstance().getIccSlotsStatus(result);
+ if (mIccSlotStatus != null) {
+ resultSuccess(result, mIccSlotStatus);
+ } else {
+ resultFail(result, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ }
+ }
+
+ @Override
+ public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
+ unimplemented(result);
+ }
+
@Override
public void supplyIccPin(String pin, Message result) {
if (mSimLockedState != SimLockState.REQUIRE_PIN) {
@@ -836,8 +871,7 @@
SignalStrength.INVALID, // lteRsrq
SignalStrength.INVALID, // lteRssnr
SignalStrength.INVALID, // lteCqi
- SignalStrength.INVALID, // tdScdmaRscp
- true // gsmFlag
+ SignalStrength.INVALID // tdScdmaRscp
);
}
@@ -930,6 +964,11 @@
VoiceRegStateResult ret = new VoiceRegStateResult();
ret.regState = mVoiceRegState;
ret.rat = mVoiceRadioTech;
+ ret.cssSupported = mCssSupported;
+ ret.roamingIndicator = mRoamingIndicator;
+ ret.systemIsInPrl = mSystemIsInPrl;
+ ret.defaultRoamingIndicator = mDefaultRoamingIndicator;
+ ret.reasonForDenial = mReasonForDenial;
resultSuccess(result, ret);
}
@@ -956,6 +995,8 @@
DataRegStateResult ret = new DataRegStateResult();
ret.regState = mDataRegState;
ret.rat = mDataRadioTech;
+ ret.maxDataCalls = mMaxDataCalls;
+ ret.reasonDataDenied = mReasonForDenial;
resultSuccess(result, ret);
}
@@ -1083,8 +1124,8 @@
unimplemented(response);
}
- public void setDataCallResponse(final boolean success, final DataCallResponse dcResponse) {
- mDcResponse = dcResponse;
+ public void setDataCallResult(final boolean success, final SetupDataCallResult dcResult) {
+ mSetupDataCallResult = dcResult;
mDcSuccess = success;
}
@@ -1096,21 +1137,37 @@
}
@Override
- public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result) {
- SimulatedCommandsVerifier.getInstance().setupDataCall(radioTechnology, dataProfile,
- isRoaming, allowRoaming, result);
+ SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
+ isRoaming, allowRoaming, reason, linkProperties, result);
- if (mDcResponse == null) {
- mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", "rmnet_data7",
- "12.34.56.78", "98.76.54.32", "11.22.33.44", "", 1440);
+ if (mSetupDataCallResult == null) {
+ try {
+ mSetupDataCallResult = new SetupDataCallResult();
+ mSetupDataCallResult.status = 0;
+ mSetupDataCallResult.suggestedRetryTime = -1;
+ mSetupDataCallResult.cid = 1;
+ mSetupDataCallResult.active = 2;
+ mSetupDataCallResult.type = "IP";
+ mSetupDataCallResult.ifname = "rmnet_data7";
+ mSetupDataCallResult.addresses = "12.34.56.78";
+ mSetupDataCallResult.dnses = "98.76.54.32";
+ mSetupDataCallResult.gateways = "11.22.33.44";
+ mSetupDataCallResult.pcscf = "";
+ mSetupDataCallResult.mtu = 1440;
+ } catch (Exception e) {
+
+ }
}
if (mDcSuccess) {
- resultSuccess(result, mDcResponse);
+ resultSuccess(result, mSetupDataCallResult);
} else {
- resultFail(result, mDcResponse, new RuntimeException("Setup data call failed!"));
+ resultFail(result, mSetupDataCallResult,
+ new RuntimeException("Setup data call failed!"));
}
}
@@ -1188,11 +1245,17 @@
@Override
public void setRadioPower(boolean on, Message result) {
+ if (mIsRadioPowerFailResponse) {
+ resultFail(result, null, new RuntimeException("setRadioPower failed!"));
+ return;
+ }
+
if(on) {
setRadioState(RadioState.RADIO_ON);
} else {
setRadioState(RadioState.RADIO_OFF);
}
+ resultSuccess(result, null);
}
@@ -1807,11 +1870,6 @@
}
@Override
- public void requestIsimAuthentication(String nonce, Message response) {
- unimplemented(response);
- }
-
- @Override
public void requestIccSimAuthentication(int authContext, String data, String aid, Message response) {
unimplemented(response);
}
@@ -1964,7 +2022,6 @@
public void startLceService(int report_interval_ms, boolean pullMode, Message result) {
SimulatedCommandsVerifier.getInstance().startLceService(report_interval_ms, pullMode,
result);
- unimplemented(result);
}
@Override
@@ -1978,6 +2035,16 @@
}
@Override
+ public void registerForLceInfo(Handler h, int what, Object obj) {
+ SimulatedCommandsVerifier.getInstance().registerForLceInfo(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForLceInfo(Handler h) {
+ SimulatedCommandsVerifier.getInstance().unregisterForLceInfo(h);
+ }
+
+ @Override
public void getModemActivityInfo(Message result) {
unimplemented(result);
}
@@ -2085,8 +2152,7 @@
SignalStrength.INVALID, // lteRsrq
SignalStrength.INVALID, // lteRssnr
SignalStrength.INVALID, // lteCqi
- SignalStrength.INVALID, // tdScdmaRscp
- true // gsmFlag
+ SignalStrength.INVALID // tdScdmaRscp
);
}
@@ -2153,6 +2219,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
@@ -2169,4 +2246,42 @@
super.setOnRestrictedStateChanged(h, what, obj);
SimulatedCommandsVerifier.getInstance().setOnRestrictedStateChanged(h, what, obj);
}
+
+ public void setRadioPowerFailResponse(boolean fail) {
+ mIsRadioPowerFailResponse = fail;
+ }
+
+ @Override
+ public void registerForIccRefresh(Handler h, int what, Object obj) {
+ super.registerForIccRefresh(h, what, obj);
+ SimulatedCommandsVerifier.getInstance().registerForIccRefresh(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForIccRefresh(Handler h) {
+ super.unregisterForIccRefresh(h);
+ SimulatedCommandsVerifier.getInstance().unregisterForIccRefresh(h);
+ }
+
+ @Override
+ public void registerForNattKeepaliveStatus(Handler h, int what, Object obj) {
+ SimulatedCommandsVerifier.getInstance().registerForNattKeepaliveStatus(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForNattKeepaliveStatus(Handler h) {
+ SimulatedCommandsVerifier.getInstance().unregisterForNattKeepaliveStatus(h);
+ }
+
+ @Override
+ public void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
+ SimulatedCommandsVerifier.getInstance().startNattKeepalive(
+ contextId, packetData, intervalMillis, result);
+ }
+
+ @Override
+ public void stopNattKeepalive(int sessionHandle, Message result) {
+ SimulatedCommandsVerifier.getInstance().stopNattKeepalive(sessionHandle, result);
+ }
}
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index d746259..f43cee0 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -16,17 +16,19 @@
package com.android.internal.telephony.test;
+import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
import android.service.carrier.CarrierIdentifier;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.data.DataProfile;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-import com.android.internal.telephony.dataconnection.DataProfile;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import java.util.List;
@@ -136,6 +138,16 @@
}
@Override
+ public void registerForIccSlotStatusChanged(Handler h, int what, Object obj) {
+
+ }
+
+ @Override
+ public void unregisterForIccSlotStatusChanged(Handler h) {
+
+ }
+
+ @Override
public void registerForCallStateChanged(Handler h, int what, Object obj) {
}
@@ -1151,8 +1163,9 @@
}
@Override
- public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, Message result) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ Message result) {
}
@Override
@@ -1186,13 +1199,16 @@
}
@Override
- public int getLteOnCdmaMode() {
- return 0;
+ public void getIccSlotsStatus(Message result) {
}
@Override
- public void requestIsimAuthentication(String nonce, Message response) {
+ public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
+ }
+ @Override
+ public int getLteOnCdmaMode() {
+ return 0;
}
@Override
@@ -1217,6 +1233,14 @@
}
@Override
+ public void registerForPhysicalChannelConfiguration(Handler h, int what, Object obj) {
+ }
+
+ @Override
+ public void unregisterForPhysicalChannelConfiguration(Handler h) {
+ }
+
+ @Override
public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
}
@@ -1390,6 +1414,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
@@ -1408,4 +1443,21 @@
@Override
public void unregisterForCarrierInfoForImsiEncryption(Handler h) {
}
+
+ @Override
+ public void registerForNattKeepaliveStatus(Handler h, int what, Object obj) {
+ }
+
+ @Override
+ public void unregisterForNattKeepaliveStatus(Handler h) {
+ }
+
+ @Override
+ public void startNattKeepalive(
+ int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
+ }
+
+ @Override
+ public void stopNattKeepalive(int sessionHandle, Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java b/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
index 735ed17..62c2a77 100644
--- a/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
+++ b/src/java/com/android/internal/telephony/test/TestConferenceEventPackageParser.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony.test;
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsConferenceState;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -26,7 +26,6 @@
import android.util.Log;
import android.util.Xml;
-import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -47,9 +46,9 @@
* </pre>
* <p>
* Note: This XML format is similar to the information stored in the
- * {@link com.android.ims.ImsConferenceState} parcelable. The {@code status} values expected in the
+ * {@link ImsConferenceState} parcelable. The {@code status} values expected in the
* XML are those found in the {@code ImsConferenceState} class (e.g.
- * {@link com.android.ims.ImsConferenceState#STATUS_CONNECTED}).
+ * {@link ImsConferenceState#STATUS_CONNECTED}).
* <p>
* Place a file formatted similar to above in /data/data/com.android.phone/files/ and invoke the
* following command while you have an ongoing IMS call:
@@ -79,10 +78,10 @@
/**
* Parses the conference event package XML file and returns an
- * {@link com.android.ims.ImsConferenceState} instance containing the participants described in
+ * {@link ImsConferenceState} instance containing the participants described in
* the XML file.
*
- * @return The {@link com.android.ims.ImsConferenceState} instance.
+ * @return The {@link ImsConferenceState} instance.
*/
public ImsConferenceState parse() {
ImsConferenceState conferenceState = new ImsConferenceState();
diff --git a/src/java/com/android/internal/telephony/uicc/AnswerToReset.java b/src/java/com/android/internal/telephony/uicc/AnswerToReset.java
new file mode 100644
index 0000000..b94df6d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/AnswerToReset.java
@@ -0,0 +1,512 @@
+/*
+ * 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.uicc;
+
+import android.annotation.Nullable;
+import android.telephony.Rlog;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class parses an Answer To Reset (ATR) message.
+ * The ATR message structure is defined in standard ISO/IEC 7816-3. The eUICC related ATR message
+ * is defined in standard ETSI TS 102 221 V14.0.0.
+ */
+public class AnswerToReset {
+ private static final String TAG = "AnswerToReset";
+ private static final boolean VDBG = false; // STOPSHIP if true
+ private static final int TAG_CARD_CAPABILITIES = 0x07;
+ private static final int EXTENDED_APDU_INDEX = 2;
+ private static final int B7_MASK = 0x40;
+ private static final int B2_MASK = 0x02;
+
+ public static final byte EUICC_SUPPORTED = (byte) 0x82;
+ public static final byte DIRECT_CONVENTION = (byte) 0x3B;
+ public static final byte INVERSE_CONVENTION = (byte) 0x3F;
+ public static final int INTERFACE_BYTES_MASK = 0xF0;
+ public static final int T_MASK = 0x0F;
+ public static final int T_VALUE_FOR_GLOBAL_INTERFACE = 15;
+ public static final int TA_MASK = 0x10;
+ public static final int TB_MASK = 0x20;
+ public static final int TC_MASK = 0x40;
+ public static final int TD_MASK = 0x80;
+
+ private boolean mIsDirectConvention;
+ private boolean mOnlyTEqualsZero = true;
+ private boolean mIsEuiccSupported;
+ private byte mFormatByte;
+ private ArrayList<InterfaceByte> mInterfaceBytes = new ArrayList<>();
+ private HistoricalBytes mHistoricalBytes;
+ private Byte mCheckByte;
+
+ /** Class for the historical bytes. */
+ public static class HistoricalBytes {
+ private static final int TAG_MASK = 0xF0;
+ private static final int LENGTH_MASK = 0x0F;
+
+ private final byte[] mRawData;
+ private final ArrayMap<Integer, byte[]> mNodes;
+ private final byte mCategory;
+
+ /** Get the category of the historical bytes. */
+ public byte getCategory() {
+ return mCategory;
+ }
+
+ /** Get the raw data of historical bytes. */
+ public byte[] getRawData() {
+ return mRawData;
+ }
+
+ /** Get the value of the tag in historical bytes. */
+ @Nullable
+ public byte[] getValue(int tag) {
+ return mNodes.get(tag);
+ }
+
+ @Nullable
+ private static HistoricalBytes parseHistoricalBytes(
+ byte[] originalData, int startIndex, int length) {
+ if (length <= 0 || startIndex + length > originalData.length) {
+ return null;
+ }
+ ArrayMap<Integer, byte[]> nodes = new ArrayMap<>();
+
+ // Start parsing from second byte since the first one is category.
+ int index = startIndex + 1;
+ while (index < startIndex + length && index > 0) {
+ index = parseLtvNode(index, nodes, originalData, startIndex + length - 1);
+ }
+ if (index < 0) {
+ return null;
+ }
+ byte[] rawData = new byte[length];
+ System.arraycopy(originalData, startIndex, rawData, 0, length);
+ return new HistoricalBytes(rawData, nodes, rawData[0]);
+ }
+
+ private HistoricalBytes(byte[] rawData, ArrayMap<Integer, byte[]> nodes, byte category) {
+ mRawData = rawData;
+ mNodes = nodes;
+ mCategory = category;
+ }
+
+ private static int parseLtvNode(
+ int index, ArrayMap<Integer, byte[]> nodes, byte[] data, int lastByteIndex) {
+ if (index > lastByteIndex) {
+ return -1;
+ }
+ int tag = (data[index] & TAG_MASK) >> 4;
+ int length = data[index++] & LENGTH_MASK;
+ if (index + length > lastByteIndex + 1 || length == 0) {
+ return -1;
+ }
+ byte[] value = new byte[length];
+ System.arraycopy(data, index, value, 0, length);
+ nodes.put(tag, value);
+ return index + length;
+ }
+ }
+
+
+ /**
+ * Returns an AnswerToReset by parsing the input atr string, return null if the parsing fails.
+ */
+ public static AnswerToReset parseAtr(String atr) {
+ AnswerToReset answerToReset = new AnswerToReset();
+ if (answerToReset.parseAtrString(atr)) {
+ return answerToReset;
+ }
+ return null;
+ }
+
+ private AnswerToReset() {}
+
+ private static String byteToStringHex(Byte b) {
+ return b == null ? null : IccUtils.byteToHex(b);
+ }
+
+ private void checkIsEuiccSupported() {
+ // eUICC is supported only if the value of the first tB after T=15 is 82.
+ for (int i = 0; i < mInterfaceBytes.size() - 1; i++) {
+ if (mInterfaceBytes.get(i).getTD() != null
+ && (mInterfaceBytes.get(i).getTD() & T_MASK) == T_VALUE_FOR_GLOBAL_INTERFACE
+ && mInterfaceBytes.get(i + 1).getTB() != null
+ && mInterfaceBytes.get(i + 1).getTB() == EUICC_SUPPORTED) {
+ mIsEuiccSupported = true;
+ return;
+ }
+ }
+ }
+
+ private int parseConventionByte(byte[] atrBytes, int index) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the convention byte.");
+ return -1;
+ }
+ byte value = atrBytes[index];
+ if (value == DIRECT_CONVENTION) {
+ mIsDirectConvention = true;
+ } else if (value == INVERSE_CONVENTION) {
+ mIsDirectConvention = false;
+ } else {
+ loge("Unrecognized convention byte " + IccUtils.byteToHex(value));
+ return -1;
+ }
+ return index + 1;
+ }
+
+ private int parseFormatByte(byte[] atrBytes, int index) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the format byte.");
+ return -1;
+ }
+ mFormatByte = atrBytes[index];
+ if (VDBG) log("mHistoricalBytesLength: " + (mFormatByte & T_MASK));
+ return index + 1;
+ }
+
+ private int parseInterfaceBytes(byte[] atrBytes, int index) {
+ // The first lastTD is actually not any TD but instead the format byte.
+ byte lastTD = mFormatByte;
+ while (true) {
+ if (VDBG) log("lastTD: " + IccUtils.byteToHex(lastTD));
+ // Parse the interface bytes.
+ if ((lastTD & INTERFACE_BYTES_MASK) == 0) {
+ break;
+ }
+
+ InterfaceByte interfaceByte = new InterfaceByte();
+ if (VDBG) log("lastTD & TA_MASK: " + IccUtils.byteToHex((byte) (lastTD & TA_MASK)));
+ if ((lastTD & TA_MASK) != 0) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the byte for TA.");
+ return -1;
+ }
+ interfaceByte.setTA(atrBytes[index]);
+ index++;
+ }
+ if (VDBG) log("lastTD & TB_MASK: " + IccUtils.byteToHex((byte) (lastTD & TB_MASK)));
+ if ((lastTD & TB_MASK) != 0) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the byte for TB.");
+ return -1;
+ }
+ interfaceByte.setTB(atrBytes[index]);
+ index++;
+ }
+ if (VDBG) log("lastTD & TC_MASK: " + IccUtils.byteToHex((byte) (lastTD & TC_MASK)));
+ if ((lastTD & TC_MASK) != 0) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the byte for TC.");
+ return -1;
+ }
+ interfaceByte.setTC(atrBytes[index]);
+ index++;
+ }
+ if (VDBG) log("lastTD & TD_MASK: " + IccUtils.byteToHex((byte) (lastTD & TD_MASK)));
+ if ((lastTD & TD_MASK) != 0) {
+ if (index >= atrBytes.length) {
+ loge("Failed to read the byte for TD.");
+ return -1;
+ }
+ interfaceByte.setTD(atrBytes[index]);
+ index++;
+ }
+ mInterfaceBytes.add(interfaceByte);
+ Byte newTD = interfaceByte.getTD();
+ if (VDBG) log("index=" + index + ", " + toString());
+ if (newTD == null) {
+ break;
+ }
+ lastTD = newTD;
+ // Parse the T values from all the TD, here we only check whether T is equal to any
+ // other values other than 0, since the check byte can be absent only when T is
+ // equal to 0.
+ if ((lastTD & T_MASK) != 0) {
+ mOnlyTEqualsZero = false;
+ }
+ }
+ return index;
+ }
+
+ private int parseHistoricalBytes(byte[] atrBytes, int index) {
+ int length = mFormatByte & T_MASK;
+ if (length + index > atrBytes.length) {
+ loge("Failed to read the historical bytes.");
+ return -1;
+ }
+ if (length > 0) {
+ mHistoricalBytes = HistoricalBytes.parseHistoricalBytes(atrBytes, index, length);
+ }
+ return index + length;
+ }
+
+ private int parseCheckBytes(byte[] atrBytes, int index) {
+ if (index < atrBytes.length) {
+ mCheckByte = atrBytes[index];
+ index++;
+ } else {
+ if (!mOnlyTEqualsZero) {
+ loge("Check byte must be present because T equals to values other than 0.");
+ return -1;
+ } else {
+ log("Check byte can be absent because T=0.");
+ }
+ }
+ return index;
+ }
+
+ private boolean parseAtrString(String atr) {
+ if (atr == null) {
+ loge("The input ATR string can not be null");
+ return false;
+ }
+
+ if (atr.length() % 2 != 0) {
+ loge("The length of input ATR string " + atr.length() + " is not even.");
+ return false;
+ }
+
+ if (atr.length() < 4) {
+ loge("Valid ATR string must at least contains TS and T0.");
+ return false;
+ }
+
+ byte[] atrBytes = IccUtils.hexStringToBytes(atr);
+ if (atrBytes == null) {
+ return false;
+ }
+
+ int index = parseConventionByte(atrBytes, 0);
+ if (index == -1) {
+ return false;
+ }
+
+ index = parseFormatByte(atrBytes, index);
+ if (index == -1) {
+ return false;
+ }
+
+ index = parseInterfaceBytes(atrBytes, index);
+ if (index == -1) {
+ return false;
+ }
+
+ index = parseHistoricalBytes(atrBytes, index);
+ if (index == -1) {
+ return false;
+ }
+
+ index = parseCheckBytes(atrBytes, index);
+ if (index == -1) {
+ return false;
+ }
+
+ if (index != atrBytes.length) {
+ loge("Unexpected bytes after the check byte.");
+ return false;
+ }
+ log("Successfully parsed the ATR string " + atr + " into " + toString());
+ checkIsEuiccSupported();
+ return true;
+ }
+
+ /**
+ * This class holds the interface bytes.
+ */
+ public static class InterfaceByte {
+ private Byte mTA;
+ private Byte mTB;
+ private Byte mTC;
+ private Byte mTD;
+
+ @Nullable
+ public Byte getTA() {
+ return mTA;
+ }
+
+ @Nullable
+ public Byte getTB() {
+ return mTB;
+ }
+
+ @Nullable
+ public Byte getTC() {
+ return mTC;
+ }
+
+ @Nullable
+ public Byte getTD() {
+ return mTD;
+ }
+
+ public void setTA(Byte tA) {
+ mTA = tA;
+ }
+
+ public void setTB(Byte tB) {
+ mTB = tB;
+ }
+
+ public void setTC(Byte tC) {
+ mTC = tC;
+ }
+
+ public void setTD(Byte tD) {
+ mTD = tD;
+ }
+
+ private InterfaceByte() {
+ mTA = null;
+ mTB = null;
+ mTC = null;
+ mTD = null;
+ }
+
+ @VisibleForTesting
+ public InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD) {
+ this.mTA = tA;
+ this.mTB = tB;
+ this.mTC = tC;
+ this.mTD = tD;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ InterfaceByte ib = (InterfaceByte) o;
+ return (Objects.equals(mTA, ib.getTA())
+ && Objects.equals(mTB, ib.getTB())
+ && Objects.equals(mTC, ib.getTC())
+ && Objects.equals(mTD, ib.getTD()));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTA, mTB, mTC, mTD);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("{");
+ sb.append("TA=").append(byteToStringHex(mTA)).append(",");
+ sb.append("TB=").append(byteToStringHex(mTB)).append(",");
+ sb.append("TC=").append(byteToStringHex(mTC)).append(",");
+ sb.append("TD=").append(byteToStringHex(mTD));
+ sb.append("}");
+ return sb.toString();
+ }
+ };
+
+ private static void log(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private static void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
+
+ public byte getConventionByte() {
+ return mIsDirectConvention ? DIRECT_CONVENTION : INVERSE_CONVENTION;
+ }
+
+ public byte getFormatByte() {
+ return mFormatByte;
+ }
+
+ public List<InterfaceByte> getInterfaceBytes() {
+ return mInterfaceBytes;
+ }
+
+ @Nullable
+ public HistoricalBytes getHistoricalBytes() {
+ return mHistoricalBytes;
+ }
+
+ @Nullable
+ public Byte getCheckByte() {
+ return mCheckByte;
+ }
+
+ public boolean isEuiccSupported() {
+ return mIsEuiccSupported;
+ }
+
+ /** Return whether the extended LC & LE is supported. */
+ public boolean isExtendedApduSupported() {
+ if (mHistoricalBytes == null) {
+ return false;
+ }
+ byte[] cardCapabilities = mHistoricalBytes.getValue(TAG_CARD_CAPABILITIES);
+ if (cardCapabilities == null || cardCapabilities.length < 3) {
+ return false;
+ }
+ if (mIsDirectConvention) {
+ return (cardCapabilities[EXTENDED_APDU_INDEX] & B7_MASK) > 0;
+ } else {
+ return (cardCapabilities[EXTENDED_APDU_INDEX] & B2_MASK) > 0;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("AnswerToReset:{");
+ sb.append("mConventionByte=")
+ .append(IccUtils.byteToHex(getConventionByte())).append(",");
+ sb.append("mFormatByte=").append(byteToStringHex(mFormatByte)).append(",");
+ sb.append("mInterfaceBytes={");
+ for (InterfaceByte ib : mInterfaceBytes) {
+ sb.append(ib.toString());
+ }
+ sb.append("},");
+ sb.append("mHistoricalBytes={");
+ if (mHistoricalBytes != null) {
+ for (byte b : mHistoricalBytes.getRawData()) {
+ sb.append(IccUtils.byteToHex(b)).append(",");
+ }
+ }
+ sb.append("},");
+ sb.append("mCheckByte=").append(byteToStringHex(mCheckByte));
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Dump
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("AnswerToReset:");
+ pw.println(toString());
+ pw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
index 18d2937..61379d2 100644
--- a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
+++ b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
@@ -43,10 +43,13 @@
* Sample xml:
* <carrierTestOverrides>
<carrierTestOverride key="isInTestMode" value="true"/>
+ <carrierTestOverride key="mccmnc" value="310010" />
<carrierTestOverride key="gid1" value="bae0000000000000"/>
<carrierTestOverride key="gid2" value="ffffffffffffffff"/>
<carrierTestOverride key="imsi" value="310010123456789"/>
<carrierTestOverride key="spn" value="Verizon"/>
+ <carrierTestOverride key="pnn" value="Verizon network"/>
+ <carrierTestOverride key="iccid" value="123456789012345678901"/>
</carrierTestOverrides>
*/
static final String DATA_CARRIER_TEST_OVERRIDE_PATH =
@@ -56,10 +59,13 @@
static final String CARRIER_TEST_XML_ITEM_KEY = "key";
static final String CARRIER_TEST_XML_ITEM_VALUE = "value";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE = "isInTestMode";
+ static final String CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC = "mccmnc";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID1 = "gid1";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID2 = "gid2";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI = "imsi";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_SPN = "spn";
+ static final String CARRIER_TEST_XML_ITEM_KEY_STRING_PNN = "pnn";
+ static final String CARRIER_TEST_XML_ITEM_KEY_STRING_ICCID = "iccid";
private HashMap<String, String> mCarrierTestParamMap;
@@ -118,6 +124,51 @@
}
}
+ String getFakePnnHomeName() {
+ try {
+ String pnn = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_PNN);
+ Rlog.d(LOG_TAG, "reading pnn from CarrierTestConfig file: " + pnn);
+ return pnn;
+ } catch (NullPointerException e) {
+ Rlog.w(LOG_TAG, "No pnn in CarrierTestConfig file ");
+ return null;
+ }
+ }
+
+ String getFakeIccid() {
+ try {
+ String iccid = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_ICCID);
+ Rlog.d(LOG_TAG, "reading iccid from CarrierTestConfig file: " + iccid);
+ return iccid;
+ } catch (NullPointerException e) {
+ Rlog.w(LOG_TAG, "No iccid in CarrierTestConfig file ");
+ return null;
+ }
+ }
+
+ String getFakeMccMnc() {
+ try {
+ String mccmnc = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC);
+ Rlog.d(LOG_TAG, "reading mccmnc from CarrierTestConfig file: " + mccmnc);
+ return mccmnc;
+ } catch (NullPointerException e) {
+ Rlog.w(LOG_TAG, "No mccmnc in CarrierTestConfig file ");
+ return null;
+ }
+ }
+
+ void override(String mccmnc, String imsi, String iccid, String gid1, String gid2, String pnn,
+ String spn) {
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE, "true");
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC, mccmnc);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI, imsi);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_ICCID, iccid);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_GID1, gid1);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_GID2, gid2);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_PNN, pnn);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_SPN, spn);
+ }
+
private void loadCarrierTestOverrides() {
FileReader carrierTestConfigReader;
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
deleted file mode 100644
index 241f211..0000000
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ /dev/null
@@ -1,986 +0,0 @@
-/*
- * Copyright (C) 2012 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.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.os.UserHandle;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.CommandsInterface.RadioState;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.IntentBroadcaster;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
-import com.android.internal.telephony.uicc.IccCardStatus.CardState;
-import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * @Deprecated use {@link UiccController}.getUiccCard instead.
- *
- * The Phone App assumes that there is only one icc card, and one icc application
- * available at a time. Moreover, it assumes such object (represented with IccCard)
- * is available all the time (whether {@link RILConstants#RIL_REQUEST_GET_SIM_STATUS} returned
- * or not, whether card has desired application or not, whether there really is a card in the
- * slot or not).
- *
- * UiccController, however, can handle multiple instances of icc objects (multiple
- * {@link UiccCardApplication}, multiple {@link IccFileHandler}, multiple {@link IccRecords})
- * created and destroyed dynamically during phone operation.
- *
- * This class implements the IccCard interface that is always available (right after default
- * phone object is constructed) to expose the current (based on voice radio technology)
- * application on the uicc card, so that external apps won't break.
- */
-
-public class IccCardProxy extends Handler implements IccCard {
- private static final boolean DBG = true;
- private static final String LOG_TAG = "IccCardProxy";
-
- private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
- private static final int EVENT_RADIO_ON = 2;
- private static final int EVENT_ICC_CHANGED = 3;
- private static final int EVENT_ICC_ABSENT = 4;
- private static final int EVENT_ICC_LOCKED = 5;
- private static final int EVENT_APP_READY = 6;
- private static final int EVENT_RECORDS_LOADED = 7;
- private static final int EVENT_IMSI_READY = 8;
- private static final int EVENT_NETWORK_LOCKED = 9;
- private static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 11;
-
- private static final int EVENT_ICC_RECORD_EVENTS = 500;
- private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
- private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
- private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 503;
-
- private Integer mPhoneId = null;
-
- private final Object mLock = new Object();
- private Context mContext;
- private CommandsInterface mCi;
- private TelephonyManager mTelephonyManager;
-
- private RegistrantList mAbsentRegistrants = new RegistrantList();
- private RegistrantList mPinLockedRegistrants = new RegistrantList();
- private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
-
- private int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
- private UiccController mUiccController = null;
- private UiccCard mUiccCard = null;
- private UiccCardApplication mUiccApplication = null;
- private IccRecords mIccRecords = null;
- private CdmaSubscriptionSourceManager mCdmaSSM = null;
- private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE;
- private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast
- // ACTION_SIM_STATE_CHANGED intents
- private boolean mInitialized = false;
- private State mExternalState = State.UNKNOWN;
-
- public static final String ACTION_INTERNAL_SIM_STATE_CHANGED = "android.intent.action.internal_sim_state_changed";
-
- public IccCardProxy(Context context, CommandsInterface ci, int phoneId) {
- if (DBG) log("ctor: ci=" + ci + " phoneId=" + phoneId);
- mContext = context;
- mCi = ci;
- mPhoneId = phoneId;
- mTelephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
- mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context,
- ci, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
- mUiccController = UiccController.getInstance();
- mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
- ci.registerForOn(this,EVENT_RADIO_ON, null);
- ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
-
- resetProperties();
- }
-
- public void dispose() {
- synchronized (mLock) {
- log("Disposing");
- //Cleanup icc references
- mUiccController.unregisterForIccChanged(this);
- mUiccController = null;
- mCi.unregisterForOn(this);
- mCi.unregisterForOffOrNotAvailable(this);
- mCdmaSSM.dispose(this);
- }
- }
-
- /*
- * The card application that the external world sees will be based on the
- * voice radio technology only!
- */
- public void setVoiceRadioTech(int radioTech) {
- synchronized (mLock) {
- if (DBG) {
- log("Setting radio tech " + ServiceState.rilRadioTechnologyToString(radioTech));
- }
- if (ServiceState.isGsm(radioTech)) {
- mCurrentAppType = UiccController.APP_FAM_3GPP;
- } else {
- mCurrentAppType = UiccController.APP_FAM_3GPP2;
- }
- updateQuietMode();
- }
- }
-
- /**
- * In case of 3gpp2 we need to find out if subscription used is coming from
- * NV in which case we shouldn't broadcast any sim states changes.
- */
- private void updateQuietMode() {
- synchronized (mLock) {
- boolean oldQuietMode = mQuietMode;
- boolean newQuietMode;
- int cdmaSource = Phone.CDMA_SUBSCRIPTION_UNKNOWN;
- boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
- == PhoneConstants.LTE_ON_CDMA_TRUE;
- if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
- newQuietMode = false;
- if (DBG) log("updateQuietMode: 3GPP subscription -> newQuietMode=" + newQuietMode);
- } else {
- if (isLteOnCdmaMode) {
- log("updateQuietMode: is cdma/lte device, force IccCardProxy into 3gpp mode");
- mCurrentAppType = UiccController.APP_FAM_3GPP;
- }
- cdmaSource = mCdmaSSM != null ?
- mCdmaSSM.getCdmaSubscriptionSource() : Phone.CDMA_SUBSCRIPTION_UNKNOWN;
-
- newQuietMode = (cdmaSource == Phone.CDMA_SUBSCRIPTION_NV)
- && (mCurrentAppType == UiccController.APP_FAM_3GPP2)
- && !isLteOnCdmaMode;
- if (DBG) {
- log("updateQuietMode: cdmaSource=" + cdmaSource
- + " mCurrentAppType=" + mCurrentAppType
- + " isLteOnCdmaMode=" + isLteOnCdmaMode
- + " newQuietMode=" + newQuietMode);
- }
- }
-
- if (mQuietMode == false && newQuietMode == true) {
- // Last thing to do before switching to quiet mode is
- // broadcast ICC_READY
- log("Switching to QuietMode.");
- setExternalState(State.READY);
- mQuietMode = newQuietMode;
- } else if (mQuietMode == true && newQuietMode == false) {
- if (DBG) {
- log("updateQuietMode: Switching out from QuietMode."
- + " Force broadcast of current state=" + mExternalState);
- }
- mQuietMode = newQuietMode;
- setExternalState(mExternalState, true);
- } else {
- if (DBG) log("updateQuietMode: no changes don't setExternalState");
- }
- if (DBG) {
- log("updateQuietMode: QuietMode is " + mQuietMode + " (app_type="
- + mCurrentAppType + " isLteOnCdmaMode=" + isLteOnCdmaMode
- + " cdmaSource=" + cdmaSource + ")");
- }
- mInitialized = true;
- sendMessage(obtainMessage(EVENT_ICC_CHANGED));
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RADIO_OFF_OR_UNAVAILABLE:
- mRadioState = mCi.getRadioState();
- updateExternalState();
- break;
- case EVENT_RADIO_ON:
- mRadioState = RadioState.RADIO_ON;
- if (!mInitialized) {
- updateQuietMode();
- } else {
- // updateQuietMode() triggers ICC_CHANGED, which eventually
- // calls updateExternalState; thus, we don't need this in the
- // above case
- updateExternalState();
- }
- break;
- case EVENT_ICC_CHANGED:
- if (mInitialized) {
- updateIccAvailability();
- }
- break;
- case EVENT_ICC_ABSENT:
- mAbsentRegistrants.notifyRegistrants();
- setExternalState(State.ABSENT);
- break;
- case EVENT_ICC_LOCKED:
- processLockedState();
- break;
- case EVENT_APP_READY:
- setExternalState(State.READY);
- break;
- case EVENT_RECORDS_LOADED:
- // Update the MCC/MNC.
- if (mIccRecords != null) {
- String operator = mIccRecords.getOperatorNumeric();
- log("operator=" + operator + " mPhoneId=" + mPhoneId);
-
- if (!TextUtils.isEmpty(operator)) {
- mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
- String countryCode = operator.substring(0,3);
- if (countryCode != null) {
- mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
- MccTable.countryCodeForMcc(Integer.parseInt(countryCode)));
- } else {
- loge("EVENT_RECORDS_LOADED Country code is null");
- }
- } else {
- loge("EVENT_RECORDS_LOADED Operator name is null");
- }
- }
- if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
- mUiccCard.registerForCarrierPrivilegeRulesLoaded(
- this, EVENT_CARRIER_PRIVILEGES_LOADED, null);
- } else {
- onRecordsLoaded();
- }
- break;
- case EVENT_IMSI_READY:
- broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_IMSI, null);
- break;
- case EVENT_NETWORK_LOCKED:
- mNetworkLockedRegistrants.notifyRegistrants();
- setExternalState(State.NETWORK_LOCKED);
- break;
- case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
- updateQuietMode();
- break;
- case EVENT_SUBSCRIPTION_ACTIVATED:
- log("EVENT_SUBSCRIPTION_ACTIVATED");
- onSubscriptionActivated();
- break;
-
- case EVENT_SUBSCRIPTION_DEACTIVATED:
- log("EVENT_SUBSCRIPTION_DEACTIVATED");
- onSubscriptionDeactivated();
- break;
-
- case EVENT_ICC_RECORD_EVENTS:
- if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
- AsyncResult ar = (AsyncResult)msg.obj;
- int eventCode = (Integer) ar.result;
- if (eventCode == SIMRecords.EVENT_SPN) {
- mTelephonyManager.setSimOperatorNameForPhone(
- mPhoneId, mIccRecords.getServiceProviderName());
- }
- }
- break;
-
- case EVENT_CARRIER_PRIVILEGES_LOADED:
- log("EVENT_CARRIER_PRIVILEGES_LOADED");
- if (mUiccCard != null) {
- mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
- }
- onRecordsLoaded();
- break;
-
- default:
- loge("Unhandled message with number: " + msg.what);
- break;
- }
- }
-
- private void onSubscriptionActivated() {
- updateIccAvailability();
- updateStateProperty();
- }
-
- private void onSubscriptionDeactivated() {
- resetProperties();
- updateIccAvailability();
- updateStateProperty();
- }
-
- private void onRecordsLoaded() {
- broadcastInternalIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
- }
-
- private void updateIccAvailability() {
- synchronized (mLock) {
- UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
- UiccCardApplication newApp = null;
- IccRecords newRecords = null;
- if (newCard != null) {
- newApp = newCard.getApplication(mCurrentAppType);
- if (newApp != null) {
- newRecords = newApp.getIccRecords();
- }
- }
-
- if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) {
- if (DBG) log("Icc changed. Reregistering.");
- unregisterUiccCardEvents();
- mUiccCard = newCard;
- mUiccApplication = newApp;
- mIccRecords = newRecords;
- registerUiccCardEvents();
- }
- updateExternalState();
- }
- }
-
- void resetProperties() {
- if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
- log("update icc_operator_numeric=" + "");
- mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, "");
- mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, "");
- mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, "");
- }
- }
-
- private void HandleDetectedState() {
- // CAF_MSIM SAND
-// setExternalState(State.DETECTED, false);
- }
-
- private void updateExternalState() {
-
- // mUiccCard could be null at bootup, before valid card states have
- // been received from UiccController.
- if (mUiccCard == null) {
- setExternalState(State.UNKNOWN);
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
- /*
- * Both IccCardProxy and UiccController are registered for
- * RadioState changes. When the UiccController receives a radio
- * state changed to Unknown it will dispose of all of the IccCard
- * objects, which will then notify the IccCardProxy and the null
- * object will force the state to unknown. However, because the
- * IccCardProxy is also registered for RadioState changes, it will
- * recieve that signal first. By triggering on radio state changes
- * directly, we reduce the time window during which the modem is
- * UNAVAILABLE but the IccStatus is reported as something valid.
- * This is not ideal.
- */
- if (mRadioState == RadioState.RADIO_UNAVAILABLE) {
- setExternalState(State.UNKNOWN);
- } else {
- setExternalState(State.ABSENT);
- }
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_ERROR) {
- setExternalState(State.CARD_IO_ERROR);
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_RESTRICTED) {
- setExternalState(State.CARD_RESTRICTED);
- return;
- }
-
- if (mUiccApplication == null) {
- setExternalState(State.NOT_READY);
- return;
- }
-
- // By process of elimination, the UICC Card State = PRESENT
- switch (mUiccApplication.getState()) {
- case APPSTATE_UNKNOWN:
- /*
- * APPSTATE_UNKNOWN is a catch-all state reported whenever the app
- * is not explicitly in one of the other states. To differentiate the
- * case where we know that there is a card present, but the APP is not
- * ready, we choose NOT_READY here instead of unknown. This is possible
- * in at least two cases:
- * 1) A transient during the process of the SIM bringup
- * 2) There is no valid App on the SIM to load, which can be the case with an
- * eSIM/soft SIM.
- */
- setExternalState(State.NOT_READY);
- break;
- case APPSTATE_DETECTED:
- HandleDetectedState();
- break;
- case APPSTATE_PIN:
- setExternalState(State.PIN_REQUIRED);
- break;
- case APPSTATE_PUK:
- PinState pin1State = mUiccApplication.getPin1State();
- if (pin1State.isPermBlocked()) {
- setExternalState(State.PERM_DISABLED);
- return;
- }
- setExternalState(State.PUK_REQUIRED);
- break;
- case APPSTATE_SUBSCRIPTION_PERSO:
- if (mUiccApplication.getPersoSubState() ==
- PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
- setExternalState(State.NETWORK_LOCKED);
- }
- // Otherwise don't change external SIM state.
- break;
- case APPSTATE_READY:
- setExternalState(State.READY);
- break;
- }
- }
-
- private void registerUiccCardEvents() {
- if (mUiccCard != null) {
- mUiccCard.registerForAbsent(this, EVENT_ICC_ABSENT, null);
- }
- if (mUiccApplication != null) {
- mUiccApplication.registerForReady(this, EVENT_APP_READY, null);
- mUiccApplication.registerForLocked(this, EVENT_ICC_LOCKED, null);
- mUiccApplication.registerForNetworkLocked(this, EVENT_NETWORK_LOCKED, null);
- }
- if (mIccRecords != null) {
- mIccRecords.registerForImsiReady(this, EVENT_IMSI_READY, null);
- mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
- mIccRecords.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
- }
- }
-
- private void unregisterUiccCardEvents() {
- if (mUiccCard != null) mUiccCard.unregisterForAbsent(this);
- if (mUiccCard != null) mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
- if (mUiccApplication != null) mUiccApplication.unregisterForReady(this);
- if (mUiccApplication != null) mUiccApplication.unregisterForLocked(this);
- if (mUiccApplication != null) mUiccApplication.unregisterForNetworkLocked(this);
- if (mIccRecords != null) mIccRecords.unregisterForImsiReady(this);
- if (mIccRecords != null) mIccRecords.unregisterForRecordsLoaded(this);
- if (mIccRecords != null) mIccRecords.unregisterForRecordsEvents(this);
- }
-
- private void updateStateProperty() {
- mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
- }
-
- private void broadcastIccStateChangedIntent(String value, String reason) {
- synchronized (mLock) {
- if (mPhoneId == null || !SubscriptionManager.isValidSlotIndex(mPhoneId)) {
- loge("broadcastIccStateChangedIntent: mPhoneId=" + mPhoneId
- + " is invalid; Return!!");
- return;
- }
-
- if (mQuietMode) {
- log("broadcastIccStateChangedIntent: QuietMode"
- + " NOT Broadcasting intent ACTION_SIM_STATE_CHANGED "
- + " value=" + value + " reason=" + reason);
- return;
- }
-
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- // TODO - we'd like this intent to have a single snapshot of all sim state,
- // but until then this should not use REPLACE_PENDING or we may lose
- // information
- // intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
- intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
- log("broadcastIccStateChangedIntent intent ACTION_SIM_STATE_CHANGED value=" + value
- + " reason=" + reason + " for mPhoneId=" + mPhoneId);
- IntentBroadcaster.getInstance().broadcastStickyIntent(intent, mPhoneId);
- }
- }
-
- private void broadcastInternalIccStateChangedIntent(String value, String reason) {
- synchronized (mLock) {
- if (mPhoneId == null) {
- loge("broadcastInternalIccStateChangedIntent: Card Index is not set; Return!!");
- return;
- }
-
- Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
- intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
- intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId); // SubId may not be valid.
- log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED value=" + value
- + " for mPhoneId : " + mPhoneId);
- ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
- }
- }
-
- private void setExternalState(State newState, boolean override) {
- synchronized (mLock) {
- if (mPhoneId == null || !SubscriptionManager.isValidSlotIndex(mPhoneId)) {
- loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
- return;
- }
-
- if (!override && newState == mExternalState) {
- log("setExternalState: !override and newstate unchanged from " + newState);
- return;
- }
- mExternalState = newState;
- log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
- mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
-
- // For locked states, we should be sending internal broadcast.
- if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(
- getIccStateIntentString(mExternalState))) {
- broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
- getIccStateReason(mExternalState));
- } else {
- broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
- getIccStateReason(mExternalState));
- }
- // TODO: Need to notify registrants for other states as well.
- if ( State.ABSENT == mExternalState) {
- mAbsentRegistrants.notifyRegistrants();
- }
- }
- }
-
- private void processLockedState() {
- synchronized (mLock) {
- if (mUiccApplication == null) {
- //Don't need to do anything if non-existent application is locked
- return;
- }
- PinState pin1State = mUiccApplication.getPin1State();
- if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
- setExternalState(State.PERM_DISABLED);
- return;
- }
-
- AppState appState = mUiccApplication.getState();
- switch (appState) {
- case APPSTATE_PIN:
- mPinLockedRegistrants.notifyRegistrants();
- setExternalState(State.PIN_REQUIRED);
- break;
- case APPSTATE_PUK:
- setExternalState(State.PUK_REQUIRED);
- break;
- case APPSTATE_DETECTED:
- case APPSTATE_READY:
- case APPSTATE_SUBSCRIPTION_PERSO:
- case APPSTATE_UNKNOWN:
- // Neither required
- break;
- }
- }
- }
-
- private void setExternalState(State newState) {
- setExternalState(newState, false);
- }
-
- public boolean getIccRecordsLoaded() {
- synchronized (mLock) {
- if (mIccRecords != null) {
- return mIccRecords.getRecordsLoaded();
- }
- return false;
- }
- }
-
- private String getIccStateIntentString(State state) {
- switch (state) {
- case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
- case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
- case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
- case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
- case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
- default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
- }
- }
-
- /**
- * Locked state have a reason (PIN, PUK, NETWORK, PERM_DISABLED, CARD_IO_ERROR)
- * @return reason
- */
- private String getIccStateReason(State state) {
- switch (state) {
- case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN;
- case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK;
- case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_LOCKED_NETWORK;
- case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED;
- case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
- case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
- default: return null;
- }
- }
-
- /* IccCard interface implementation */
- @Override
- public State getState() {
- synchronized (mLock) {
- return mExternalState;
- }
- }
-
- @Override
- public IccRecords getIccRecords() {
- synchronized (mLock) {
- return mIccRecords;
- }
- }
-
- @Override
- public IccFileHandler getIccFileHandler() {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- return mUiccApplication.getIccFileHandler();
- }
- return null;
- }
- }
-
- /**
- * Notifies handler of any transition into State.ABSENT
- */
- @Override
- public void registerForAbsent(Handler h, int what, Object obj) {
- synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mAbsentRegistrants.add(r);
-
- if (getState() == State.ABSENT) {
- r.notifyRegistrant();
- }
- }
- }
-
- @Override
- public void unregisterForAbsent(Handler h) {
- synchronized (mLock) {
- mAbsentRegistrants.remove(h);
- }
- }
-
- /**
- * Notifies handler of any transition into State.NETWORK_LOCKED
- */
- @Override
- public void registerForNetworkLocked(Handler h, int what, Object obj) {
- synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mNetworkLockedRegistrants.add(r);
-
- if (getState() == State.NETWORK_LOCKED) {
- r.notifyRegistrant();
- }
- }
- }
-
- @Override
- public void unregisterForNetworkLocked(Handler h) {
- synchronized (mLock) {
- mNetworkLockedRegistrants.remove(h);
- }
- }
-
- /**
- * Notifies handler of any transition into State.isPinLocked()
- */
- @Override
- public void registerForLocked(Handler h, int what, Object obj) {
- synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mPinLockedRegistrants.add(r);
-
- if (getState().isPinLocked()) {
- r.notifyRegistrant();
- }
- }
- }
-
- @Override
- public void unregisterForLocked(Handler h) {
- synchronized (mLock) {
- mPinLockedRegistrants.remove(h);
- }
- }
-
- @Override
- public void supplyPin(String pin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPin(pin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPuk(String puk, String newPin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPuk(puk, newPin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPin2(String pin2, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPin2(pin2, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPuk2(puk2, newPin2, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyNetworkDepersonalization(String pin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyNetworkDepersonalization(pin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("CommandsInterface is not set.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public boolean getIccLockEnabled() {
- synchronized (mLock) {
- /* defaults to false, if ICC is absent/deactivated */
- Boolean retValue = mUiccApplication != null ?
- mUiccApplication.getIccLockEnabled() : false;
- return retValue;
- }
- }
-
- @Override
- public boolean getIccFdnEnabled() {
- synchronized (mLock) {
- Boolean retValue = mUiccApplication != null ?
- mUiccApplication.getIccFdnEnabled() : false;
- return retValue;
- }
- }
-
- public boolean getIccFdnAvailable() {
- boolean retValue = mUiccApplication != null ? mUiccApplication.getIccFdnAvailable() : false;
- return retValue;
- }
-
- public boolean getIccPin2Blocked() {
- /* defaults to disabled */
- Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPin2Blocked() : false;
- return retValue;
- }
-
- public boolean getIccPuk2Blocked() {
- /* defaults to disabled */
- Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPuk2Blocked() : false;
- return retValue;
- }
-
- @Override
- public void setIccLockEnabled(boolean enabled, String password, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.setIccLockEnabled(enabled, password, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void setIccFdnEnabled(boolean enabled, String password, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.setIccFdnEnabled(enabled, password, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void changeIccLockPassword(String oldPassword, String newPassword, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.changeIccLockPassword(oldPassword, newPassword, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void changeIccFdnPassword(String oldPassword, String newPassword, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.changeIccFdnPassword(oldPassword, newPassword, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public String getServiceProviderName() {
- synchronized (mLock) {
- if (mIccRecords != null) {
- return mIccRecords.getServiceProviderName();
- }
- return null;
- }
- }
-
- @Override
- public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
- synchronized (mLock) {
- Boolean retValue = mUiccCard != null ? mUiccCard.isApplicationOnIcc(type) : false;
- return retValue;
- }
- }
-
- @Override
- public boolean hasIccCard() {
- synchronized (mLock) {
- if (mUiccCard != null && mUiccCard.getCardState() != CardState.CARDSTATE_ABSENT) {
- return true;
- }
- return false;
- }
- }
-
- private void setSystemProperty(String property, String value) {
- TelephonyManager.setTelephonyProperty(mPhoneId, property, value);
- }
-
- public IccRecords getIccRecord() {
- return mIccRecords;
- }
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-
- private void loge(String msg) {
- Rlog.e(LOG_TAG, msg);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("IccCardProxy: " + this);
- pw.println(" mContext=" + mContext);
- pw.println(" mCi=" + mCi);
- pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
- for (int i = 0; i < mAbsentRegistrants.size(); i++) {
- pw.println(" mAbsentRegistrants[" + i + "]="
- + ((Registrant)mAbsentRegistrants.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());
- }
- pw.println(" mCurrentAppType=" + mCurrentAppType);
- pw.println(" mUiccController=" + mUiccController);
- pw.println(" mUiccCard=" + mUiccCard);
- pw.println(" mUiccApplication=" + mUiccApplication);
- pw.println(" mIccRecords=" + mIccRecords);
- pw.println(" mCdmaSSM=" + mCdmaSSM);
- pw.println(" mRadioState=" + mRadioState);
- pw.println(" mQuietMode=" + mQuietMode);
- pw.println(" mInitialized=" + mInitialized);
- pw.println(" mExternalState=" + mExternalState);
-
- pw.flush();
- }
-}
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index f14f21d..3998eb2 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.uicc;
+import android.telephony.SubscriptionInfo;
+
/**
* See also RIL_CardStatus in include/telephony/ril.h
*
@@ -62,6 +64,9 @@
public int mGsmUmtsSubscriptionAppIndex;
public int mCdmaSubscriptionAppIndex;
public int mImsSubscriptionAppIndex;
+ public int physicalSlotIndex = UiccController.INVALID_SLOT_ID;
+ public String atr;
+ public String iccid;
public IccCardApplicationStatus[] mApplications;
@@ -142,6 +147,9 @@
sb.append(app == null ? "null" : app);
}
+ sb.append(",physical_slot_id=").append(physicalSlotIndex).append(",atr=").append(atr);
+ sb.append(",iccid=").append(SubscriptionInfo.givePrintableIccid(iccid));
+
sb.append("}");
return sb.toString();
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index 4750814..33dd381 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -471,6 +471,7 @@
response = lc.mOnLoaded;
if (processException(response, (AsyncResult) msg.obj)) {
+ loge("exception caught from EVENT_GET_RECORD_SIZE");
break;
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccIoResult.java b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
index 4a35e14..b1b6e75 100644
--- a/src/java/com/android/internal/telephony/uicc/IccIoResult.java
+++ b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc;
+import android.os.Build;
/**
* {@hide}
@@ -154,6 +155,12 @@
+ "CHV blocked"
+ "UNBLOCK CHV blocked";
case 0x50: return "increase cannot be performed, Max value reached";
+ // The definition for these status codes can be found in TS 31.102 7.3.1
+ case 0x62: return "authentication error, application specific";
+ case 0x64: return "authentication error, security context not supported";
+ case 0x65: return "key freshness failure";
+ case 0x66: return "authentication error, no memory space available";
+ case 0x67: return "authentication error, no memory space available in EF_MUK";
}
break;
case 0x9E: return null; // success
@@ -181,7 +188,9 @@
@Override
public String toString() {
return "IccIoResult sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
- + Integer.toHexString(sw2) + ((!success()) ? " Error: " + getErrorString() : "");
+ + Integer.toHexString(sw2) + " Payload: "
+ + ((Build.IS_DEBUGGABLE && Build.IS_ENG) ? payload : "*******")
+ + ((!success()) ? " Error: " + getErrorString() : "");
}
/**
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index af26f5c..6a630d6 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -23,11 +23,12 @@
import android.os.Registrant;
import android.os.RegistrantList;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -46,6 +47,7 @@
// ***** Instance Variables
protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
+ protected AtomicBoolean mLoaded = new AtomicBoolean(false);
protected Context mContext;
protected CommandsInterface mCi;
protected IccFileHandler mFh;
@@ -53,10 +55,13 @@
protected TelephonyManager mTelephonyManager;
protected RegistrantList mRecordsLoadedRegistrants = new RegistrantList();
+ protected RegistrantList mLockedRecordsLoadedRegistrants = new RegistrantList();
+ protected RegistrantList mNetworkLockedRecordsLoadedRegistrants = new RegistrantList();
protected RegistrantList mImsiReadyRegistrants = new RegistrantList();
protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
protected RegistrantList mNewSmsRegistrants = new RegistrantList();
protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
+ protected RegistrantList mSpnUpdatedRegistrants = new RegistrantList();
protected int mRecordsToLoad; // number of pending load requests
@@ -64,9 +69,18 @@
// ***** Cached SIM State; cleared on channel close
+ // SIM is not locked
+ protected static final int LOCKED_RECORDS_REQ_REASON_NONE = 0;
+ // Records requested for PIN or PUK locked SIM
+ protected static final int LOCKED_RECORDS_REQ_REASON_LOCKED = 1;
+ // Records requested for network locked SIM
+ protected static final int LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED = 2;
+
protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
+ protected int mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
protected String mIccId; // Includes only decimals (no hex)
+
protected String mFullIccId; // Includes hex characters in ICCID
protected String mMsisdn = null; // My mobile number
protected String mMsisdnTag = null;
@@ -78,19 +92,17 @@
protected String mNewVoiceMailTag = null;
protected boolean mIsVoiceMailFixed = false;
protected String mImsi;
- protected String mFakeImsi;
private IccIoResult auth_rsp;
protected int mMncLength = UNINITIALIZED;
protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
private String mSpn;
- private String mFakeSpn;
protected String mGid1;
- protected String mFakeGid1;
protected String mGid2;
- protected String mFakeGid2;
+
+ protected String mPnnHomeName;
protected String mPrefLang;
@@ -128,6 +140,7 @@
public static final int EVENT_SPN = 2; // Service Provider Name
public static final int EVENT_GET_ICC_RECORD_DONE = 100;
+ public static final int EVENT_REFRESH = 31; // ICC refresh occurred
protected static final int EVENT_APP_READY = 1;
private static final int EVENT_AKA_AUTHENTICATE_DONE = 90;
@@ -146,16 +159,13 @@
+ " mCi=" + mCi
+ " mFh=" + mFh
+ " mParentApp=" + mParentApp
- + " recordsLoadedRegistrants=" + mRecordsLoadedRegistrants
- + " mImsiReadyRegistrants=" + mImsiReadyRegistrants
- + " mRecordsEventsRegistrants=" + mRecordsEventsRegistrants
- + " mNewSmsRegistrants=" + mNewSmsRegistrants
- + " mNetworkSelectionModeAutomaticRegistrants="
- + mNetworkSelectionModeAutomaticRegistrants
+ " recordsToLoad=" + mRecordsToLoad
+ " adnCache=" + mAdnCache
+ " recordsRequested=" + mRecordsRequested
+ + " lockedRecordsReqReason=" + mLockedRecordsReqReason
+ " iccid=" + iccIdToPrint
+ + (mCarrierTestOverride.isInTestMode() ? "mFakeIccid="
+ + mCarrierTestOverride.getFakeIccid() : "")
+ " msisdnTag=" + mMsisdnTag
+ " voiceMailNum=" + Rlog.pii(VDBG, mVoiceMailNum)
+ " voiceMailTag=" + mVoiceMailTag
@@ -164,14 +174,13 @@
+ " isVoiceMailFixed=" + mIsVoiceMailFixed
+ " mImsi=" + ((mImsi != null) ?
mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null")
- + (mCarrierTestOverride.isInTestMode()
- ? (" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null")) : "")
+ + (mCarrierTestOverride.isInTestMode() ? " mFakeImsi="
+ + mCarrierTestOverride.getFakeIMSI() : "")
+ " mncLength=" + mMncLength
+ " mailboxIndex=" + mMailboxIndex
+ " spn=" + mSpn
- + (mCarrierTestOverride.isInTestMode()
- ? (" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null")) : "");
-
+ + (mCarrierTestOverride.isInTestMode() ? " mFakeSpn="
+ + mCarrierTestOverride.getFakeSpn() : "");
}
/**
@@ -200,20 +209,16 @@
Context.TELEPHONY_SERVICE);
mCarrierTestOverride = new CarrierTestOverride();
+ mCi.registerForIccRefresh(this, EVENT_REFRESH, null);
+ }
- if (mCarrierTestOverride.isInTestMode()) {
- mFakeImsi = mCarrierTestOverride.getFakeIMSI();
- log("load mFakeImsi: " + mFakeImsi);
-
- mFakeGid1 = mCarrierTestOverride.getFakeGid1();
- log("load mFakeGid1: " + mFakeGid1);
-
- mFakeGid2 = mCarrierTestOverride.getFakeGid2();
- log("load mFakeGid2: " + mFakeGid2);
-
- mFakeSpn = mCarrierTestOverride.getFakeSpn();
- log("load mFakeSpn: " + mFakeSpn);
- }
+ // Override IccRecords for testing
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ mCarrierTestOverride.override(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
+ mTelephonyManager.setSimOperatorNameForPhone(mParentApp.getPhoneId(), spn);
+ mTelephonyManager.setSimOperatorNumericForPhone(mParentApp.getPhoneId(), mccmnc);
+ mRecordsLoadedRegistrants.notifyRegistrants();
}
/**
@@ -229,10 +234,15 @@
mLock.notifyAll();
}
+ mCi.unregisterForIccRefresh(this);
mParentApp = null;
mFh = null;
mCi = null;
mContext = null;
+ if (mAdnCache != null) {
+ mAdnCache.reset();
+ }
+ mLoaded.set(false);
}
public abstract void onReady();
@@ -271,7 +281,11 @@
* @return ICC ID without hex digits
*/
public String getIccId() {
- return mIccId;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIccid() != null) {
+ return mCarrierTestOverride.getFakeIccid();
+ } else {
+ return mIccId;
+ }
}
/**
@@ -290,7 +304,7 @@
Registrant r = new Registrant(h, what, obj);
mRecordsLoadedRegistrants.add(r);
- if (mRecordsToLoad == 0 && mRecordsRequested == true) {
+ if (getRecordsLoaded()) {
r.notifyRegistrant(new AsyncResult(null, null, null));
}
}
@@ -298,6 +312,52 @@
mRecordsLoadedRegistrants.remove(h);
}
+ /**
+ * Register to be notified when records are loaded for a PIN or PUK locked SIM
+ */
+ public void registerForLockedRecordsLoaded(Handler h, int what, Object obj) {
+ if (mDestroyed.get()) {
+ return;
+ }
+
+ Registrant r = new Registrant(h, what, obj);
+ mLockedRecordsLoadedRegistrants.add(r);
+
+ if (getLockedRecordsLoaded()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+
+ /**
+ * Unregister corresponding to registerForLockedRecordsLoaded()
+ */
+ public void unregisterForLockedRecordsLoaded(Handler h) {
+ mLockedRecordsLoadedRegistrants.remove(h);
+ }
+
+ /**
+ * Register to be notified when records are loaded for a network locked SIM
+ */
+ public void registerForNetworkLockedRecordsLoaded(Handler h, int what, Object obj) {
+ if (mDestroyed.get()) {
+ return;
+ }
+
+ Registrant r = new Registrant(h, what, obj);
+ mNetworkLockedRecordsLoadedRegistrants.add(r);
+
+ if (getNetworkLockedRecordsLoaded()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+
+ /**
+ * Unregister corresponding to registerForLockedRecordsLoaded()
+ */
+ public void unregisterForNetworkLockedRecordsLoaded(Handler h) {
+ mNetworkLockedRecordsLoadedRegistrants.remove(h);
+ }
+
public void registerForImsiReady(Handler h, int what, Object obj) {
if (mDestroyed.get()) {
return;
@@ -314,6 +374,22 @@
mImsiReadyRegistrants.remove(h);
}
+ public void registerForSpnUpdate(Handler h, int what, Object obj) {
+ if (mDestroyed.get()) {
+ return;
+ }
+
+ Registrant r = new Registrant(h, what, obj);
+ mSpnUpdatedRegistrants.add(r);
+
+ if (!TextUtils.isEmpty(mSpn)) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ public void unregisterForSpnUpdate(Handler h) {
+ mSpnUpdatedRegistrants.remove(h);
+ }
+
public void registerForRecordsEvents(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
mRecordsEventsRegistrants.add(r);
@@ -352,8 +428,8 @@
* @return null if SIM is not yet ready or unavailable
*/
public String getIMSI() {
- if (mCarrierTestOverride.isInTestMode() && mFakeImsi != null) {
- return mFakeImsi;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIMSI() != null) {
+ return mCarrierTestOverride.getFakeIMSI();
} else {
return mImsi;
}
@@ -387,8 +463,8 @@
* @return null if SIM is not yet ready
*/
public String getGid1() {
- if (mCarrierTestOverride.isInTestMode() && mFakeGid1 != null) {
- return mFakeGid1;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid1() != null) {
+ return mCarrierTestOverride.getFakeGid1();
} else {
return mGid1;
}
@@ -399,13 +475,26 @@
* @return null if SIM is not yet ready
*/
public String getGid2() {
- if (mCarrierTestOverride.isInTestMode() && mFakeGid2 != null) {
- return mFakeGid2;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid2() != null) {
+ return mCarrierTestOverride.getFakeGid2();
} else {
return mGid2;
}
}
+ /**
+ * Get the PLMN network name on a SIM.
+ * @return null if SIM is not yet ready
+ */
+ public String getPnnHomeName() {
+ if (mCarrierTestOverride.isInTestMode()
+ && mCarrierTestOverride.getFakePnnHomeName() != null) {
+ return mCarrierTestOverride.getFakePnnHomeName();
+ } else {
+ return mPnnHomeName;
+ }
+ }
+
public void setMsisdnNumber(String alphaTag, String number,
Message onComplete) {
loge("setMsisdn() should not be invoked on base IccRecords");
@@ -429,8 +518,8 @@
* @return null if SIM is not yet ready or no RUIM entry
*/
public String getServiceProviderName() {
- if (mCarrierTestOverride.isInTestMode() && mFakeSpn != null) {
- return mFakeSpn;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeSpn() != null) {
+ return mCarrierTestOverride.getFakeSpn();
}
String providerName = mSpn;
@@ -438,9 +527,9 @@
// which did occur after removing a SIM.
UiccCardApplication parentApp = mParentApp;
if (parentApp != null) {
- UiccCard card = parentApp.getUiccCard();
- if (card != null) {
- String brandOverride = card.getOperatorBrandOverride();
+ UiccProfile profile = parentApp.getUiccProfile();
+ if (profile != null) {
+ String brandOverride = profile.getOperatorBrandOverride();
if (brandOverride != null) {
log("getServiceProviderName: override, providerName=" + providerName);
providerName = brandOverride;
@@ -457,7 +546,10 @@
}
protected void setServiceProviderName(String spn) {
- mSpn = spn;
+ if (!TextUtils.equals(mSpn, spn)) {
+ mSpnUpdatedRegistrants.notifyRegistrants();
+ mSpn = spn;
+ }
}
/**
@@ -512,27 +604,18 @@
*/
public abstract void onRefresh(boolean fileChanged, int[] fileList);
- /**
- * Called by subclasses (SimRecords and RuimRecords) whenever
- * IccRefreshResponse.REFRESH_RESULT_INIT event received
- */
- protected void onIccRefreshInit() {
- mAdnCache.reset();
- mMncLength = UNINITIALIZED;
- UiccCardApplication parentApp = mParentApp;
- if ((parentApp != null) &&
- (parentApp.getState() == AppState.APPSTATE_READY)) {
- // This will cause files to be reread
- sendMessage(obtainMessage(EVENT_APP_READY));
- }
+ public boolean getRecordsLoaded() {
+ return mRecordsToLoad == 0 && mRecordsRequested;
}
- public boolean getRecordsLoaded() {
- if (mRecordsToLoad == 0 && mRecordsRequested == true) {
- return true;
- } else {
- return false;
- }
+ protected boolean getLockedRecordsLoaded() {
+ return mRecordsToLoad == 0
+ && mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED;
+ }
+
+ protected boolean getNetworkLockedRecordsLoaded() {
+ return mRecordsToLoad == 0
+ && mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED;
}
//***** Overridden from Handler
@@ -561,6 +644,16 @@
}
break;
+ case EVENT_REFRESH:
+ ar = (AsyncResult)msg.obj;
+ if (DBG) log("Card REFRESH occurred: ");
+ if (ar.exception == null) {
+ handleRefresh((IccRefreshResponse)ar.result);
+ } else {
+ loge("Icc refresh Exception: " + ar.exception);
+ }
+ break;
+
case EVENT_AKA_AUTHENTICATE_DONE:
ar = (AsyncResult)msg.obj;
auth_rsp = null;
@@ -629,19 +722,51 @@
return null;
}
+ protected abstract void handleFileUpdate(int efid);
+
+ protected void handleRefresh(IccRefreshResponse refreshResponse){
+ if (refreshResponse == null) {
+ if (DBG) log("handleRefresh received without input");
+ return;
+ }
+
+ if (!TextUtils.isEmpty(refreshResponse.aid) &&
+ !refreshResponse.aid.equals(mParentApp.getAid())) {
+ // This is for different app. Ignore.
+ return;
+ }
+
+ switch (refreshResponse.refreshResult) {
+ case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
+ if (DBG) log("handleRefresh with SIM_FILE_UPDATED");
+ handleFileUpdate(refreshResponse.efId);
+ break;
+ default:
+ // unknown refresh operation
+ if (DBG) log("handleRefresh with unknown operation");
+ break;
+ }
+ }
+
protected abstract void onRecordLoaded();
protected abstract void onAllRecordsLoaded();
/**
* Returns the SpnDisplayRule based on settings on the SIM and the
- * specified plmn (currently-registered PLMN). See TS 22.101 Annex A
- * and TS 51.011 10.3.11 for details.
+ * current service state. See TS 22.101 Annex A and TS 51.011 10.3.11
+ * for details.
*
* If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
* Generally used for GSM/UMTS and the like SIMs.
+ *
+ * @param serviceState Service state
+ * @return the display rule
+ *
+ * @see #SPN_RULE_SHOW_SPN
+ * @see #SPN_RULE_SHOW_PLMN
*/
- public abstract int getDisplayRule(String plmn);
+ public abstract int getDisplayRule(ServiceState serviceState);
/**
* Return true if "Restriction of menu options for manual PLMN selection"
@@ -682,6 +807,15 @@
}
/**
+ * Indicates wether the ICC records have been loaded or not
+ *
+ * @return true if the records have been loaded, false otherwise.
+ */
+ public boolean isLoaded() {
+ return mLoaded.get();
+ }
+
+ /**
* Indicates wether SIM is in provisioned state or not.
* Overridden only if SIM can be dynamically provisioned via OTA.
*
@@ -784,6 +918,18 @@
pw.println(" recordsLoadedRegistrants[" + i + "]="
+ ((Registrant)mRecordsLoadedRegistrants.get(i)).getHandler());
}
+ pw.println(" mLockedRecordsLoadedRegistrants: size="
+ + mLockedRecordsLoadedRegistrants.size());
+ for (int i = 0; i < mLockedRecordsLoadedRegistrants.size(); i++) {
+ pw.println(" mLockedRecordsLoadedRegistrants[" + i + "]="
+ + ((Registrant) mLockedRecordsLoadedRegistrants.get(i)).getHandler());
+ }
+ pw.println(" mNetworkLockedRecordsLoadedRegistrants: size="
+ + mNetworkLockedRecordsLoadedRegistrants.size());
+ for (int i = 0; i < mNetworkLockedRecordsLoadedRegistrants.size(); i++) {
+ pw.println(" mLockedRecordsLoadedRegistrants[" + i + "]="
+ + ((Registrant) mNetworkLockedRecordsLoadedRegistrants.get(i)).getHandler());
+ }
pw.println(" mImsiReadyRegistrants: size=" + mImsiReadyRegistrants.size());
for (int i = 0; i < mImsiReadyRegistrants.size(); i++) {
pw.println(" mImsiReadyRegistrants[" + i + "]="
@@ -806,6 +952,7 @@
+ ((Registrant)mNetworkSelectionModeAutomaticRegistrants.get(i)).getHandler());
}
pw.println(" mRecordsRequested=" + mRecordsRequested);
+ pw.println(" mLockedRecordsReqReason=" + mLockedRecordsReqReason);
pw.println(" mRecordsToLoad=" + mRecordsToLoad);
pw.println(" mRdnCache=" + mAdnCache);
@@ -821,13 +968,13 @@
pw.println(" mImsi=" + ((mImsi != null) ?
mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null"));
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null"));
+ pw.println(" mFakeImsi=" + mCarrierTestOverride.getFakeIMSI());
}
pw.println(" mMncLength=" + mMncLength);
pw.println(" mMailboxIndex=" + mMailboxIndex);
pw.println(" mSpn=" + mSpn);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null"));
+ pw.println(" mFakeSpn=" + mCarrierTestOverride.getFakeSpn());
}
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
new file mode 100644
index 0000000..5fdd322
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 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.uicc;
+
+import android.telephony.SubscriptionInfo;
+import android.text.TextUtils;
+
+/**
+ * This class represents the status of the physical UICC slots.
+ */
+public class IccSlotStatus {
+
+ public enum SlotState {
+ SLOTSTATE_INACTIVE,
+ SLOTSTATE_ACTIVE;
+ }
+
+ public IccCardStatus.CardState cardState;
+ public SlotState slotState;
+ public int logicalSlotIndex;
+ public String atr;
+ public String iccid;
+
+ /**
+ * Set the cardState according to the input state.
+ */
+ public void setCardState(int state) {
+ switch(state) {
+ case 0:
+ cardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ break;
+ case 1:
+ cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ break;
+ case 2:
+ cardState = IccCardStatus.CardState.CARDSTATE_ERROR;
+ break;
+ case 3:
+ cardState = IccCardStatus.CardState.CARDSTATE_RESTRICTED;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_CardState: " + state);
+ }
+ }
+
+ /**
+ * Set the slotState according to the input state.
+ */
+ public void setSlotState(int state) {
+ switch(state) {
+ case 0:
+ slotState = SlotState.SLOTSTATE_INACTIVE;
+ break;
+ case 1:
+ slotState = SlotState.SLOTSTATE_ACTIVE;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_SlotState: " + state);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("IccSlotStatus {").append(cardState).append(",")
+ .append(slotState).append(",")
+ .append("logicalSlotIndex=").append(logicalSlotIndex).append(",")
+ .append("atr=").append(atr).append(",iccid=")
+ .append(SubscriptionInfo.givePrintableIccid(iccid));
+
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ IccSlotStatus that = (IccSlotStatus) obj;
+ return (cardState == that.cardState)
+ && (slotState == that.slotState)
+ && (logicalSlotIndex == that.logicalSlotIndex)
+ && (TextUtils.equals(atr, that.atr))
+ && (TextUtils.equals(iccid, that.iccid));
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimRecords.java b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
index b176d4e..019cc79 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
@@ -54,11 +54,4 @@
* not present or not loaded
*/
String[] getIsimPcscf();
-
- /**
- * Returns the response of ISIM Authetification through RIL.
- * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
- * @return the response of ISIM Authetification, or null if not available
- */
- String getIsimChallengeResponse(String nonce);
}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 194d259..3b33c03 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -16,18 +16,12 @@
package com.android.internal.telephony.uicc;
-import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
-import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
-
import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
import android.telephony.Rlog;
-import android.text.TextUtils;
+import android.telephony.ServiceState;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.gsm.SimTlv;
@@ -52,7 +46,6 @@
public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
private static final int EVENT_APP_READY = 1;
- private static final int EVENT_ISIM_REFRESH = 31;
private static final int EVENT_ISIM_AUTHENTICATE_DONE = 91;
// ISIM EF records (see 3GPP TS 31.103)
@@ -81,12 +74,14 @@
super(app, c, ci);
mRecordsRequested = false; // No load request is made till SIM ready
+ //todo: currently locked state for ISIM is not handled well and may cause app state to not
+ //be broadcast
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
// recordsToLoad is set to 0 because no requests are made yet
mRecordsToLoad = 0;
// Start off by setting empty state
resetRecords();
- mCi.registerForIccRefresh(this, EVENT_ISIM_REFRESH, null);
mParentApp.registerForReady(this, EVENT_APP_READY, null);
if (DBG) log("IsimUiccRecords X ctor this=" + this);
@@ -119,15 +114,9 @@
onReady();
break;
- case EVENT_ISIM_REFRESH:
- ar = (AsyncResult)msg.obj;
- loge("ISim REFRESH(EVENT_ISIM_REFRESH) with exception: " + ar.exception);
- if (ar.exception == null) {
- Intent intent = new Intent(INTENT_ISIM_REFRESH);
- loge("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
- mContext.sendBroadcast(intent);
- handleIsimRefresh((IccRefreshResponse)ar.result);
- }
+ case EVENT_REFRESH:
+ broadcastRefresh();
+ super.handleMessage(msg);
break;
case EVENT_ISIM_AUTHENTICATE_DONE:
@@ -195,6 +184,8 @@
auth_rsp = null;
mRecordsRequested = false;
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
+ mLoaded.set(false);
}
private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
@@ -290,22 +281,38 @@
mRecordsToLoad -= 1;
if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
- if (mRecordsToLoad == 0 && mRecordsRequested == true) {
+ if (getRecordsLoaded()) {
onAllRecordsLoaded();
+ } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
+ onLockedAllRecordsLoaded();
} else if (mRecordsToLoad < 0) {
loge("recordsToLoad <0, programmer error suspected");
mRecordsToLoad = 0;
}
}
+ private void onLockedAllRecordsLoaded() {
+ if (DBG) log("SIM locked; record load complete");
+ if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
+ mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+ } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
+ mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
+ new AsyncResult(null, null, null));
+ } else {
+ loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
+ + mLockedRecordsReqReason);
+ }
+ }
+
@Override
protected void onAllRecordsLoaded() {
if (DBG) log("record load complete");
- mRecordsLoadedRegistrants.notifyRegistrants(
- new AsyncResult(null, null, null));
+ mLoaded.set(true);
+ mRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
}
- private void handleFileUpdate(int efid) {
+ @Override
+ protected void handleFileUpdate(int efid) {
switch (efid) {
case EF_IMPI:
mFh.loadEFTransparent(EF_IMPI, obtainMessage(
@@ -342,42 +349,10 @@
}
}
- private void handleIsimRefresh(IccRefreshResponse refreshResponse) {
- if (refreshResponse == null) {
- if (DBG) log("handleIsimRefresh received without input");
- return;
- }
-
- if (!TextUtils.isEmpty(refreshResponse.aid)
- && !refreshResponse.aid.equals(mParentApp.getAid())) {
- // This is for different app. Ignore.
- if (DBG) log("handleIsimRefresh received different app");
- return;
- }
-
- switch (refreshResponse.refreshResult) {
- case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
- if (DBG) log("handleIsimRefresh with REFRESH_RESULT_FILE_UPDATE");
- handleFileUpdate(refreshResponse.efId);
- break;
-
- case IccRefreshResponse.REFRESH_RESULT_INIT:
- if (DBG) log("handleIsimRefresh with REFRESH_RESULT_INIT");
- // need to reload all files (that we care about)
- // onIccRefreshInit();
- fetchIsimRecords();
- break;
-
- case IccRefreshResponse.REFRESH_RESULT_RESET:
- // Refresh reset is handled by the UiccCard object.
- if (DBG) log("handleIsimRefresh with REFRESH_RESULT_RESET");
- break;
-
- default:
- // unknown refresh operation
- if (DBG) log("handleIsimRefresh with unknown operation");
- break;
- }
+ private void broadcastRefresh() {
+ Intent intent = new Intent(INTENT_ISIM_REFRESH);
+ log("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
+ mContext.sendBroadcast(intent);
}
/**
@@ -429,35 +404,8 @@
return (mIsimPcscf != null) ? mIsimPcscf.clone() : null;
}
- /**
- * Returns the response of ISIM Authetification through RIL.
- * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
- * @return the response of ISIM Authetification, or null if not available
- */
@Override
- public String getIsimChallengeResponse(String nonce){
- if (DBG) log("getIsimChallengeResponse-nonce:"+nonce);
- try {
- synchronized(mLock) {
- mCi.requestIsimAuthentication(nonce,obtainMessage(EVENT_ISIM_AUTHENTICATE_DONE));
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- log("interrupted while trying to request Isim Auth");
- }
- }
- } catch(Exception e) {
- if (DBG) log( "Fail while trying to request Isim Auth");
- return null;
- }
-
- if (DBG) log("getIsimChallengeResponse-auth_rsp"+auth_rsp);
-
- return auth_rsp;
- }
-
- @Override
- public int getDisplayRule(String plmn) {
+ public int getDisplayRule(ServiceState serviceState) {
// Not applicable to Isim
return 0;
}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index b303ca8..fd4debf 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -24,6 +24,7 @@
import android.os.Message;
import android.os.SystemProperties;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
@@ -96,7 +97,8 @@
private static final int EVENT_SMS_ON_RUIM = 21;
private static final int EVENT_GET_SMS_DONE = 22;
- private static final int EVENT_RUIM_REFRESH = 31;
+ private static final int EVENT_APP_LOCKED = 32;
+ private static final int EVENT_APP_NETWORK_LOCKED = 33;
public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
super(app, c, ci);
@@ -104,17 +106,19 @@
mAdnCache = new AdnRecordCache(mFh);
mRecordsRequested = false; // No load request is made till SIM ready
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
// recordsToLoad is set to 0 because no requests are made yet
mRecordsToLoad = 0;
// NOTE the EVENT_SMS_ON_RUIM is not registered
- mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
// Start off by setting empty state
resetRecords();
mParentApp.registerForReady(this, EVENT_APP_READY, null);
+ mParentApp.registerForLocked(this, EVENT_APP_LOCKED, null);
+ mParentApp.registerForNetworkLocked(this, EVENT_APP_NETWORK_LOCKED, null);
if (DBG) log("RuimRecords X ctor this=" + this);
}
@@ -122,8 +126,9 @@
public void dispose() {
if (DBG) log("Disposing RuimRecords " + this);
//Unregister for all events
- mCi.unregisterForIccRefresh(this);
mParentApp.unregisterForReady(this);
+ mParentApp.unregisterForLocked(this);
+ mParentApp.unregisterForNetworkLocked(this);
resetRecords();
super.dispose();
}
@@ -151,6 +156,8 @@
// read requests made so far are not valid. This is set to
// true only when fresh set of read requests are made.
mRecordsRequested = false;
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
+ mLoaded.set(false);
}
public String getMdnNumber() {
@@ -596,11 +603,17 @@
return;
}
- try { switch (msg.what) {
+ try {
+ switch (msg.what) {
case EVENT_APP_READY:
onReady();
break;
+ case EVENT_APP_LOCKED:
+ case EVENT_APP_NETWORK_LOCKED:
+ onLocked(msg.what);
+ break;
+
case EVENT_GET_DEVICE_IDENTITY_DONE:
log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
break;
@@ -632,7 +645,7 @@
if (operatorNumeric != null) {
if (operatorNumeric.length() <= 6) {
log("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
}
}
} else {
@@ -693,14 +706,6 @@
log("Event EVENT_GET_SST_DONE Received");
break;
- case EVENT_RUIM_REFRESH:
- isRecordLoadResponse = false;
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- handleRuimRefresh((IccRefreshResponse)ar.result);
- }
- break;
-
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
@@ -744,14 +749,28 @@
mRecordsToLoad -= 1;
if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
- if (mRecordsToLoad == 0 && mRecordsRequested == true) {
+ if (getRecordsLoaded()) {
onAllRecordsLoaded();
+ } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
+ onLockedAllRecordsLoaded();
} else if (mRecordsToLoad < 0) {
loge("recordsToLoad <0, programmer error suspected");
mRecordsToLoad = 0;
}
}
+ private void onLockedAllRecordsLoaded() {
+ if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
+ mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+ } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
+ mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
+ new AsyncResult(null, null, null));
+ } else {
+ loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
+ + mLockedRecordsReqReason);
+ }
+ }
+
@Override
protected void onAllRecordsLoaded() {
if (DBG) log("record load complete");
@@ -775,10 +794,8 @@
if (!TextUtils.isEmpty(imsi)) {
log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + imsi) : ""));
- mTelephonyManager.setSimCountryIsoForPhone(
- mParentApp.getPhoneId(),
- MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0, 3))));
+ mTelephonyManager.setSimCountryIsoForPhone(mParentApp.getPhoneId(),
+ MccTable.countryCodeForMcc(imsi.substring(0, 3)));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
}
@@ -789,12 +806,12 @@
setSimLanguage(mEFli, mEFpl);
}
- mRecordsLoadedRegistrants.notifyRegistrants(
- new AsyncResult(null, null, null));
+ 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.getUiccCard().getPhoneId();
+ int phoneId = mParentApp.getUiccProfile().getPhoneId();
int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(phoneId);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subId);
@@ -811,6 +828,14 @@
mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
}
+ private void onLocked(int msg) {
+ if (DBG) log("only fetch EF_ICCID in locked state");
+ mLockedRecordsReqReason = msg == EVENT_APP_LOCKED ? LOCKED_RECORDS_REQ_REASON_LOCKED :
+ LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED;
+
+ mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
+ mRecordsToLoad++;
+ }
private void fetchRuimRecords() {
mRecordsRequested = true;
@@ -868,7 +893,7 @@
* No Display rule for RUIMs yet.
*/
@Override
- public int getDisplayRule(String plmn) {
+ public int getDisplayRule(ServiceState serviceState) {
// TODO together with spn
return 0;
}
@@ -911,38 +936,10 @@
return 0;
}
- private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
- if (refreshResponse == null) {
- if (DBG) log("handleRuimRefresh received without input");
- return;
- }
-
- if (!TextUtils.isEmpty(refreshResponse.aid)
- && !refreshResponse.aid.equals(mParentApp.getAid())) {
- // This is for different app. Ignore.
- return;
- }
-
- switch (refreshResponse.refreshResult) {
- case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
- if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
- mAdnCache.reset();
- fetchRuimRecords();
- break;
- case IccRefreshResponse.REFRESH_RESULT_INIT:
- if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
- // need to reload all files (that we care about)
- onIccRefreshInit();
- break;
- case IccRefreshResponse.REFRESH_RESULT_RESET:
- // Refresh reset is handled by the UiccCard object.
- if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
- break;
- default:
- // unknown refresh operation
- if (DBG) log("handleRuimRefresh with unknown operation");
- break;
- }
+ @Override
+ protected void handleFileUpdate(int efid) {
+ mAdnCache.reset();
+ fetchRuimRecords();
}
public String getMdn() {
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index dad1ee2..ca5e562 100755
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -16,16 +16,15 @@
package com.android.internal.telephony.uicc;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionInfo;
import android.text.TextUtils;
@@ -33,8 +32,8 @@
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.AppState;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import java.io.FileDescriptor;
@@ -56,9 +55,6 @@
VoiceMailConstants mVmConfig;
-
- SpnOverride mSpnOverride;
-
// ***** Cached SIM State; cleared on channel close
private int mCallForwardingStatus;
@@ -89,15 +85,12 @@
// Numeric network codes listed in TS 51.011 EF[SPDI]
ArrayList<String> mSpdiNetworks = null;
- String mPnnHomeName = null;
-
UsimServiceTable mUsimServiceTable;
@Override
public String toString() {
return "SimRecords: " + super.toString()
+ " mVmConfig" + mVmConfig
- + " mSpnOverride=" + mSpnOverride
+ " callForwardingEnabled=" + mCallForwardingStatus
+ " spnState=" + mSpnState
+ " mCphsInfo=" + mCphsInfo
@@ -175,9 +168,9 @@
// TODO: Possibly move these to IccRecords.java
private static final int SYSTEM_EVENT_BASE = 0x100;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 1 + SYSTEM_EVENT_BASE;
private static final int EVENT_APP_LOCKED = 2 + SYSTEM_EVENT_BASE;
- private static final int EVENT_SIM_REFRESH = 3 + SYSTEM_EVENT_BASE;
+ private static final int EVENT_APP_NETWORK_LOCKED = 3 + SYSTEM_EVENT_BASE;
+
// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
@@ -212,45 +205,31 @@
mAdnCache = new AdnRecordCache(mFh);
mVmConfig = new VoiceMailConstants();
- mSpnOverride = new SpnOverride();
mRecordsRequested = false; // No load request is made till SIM ready
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
// recordsToLoad is set to 0 because no requests are made yet
mRecordsToLoad = 0;
mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
- mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
// Start off by setting empty state
resetRecords();
mParentApp.registerForReady(this, EVENT_APP_READY, null);
mParentApp.registerForLocked(this, EVENT_APP_LOCKED, null);
+ mParentApp.registerForNetworkLocked(this, EVENT_APP_NETWORK_LOCKED, null);
if (DBG) log("SIMRecords X ctor this=" + this);
-
- IntentFilter intentfilter = new IntentFilter();
- intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- c.registerReceiver(mReceiver, intentfilter);
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- sendMessage(obtainMessage(EVENT_CARRIER_CONFIG_CHANGED));
- }
- }
- };
-
@Override
public void dispose() {
if (DBG) log("Disposing SIMRecords this=" + this);
//Unregister for all events
- mCi.unregisterForIccRefresh(this);
mCi.unSetOnSmsOnSim(this);
mParentApp.unregisterForReady(this);
mParentApp.unregisterForLocked(this);
- mContext.unregisterReceiver(mReceiver);
+ mParentApp.unregisterForNetworkLocked(this);
resetRecords();
super.dispose();
}
@@ -294,6 +273,8 @@
// read requests made so far are not valid. This is set to
// true only when fresh set of read requests are made.
mRecordsRequested = false;
+ mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
+ mLoaded.set(false);
}
//***** Public Methods
@@ -482,7 +463,7 @@
}
}
- // Validate data is !null.
+ // Validate data is not null and not empty.
private boolean validEfCfis(byte[] data) {
if (data != null) {
if (data[0] < 1 || data[0] > 4) {
@@ -490,7 +471,12 @@
// 1 and 4 according to ETSI TS 131 102 v11.3.0 section 4.2.64.
logw("MSP byte: " + data[0] + " is not between 1 and 4", null);
}
- return true;
+ // empty EF_CFIS should be considered as call forward disabled
+ for (byte b : data) {
+ if (b != (byte) 0xFF) {
+ return true;
+ }
+ }
}
return false;
}
@@ -663,7 +649,8 @@
break;
case EVENT_APP_LOCKED:
- onLocked();
+ case EVENT_APP_NETWORK_LOCKED:
+ onLocked(msg.what);
break;
/* IO events */
@@ -726,7 +713,7 @@
// finally have both the imsi and the mncLength and
// can parse the imsi properly
MccTable.updateMccMncConfiguration(mContext,
- imsi.substring(0, 3 + mMncLength), false);
+ imsi.substring(0, 3 + mMncLength));
}
mImsiReadyRegistrants.notifyRegistrants();
break;
@@ -915,7 +902,6 @@
log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
break;
-
case EVENT_GET_AD_DONE:
try {
isRecordLoadResponse = true;
@@ -1004,7 +990,7 @@
// the imsi properly
log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
MccTable.updateMccMncConfiguration(mContext,
- imsi.substring(0, 3 + mMncLength), false);
+ imsi.substring(0, 3 + mMncLength));
}
}
break;
@@ -1066,6 +1052,7 @@
if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
mPnnHomeName = IccUtils.networkNameToString(
tlv.getData(), 0, tlv.getData().length);
+ log("PNN: " + mPnnHomeName);
break;
}
}
@@ -1210,14 +1197,6 @@
((Message) ar.userObj).sendToTarget();
}
break;
- case EVENT_SIM_REFRESH:
- isRecordLoadResponse = false;
- ar = (AsyncResult) msg.obj;
- if (DBG) log("Sim REFRESH with exception: " + ar.exception);
- if (ar.exception == null) {
- handleSimRefresh((IccRefreshResponse) ar.result);
- }
- break;
case EVENT_GET_CFIS_DONE:
isRecordLoadResponse = true;
@@ -1367,10 +1346,6 @@
}
break;
- case EVENT_CARRIER_CONFIG_CHANGED:
- handleCarrierNameOverride();
- break;
-
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
}
@@ -1407,7 +1382,8 @@
}
}
- private void handleFileUpdate(int efid) {
+ @Override
+ protected void handleFileUpdate(int efid) {
switch(efid) {
case EF_MBDN:
mRecordsToLoad++;
@@ -1451,39 +1427,6 @@
}
}
- private void handleSimRefresh(IccRefreshResponse refreshResponse){
- if (refreshResponse == null) {
- if (DBG) log("handleSimRefresh received without input");
- return;
- }
-
- if (!TextUtils.isEmpty(refreshResponse.aid)
- && !refreshResponse.aid.equals(mParentApp.getAid())) {
- // This is for different app. Ignore.
- return;
- }
-
- switch (refreshResponse.refreshResult) {
- case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
- if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED");
- handleFileUpdate(refreshResponse.efId);
- break;
- case IccRefreshResponse.REFRESH_RESULT_INIT:
- if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
- // need to reload all files (that we care about)
- onIccRefreshInit();
- break;
- case IccRefreshResponse.REFRESH_RESULT_RESET:
- // Refresh reset is handled by the UiccCard object.
- if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
- break;
- default:
- // unknown refresh operation
- if (DBG) log("handleSimRefresh with unknown operation");
- break;
- }
- }
-
/**
* Dispatch 3GPP format message to registrant ({@code GsmCdmaPhone}) to pass to the 3GPP SMS
* dispatcher for delivery.
@@ -1556,8 +1499,10 @@
mRecordsToLoad -= 1;
if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
- if (mRecordsToLoad == 0 && mRecordsRequested == true) {
+ if (getRecordsLoaded()) {
onAllRecordsLoaded();
+ } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
+ onLockedAllRecordsLoaded();
} else if (mRecordsToLoad < 0) {
loge("recordsToLoad <0, programmer error suspected");
mRecordsToLoad = 0;
@@ -1580,26 +1525,34 @@
}
}
- @Override
- protected void onAllRecordsLoaded() {
- if (DBG) log("record load complete");
-
+ private void setSimLanguageFromEF() {
Resources resource = Resources.getSystem();
if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
setSimLanguage(mEfLi, mEfPl);
} else {
if (DBG) log ("Not using EF LI/EF PL");
}
+ }
- setVoiceCallForwardingFlagFromSimRecords();
-
- if (mParentApp.getState() == AppState.APPSTATE_PIN ||
- mParentApp.getState() == AppState.APPSTATE_PUK) {
- // reset recordsRequested, since sim is not loaded really
- mRecordsRequested = false;
- // lock state, only update language
- return ;
+ private void onLockedAllRecordsLoaded() {
+ setSimLanguageFromEF();
+ if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
+ mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+ } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
+ mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
+ new AsyncResult(null, null, null));
+ } else {
+ loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
+ + mLockedRecordsReqReason);
}
+ }
+
+ @Override
+ protected void onAllRecordsLoaded() {
+ if (DBG) log("record load complete");
+
+ setSimLanguageFromEF();
+ setVoiceCallForwardingFlagFromSimRecords();
// Some fields require more than one SIM record to set
@@ -1618,44 +1571,18 @@
if (!TextUtils.isEmpty(imsi) && imsi.length() >= 3) {
log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + imsi) : ""));
mTelephonyManager.setSimCountryIsoForPhone(
- mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0, 3))));
+ mParentApp.getPhoneId(), MccTable.countryCodeForMcc(imsi.substring(0, 3)));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
}
setVoiceMailByCountry(operator);
-
- mRecordsLoadedRegistrants.notifyRegistrants(
- new AsyncResult(null, null, null));
+ mLoaded.set(true);
+ mRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
}
//***** Private methods
- private void handleCarrierNameOverride() {
- CarrierConfigManager configLoader = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configLoader != null && configLoader.getConfig().getBoolean(
- CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL)) {
- String carrierName = configLoader.getConfig().getString(
- CarrierConfigManager.KEY_CARRIER_NAME_STRING);
- setServiceProviderName(carrierName);
- mTelephonyManager.setSimOperatorNameForPhone(mParentApp.getPhoneId(),
- carrierName);
- } else {
- setSpnFromConfig(getOperatorNumeric());
- }
- }
-
- private void setSpnFromConfig(String carrier) {
- if (mSpnOverride.containsCarrier(carrier)) {
- setServiceProviderName(mSpnOverride.getSpn(carrier));
- mTelephonyManager.setSimOperatorNameForPhone(
- mParentApp.getPhoneId(), getServiceProviderName());
- }
- }
-
-
private void setVoiceMailByCountry (String spn) {
if (mVmConfig.containsCarrier(spn)) {
mIsVoiceMailFixed = true;
@@ -1679,14 +1606,19 @@
fetchSimRecords();
}
- private void onLocked() {
- if (DBG) log("only fetch EF_LI and EF_PL in lock state");
+ private void onLocked(int msg) {
+ if (DBG) log("only fetch EF_LI, EF_PL and EF_ICCID in locked state");
+ mLockedRecordsReqReason = msg == EVENT_APP_LOCKED ? LOCKED_RECORDS_REQ_REASON_LOCKED :
+ LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED;
+
loadEfLiAndEfPl();
+
+ mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
+ mRecordsToLoad++;
}
private void loadEfLiAndEfPl() {
if (mParentApp.getType() == AppType.APPTYPE_USIM) {
- mRecordsRequested = true;
mFh.loadEFTransparent(EF_LI,
obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfUsimLiLoaded()));
mRecordsToLoad++;
@@ -1811,24 +1743,31 @@
/**
* Returns the SpnDisplayRule based on settings on the SIM and the
- * specified plmn (currently-registered PLMN). See TS 22.101 Annex A
- * and TS 51.011 10.3.11 for details.
+ * current service state. See TS 22.101 Annex A and TS 51.011 10.3.11
+ * for details.
*
* If the SPN is not found on the SIM or is empty, the rule is
* always PLMN_ONLY.
+ *
+ * @param serviceState Service state
+ * @return the display rule
+ *
+ * @see #SPN_RULE_SHOW_SPN
+ * @see #SPN_RULE_SHOW_PLMN
*/
@Override
- public int getDisplayRule(String plmn) {
+ public int getDisplayRule(ServiceState serviceState) {
int rule;
- if (mParentApp != null && mParentApp.getUiccCard() != null &&
- mParentApp.getUiccCard().getOperatorBrandOverride() != null) {
+ if (mParentApp != null && mParentApp.getUiccProfile() != null
+ && mParentApp.getUiccProfile().getOperatorBrandOverride() != null) {
// If the operator has been overridden, treat it as the SPN file on the SIM did not exist.
rule = SPN_RULE_SHOW_PLMN;
} else if (TextUtils.isEmpty(getServiceProviderName()) || mSpnDisplayCondition == -1) {
// No EF_SPN content was found on the SIM, or not yet loaded. Just show ONS.
rule = SPN_RULE_SHOW_PLMN;
- } else if (isOnMatchingPlmn(plmn)) {
+ } else if (useRoamingFromServiceState() ? !serviceState.getRoaming()
+ : isOnMatchingPlmn(serviceState.getOperatorNumeric())) {
rule = SPN_RULE_SHOW_SPN;
if ((mSpnDisplayCondition & 0x01) == 0x01) {
// ONS required when registered to HPLMN or PLMN in EF_SPDI
@@ -1844,6 +1783,21 @@
return rule;
}
+ private boolean useRoamingFromServiceState() {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(
+ SubscriptionController.getInstance().getSubIdUsingPhoneId(
+ mParentApp.getPhoneId()));
+ if (b != null && b.getBoolean(CarrierConfigManager
+ .KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Checks if plmn is HPLMN or on the spdiNetworks list.
*/
@@ -2072,10 +2026,14 @@
return null;
}
int numPlmns = data.length / packedBcdPlmnLenBytes;
- String[] ret = new String[numPlmns];
+ int numValidPlmns = 0;
+ String[] parsed = new String[numPlmns];
for (int i = 0; i < numPlmns; i++) {
- ret[i] = IccUtils.bcdPlmnToString(data, i * packedBcdPlmnLenBytes);
+ parsed[numValidPlmns] = IccUtils.bcdPlmnToString(data, i * packedBcdPlmnLenBytes);
+ // we count the valid (non empty) records and only increment if valid
+ if (!TextUtils.isEmpty(parsed[numValidPlmns])) numValidPlmns++;
}
+ String[] ret = Arrays.copyOf(parsed, numValidPlmns);
if (VDBG) logv(description + " PLMNs: " + Arrays.toString(ret));
return ret;
}
@@ -2162,7 +2120,6 @@
pw.println(" extends:");
super.dump(fd, pw, args);
pw.println(" mVmConfig=" + mVmConfig);
- pw.println(" mSpnOverride=" + mSpnOverride);
pw.println(" mCallForwardingStatus=" + mCallForwardingStatus);
pw.println(" mSpnState=" + mSpnState);
pw.println(" mCphsInfo=" + mCphsInfo);
@@ -2173,15 +2130,18 @@
pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
- pw.println(" mPnnHomeName=" + mPnnHomeName);
pw.println(" mUsimServiceTable=" + mUsimServiceTable);
pw.println(" mGid1=" + mGid1);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeGid1=" + ((mFakeGid1 != null) ? mFakeGid1 : "null"));
+ pw.println(" mFakeGid1=" + mCarrierTestOverride.getFakeGid1());
}
pw.println(" mGid2=" + mGid2);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeGid2=" + ((mFakeGid2 != null) ? mFakeGid2 : "null"));
+ pw.println(" mFakeGid2=" + mCarrierTestOverride.getFakeGid2());
+ }
+ pw.println(" mPnnHomeName=" + mPnnHomeName);
+ if (mCarrierTestOverride.isInTestMode()) {
+ pw.println(" mFakePnnHomeName=" + mCarrierTestOverride.getFakePnnHomeName());
}
pw.println(" mPlmnActRecords[]=" + Arrays.toString(mPlmnActRecords));
pw.println(" mOplmnActRecords[]=" + Arrays.toString(mOplmnActRecords));
diff --git a/src/java/com/android/internal/telephony/uicc/SpnOverride.java b/src/java/com/android/internal/telephony/uicc/SpnOverride.java
deleted file mode 100644
index 3a01af6..0000000
--- a/src/java/com/android/internal/telephony/uicc/SpnOverride.java
+++ /dev/null
@@ -1,114 +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.uicc;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.os.Environment;
-import android.telephony.Rlog;
-import android.util.Xml;
-
-import com.android.internal.util.XmlUtils;
-
-public class SpnOverride {
- private HashMap<String, String> mCarrierSpnMap;
-
-
- static final String LOG_TAG = "SpnOverride";
- static final String PARTNER_SPN_OVERRIDE_PATH ="etc/spn-conf.xml";
- static final String OEM_SPN_OVERRIDE_PATH = "telephony/spn-conf.xml";
-
- SpnOverride () {
- mCarrierSpnMap = new HashMap<String, String>();
- loadSpnOverrides();
- }
-
- boolean containsCarrier(String carrier) {
- return mCarrierSpnMap.containsKey(carrier);
- }
-
- String getSpn(String carrier) {
- return mCarrierSpnMap.get(carrier);
- }
-
- private void loadSpnOverrides() {
- FileReader spnReader;
-
- File spnFile = new File(Environment.getRootDirectory(),
- PARTNER_SPN_OVERRIDE_PATH);
- File oemSpnFile = new File(Environment.getOemDirectory(),
- OEM_SPN_OVERRIDE_PATH);
-
- if (oemSpnFile.exists()) {
- // OEM image exist SPN xml, get the timestamp from OEM & System image for comparison.
- long oemSpnTime = oemSpnFile.lastModified();
- long sysSpnTime = spnFile.lastModified();
- Rlog.d(LOG_TAG, "SPN Timestamp: oemTime = " + oemSpnTime + " sysTime = " + sysSpnTime);
-
- // To get the newer version of SPN from OEM image
- if (oemSpnTime > sysSpnTime) {
- Rlog.d(LOG_TAG, "SPN in OEM image is newer than System image");
- spnFile = oemSpnFile;
- }
- } else {
- // No SPN in OEM image, so load it from system image.
- Rlog.d(LOG_TAG, "No SPN in OEM image = " + oemSpnFile.getPath() +
- " Load SPN from system image");
- }
-
- try {
- spnReader = new FileReader(spnFile);
- } catch (FileNotFoundException e) {
- Rlog.w(LOG_TAG, "Can not open " + spnFile.getAbsolutePath());
- return;
- }
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(spnReader);
-
- XmlUtils.beginDocument(parser, "spnOverrides");
-
- while (true) {
- XmlUtils.nextElement(parser);
-
- String name = parser.getName();
- if (!"spnOverride".equals(name)) {
- break;
- }
-
- String numeric = parser.getAttributeValue(null, "numeric");
- String data = parser.getAttributeValue(null, "spn");
-
- mCarrierSpnMap.put(numeric, data);
- }
- spnReader.close();
- } catch (XmlPullParserException e) {
- Rlog.w(LOG_TAG, "Exception in spn-conf parser " + e);
- } catch (IOException e) {
- Rlog.w(LOG_TAG, "Exception in spn-conf parser " + e);
- }
- }
-
-}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index baad60b..361f70b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -16,46 +16,24 @@
package com.android.internal.telephony.uicc;
-import android.app.AlertDialog;
-import android.app.usage.UsageStatsManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
-import android.os.PowerManager;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.view.WindowManager;
-import com.android.internal.R;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.CommandsInterface.RadioState;
-import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccCardStatus.PinState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
/**
@@ -68,137 +46,53 @@
public static final String EXTRA_ICC_CARD_ADDED =
"com.android.internal.telephony.uicc.ICC_CARD_ADDED";
- private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
-
- private final Object mLock = new Object();
+ // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
+ // between UiccSlot, UiccCard and UiccProfile for now.
+ private final Object mLock;
private CardState mCardState;
- private PinState mUniversalPinState;
- private int mGsmUmtsSubscriptionAppIndex;
- private int mCdmaSubscriptionAppIndex;
- private int mImsSubscriptionAppIndex;
- private UiccCardApplication[] mUiccApplications =
- new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
+ private String mIccid;
+ protected String mCardId;
+ private UiccProfile mUiccProfile;
private Context mContext;
private CommandsInterface mCi;
- private CatService mCatService;
- private RadioState mLastRadioState = RadioState.RADIO_UNAVAILABLE;
- private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
-
- private RegistrantList mAbsentRegistrants = new RegistrantList();
- private RegistrantList mCarrierPrivilegeRegistrants = new RegistrantList();
-
- private static final int EVENT_CARD_REMOVED = 13;
- private static final int EVENT_CARD_ADDED = 14;
- private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 15;
- private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 16;
- private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 17;
- private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 18;
- private static final int EVENT_SIM_IO_DONE = 19;
- private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 20;
-
- private static final LocalLog mLocalLog = new LocalLog(100);
-
private final int mPhoneId;
- public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+ public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
if (DBG) log("Creating");
mCardState = ics.mCardState;
mPhoneId = phoneId;
+ mLock = lock;
update(c, ci, ics);
}
public void dispose() {
synchronized (mLock) {
if (DBG) log("Disposing card");
- if (mCatService != null) mCatService.dispose();
- for (UiccCardApplication app : mUiccApplications) {
- if (app != null) {
- app.dispose();
- }
+ if (mUiccProfile != null) {
+ mUiccProfile.dispose();
}
- mCatService = null;
- mUiccApplications = null;
- mCarrierPrivilegeRules = null;
+ mUiccProfile = null;
}
}
public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
- CardState oldState = mCardState;
mCardState = ics.mCardState;
- mUniversalPinState = ics.mUniversalPinState;
- mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
- mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
- mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
mContext = c;
mCi = ci;
+ mIccid = ics.iccid;
+ updateCardId();
- //update applications
- if (DBG) log(ics.mApplications.length + " applications");
- for ( int i = 0; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] == null) {
- //Create newly added Applications
- if (i < ics.mApplications.length) {
- mUiccApplications[i] = new UiccCardApplication(this,
- ics.mApplications[i], mContext, mCi);
- }
- } else if (i >= ics.mApplications.length) {
- //Delete removed applications
- mUiccApplications[i].dispose();
- mUiccApplications[i] = null;
+ if (mCardState != CardState.CARDSTATE_ABSENT) {
+ if (mUiccProfile == null) {
+ mUiccProfile = TelephonyComponentFactory.getInstance().makeUiccProfile(
+ mContext, mCi, ics, mPhoneId, this, mLock);
} else {
- //Update the rest
- mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
+ mUiccProfile.update(mContext, mCi, ics);
}
- }
-
- createAndUpdateCatServiceLocked();
-
- // Reload the carrier privilege rules if necessary.
- log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
- if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
- mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
- mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
- } else if (mCarrierPrivilegeRules != null
- && mCardState != CardState.CARDSTATE_PRESENT) {
- mCarrierPrivilegeRules = null;
- }
-
- sanitizeApplicationIndexesLocked();
-
- RadioState radioState = mCi.getRadioState();
- if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
- + mLastRadioState);
- // No notifications while radio is off or we just powering up
- if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
- if (oldState != CardState.CARDSTATE_ABSENT &&
- mCardState == CardState.CARDSTATE_ABSENT) {
- if (DBG) log("update: notify card removed");
- mAbsentRegistrants.notifyRegistrants();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
- } else if (oldState == CardState.CARDSTATE_ABSENT &&
- mCardState != CardState.CARDSTATE_ABSENT) {
- if (DBG) log("update: notify card added");
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
- }
- }
- mLastRadioState = radioState;
- }
- }
-
- private void createAndUpdateCatServiceLocked() {
- if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
- // Initialize or Reinitialize CatService
- if (mCatService == null) {
- mCatService = CatService.getInstance(mCi, mContext, this, mPhoneId);
} else {
- mCatService.update(mCi, mContext, this);
+ throw new RuntimeException("Card state is absent when updating!");
}
- } else {
- if (mCatService != null) {
- mCatService.dispose();
- }
- mCatService = null;
}
}
@@ -208,277 +102,59 @@
}
/**
- * This function makes sure that application indexes are valid
- * and resets invalid indexes. (This should never happen, but in case
- * RIL misbehaves we need to manage situation gracefully)
+ * Updates the ID of the SIM card.
+ *
+ * <p>Whenever the {@link UiccCard#update(Context, CommandsInterface, IccCardStatus)} is called,
+ * this function needs to be called to update the card ID. Subclasses of {@link UiccCard}
+ * could override this function to set the {@link UiccCard#mCardId} to be something else instead
+ * of {@link UiccCard#mIccid}.</p>
*/
- private void sanitizeApplicationIndexesLocked() {
- mGsmUmtsSubscriptionAppIndex =
- checkIndexLocked(
- mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
- mCdmaSubscriptionAppIndex =
- checkIndexLocked(
- mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
- mImsSubscriptionAppIndex =
- checkIndexLocked(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
- }
-
- private int checkIndexLocked(int index, AppType expectedAppType, AppType altExpectedAppType) {
- if (mUiccApplications == null || index >= mUiccApplications.length) {
- loge("App index " + index + " is invalid since there are no applications");
- return -1;
- }
-
- if (index < 0) {
- // This is normal. (i.e. no application of this type)
- return -1;
- }
-
- if (mUiccApplications[index].getType() != expectedAppType &&
- mUiccApplications[index].getType() != altExpectedAppType) {
- loge("App index " + index + " is invalid since it's not " +
- expectedAppType + " and not " + altExpectedAppType);
- return -1;
- }
-
- // Seems to be valid
- return index;
- }
-
- /**
- * Notifies handler of any transition into State.ABSENT
- */
- public void registerForAbsent(Handler h, int what, Object obj) {
- synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mAbsentRegistrants.add(r);
-
- if (mCardState == CardState.CARDSTATE_ABSENT) {
- r.notifyRegistrant();
- }
- }
- }
-
- public void unregisterForAbsent(Handler h) {
- synchronized (mLock) {
- mAbsentRegistrants.remove(h);
- }
+ protected void updateCardId() {
+ mCardId = mIccid;
}
/**
* Notifies handler when carrier privilege rules are loaded.
+ * @deprecated Please use
+ * {@link UiccProfile#registerForCarrierPrivilegeRulesLoaded(Handler, int, Object)} instead.
*/
+ @Deprecated
public void registerForCarrierPrivilegeRulesLoaded(Handler h, int what, Object obj) {
synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mCarrierPrivilegeRegistrants.add(r);
-
- if (areCarrierPriviligeRulesLoaded()) {
- r.notifyRegistrant();
+ if (mUiccProfile != null) {
+ mUiccProfile.registerForCarrierPrivilegeRulesLoaded(h, what, obj);
+ } else {
+ loge("registerForCarrierPrivilegeRulesLoaded Failed!");
}
}
}
+ /**
+ * @deprecated Please use
+ * {@link UiccProfile#unregisterForCarrierPrivilegeRulesLoaded(Handler)} instead.
+ */
+ @Deprecated
public void unregisterForCarrierPrivilegeRulesLoaded(Handler h) {
synchronized (mLock) {
- mCarrierPrivilegeRegistrants.remove(h);
- }
- }
-
- private void onIccSwap(boolean isAdded) {
-
- boolean isHotSwapSupported = mContext.getResources().getBoolean(
- R.bool.config_hotswapCapable);
-
- if (isHotSwapSupported) {
- log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting");
- return;
- }
- log("onIccSwap: isHotSwapSupported is false, prompt for rebooting");
-
- promptForRestart(isAdded);
- }
-
- private void promptForRestart(boolean isAdded) {
- synchronized (mLock) {
- final Resources res = mContext.getResources();
- final String dialogComponent = res.getString(
- R.string.config_iccHotswapPromptForRestartDialogComponent);
- if (dialogComponent != null) {
- Intent intent = new Intent().setComponent(ComponentName.unflattenFromString(
- dialogComponent)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(EXTRA_ICC_CARD_ADDED, isAdded);
- try {
- mContext.startActivity(intent);
- return;
- } catch (ActivityNotFoundException e) {
- loge("Unable to find ICC hotswap prompt for restart activity: " + e);
- }
- }
-
- // TODO: Here we assume the device can't handle SIM hot-swap
- // and has to reboot. We may want to add a property,
- // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
- // hot-swap.
- DialogInterface.OnClickListener listener = null;
-
-
- // TODO: SimRecords is not reset while SIM ABSENT (only reset while
- // Radio_off_or_not_available). Have to reset in both both
- // added or removed situation.
- listener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- synchronized (mLock) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- if (DBG) log("Reboot due to SIM swap");
- PowerManager pm = (PowerManager) mContext
- .getSystemService(Context.POWER_SERVICE);
- pm.reboot("SIM is added.");
- }
- }
- }
-
- };
-
- Resources r = Resources.getSystem();
-
- String title = (isAdded) ? r.getString(R.string.sim_added_title) :
- r.getString(R.string.sim_removed_title);
- String message = (isAdded) ? r.getString(R.string.sim_added_message) :
- r.getString(R.string.sim_removed_message);
- String buttonTxt = r.getString(R.string.sim_restart_button);
-
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(buttonTxt, listener)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- }
- }
-
- protected Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg){
- switch (msg.what) {
- case EVENT_CARD_REMOVED:
- onIccSwap(false);
- break;
- case EVENT_CARD_ADDED:
- onIccSwap(true);
- break;
- case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
- case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
- case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
- case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
- case EVENT_SIM_IO_DONE:
- AsyncResult ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- loglocal("Exception: " + ar.exception);
- log("Error in SIM access with exception" + ar.exception);
- }
- AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_CARRIER_PRIVILEGES_LOADED:
- onCarrierPriviligesLoadedMessage();
- break;
- default:
- loge("Unknown Event " + msg.what);
- }
- }
- };
-
- private boolean isPackageInstalled(String pkgName) {
- PackageManager pm = mContext.getPackageManager();
- try {
- pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);
- if (DBG) log(pkgName + " is installed.");
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- if (DBG) log(pkgName + " is not installed.");
- return false;
- }
- }
-
- private class ClickListener implements DialogInterface.OnClickListener {
- String pkgName;
- public ClickListener(String pkgName) {
- this.pkgName = pkgName;
- }
- @Override
- public void onClick(DialogInterface dialog, int which) {
- synchronized (mLock) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- Intent market = new Intent(Intent.ACTION_VIEW);
- market.setData(Uri.parse("market://details?id=" + pkgName));
- market.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(market);
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- if (DBG) log("Not now clicked for carrier app dialog.");
- }
+ if (mUiccProfile != null) {
+ mUiccProfile.unregisterForCarrierPrivilegeRulesLoaded(h);
+ } else {
+ loge("unregisterForCarrierPrivilegeRulesLoaded Failed!");
}
}
}
- private void promptInstallCarrierApp(String pkgName) {
- DialogInterface.OnClickListener listener = new ClickListener(pkgName);
-
- Resources r = Resources.getSystem();
- String message = r.getString(R.string.carrier_app_dialog_message);
- String buttonTxt = r.getString(R.string.carrier_app_dialog_button);
- String notNowTxt = r.getString(R.string.carrier_app_dialog_not_now);
-
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setMessage(message)
- .setNegativeButton(notNowTxt, listener)
- .setPositiveButton(buttonTxt, listener)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- dialog.show();
- }
-
- private void onCarrierPriviligesLoadedMessage() {
- UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(
- Context.USAGE_STATS_SERVICE);
- if (usm != null) {
- usm.onCarrierPrivilegedAppsChanged();
- }
- synchronized (mLock) {
- mCarrierPrivilegeRegistrants.notifyRegistrants();
- String whitelistSetting = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.CARRIER_APP_WHITELIST);
- if (TextUtils.isEmpty(whitelistSetting)) {
- return;
- }
- HashSet<String> carrierAppSet = new HashSet<String>(
- Arrays.asList(whitelistSetting.split("\\s*;\\s*")));
- if (carrierAppSet.isEmpty()) {
- return;
- }
-
- List<String> pkgNames = mCarrierPrivilegeRules.getPackageNames();
- for (String pkgName : pkgNames) {
- if (!TextUtils.isEmpty(pkgName) && carrierAppSet.contains(pkgName)
- && !isPackageInstalled(pkgName)) {
- promptInstallCarrierApp(pkgName);
- }
- }
- }
- }
-
+ /**
+ * @deprecated Please use {@link UiccProfile#isApplicationOnIcc(AppType)} instead.
+ */
+ @Deprecated
public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
synchronized (mLock) {
- for (int i = 0 ; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) {
- return true;
- }
+ if (mUiccProfile != null) {
+ return mUiccProfile.isApplicationOnIcc(type);
+ } else {
+ return false;
}
- return false;
}
}
@@ -488,39 +164,45 @@
}
}
+ /**
+ * @deprecated Please use {@link UiccProfile#getUniversalPinState()} instead.
+ */
+ @Deprecated
public PinState getUniversalPinState() {
synchronized (mLock) {
- return mUniversalPinState;
+ if (mUiccProfile != null) {
+ return mUiccProfile.getUniversalPinState();
+ } else {
+ return PinState.PINSTATE_UNKNOWN;
+ }
}
}
+ /**
+ * @deprecated Please use {@link UiccProfile#getApplication(int)} instead.
+ */
+ @Deprecated
public UiccCardApplication getApplication(int family) {
synchronized (mLock) {
- int index = IccCardStatus.CARD_MAX_APPS;
- switch (family) {
- case UiccController.APP_FAM_3GPP:
- index = mGsmUmtsSubscriptionAppIndex;
- break;
- case UiccController.APP_FAM_3GPP2:
- index = mCdmaSubscriptionAppIndex;
- break;
- case UiccController.APP_FAM_IMS:
- index = mImsSubscriptionAppIndex;
- break;
+ if (mUiccProfile != null) {
+ return mUiccProfile.getApplication(family);
+ } else {
+ return null;
}
- if (index >= 0 && index < mUiccApplications.length) {
- return mUiccApplications[index];
- }
- return null;
}
}
+ /**
+ * @deprecated Please use {@link UiccProfile#getApplicationIndex(int)} instead.
+ */
+ @Deprecated
public UiccCardApplication getApplicationIndex(int index) {
synchronized (mLock) {
- if (index >= 0 && index < mUiccApplications.length) {
- return mUiccApplications[index];
+ if (mUiccProfile != null) {
+ return mUiccProfile.getApplicationIndex(index);
+ } else {
+ return null;
}
- return null;
}
}
@@ -529,16 +211,17 @@
*
* @param type ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
* @return application corresponding to type or a null if no match found
+ *
+ * @deprecated Please use {@link UiccProfile#getApplicationByType(int)} instead.
*/
+ @Deprecated
public UiccCardApplication getApplicationByType(int type) {
synchronized (mLock) {
- for (int i = 0 ; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] != null &&
- mUiccApplications[i].getType().ordinal() == type) {
- return mUiccApplications[i];
- }
+ if (mUiccProfile != null) {
+ return mUiccProfile.getApplicationByType(type);
+ } else {
+ return null;
}
- return null;
}
}
@@ -546,218 +229,272 @@
* Resets the application with the input AID. Returns true if any changes were made.
*
* A null aid implies a card level reset - all applications must be reset.
+ *
+ * @deprecated Please use {@link UiccProfile#resetAppWithAid(String, boolean)} instead.
*/
- public boolean resetAppWithAid(String aid) {
+ @Deprecated
+ public boolean resetAppWithAid(String aid, boolean reset) {
synchronized (mLock) {
- boolean changed = false;
- for (int i = 0; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] != null
- && (TextUtils.isEmpty(aid) || aid.equals(mUiccApplications[i].getAid()))) {
- // Delete removed applications
- mUiccApplications[i].dispose();
- mUiccApplications[i] = null;
- changed = true;
- }
+ if (mUiccProfile != null) {
+ return mUiccProfile.resetAppWithAid(aid, reset);
+ } else {
+ return false;
}
- if (TextUtils.isEmpty(aid)) {
- if (mCarrierPrivilegeRules != null) {
- mCarrierPrivilegeRules = null;
- changed = true;
- }
- if (mCatService != null) {
- mCatService.dispose();
- mCatService = null;
- changed = true;
- }
- }
- return changed;
}
}
/**
* Exposes {@link CommandsInterface#iccOpenLogicalChannel}
+ * @deprecated Please use
+ * {@link UiccProfile#iccOpenLogicalChannel(String, int, Message)} instead.
*/
+ @Deprecated
public void iccOpenLogicalChannel(String AID, int p2, Message response) {
- loglocal("Open Logical Channel: " + AID + " , " + p2 + " by pid:" + Binder.getCallingPid()
- + " uid:" + Binder.getCallingUid());
- mCi.iccOpenLogicalChannel(AID, p2,
- mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
+ if (mUiccProfile != null) {
+ mUiccProfile.iccOpenLogicalChannel(AID, p2, response);
+ } else {
+ loge("iccOpenLogicalChannel Failed!");
+ }
}
/**
* Exposes {@link CommandsInterface#iccCloseLogicalChannel}
+ * @deprecated Please use
+ * {@link UiccProfile#iccCloseLogicalChannel(int, Message)} instead.
*/
+ @Deprecated
public void iccCloseLogicalChannel(int channel, Message response) {
- loglocal("Close Logical Channel: " + channel);
- mCi.iccCloseLogicalChannel(channel,
- mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
+ if (mUiccProfile != null) {
+ mUiccProfile.iccCloseLogicalChannel(channel, response);
+ } else {
+ loge("iccCloseLogicalChannel Failed!");
+ }
}
/**
* Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel}
+ * @deprecated Please use {@link
+ * UiccProfile#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String, Message)}
+ * instead.
*/
+ @Deprecated
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));
+ if (mUiccProfile != null) {
+ mUiccProfile.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3,
+ data, response);
+ } else {
+ loge("iccTransmitApduLogicalChannel Failed!");
+ }
}
/**
* Exposes {@link CommandsInterface#iccTransmitApduBasicChannel}
+ * @deprecated Please use
+ * {@link UiccProfile#iccTransmitApduBasicChannel(int, int, int, int, int, String, Message)}
+ * instead.
*/
+ @Deprecated
public void iccTransmitApduBasicChannel(int cla, int command,
int p1, int p2, int p3, String data, Message response) {
- mCi.iccTransmitApduBasicChannel(cla, command, p1, p2, p3,
- data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, response));
+ if (mUiccProfile != null) {
+ mUiccProfile.iccTransmitApduBasicChannel(cla, command, p1, p2, p3, data, response);
+ } else {
+ loge("iccTransmitApduBasicChannel Failed!");
+ }
}
/**
* Exposes {@link CommandsInterface#iccIO}
+ * @deprecated Please use
+ * {@link UiccProfile#iccExchangeSimIO(int, int, int, int, int, String, Message)} instead.
*/
+ @Deprecated
public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String pathID, Message response) {
- mCi.iccIO(command, fileID, pathID, p1, p2, p3, null, null,
- mHandler.obtainMessage(EVENT_SIM_IO_DONE, response));
+ if (mUiccProfile != null) {
+ mUiccProfile.iccExchangeSimIO(fileID, command, p1, p2, p3, pathID, response);
+ } else {
+ loge("iccExchangeSimIO Failed!");
+ }
}
/**
* Exposes {@link CommandsInterface#sendEnvelopeWithStatus}
+ * @deprecated Please use {@link UiccProfile#sendEnvelopeWithStatus(String, Message)} instead.
*/
+ @Deprecated
public void sendEnvelopeWithStatus(String contents, Message response) {
- mCi.sendEnvelopeWithStatus(contents, response);
+ if (mUiccProfile != null) {
+ mUiccProfile.sendEnvelopeWithStatus(contents, response);
+ } else {
+ loge("sendEnvelopeWithStatus Failed!");
+ }
}
- /* Returns number of applications on this card */
+ /**
+ * Returns number of applications on this card
+ * @deprecated Please use {@link UiccProfile#getNumApplications()} instead.
+ */
+ @Deprecated
public int getNumApplications() {
- int count = 0;
- for (UiccCardApplication a : mUiccApplications) {
- if (a != null) {
- count++;
- }
+ if (mUiccProfile != null) {
+ return mUiccProfile.getNumApplications();
+ } else {
+ return 0;
}
- return count;
}
public int getPhoneId() {
return mPhoneId;
}
+ public UiccProfile getUiccProfile() {
+ return mUiccProfile;
+ }
+
/**
* Returns true iff carrier privileges rules are null (dont need to be loaded) or loaded.
+ * @deprecated Please use {@link UiccProfile#areCarrierPriviligeRulesLoaded()} instead.
*/
+ @Deprecated
public boolean areCarrierPriviligeRulesLoaded() {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null
- || carrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
+ if (mUiccProfile != null) {
+ return mUiccProfile.areCarrierPriviligeRulesLoaded();
+ } else {
+ return false;
+ }
}
/**
* Returns true if there are some carrier privilege rules loaded and specified.
+ * @deprecated Please use {@link UiccProfile#hasCarrierPrivilegeRules()} instead.
*/
+ @Deprecated
public boolean hasCarrierPrivilegeRules() {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules != null && carrierPrivilegeRules.hasCarrierPrivilegeRules();
+ if (mUiccProfile != null) {
+ return mUiccProfile.hasCarrierPrivilegeRules();
+ } else {
+ return false;
+ }
}
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ * @deprecated Please use
+ * {@link UiccProfile#getCarrierPrivilegeStatus(Signature, String)} instead.
*/
+ @Deprecated
public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null
- ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
- carrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
+ if (mUiccProfile != null) {
+ return mUiccProfile.getCarrierPrivilegeStatus(signature, packageName);
+ } else {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
+ }
}
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ * @deprecated Please use
+ * {@link UiccProfile#getCarrierPrivilegeStatus(PackageManager, String)} instead.
*/
+ @Deprecated
public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null
- ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
- carrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
+ if (mUiccProfile != null) {
+ return mUiccProfile.getCarrierPrivilegeStatus(packageManager, packageName);
+ } else {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
+ }
}
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ * @deprecated Please use {@link UiccProfile#getCarrierPrivilegeStatus(PackageInfo)} instead.
*/
+ @Deprecated
public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null
- ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
- carrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
+ if (mUiccProfile != null) {
+ return mUiccProfile.getCarrierPrivilegeStatus(packageInfo);
+ } else {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
+ }
}
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatusForCurrentTransaction}.
+ * @deprecated Please use
+ * {@link UiccProfile#getCarrierPrivilegeStatusForCurrentTransaction(PackageManager)} instead.
*/
+ @Deprecated
public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null
- ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
- carrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(
- packageManager);
+ if (mUiccProfile != null) {
+ return mUiccProfile.getCarrierPrivilegeStatusForCurrentTransaction(packageManager);
+ } else {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
+ }
}
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
+ * @deprecated Please use
+ * {@link UiccProfile#getCarrierPackageNamesForIntent(PackageManager, Intent)} instead.
*/
+ @Deprecated
public List<String> getCarrierPackageNamesForIntent(
PackageManager packageManager, Intent intent) {
- UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
- return carrierPrivilegeRules == null ? null :
- carrierPrivilegeRules.getCarrierPackageNamesForIntent(
- packageManager, intent);
- }
-
- /** Returns a reference to the current {@link UiccCarrierPrivilegeRules}. */
- private UiccCarrierPrivilegeRules getCarrierPrivilegeRules() {
- synchronized (mLock) {
- return mCarrierPrivilegeRules;
- }
- }
-
- public boolean setOperatorBrandOverride(String brand) {
- log("setOperatorBrandOverride: " + brand);
- log("current iccId: " + getIccId());
-
- String iccId = getIccId();
- if (TextUtils.isEmpty(iccId)) {
- return false;
- }
-
- SharedPreferences.Editor spEditor =
- PreferenceManager.getDefaultSharedPreferences(mContext).edit();
- String key = OPERATOR_BRAND_OVERRIDE_PREFIX + iccId;
- if (brand == null) {
- spEditor.remove(key).commit();
+ if (mUiccProfile != null) {
+ return mUiccProfile.getCarrierPackageNamesForIntent(packageManager, intent);
} else {
- spEditor.putString(key, brand).commit();
- }
- return true;
- }
-
- public String getOperatorBrandOverride() {
- String iccId = getIccId();
- if (TextUtils.isEmpty(iccId)) {
return null;
}
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- return sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
+ }
+
+ /**
+ * @deprecated Please use {@link UiccProfile#setOperatorBrandOverride(String)} instead.
+ */
+ @Deprecated
+ public boolean setOperatorBrandOverride(String brand) {
+ if (mUiccProfile != null) {
+ return mUiccProfile.setOperatorBrandOverride(brand);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @deprecated Please use {@link UiccProfile#getOperatorBrandOverride()} instead.
+ */
+ @Deprecated
+ public String getOperatorBrandOverride() {
+ if (mUiccProfile != null) {
+ return mUiccProfile.getOperatorBrandOverride();
+ } else {
+ return null;
+ }
}
public String getIccId() {
- // ICCID should be same across all the apps.
- for (UiccCardApplication app : mUiccApplications) {
- if (app != null) {
- IccRecords ir = app.getIccRecords();
- if (ir != null && ir.getIccId() != null) {
- return ir.getIccId();
- }
- }
+ if (mIccid != null) {
+ return mIccid;
+ } else if (mUiccProfile != null) {
+ return mUiccProfile.getIccId();
+ } else {
+ return null;
}
- return null;
+ }
+
+ /**
+ * Returns the ID of this SIM card, it is the ICCID of the active profile on the card for a UICC
+ * card or the EID of the card for an eUICC card.
+ */
+ public String getCardId() {
+ if (mCardId != null) {
+ return mCardId;
+ } else if (mUiccProfile != null) {
+ return mUiccProfile.getIccId();
+ } else {
+ return null;
+ }
}
private void log(String msg) {
@@ -768,72 +505,13 @@
Rlog.e(LOG_TAG, msg);
}
- private void loglocal(String msg) {
- if (DBG) mLocalLog.log(msg);
- }
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UiccCard:");
pw.println(" mCi=" + mCi);
- pw.println(" mLastRadioState=" + mLastRadioState);
- pw.println(" mCatService=" + mCatService);
- pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
- for (int i = 0; i < mAbsentRegistrants.size(); i++) {
- pw.println(" mAbsentRegistrants[" + i + "]="
- + ((Registrant)mAbsentRegistrants.get(i)).getHandler());
- }
- for (int i = 0; i < mCarrierPrivilegeRegistrants.size(); i++) {
- pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
- + ((Registrant)mCarrierPrivilegeRegistrants.get(i)).getHandler());
- }
pw.println(" mCardState=" + mCardState);
- pw.println(" mUniversalPinState=" + mUniversalPinState);
- pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
- pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
- pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
- pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
- pw.println(" mUiccApplications: length=" + mUiccApplications.length);
- for (int i = 0; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] == null) {
- pw.println(" mUiccApplications[" + i + "]=" + null);
- } else {
- pw.println(" mUiccApplications[" + i + "]="
- + mUiccApplications[i].getType() + " " + mUiccApplications[i]);
- }
- }
pw.println();
- // Print details of all applications
- for (UiccCardApplication app : mUiccApplications) {
- if (app != null) {
- app.dump(fd, pw, args);
- pw.println();
- }
+ if (mUiccProfile != null) {
+ mUiccProfile.dump(fd, pw, args);
}
- // Print details of all IccRecords
- for (UiccCardApplication app : mUiccApplications) {
- if (app != null) {
- IccRecords ir = app.getIccRecords();
- if (ir != null) {
- ir.dump(fd, pw, args);
- pw.println();
- }
- }
- }
- // Print UiccCarrierPrivilegeRules and registrants.
- if (mCarrierPrivilegeRules == null) {
- pw.println(" mCarrierPrivilegeRules: null");
- } else {
- pw.println(" mCarrierPrivilegeRules: " + mCarrierPrivilegeRules);
- mCarrierPrivilegeRules.dump(fd, pw, args);
- }
- pw.println(" mCarrierPrivilegeRegistrants: size=" + mCarrierPrivilegeRegistrants.size());
- for (int i = 0; i < mCarrierPrivilegeRegistrants.size(); i++) {
- pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
- + ((Registrant)mCarrierPrivilegeRegistrants.get(i)).getHandler());
- }
- pw.flush();
- pw.println("mLocalLog:");
- mLocalLog.dump(fd, pw, args);
- pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index fa6bc3a..7a361e3 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -30,7 +30,6 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.SubscriptionController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -60,7 +59,7 @@
public static final int AUTH_CONTEXT_UNDEFINED = PhoneConstants.AUTH_CONTEXT_UNDEFINED;
private final Object mLock = new Object();
- private UiccCard mUiccCard; //parent
+ private UiccProfile mUiccProfile; //parent
private AppState mAppState;
private AppType mAppType;
private int mAuthContext;
@@ -74,6 +73,9 @@
private boolean mDesiredFdnEnabled;
private boolean mIccLockEnabled;
private boolean mDesiredPinLocked;
+
+ // App state will be ignored while deciding whether the card is ready or not.
+ private boolean mIgnoreApp;
private boolean mIccFdnAvailable = true; // Default is enabled.
private CommandsInterface mCi;
@@ -87,12 +89,12 @@
private RegistrantList mPinLockedRegistrants = new RegistrantList();
private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
- public UiccCardApplication(UiccCard uiccCard,
+ public UiccCardApplication(UiccProfile uiccProfile,
IccCardApplicationStatus as,
Context c,
CommandsInterface ci) {
if (DBG) log("Creating UiccApp: " + as);
- mUiccCard = uiccCard;
+ mUiccProfile = uiccProfile;
mAppState = as.app_state;
mAppType = as.app_type;
mAuthContext = getAuthContext(mAppType);
@@ -102,6 +104,7 @@
mPin1Replaced = (as.pin1_replaced != 0);
mPin1State = as.pin1;
mPin2State = as.pin2;
+ mIgnoreApp = false;
mContext = c;
mCi = ci;
@@ -432,7 +435,7 @@
/**
* Notifies handler of any transition into State.isPinLocked()
*/
- public void registerForLocked(Handler h, int what, Object obj) {
+ protected void registerForLocked(Handler h, int what, Object obj) {
synchronized (mLock) {
Registrant r = new Registrant (h, what, obj);
mPinLockedRegistrants.add(r);
@@ -440,7 +443,7 @@
}
}
- public void unregisterForLocked(Handler h) {
+ protected void unregisterForLocked(Handler h) {
synchronized (mLock) {
mPinLockedRegistrants.remove(h);
}
@@ -449,7 +452,7 @@
/**
* Notifies handler of any transition into State.NETWORK_LOCKED
*/
- public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ protected void registerForNetworkLocked(Handler h, int what, Object obj) {
synchronized (mLock) {
Registrant r = new Registrant (h, what, obj);
mNetworkLockedRegistrants.add(r);
@@ -457,7 +460,7 @@
}
}
- public void unregisterForNetworkLocked(Handler h) {
+ protected void unregisterForNetworkLocked(Handler h) {
synchronized (mLock) {
mNetworkLockedRegistrants.remove(h);
}
@@ -604,7 +607,7 @@
public PinState getPin1State() {
synchronized (mLock) {
if (mPin1Replaced) {
- return mUiccCard.getUniversalPinState();
+ return mUiccProfile.getUniversalPinState();
}
return mPin1State;
}
@@ -836,6 +839,24 @@
}
/**
+ * @return true if the UiccCardApplication is ready.
+ */
+ public boolean isReady() {
+ synchronized (mLock) {
+ if (mAppState != AppState.APPSTATE_READY) {
+ return false;
+ } else if (mPin1State == PinState.PINSTATE_ENABLED_NOT_VERIFIED
+ || mPin1State == PinState.PINSTATE_ENABLED_BLOCKED
+ || mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
+ loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!");
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
* @return true if ICC card is PIN2 blocked
*/
public boolean getIccPin2Blocked() {
@@ -854,11 +875,19 @@
}
public int getPhoneId() {
- return mUiccCard.getPhoneId();
+ return mUiccProfile.getPhoneId();
}
- protected UiccCard getUiccCard() {
- return mUiccCard;
+ public boolean isAppIgnored() {
+ return mIgnoreApp;
+ }
+
+ public void setAppIgnoreState(boolean ignore) {
+ mIgnoreApp = ignore;
+ }
+
+ protected UiccProfile getUiccProfile() {
+ return mUiccProfile;
}
private void log(String msg) {
@@ -871,7 +900,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UiccCardApplication: " + this);
- pw.println(" mUiccCard=" + mUiccCard);
+ pw.println(" mUiccProfile=" + mUiccProfile);
pw.println(" mAppState=" + mAppState);
pw.println(" mAppType=" + mAppType);
pw.println(" mPersoSubState=" + mPersoSubState);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index bfa458b..6620d83 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -178,7 +178,7 @@
}
}
- private UiccCard mUiccCard; // Parent
+ private UiccProfile mUiccProfile; // Parent
private UiccPkcs15 mUiccPkcs15; // ARF fallback
private AtomicInteger mState;
private List<UiccAccessRule> mAccessRules;
@@ -200,13 +200,13 @@
// Send open logical channel request.
String aid = (aidId == ARAD) ? ARAD_AID : ARAM_AID;
int p2 = 0x00;
- mUiccCard.iccOpenLogicalChannel(aid, p2, /* supported p2 value */
+ mUiccProfile.iccOpenLogicalChannel(aid, p2, /* supported p2 value */
obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, 0, aidId, null));
}
- public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
+ public UiccCarrierPrivilegeRules(UiccProfile uiccProfile, Message loadedCallback) {
log("Creating UiccCarrierPrivilegeRules");
- mUiccCard = uiccCard;
+ mUiccProfile = uiccProfile;
mState = new AtomicInteger(STATE_LOADING);
mStatusMessage = "Not loaded.";
mLoadedCallback = loadedCallback;
@@ -302,7 +302,7 @@
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS);
return getCarrierPrivilegeStatus(pInfo);
} catch (PackageManager.NameNotFoundException ex) {
- Rlog.e(LOG_TAG, "NameNotFoundException", ex);
+ log("Package " + packageName + " not found for carrier privilege status check");
}
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
@@ -337,7 +337,18 @@
* @return Access status.
*/
public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
- String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
+ return getCarrierPrivilegeStatusForUid(packageManager, Binder.getCallingUid());
+ }
+
+ /**
+ * Returns the status of the carrier privileges for the caller of the current transaction.
+ *
+ * @param packageManager PackageManager for getting signatures and package names.
+ * @return Access status.
+ */
+ public int getCarrierPrivilegeStatusForUid(
+ PackageManager packageManager, int uid) {
+ String[] packages = packageManager.getPackagesForUid(uid);
for (String pkg : packages) {
int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
@@ -408,7 +419,7 @@
ar = (AsyncResult) msg.obj;
if (ar.exception == null && ar.result != null) {
mChannelId = ((int[]) ar.result)[0];
- mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3,
+ mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3,
DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId,
mAIDInUse));
} else {
@@ -433,7 +444,7 @@
// if rules cannot be read from both ARA_D and ARA_M applet,
// fallback to PKCS15-based ARF.
log("No ARA, try ARF next.");
- mUiccPkcs15 = new UiccPkcs15(mUiccCard,
+ mUiccPkcs15 = new UiccPkcs15(mUiccProfile,
obtainMessage(EVENT_PKCS15_READ_DONE));
}
}
@@ -459,7 +470,7 @@
updateState(STATE_LOADED, "Success!");
}
} else {
- mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
+ mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
P1, P2_EXTENDED_DATA, P3, DATA,
obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
mChannelId, mAIDInUse));
@@ -483,7 +494,7 @@
}
}
- mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
+ mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage(
EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse));
mChannelId = -1;
break;
@@ -690,4 +701,4 @@
return "UNKNOWN";
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index a948b75..783d82f 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -17,24 +17,34 @@
package com.android.internal.telephony.uicc;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
-import android.os.SystemProperties;
import android.os.storage.StorageManager;
-import android.telephony.TelephonyManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
-import android.text.format.Time;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RadioConfig;
+import com.android.internal.telephony.SubscriptionInfoUpdater;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* This class is responsible for keeping all knowledge about
@@ -56,7 +66,13 @@
* UiccController
* #
* |
+ * UiccSlot[]
+ * #
+ * |
* UiccCard
+ * #
+ * |
+ * UiccProfile
* # #
* | ------------------
* UiccCardApplication CatService
@@ -74,44 +90,55 @@
* ^ stands for Generalization
*
* See also {@link com.android.internal.telephony.IccCard}
- * and {@link com.android.internal.telephony.uicc.IccCardProxy}
*/
public class UiccController extends Handler {
private static final boolean DBG = true;
+ private static final boolean VDBG = false; //STOPSHIP if true
private static final String LOG_TAG = "UiccController";
+ public static final int INVALID_SLOT_ID = -1;
+
public static final int APP_FAM_3GPP = 1;
public static final int APP_FAM_3GPP2 = 2;
public static final int APP_FAM_IMS = 3;
private static final int EVENT_ICC_STATUS_CHANGED = 1;
- private static final int EVENT_GET_ICC_STATUS_DONE = 2;
- private static final int EVENT_RADIO_UNAVAILABLE = 3;
- private static final int EVENT_SIM_REFRESH = 4;
+ private static final int EVENT_SLOT_STATUS_CHANGED = 2;
+ private static final int EVENT_GET_ICC_STATUS_DONE = 3;
+ private static final int EVENT_GET_SLOT_STATUS_DONE = 4;
+ private static final int EVENT_RADIO_ON = 5;
+ private static final int EVENT_RADIO_AVAILABLE = 6;
+ private static final int EVENT_RADIO_UNAVAILABLE = 7;
+ private static final int EVENT_SIM_REFRESH = 8;
+ // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
private CommandsInterface[] mCis;
- private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
+ private UiccSlot[] mUiccSlots;
+ private int[] mPhoneIdToSlotId;
+ private boolean mIsSlotStatusSupported = true;
+ private boolean mIsCdmaSupported = true;
private static final Object mLock = new Object();
private static UiccController mInstance;
+ private static ArrayList<IccSlotStatus> sLastSlotStatus;
private Context mContext;
protected RegistrantList mIccChangedRegistrants = new RegistrantList();
private UiccStateChangedLauncher mLauncher;
+ private RadioConfig mRadioConfig;
- // Logging for dumpsys. Useful in cases when the cards run into errors.
- private static final int MAX_PROACTIVE_COMMANDS_TO_LOG = 20;
- private LinkedList<String> mCardLogs = new LinkedList<String>();
+ // LocalLog buffer to hold important SIM related events for debugging
+ static LocalLog sLocalLog = new LocalLog(100);
public static UiccController make(Context c, CommandsInterface[] ci) {
synchronized (mLock) {
if (mInstance != null) {
- throw new RuntimeException("MSimUiccController.make() should only be called once");
+ throw new RuntimeException("UiccController.make() should only be called once");
}
mInstance = new UiccController(c, ci);
- return (UiccController)mInstance;
+ return mInstance;
}
}
@@ -119,24 +146,53 @@
if (DBG) log("Creating UiccController");
mContext = c;
mCis = ci;
+ if (DBG) {
+ String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
+ com.android.internal.R.integer.config_num_physical_slots);
+ log(logStr);
+ sLocalLog.log(logStr);
+ }
+ int numPhysicalSlots = c.getResources().getInteger(
+ com.android.internal.R.integer.config_num_physical_slots);
+ // Minimum number of physical slot count should be equals to or greater than phone count,
+ // if it is less than phone count use phone count as physical slot count.
+ if (numPhysicalSlots < mCis.length) {
+ numPhysicalSlots = mCis.length;
+ }
+
+ mUiccSlots = new UiccSlot[numPhysicalSlots];
+ mPhoneIdToSlotId = new int[ci.length];
+ Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
+ if (VDBG) logPhoneIdToSlotIdMapping();
+ mRadioConfig = RadioConfig.getInstance(mContext);
+ mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null);
for (int i = 0; i < mCis.length; i++) {
- Integer index = new Integer(i);
- mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
+ mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
+
// TODO remove this once modem correctly notifies the unsols
// If the device is unencrypted or has been decrypted or FBE is supported,
- // i.e. not in cryptkeeper bounce, read SIM when radio state isavailable.
+ // i.e. not in CryptKeeper bounce, read SIM when radio state is available.
// Else wait for radio to be on. This is needed for the scenario when SIM is locked --
// to avoid overlap of CryptKeeper and SIM unlock screen.
if (!StorageManager.inCryptKeeperBounce()) {
- mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
+ mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
} else {
- mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
+ mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
}
- mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
- mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);
+ mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
+ mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
}
mLauncher = new UiccStateChangedLauncher(c, this);
+
+ // set mIsCdmaSupported based on PackageManager.FEATURE_TELEPHONY_CDMA
+ PackageManager packageManager = c.getPackageManager();
+ mIsCdmaSupported =
+ packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
+ }
+
+ private int getSlotIdFromPhoneId(int phoneId) {
+ return mPhoneIdToSlotId[phoneId];
}
public static UiccController getInstance() {
@@ -151,18 +207,126 @@
public UiccCard getUiccCard(int phoneId) {
synchronized (mLock) {
- if (isValidCardIndex(phoneId)) {
- return mUiccCards[phoneId];
+ return getUiccCardForPhone(phoneId);
+ }
+ }
+
+ /**
+ * API to get UiccCard corresponding to given physical slot index
+ * @param slotId index of physical slot on the device
+ * @return UiccCard object corresponting to given physical slot index; null if card is
+ * absent
+ */
+ public UiccCard getUiccCardForSlot(int slotId) {
+ synchronized (mLock) {
+ UiccSlot uiccSlot = getUiccSlot(slotId);
+ if (uiccSlot != null) {
+ return uiccSlot.getUiccCard();
}
return null;
}
}
- public UiccCard[] getUiccCards() {
- // Return cloned array since we don't want to give out reference
- // to internal data structure.
+ /**
+ * API to get UiccCard corresponding to given phone id
+ * @return UiccCard object corresponding to given phone id; null if there is no card present for
+ * the phone id
+ */
+ public UiccCard getUiccCardForPhone(int phoneId) {
synchronized (mLock) {
- return mUiccCards.clone();
+ if (isValidPhoneIndex(phoneId)) {
+ UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
+ if (uiccSlot != null) {
+ return uiccSlot.getUiccCard();
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * API to get UiccProfile corresponding to given phone id
+ * @return UiccProfile object corresponding to given phone id; null if there is no card/profile
+ * present for the phone id
+ */
+ public UiccProfile getUiccProfileForPhone(int phoneId) {
+ synchronized (mLock) {
+ if (isValidPhoneIndex(phoneId)) {
+ UiccCard uiccCard = getUiccCardForPhone(phoneId);
+ return uiccCard != null ? uiccCard.getUiccProfile() : null;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * API to get all the UICC slots.
+ * @return UiccSlots array.
+ */
+ public UiccSlot[] getUiccSlots() {
+ synchronized (mLock) {
+ return mUiccSlots;
+ }
+ }
+
+ /** Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. */
+ public void switchSlots(int[] physicalSlots, Message response) {
+ mRadioConfig.setSimSlotsMapping(physicalSlots, response);
+ }
+
+ /**
+ * API to get UiccSlot object for a specific physical slot index on the device
+ * @return UiccSlot object for the given physical slot index
+ */
+ public UiccSlot getUiccSlot(int slotId) {
+ synchronized (mLock) {
+ if (isValidSlotIndex(slotId)) {
+ return mUiccSlots[slotId];
+ }
+ return null;
+ }
+ }
+
+ /**
+ * API to get UiccSlot object for a given phone id
+ * @return UiccSlot object for the given phone id
+ */
+ public UiccSlot getUiccSlotForPhone(int phoneId) {
+ synchronized (mLock) {
+ if (isValidPhoneIndex(phoneId)) {
+ int slotId = getSlotIdFromPhoneId(phoneId);
+ if (isValidSlotIndex(slotId)) {
+ return mUiccSlots[slotId];
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * API to get UiccSlot object for a given cardId
+ * @param cardId Identifier for a SIM. This can be an ICCID, or an EID in case of an eSIM.
+ * @return int Index of UiccSlot for the given cardId if one is found, {@link #INVALID_SLOT_ID}
+ * otherwise
+ */
+ public int getUiccSlotForCardId(String cardId) {
+ synchronized (mLock) {
+ // first look up based on cardId
+ for (int idx = 0; idx < mUiccSlots.length; idx++) {
+ if (mUiccSlots[idx] != null) {
+ UiccCard uiccCard = mUiccSlots[idx].getUiccCard();
+ if (uiccCard != null && cardId.equals(uiccCard.getCardId())) {
+ return idx;
+ }
+ }
+ }
+ // if a match is not found, do a lookup based on ICCID
+ for (int idx = 0; idx < mUiccSlots.length; idx++) {
+ if (mUiccSlots[idx] != null && cardId.equals(mUiccSlots[idx].getIccId())) {
+ return idx;
+ }
+ }
+ return INVALID_SLOT_ID;
}
}
@@ -209,37 +373,67 @@
@Override
public void handleMessage (Message msg) {
synchronized (mLock) {
- Integer index = getCiIndex(msg);
+ Integer phoneId = getCiIndex(msg);
- if (index < 0 || index >= mCis.length) {
- Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);
+ if (phoneId < 0 || phoneId >= mCis.length) {
+ Rlog.e(LOG_TAG, "Invalid phoneId : " + phoneId + " received with event "
+ + msg.what);
return;
}
+ sLocalLog.log("handleMessage: Received " + msg.what + " for phoneId " + phoneId);
+
AsyncResult ar = (AsyncResult)msg.obj;
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
- mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
+ mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
+ phoneId));
+ break;
+ case EVENT_RADIO_AVAILABLE:
+ case EVENT_RADIO_ON:
+ if (DBG) {
+ log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON, calling "
+ + "getIccCardStatus");
+ }
+ mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
+ phoneId));
+ // slot status should be the same on all RILs; request it only for phoneId 0
+ if (phoneId == 0) {
+ if (DBG) {
+ log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON for phoneId 0, "
+ + "calling getIccSlotsStatus");
+ }
+ mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
+ phoneId));
+ }
break;
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
- onGetIccCardStatusDone(ar, index);
+ onGetIccCardStatusDone(ar, phoneId);
+ break;
+ case EVENT_SLOT_STATUS_CHANGED:
+ case EVENT_GET_SLOT_STATUS_DONE:
+ if (DBG) {
+ log("Received EVENT_SLOT_STATUS_CHANGED or EVENT_GET_SLOT_STATUS_DONE");
+ }
+ onGetSlotStatusDone(ar);
break;
case EVENT_RADIO_UNAVAILABLE:
if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
- if (mUiccCards[index] != null) {
- mUiccCards[index].dispose();
+ UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
+ if (uiccSlot != null) {
+ uiccSlot.onRadioStateUnavailable();
}
- mUiccCards[index] = null;
- mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
+ mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, phoneId, null));
break;
case EVENT_SIM_REFRESH:
if (DBG) log("Received EVENT_SIM_REFRESH");
- onSimRefresh(ar, index);
+ onSimRefresh(ar, phoneId);
break;
default:
Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
+ break;
}
}
}
@@ -269,16 +463,23 @@
// Easy to use API
public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
synchronized (mLock) {
- if (isValidCardIndex(phoneId)) {
- UiccCard c = mUiccCards[phoneId];
- if (c != null) {
- return mUiccCards[phoneId].getApplication(family);
- }
+ UiccCard uiccCard = getUiccCardForPhone(phoneId);
+ if (uiccCard != null) {
+ return uiccCard.getApplication(family);
}
return null;
}
}
+ static void updateInternalIccState(String value, String reason, int phoneId) {
+ SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
+ if (subInfoUpdator != null) {
+ subInfoUpdator.updateInternalIccState(value, reason, phoneId);
+ } else {
+ Rlog.e(LOG_TAG, "subInfoUpdate is null.");
+ }
+ }
+
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
if (ar.exception != null) {
Rlog.e(LOG_TAG,"Error getting ICC status. "
@@ -286,81 +487,218 @@
+ "never return an error", ar.exception);
return;
}
- if (!isValidCardIndex(index)) {
+ if (!isValidPhoneIndex(index)) {
Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
return;
}
IccCardStatus status = (IccCardStatus)ar.result;
- if (mUiccCards[index] == null) {
- //Create new card
- mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
- } else {
- //Update already existing card
- mUiccCards[index].update(mContext, mCis[index] , status);
+ sLocalLog.log("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
+
+ int slotId = status.physicalSlotIndex;
+ if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
+ if (slotId == INVALID_SLOT_ID) {
+ slotId = index;
}
+ mPhoneIdToSlotId[index] = slotId;
+
+ if (VDBG) logPhoneIdToSlotIdMapping();
+
+ if (mUiccSlots[slotId] == null) {
+ if (VDBG) {
+ log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = "
+ + mUiccSlots.length);
+ }
+ mUiccSlots[slotId] = new UiccSlot(mContext, true);
+ }
+
+ mUiccSlots[slotId].update(mCis[index], status, index);
if (DBG) log("Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
+ }
+ private synchronized void onGetSlotStatusDone(AsyncResult ar) {
+ if (!mIsSlotStatusSupported) {
+ if (VDBG) log("onGetSlotStatusDone: ignoring since mIsSlotStatusSupported is false");
+ return;
+ }
+ Throwable e = ar.exception;
+ if (e != null) {
+ String logStr;
+ if (!(e instanceof CommandException) || ((CommandException) e).getCommandError()
+ != CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ // this is not expected; there should be no exception other than
+ // REQUEST_NOT_SUPPORTED
+ logStr = "Unexpected error getting slot status: " + ar.exception;
+ Rlog.e(LOG_TAG, logStr);
+ sLocalLog.log(logStr);
+ } else {
+ // REQUEST_NOT_SUPPORTED
+ logStr = "onGetSlotStatusDone: request not supported; marking "
+ + "mIsSlotStatusSupported to false";
+ log(logStr);
+ sLocalLog.log(logStr);
+ mIsSlotStatusSupported = false;
+ }
+ return;
+ }
+
+ ArrayList<IccSlotStatus> status = (ArrayList<IccSlotStatus>) ar.result;
+
+ if (!slotStatusChanged(status)) {
+ log("onGetSlotStatusDone: No change in slot status");
+ return;
+ }
+
+ sLastSlotStatus = status;
+
+ int numActiveSlots = 0;
+ for (int i = 0; i < status.size(); i++) {
+ IccSlotStatus iss = status.get(i);
+ boolean isActive = (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE);
+ if (isActive) {
+ numActiveSlots++;
+
+ // sanity check: logicalSlotIndex should be valid for an active slot
+ if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
+ throw new RuntimeException("Logical slot index " + iss.logicalSlotIndex
+ + " invalid for physical slot " + i);
+ }
+ mPhoneIdToSlotId[iss.logicalSlotIndex] = i;
+ }
+
+ if (mUiccSlots[i] == null) {
+ if (VDBG) {
+ log("Creating mUiccSlot[" + i + "]; mUiccSlots.length = " + mUiccSlots.length);
+ }
+ mUiccSlots[i] = new UiccSlot(mContext, isActive);
+ }
+
+ mUiccSlots[i].update(isActive ? mCis[iss.logicalSlotIndex] : null, iss);
+ }
+
+ if (VDBG) logPhoneIdToSlotIdMapping();
+
+ // sanity check: number of active slots should be valid
+ if (numActiveSlots != mPhoneIdToSlotId.length) {
+ throw new RuntimeException("Number of active slots " + numActiveSlots
+ + " does not match the expected value " + mPhoneIdToSlotId.length);
+ }
+
+ // sanity check: slotIds should be unique in mPhoneIdToSlotId
+ Set<Integer> slotIds = new HashSet<>();
+ for (int slotId : mPhoneIdToSlotId) {
+ if (slotIds.contains(slotId)) {
+ throw new RuntimeException("slotId " + slotId + " mapped to multiple phoneIds");
+ }
+ slotIds.add(slotId);
+ }
+
+ // broadcast slot status changed
+ Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+
+ private boolean slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList) {
+ if (sLastSlotStatus == null || sLastSlotStatus.size() != slotStatusList.size()) {
+ return true;
+ }
+ for (IccSlotStatus iccSlotStatus : slotStatusList) {
+ if (!sLastSlotStatus.contains(iccSlotStatus)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void logPhoneIdToSlotIdMapping() {
+ log("mPhoneIdToSlotId mapping:");
+ for (int i = 0; i < mPhoneIdToSlotId.length; i++) {
+ log(" phoneId " + i + " slotId " + mPhoneIdToSlotId[i]);
+ }
}
private void onSimRefresh(AsyncResult ar, Integer index) {
if (ar.exception != null) {
- Rlog.e(LOG_TAG, "Sim REFRESH with exception: " + ar.exception);
+ Rlog.e(LOG_TAG, "onSimRefresh: Sim REFRESH with exception: " + ar.exception);
return;
}
- if (!isValidCardIndex(index)) {
+ if (!isValidPhoneIndex(index)) {
Rlog.e(LOG_TAG,"onSimRefresh: invalid index : " + index);
return;
}
IccRefreshResponse resp = (IccRefreshResponse) ar.result;
- Rlog.d(LOG_TAG, "onSimRefresh: " + resp);
+ log("onSimRefresh: " + resp);
+ sLocalLog.log("onSimRefresh: " + resp);
- if (mUiccCards[index] == null) {
+ if (resp == null) {
+ Rlog.e(LOG_TAG, "onSimRefresh: received without input");
+ return;
+ }
+
+ UiccCard uiccCard = getUiccCardForPhone(index);
+ if (uiccCard == null) {
Rlog.e(LOG_TAG,"onSimRefresh: refresh on null card : " + index);
return;
}
- if (resp.refreshResult != IccRefreshResponse.REFRESH_RESULT_RESET) {
- Rlog.d(LOG_TAG, "Ignoring non reset refresh: " + resp);
- return;
+ boolean changed = false;
+ switch(resp.refreshResult) {
+ // Reset the required apps when we know about the refresh so that
+ // anyone interested does not get stale state.
+ case IccRefreshResponse.REFRESH_RESULT_RESET:
+ changed = uiccCard.resetAppWithAid(resp.aid, true /* reset */);
+ break;
+ case IccRefreshResponse.REFRESH_RESULT_INIT:
+ // don't dispose CatService on SIM REFRESH of type INIT
+ changed = uiccCard.resetAppWithAid(resp.aid, false /* initialize */);
+ break;
+ default:
+ return;
}
- Rlog.d(LOG_TAG, "Handling refresh reset: " + resp);
+ if (changed && resp.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
+ // If there is any change on RESET, reset carrier config as well. From carrier config
+ // perspective, this is treated the same as sim state unknown
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ configManager.updateConfigForPhoneId(index, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
- boolean changed = mUiccCards[index].resetAppWithAid(resp.aid);
- if (changed) {
boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
+ com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
if (requirePowerOffOnSimRefreshReset) {
mCis[index].setRadioPower(false, null);
- } else {
- mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
}
- mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
}
+
+ // The card status could have changed. Get the latest state.
+ mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
}
- private boolean isValidCardIndex(int index) {
- return (index >= 0 && index < mUiccCards.length);
+ public boolean isCdmaSupported() {
+ return mIsCdmaSupported;
+ }
+
+ private boolean isValidPhoneIndex(int index) {
+ return (index >= 0 && index < TelephonyManager.getDefault().getPhoneCount());
+ }
+
+ private boolean isValidSlotIndex(int index) {
+ return (index >= 0 && index < mUiccSlots.length);
}
private void log(String string) {
Rlog.d(LOG_TAG, string);
}
- // TODO: This is hacky. We need a better way of saving the logs.
public void addCardLog(String data) {
- Time t = new Time();
- t.setToNow();
- mCardLogs.addLast(t.format("%m-%d %H:%M:%S") + " " + data);
- if (mCardLogs.size() > MAX_PROACTIVE_COMMANDS_TO_LOG) {
- mCardLogs.removeFirst();
- }
+ sLocalLog.log(data);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -374,18 +712,17 @@
}
pw.println();
pw.flush();
- pw.println(" mUiccCards: size=" + mUiccCards.length);
- for (int i = 0; i < mUiccCards.length; i++) {
- if (mUiccCards[i] == null) {
- pw.println(" mUiccCards[" + i + "]=null");
+ pw.println(" mIsCdmaSupported=" + mIsCdmaSupported);
+ pw.println(" mUiccSlots: size=" + mUiccSlots.length);
+ for (int i = 0; i < mUiccSlots.length; i++) {
+ if (mUiccSlots[i] == null) {
+ pw.println(" mUiccSlots[" + i + "]=null");
} else {
- pw.println(" mUiccCards[" + i + "]=" + mUiccCards[i]);
- mUiccCards[i].dump(fd, pw, args);
+ pw.println(" mUiccSlots[" + i + "]=" + mUiccSlots[i]);
+ mUiccSlots[i].dump(fd, pw, args);
}
}
- pw.println("mCardLogs: ");
- for (int i = 0; i < mCardLogs.size(); ++i) {
- pw.println(" " + mCardLogs.get(i));
- }
+ pw.println(" sLocalLog= ");
+ 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 80ebcbf..b3a3482 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -17,24 +17,15 @@
package com.android.internal.telephony.uicc;
import android.os.AsyncResult;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV;
-import java.io.ByteArrayInputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.IllegalArgumentException;
-import java.lang.IndexOutOfBoundsException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -89,7 +80,7 @@
private void selectFile() {
if (mChannelId >= 0) {
- mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02,
+ mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02,
mFileId, obtainMessage(EVENT_SELECT_FILE_DONE));
} else {
log("EF based");
@@ -98,7 +89,7 @@
private void readBinary() {
if (mChannelId >=0 ) {
- mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
+ mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
"", obtainMessage(EVENT_READ_BINARY_DONE));
} else {
log("EF based");
@@ -146,7 +137,7 @@
mCallback = callBack;
// Specified in ISO 7816-4 clause 7.1.1 0x04 means that FCP template is requested.
int p2 = 0x04;
- mUiccCard.iccOpenLogicalChannel(PKCS15_AID, p2, /* supported P2 value */
+ mUiccProfile.iccOpenLogicalChannel(PKCS15_AID, p2, /* supported P2 value */
obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE));
}
@@ -176,7 +167,7 @@
}
}
- private UiccCard mUiccCard; // Parent
+ private UiccProfile mUiccProfile; // Parent
private Message mLoadedCallback;
private int mChannelId = -1; // Channel Id for communicating with UICC.
private List<String> mRules = new ArrayList<String>();
@@ -191,9 +182,9 @@
private static final int EVENT_LOAD_ACCF_DONE = 6;
private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 7;
- public UiccPkcs15(UiccCard uiccCard, Message loadedCallback) {
+ public UiccPkcs15(UiccProfile uiccProfile, Message loadedCallback) {
log("Creating UiccPkcs15");
- mUiccCard = uiccCard;
+ mUiccProfile = uiccProfile;
mLoadedCallback = loadedCallback;
mPkcs15Selector = new Pkcs15Selector(obtainMessage(EVENT_SELECT_PKCS15_DONE));
}
@@ -249,7 +240,7 @@
private void cleanUp() {
log("cleanUp");
if (mChannelId >= 0) {
- mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
+ mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage(
EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
mChannelId = -1;
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
new file mode 100644
index 0000000..f7aeaa1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -0,0 +1,1596 @@
+/*
+ * Copyright 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.uicc;
+
+import android.app.AlertDialog;
+import android.app.usage.UsageStatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.MccTable;
+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.cat.CatService;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.IccCardStatus.PinState;
+import com.android.internal.telephony.uicc.euicc.EuiccCard;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * This class represents the carrier profiles in the {@link UiccCard}. Each profile contains
+ * multiple {@link UiccCardApplication}, one {@link UiccCarrierPrivilegeRules} and one
+ * {@link CatService}.
+ *
+ * Profile is related to {@link android.telephony.SubscriptionInfo} but those two concepts are
+ * different. {@link android.telephony.SubscriptionInfo} contains all the subscription information
+ * while Profile contains all the {@link UiccCardApplication} which will be used to fetch those
+ * subscription information from the {@link UiccCard}.
+ *
+ * {@hide}
+ */
+public class UiccProfile extends IccCard {
+ protected static final String LOG_TAG = "UiccProfile";
+ protected static final boolean DBG = true;
+ private static final boolean VDBG = false; //STOPSHIP if true
+
+ private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
+
+ // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
+ // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
+ private final Object mLock;
+ private PinState mUniversalPinState;
+ private int mGsmUmtsSubscriptionAppIndex;
+ private int mCdmaSubscriptionAppIndex;
+ private int mImsSubscriptionAppIndex;
+ private UiccCardApplication[] mUiccApplications =
+ new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
+ private Context mContext;
+ private CommandsInterface mCi;
+ private final UiccCard mUiccCard; //parent
+ private CatService mCatService;
+ private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
+ private boolean mDisposed = false;
+
+ private RegistrantList mCarrierPrivilegeRegistrants = new RegistrantList();
+ private RegistrantList mOperatorBrandOverrideRegistrants = new RegistrantList();
+
+ private final int mPhoneId;
+
+ private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
+ private static final int EVENT_ICC_LOCKED = 2;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static final int EVENT_APP_READY = 3;
+ private static final int EVENT_RECORDS_LOADED = 4;
+ private static final int EVENT_NETWORK_LOCKED = 5;
+ private static final int EVENT_EID_READY = 6;
+ private static final int EVENT_ICC_RECORD_EVENTS = 7;
+ private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 8;
+ private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 9;
+ private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 10;
+ private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 11;
+ private static final int EVENT_SIM_IO_DONE = 12;
+ private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 13;
+ private static final int EVENT_CARRIER_CONFIG_CHANGED = 14;
+
+ private TelephonyManager mTelephonyManager;
+
+ private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
+
+ private int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
+ private UiccCardApplication mUiccApplication = null;
+ private IccRecords mIccRecords = null;
+ private IccCardConstants.State mExternalState = IccCardConstants.State.UNKNOWN;
+
+
+ private final BroadcastReceiver mReceiver = 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));
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ // We still need to handle the following response messages even the UiccProfile has been
+ // disposed because whoever sent the request may be still waiting for the response.
+ if (mDisposed && msg.what != EVENT_OPEN_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_CLOSE_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE
+ && msg.what != EVENT_SIM_IO_DONE) {
+ loge("handleMessage: Received " + msg.what
+ + " after dispose(); ignoring the message");
+ return;
+ }
+ loglocal("handleMessage: Received " + msg.what + " for phoneId " + mPhoneId);
+ switch (msg.what) {
+ case EVENT_NETWORK_LOCKED:
+ mNetworkLockedRegistrants.notifyRegistrants();
+ // intentional fall through
+ case EVENT_RADIO_OFF_OR_UNAVAILABLE:
+ case EVENT_ICC_LOCKED:
+ case EVENT_APP_READY:
+ case EVENT_RECORDS_LOADED:
+ case EVENT_EID_READY:
+ if (VDBG) log("handleMessage: Received " + msg.what);
+ updateExternalState();
+ break;
+
+ case EVENT_ICC_RECORD_EVENTS:
+ if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ int eventCode = (Integer) ar.result;
+ if (eventCode == SIMRecords.EVENT_SPN) {
+ mTelephonyManager.setSimOperatorNameForPhone(
+ mPhoneId, mIccRecords.getServiceProviderName());
+ }
+ }
+ break;
+
+ case EVENT_CARRIER_PRIVILEGES_LOADED:
+ if (VDBG) log("handleMessage: EVENT_CARRIER_PRIVILEGES_LOADED");
+ onCarrierPriviligesLoadedMessage();
+ updateExternalState();
+ break;
+
+ case EVENT_CARRIER_CONFIG_CHANGED:
+ handleCarrierNameOverride();
+ break;
+
+ case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+ case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
+ case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
+ case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
+ case EVENT_SIM_IO_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loglocal("handleMessage: Exception " + ar.exception);
+ log("handleMessage: Error in SIM access with exception" + ar.exception);
+ }
+ AsyncResult.forMessage((Message) ar.userObj, ar.result, ar.exception);
+ ((Message) ar.userObj).sendToTarget();
+ break;
+
+ default:
+ loge("handleMessage: Unhandled message with number: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
+ UiccCard uiccCard, Object lock) {
+ if (DBG) log("Creating profile");
+ mLock = lock;
+ mUiccCard = uiccCard;
+ mPhoneId = phoneId;
+ // set current app type based on phone type - do this before calling update() as that
+ // calls updateIccAvailability() which uses mCurrentAppType
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ setCurrentAppType(phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM);
+ }
+
+ if (mUiccCard instanceof EuiccCard) {
+ ((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null);
+ }
+
+ update(c, ci, ics);
+ ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
+ resetProperties();
+
+ IntentFilter intentfilter = new IntentFilter();
+ intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ c.registerReceiver(mReceiver, intentfilter);
+ }
+
+ /**
+ * Dispose the UiccProfile.
+ */
+ public void dispose() {
+ if (DBG) log("Disposing profile");
+
+ // mUiccCard is outside of mLock in order to prevent deadlocking. This is safe because
+ // EuiccCard#unregisterForEidReady handles its own lock
+ if (mUiccCard instanceof EuiccCard) {
+ ((EuiccCard) mUiccCard).unregisterForEidReady(mHandler);
+ }
+ synchronized (mLock) {
+ unregisterAllAppEvents();
+ unregisterCurrAppEvents();
+
+ mCi.unregisterForOffOrNotAvailable(mHandler);
+ mContext.unregisterReceiver(mReceiver);
+
+ if (mCatService != null) mCatService.dispose();
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ app.dispose();
+ }
+ }
+ mCatService = null;
+ mUiccApplications = null;
+ mCarrierPrivilegeRules = null;
+ mDisposed = true;
+ }
+ }
+
+ /**
+ * The card application that the external world sees will be based on the
+ * voice radio technology only!
+ */
+ public void setVoiceRadioTech(int radioTech) {
+ synchronized (mLock) {
+ if (DBG) {
+ log("Setting radio tech " + ServiceState.rilRadioTechnologyToString(radioTech));
+ }
+ setCurrentAppType(ServiceState.isGsm(radioTech));
+ updateIccAvailability(false);
+ }
+ }
+
+ private void setCurrentAppType(boolean isGsm) {
+ if (VDBG) log("setCurrentAppType");
+ synchronized (mLock) {
+ if (isGsm) {
+ mCurrentAppType = UiccController.APP_FAM_3GPP;
+ } else {
+ mCurrentAppType = UiccController.APP_FAM_3GPP2;
+ }
+ }
+ }
+
+ /**
+ * Override the carrier name with either carrier config or SPN
+ * if an override is provided.
+ */
+ private void handleCarrierNameOverride() {
+ SubscriptionController subCon = SubscriptionController.getInstance();
+ final int subId = subCon.getSubIdUsingPhoneId(mPhoneId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ loge("subId not valid for Phone " + mPhoneId);
+ return;
+ }
+
+ CarrierConfigManager configLoader = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configLoader == null) {
+ loge("Failed to load a Carrier Config");
+ return;
+ }
+
+ PersistableBundle config = configLoader.getConfigForSubId(subId);
+ boolean preferCcName = config.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ String ccName = config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ // If carrier config is priority, use it regardless - the preference
+ // and the name were both set by the carrier, so this is safe;
+ // otherwise, if the SPN is priority but we don't have one *and* we have
+ // a name in carrier config, use the carrier config name as a backup.
+ if (preferCcName || (TextUtils.isEmpty(getServiceProviderName())
+ && !TextUtils.isEmpty(ccName))) {
+ if (mIccRecords != null) {
+ mIccRecords.setServiceProviderName(ccName);
+ }
+ mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, ccName);
+ mOperatorBrandOverrideRegistrants.notifyRegistrants();
+ }
+
+ updateCarrierNameForSubscription(subCon, subId);
+ }
+
+ private void updateCarrierNameForSubscription(SubscriptionController subCon, int subId) {
+ /* update display name with carrier override */
+ SubscriptionInfo subInfo = subCon.getActiveSubscriptionInfo(
+ subId, mContext.getOpPackageName());
+
+ if (subInfo == null || subInfo.getNameSource()
+ == SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+ // either way, there is no subinfo to update
+ return;
+ }
+
+ CharSequence oldSubName = subInfo.getDisplayName();
+ String newCarrierName = mTelephonyManager.getSimOperatorName(subId);
+
+ if (!TextUtils.isEmpty(newCarrierName) && !newCarrierName.equals(oldSubName)) {
+ log("sim name[" + mPhoneId + "] = " + newCarrierName);
+ subCon.setDisplayName(newCarrierName, subId);
+ }
+ }
+
+ private void updateIccAvailability(boolean allAppsChanged) {
+ synchronized (mLock) {
+ UiccCardApplication newApp;
+ IccRecords newRecords = null;
+ newApp = getApplication(mCurrentAppType);
+ if (newApp != null) {
+ newRecords = newApp.getIccRecords();
+ }
+
+ if (allAppsChanged) {
+ unregisterAllAppEvents();
+ registerAllAppEvents();
+ }
+
+ if (mIccRecords != newRecords || mUiccApplication != newApp) {
+ if (DBG) log("Icc changed. Reregistering.");
+ unregisterCurrAppEvents();
+ mUiccApplication = newApp;
+ mIccRecords = newRecords;
+ registerCurrAppEvents();
+ }
+ updateExternalState();
+ }
+ }
+
+ void resetProperties() {
+ if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
+ log("update icc_operator_numeric=" + "");
+ mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, "");
+ mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, "");
+ mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, "");
+ }
+ }
+
+ /**
+ * Update the external SIM state
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void updateExternalState() {
+ // First check if card state is IO_ERROR or RESTRICTED
+ if (mUiccCard.getCardState() == IccCardStatus.CardState.CARDSTATE_ERROR) {
+ setExternalState(IccCardConstants.State.CARD_IO_ERROR);
+ return;
+ }
+
+ if (mUiccCard.getCardState() == IccCardStatus.CardState.CARDSTATE_RESTRICTED) {
+ setExternalState(IccCardConstants.State.CARD_RESTRICTED);
+ return;
+ }
+
+ if (mUiccCard instanceof EuiccCard && ((EuiccCard) mUiccCard).getEid() == null) {
+ if (DBG) log("EID is not ready yet.");
+ return;
+ }
+
+ // By process of elimination, the UICC Card State = PRESENT and state needs to be decided
+ // based on apps
+ if (mUiccApplication == null) {
+ loge("updateExternalState: setting state to NOT_READY because mUiccApplication is "
+ + "null");
+ setExternalState(IccCardConstants.State.NOT_READY);
+ return;
+ }
+
+ // Check if SIM is locked
+ boolean cardLocked = false;
+ IccCardConstants.State lockedState = null;
+ IccCardApplicationStatus.AppState appState = mUiccApplication.getState();
+
+ PinState pin1State = mUiccApplication.getPin1State();
+ if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
+ if (VDBG) log("updateExternalState: PERM_DISABLED");
+ cardLocked = true;
+ lockedState = IccCardConstants.State.PERM_DISABLED;
+ } else {
+ if (appState == IccCardApplicationStatus.AppState.APPSTATE_PIN) {
+ if (VDBG) log("updateExternalState: PIN_REQUIRED");
+ cardLocked = true;
+ lockedState = IccCardConstants.State.PIN_REQUIRED;
+ } else if (appState == IccCardApplicationStatus.AppState.APPSTATE_PUK) {
+ if (VDBG) log("updateExternalState: PUK_REQUIRED");
+ cardLocked = true;
+ lockedState = IccCardConstants.State.PUK_REQUIRED;
+ } else if (appState == IccCardApplicationStatus.AppState.APPSTATE_SUBSCRIPTION_PERSO) {
+ if (mUiccApplication.getPersoSubState()
+ == IccCardApplicationStatus.PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
+ if (VDBG) log("updateExternalState: PERSOSUBSTATE_SIM_NETWORK");
+ cardLocked = true;
+ lockedState = IccCardConstants.State.NETWORK_LOCKED;
+ }
+ }
+ }
+
+ // If SIM is locked, broadcast state as NOT_READY/LOCKED depending on if records are loaded
+ if (cardLocked) {
+ if (mIccRecords != null && (mIccRecords.getLockedRecordsLoaded()
+ || mIccRecords.getNetworkLockedRecordsLoaded())) { // locked records loaded
+ if (VDBG) {
+ log("updateExternalState: card locked and records loaded; "
+ + "setting state to locked");
+ }
+ setExternalState(lockedState);
+ } else {
+ if (VDBG) {
+ log("updateExternalState: card locked but records not loaded; "
+ + "setting state to NOT_READY");
+ }
+ setExternalState(IccCardConstants.State.NOT_READY);
+ }
+ return;
+ }
+
+ // Check for remaining app states
+ switch (appState) {
+ case APPSTATE_UNKNOWN:
+ /*
+ * APPSTATE_UNKNOWN is a catch-all state reported whenever the app
+ * is not explicitly in one of the other states. To differentiate the
+ * case where we know that there is a card present, but the APP is not
+ * ready, we choose NOT_READY here instead of unknown. This is possible
+ * in at least two cases:
+ * 1) A transient during the process of the SIM bringup
+ * 2) There is no valid App on the SIM to load, which can be the case with an
+ * eSIM/soft SIM.
+ */
+ if (VDBG) {
+ log("updateExternalState: app state is unknown; setting state to NOT_READY");
+ }
+ setExternalState(IccCardConstants.State.NOT_READY);
+ break;
+ case APPSTATE_READY:
+ checkAndUpdateIfAnyAppToBeIgnored();
+ if (areAllApplicationsReady()) {
+ if (areAllRecordsLoaded() && areCarrierPriviligeRulesLoaded()) {
+ if (VDBG) log("updateExternalState: setting state to LOADED");
+ setExternalState(IccCardConstants.State.LOADED);
+ } else {
+ if (VDBG) {
+ log("updateExternalState: setting state to READY; records loaded "
+ + areAllRecordsLoaded() + ", carrier privilige rules loaded "
+ + areCarrierPriviligeRulesLoaded());
+ }
+ setExternalState(IccCardConstants.State.READY);
+ }
+ } else {
+ if (VDBG) {
+ log("updateExternalState: app state is READY but not for all apps; "
+ + "setting state to NOT_READY");
+ }
+ setExternalState(IccCardConstants.State.NOT_READY);
+ }
+ break;
+ }
+ }
+
+ private void registerAllAppEvents() {
+ // todo: all of these should be notified to UiccProfile directly without needing to register
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ if (VDBG) log("registerUiccCardEvents: registering for EVENT_APP_READY");
+ app.registerForReady(mHandler, EVENT_APP_READY, null);
+ IccRecords ir = app.getIccRecords();
+ if (ir != null) {
+ if (VDBG) log("registerUiccCardEvents: registering for EVENT_RECORDS_LOADED");
+ ir.registerForRecordsLoaded(mHandler, EVENT_RECORDS_LOADED, null);
+ ir.registerForRecordsEvents(mHandler, EVENT_ICC_RECORD_EVENTS, null);
+ }
+ }
+ }
+ }
+
+ private void unregisterAllAppEvents() {
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ app.unregisterForReady(mHandler);
+ IccRecords ir = app.getIccRecords();
+ if (ir != null) {
+ ir.unregisterForRecordsLoaded(mHandler);
+ ir.unregisterForRecordsEvents(mHandler);
+ }
+ }
+ }
+ }
+
+ private void registerCurrAppEvents() {
+ // In case of locked, only listen to the current application.
+ if (mIccRecords != null) {
+ mIccRecords.registerForLockedRecordsLoaded(mHandler, EVENT_ICC_LOCKED, null);
+ mIccRecords.registerForNetworkLockedRecordsLoaded(mHandler, EVENT_NETWORK_LOCKED, null);
+ }
+ }
+
+ private void unregisterCurrAppEvents() {
+ if (mIccRecords != null) {
+ mIccRecords.unregisterForLockedRecordsLoaded(mHandler);
+ mIccRecords.unregisterForNetworkLockedRecordsLoaded(mHandler);
+ }
+ }
+
+ private void setExternalState(IccCardConstants.State newState, boolean override) {
+ synchronized (mLock) {
+ if (!SubscriptionManager.isValidSlotIndex(mPhoneId)) {
+ loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
+ return;
+ }
+
+ if (!override && newState == mExternalState) {
+ log("setExternalState: !override and newstate unchanged from " + newState);
+ return;
+ }
+ mExternalState = newState;
+ if (mExternalState == IccCardConstants.State.LOADED) {
+ // Update the MCC/MNC.
+ if (mIccRecords != null) {
+ String operator = mIccRecords.getOperatorNumeric();
+ log("setExternalState: operator=" + operator + " mPhoneId=" + mPhoneId);
+
+ if (!TextUtils.isEmpty(operator)) {
+ mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
+ String countryCode = operator.substring(0, 3);
+ if (countryCode != null) {
+ mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
+ MccTable.countryCodeForMcc(countryCode));
+ } else {
+ loge("setExternalState: state LOADED; Country code is null");
+ }
+ } else {
+ loge("setExternalState: state LOADED; Operator name is null");
+ }
+ }
+ }
+ log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
+ mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
+
+ UiccController.updateInternalIccState(getIccStateIntentString(mExternalState),
+ getIccStateReason(mExternalState), mPhoneId);
+ }
+ }
+
+ private void setExternalState(IccCardConstants.State newState) {
+ setExternalState(newState, false);
+ }
+
+ /**
+ * Function to check if all ICC records have been loaded
+ * @return true if all ICC records have been loaded, false otherwise.
+ */
+ public boolean getIccRecordsLoaded() {
+ synchronized (mLock) {
+ if (mIccRecords != null) {
+ return mIccRecords.getRecordsLoaded();
+ }
+ return false;
+ }
+ }
+
+ private String getIccStateIntentString(IccCardConstants.State state) {
+ switch (state) {
+ case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
+ case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
+ case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
+ case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
+ case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
+ case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
+ case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
+ case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
+ case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
+ case LOADED: return IccCardConstants.INTENT_VALUE_ICC_LOADED;
+ default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
+ }
+ }
+
+ /**
+ * Locked state have a reason (PIN, PUK, NETWORK, PERM_DISABLED, CARD_IO_ERROR)
+ * @return reason
+ */
+ private String getIccStateReason(IccCardConstants.State state) {
+ switch (state) {
+ case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN;
+ case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK;
+ case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_LOCKED_NETWORK;
+ case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED;
+ case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
+ case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
+ default: return null;
+ }
+ }
+
+ /* IccCard interface implementation */
+ @Override
+ public IccCardConstants.State getState() {
+ synchronized (mLock) {
+ return mExternalState;
+ }
+ }
+
+ @Override
+ public IccRecords getIccRecords() {
+ synchronized (mLock) {
+ return mIccRecords;
+ }
+ }
+
+ /**
+ * Notifies handler of any transition into State.NETWORK_LOCKED
+ */
+ @Override
+ public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ synchronized (mLock) {
+ Registrant r = new Registrant(h, what, obj);
+
+ mNetworkLockedRegistrants.add(r);
+
+ if (getState() == IccCardConstants.State.NETWORK_LOCKED) {
+ r.notifyRegistrant();
+ }
+ }
+ }
+
+ @Override
+ public void unregisterForNetworkLocked(Handler h) {
+ synchronized (mLock) {
+ mNetworkLockedRegistrants.remove(h);
+ }
+ }
+
+ @Override
+ public void supplyPin(String pin, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.supplyPin(pin, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void supplyPuk(String puk, String newPin, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.supplyPuk(puk, newPin, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void supplyPin2(String pin2, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.supplyPin2(pin2, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.supplyPuk2(puk2, newPin2, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void supplyNetworkDepersonalization(String pin, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.supplyNetworkDepersonalization(pin, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("CommandsInterface is not set.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public boolean getIccLockEnabled() {
+ synchronized (mLock) {
+ /* defaults to false, if ICC is absent/deactivated */
+ return mUiccApplication != null && mUiccApplication.getIccLockEnabled();
+ }
+ }
+
+ @Override
+ public boolean getIccFdnEnabled() {
+ synchronized (mLock) {
+ return mUiccApplication != null && mUiccApplication.getIccFdnEnabled();
+ }
+ }
+
+ @Override
+ public boolean getIccFdnAvailable() {
+ synchronized (mLock) {
+ return mUiccApplication != null && mUiccApplication.getIccFdnAvailable();
+ }
+ }
+
+ @Override
+ public boolean getIccPin2Blocked() {
+ /* defaults to disabled */
+ return mUiccApplication != null && mUiccApplication.getIccPin2Blocked();
+ }
+
+ @Override
+ public boolean getIccPuk2Blocked() {
+ /* defaults to disabled */
+ return mUiccApplication != null && mUiccApplication.getIccPuk2Blocked();
+ }
+
+ @Override
+ public void setIccLockEnabled(boolean enabled, String password, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.setIccLockEnabled(enabled, password, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void setIccFdnEnabled(boolean enabled, String password, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.setIccFdnEnabled(enabled, password, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void changeIccLockPassword(String oldPassword, String newPassword, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.changeIccLockPassword(oldPassword, newPassword, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void changeIccFdnPassword(String oldPassword, String newPassword, Message onComplete) {
+ synchronized (mLock) {
+ if (mUiccApplication != null) {
+ mUiccApplication.changeIccFdnPassword(oldPassword, newPassword, onComplete);
+ } else if (onComplete != null) {
+ Exception e = new RuntimeException("ICC card is absent.");
+ AsyncResult.forMessage(onComplete).exception = e;
+ onComplete.sendToTarget();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public String getServiceProviderName() {
+ synchronized (mLock) {
+ if (mIccRecords != null) {
+ return mIccRecords.getServiceProviderName();
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public boolean hasIccCard() {
+ // mUiccCard is initialized in constructor, so won't be null
+ if (mUiccCard.getCardState()
+ != IccCardStatus.CardState.CARDSTATE_ABSENT) {
+ return true;
+ }
+ loge("hasIccCard: UiccProfile is not null but UiccCard is null or card state is "
+ + "ABSENT");
+ return false;
+ }
+
+ /**
+ * Update the UiccProfile.
+ */
+ public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
+ synchronized (mLock) {
+ mUniversalPinState = ics.mUniversalPinState;
+ mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
+ mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
+ mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
+ mContext = c;
+ mCi = ci;
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+
+ //update applications
+ if (DBG) log(ics.mApplications.length + " applications");
+ for (int i = 0; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] == null) {
+ //Create newly added Applications
+ if (i < ics.mApplications.length) {
+ mUiccApplications[i] = new UiccCardApplication(this,
+ ics.mApplications[i], mContext, mCi);
+ }
+ } else if (i >= ics.mApplications.length) {
+ //Delete removed applications
+ mUiccApplications[i].dispose();
+ mUiccApplications[i] = null;
+ } else {
+ //Update the rest
+ mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
+ }
+ }
+
+ createAndUpdateCatServiceLocked();
+
+ // Reload the carrier privilege rules if necessary.
+ log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + ics.mCardState);
+ if (mCarrierPrivilegeRules == null && ics.mCardState == CardState.CARDSTATE_PRESENT) {
+ mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
+ mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+ } else if (mCarrierPrivilegeRules != null
+ && ics.mCardState != CardState.CARDSTATE_PRESENT) {
+ mCarrierPrivilegeRules = null;
+ }
+
+ sanitizeApplicationIndexesLocked();
+ updateIccAvailability(true);
+ }
+ }
+
+ private void createAndUpdateCatServiceLocked() {
+ if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
+ // Initialize or Reinitialize CatService
+ if (mCatService == null) {
+ mCatService = CatService.getInstance(mCi, mContext, this, mPhoneId);
+ } else {
+ mCatService.update(mCi, mContext, this);
+ }
+ } else {
+ if (mCatService != null) {
+ mCatService.dispose();
+ }
+ mCatService = null;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ if (DBG) log("UiccProfile finalized");
+ }
+
+ /**
+ * This function makes sure that application indexes are valid
+ * and resets invalid indexes. (This should never happen, but in case
+ * RIL misbehaves we need to manage situation gracefully)
+ */
+ private void sanitizeApplicationIndexesLocked() {
+ mGsmUmtsSubscriptionAppIndex =
+ checkIndexLocked(
+ mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
+ mCdmaSubscriptionAppIndex =
+ checkIndexLocked(
+ mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
+ mImsSubscriptionAppIndex =
+ checkIndexLocked(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
+ }
+
+ /**
+ * Checks if the app is supported for the purposes of checking if all apps are ready/loaded, so
+ * this only checks for SIM/USIM and CSIM/RUIM apps. ISIM is considered not supported for this
+ * purpose as there are cards that have ISIM app that is never read (there are SIMs for which
+ * the state of ISIM goes to DETECTED but never to READY).
+ * CSIM/RUIM apps are considered not supported if CDMA is not supported.
+ */
+ private boolean isSupportedApplication(UiccCardApplication app) {
+ // TODO: 2/15/18 Add check to see if ISIM app will go to READY state, and if yes, check for
+ // ISIM also (currently ISIM is considered as not supported in this function)
+ if (app.getType() == AppType.APPTYPE_USIM || app.getType() == AppType.APPTYPE_SIM
+ || (UiccController.getInstance().isCdmaSupported()
+ && (app.getType() == AppType.APPTYPE_CSIM
+ || app.getType() == AppType.APPTYPE_RUIM))) {
+ return true;
+ }
+ return false;
+ }
+
+ private void checkAndUpdateIfAnyAppToBeIgnored() {
+ boolean[] appReadyStateTracker = new boolean[AppType.APPTYPE_ISIM.ordinal() + 1];
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && app.isReady()) {
+ appReadyStateTracker[app.getType().ordinal()] = true;
+ }
+ }
+
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && !app.isReady()) {
+ /* Checks if the appReadyStateTracker has already an entry in ready state
+ with same type as app */
+ if (appReadyStateTracker[app.getType().ordinal()]) {
+ app.setAppIgnoreState(true);
+ }
+ }
+ }
+ }
+
+ private boolean areAllApplicationsReady() {
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && !app.isReady()
+ && !app.isAppIgnored()) {
+ if (VDBG) log("areAllApplicationsReady: return false");
+ return false;
+ }
+ }
+
+ if (VDBG) {
+ log("areAllApplicationsReady: outside loop, return " + (mUiccApplication != null));
+ }
+ return mUiccApplication != null;
+ }
+
+ private boolean areAllRecordsLoaded() {
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && !app.isAppIgnored()) {
+ IccRecords ir = app.getIccRecords();
+ if (ir == null || !ir.isLoaded()) {
+ if (VDBG) log("areAllRecordsLoaded: return false");
+ return false;
+ }
+ }
+ }
+ if (VDBG) {
+ log("areAllRecordsLoaded: outside loop, return " + (mUiccApplication != null));
+ }
+ return mUiccApplication != null;
+ }
+
+ private int checkIndexLocked(int index, AppType expectedAppType, AppType altExpectedAppType) {
+ if (mUiccApplications == null || index >= mUiccApplications.length) {
+ loge("App index " + index + " is invalid since there are no applications");
+ return -1;
+ }
+
+ if (index < 0) {
+ // This is normal. (i.e. no application of this type)
+ return -1;
+ }
+
+ if (mUiccApplications[index].getType() != expectedAppType
+ && mUiccApplications[index].getType() != altExpectedAppType) {
+ loge("App index " + index + " is invalid since it's not "
+ + expectedAppType + " and not " + altExpectedAppType);
+ return -1;
+ }
+
+ // Seems to be valid
+ return index;
+ }
+
+ /**
+ * Registers the handler when operator brand name is overridden.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForOpertorBrandOverride(Handler h, int what, Object obj) {
+ synchronized (mLock) {
+ Registrant r = new Registrant(h, what, obj);
+ mOperatorBrandOverrideRegistrants.add(r);
+ }
+ }
+
+ /**
+ * Registers the handler when carrier privilege rules are loaded.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCarrierPrivilegeRulesLoaded(Handler h, int what, Object obj) {
+ synchronized (mLock) {
+ Registrant r = new Registrant(h, what, obj);
+
+ mCarrierPrivilegeRegistrants.add(r);
+
+ if (areCarrierPriviligeRulesLoaded()) {
+ r.notifyRegistrant();
+ }
+ }
+ }
+
+ /**
+ * Unregister for notifications when carrier privilege rules are loaded.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCarrierPrivilegeRulesLoaded(Handler h) {
+ synchronized (mLock) {
+ mCarrierPrivilegeRegistrants.remove(h);
+ }
+ }
+
+ /**
+ * Unregister for notifications when operator brand name is overriden.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForOperatorBrandOverride(Handler h) {
+ synchronized (mLock) {
+ mOperatorBrandOverrideRegistrants.remove(h);
+ }
+ }
+
+ private boolean isPackageInstalled(String pkgName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);
+ if (DBG) log(pkgName + " is installed.");
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DBG) log(pkgName + " is not installed.");
+ return false;
+ }
+ }
+
+ private class ClickListener implements DialogInterface.OnClickListener {
+ String mPkgName;
+ ClickListener(String pkgName) {
+ this.mPkgName = pkgName;
+ }
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ synchronized (mLock) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Intent market = new Intent(Intent.ACTION_VIEW);
+ market.setData(Uri.parse("market://details?id=" + mPkgName));
+ market.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(market);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ if (DBG) log("Not now clicked for carrier app dialog.");
+ }
+ }
+ }
+ }
+
+ private void promptInstallCarrierApp(String pkgName) {
+ DialogInterface.OnClickListener listener = new ClickListener(pkgName);
+
+ Resources r = Resources.getSystem();
+ String message = r.getString(R.string.carrier_app_dialog_message);
+ String buttonTxt = r.getString(R.string.carrier_app_dialog_button);
+ String notNowTxt = r.getString(R.string.carrier_app_dialog_not_now);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setMessage(message)
+ .setNegativeButton(notNowTxt, listener)
+ .setPositiveButton(buttonTxt, listener)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void onCarrierPriviligesLoadedMessage() {
+ UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(
+ Context.USAGE_STATS_SERVICE);
+ if (usm != null) {
+ usm.onCarrierPrivilegedAppsChanged();
+ }
+ synchronized (mLock) {
+ mCarrierPrivilegeRegistrants.notifyRegistrants();
+ String whitelistSetting = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.CARRIER_APP_WHITELIST);
+ if (TextUtils.isEmpty(whitelistSetting)) {
+ return;
+ }
+ HashSet<String> carrierAppSet = new HashSet<String>(
+ Arrays.asList(whitelistSetting.split("\\s*;\\s*")));
+ if (carrierAppSet.isEmpty()) {
+ return;
+ }
+
+ List<String> pkgNames = mCarrierPrivilegeRules.getPackageNames();
+ for (String pkgName : pkgNames) {
+ if (!TextUtils.isEmpty(pkgName) && carrierAppSet.contains(pkgName)
+ && !isPackageInstalled(pkgName)) {
+ promptInstallCarrierApp(pkgName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether the specified type of application exists in the profile.
+ *
+ * @param type UICC application type.
+ */
+ public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
+ synchronized (mLock) {
+ for (int i = 0; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Return the universal pin state of the profile.
+ */
+ public PinState getUniversalPinState() {
+ synchronized (mLock) {
+ return mUniversalPinState;
+ }
+ }
+
+ /**
+ * Return the application of the specified family.
+ *
+ * @param family UICC application family.
+ * @return application corresponding to family or a null if no match found
+ */
+ public UiccCardApplication getApplication(int family) {
+ synchronized (mLock) {
+ int index = IccCardStatus.CARD_MAX_APPS;
+ switch (family) {
+ case UiccController.APP_FAM_3GPP:
+ index = mGsmUmtsSubscriptionAppIndex;
+ break;
+ case UiccController.APP_FAM_3GPP2:
+ index = mCdmaSubscriptionAppIndex;
+ break;
+ case UiccController.APP_FAM_IMS:
+ index = mImsSubscriptionAppIndex;
+ break;
+ }
+ if (index >= 0 && index < mUiccApplications.length) {
+ return mUiccApplications[index];
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Return the application with the index of the array.
+ *
+ * @param index Index of the application array.
+ * @return application corresponding to index or a null if no match found
+ */
+ public UiccCardApplication getApplicationIndex(int index) {
+ synchronized (mLock) {
+ if (index >= 0 && index < mUiccApplications.length) {
+ return mUiccApplications[index];
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Returns the SIM application of the specified type.
+ *
+ * @param type ICC application type
+ * (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
+ * @return application corresponding to type or a null if no match found
+ */
+ public UiccCardApplication getApplicationByType(int type) {
+ synchronized (mLock) {
+ for (int i = 0; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] != null
+ && mUiccApplications[i].getType().ordinal() == type) {
+ return mUiccApplications[i];
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Resets the application with the input AID. Returns true if any changes were made.
+ *
+ * A null aid implies a card level reset - all applications must be reset.
+ *
+ * @param aid aid of the application which should be reset; null imples all applications
+ * @param reset true if reset is required. false for initialization.
+ * @return boolean indicating if there was any change made as part of the reset
+ */
+ public boolean resetAppWithAid(String aid, boolean reset) {
+ synchronized (mLock) {
+ boolean changed = false;
+ for (int i = 0; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] != null
+ && (TextUtils.isEmpty(aid) || aid.equals(mUiccApplications[i].getAid()))) {
+ // Delete removed applications
+ mUiccApplications[i].dispose();
+ mUiccApplications[i] = null;
+ changed = true;
+ }
+ }
+ if (reset && TextUtils.isEmpty(aid)) {
+ if (mCarrierPrivilegeRules != null) {
+ mCarrierPrivilegeRules = null;
+ changed = true;
+ }
+ // CatService shall be disposed only when a card level reset happens.
+ if (mCatService != null) {
+ mCatService.dispose();
+ mCatService = null;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * Exposes {@link CommandsInterface#iccOpenLogicalChannel}
+ */
+ public void iccOpenLogicalChannel(String aid, int p2, Message response) {
+ loglocal("iccOpenLogicalChannel: " + aid + " , " + p2 + " by pid:" + Binder.getCallingPid()
+ + " uid:" + Binder.getCallingUid());
+ mCi.iccOpenLogicalChannel(aid, p2,
+ mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
+ }
+
+ /**
+ * Exposes {@link CommandsInterface#iccCloseLogicalChannel}
+ */
+ public void iccCloseLogicalChannel(int channel, Message response) {
+ loglocal("iccCloseLogicalChannel: " + channel);
+ mCi.iccCloseLogicalChannel(channel,
+ mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Exposes {@link CommandsInterface#iccTransmitApduBasicChannel}
+ */
+ public void iccTransmitApduBasicChannel(int cla, int command,
+ int p1, int p2, int p3, String data, Message response) {
+ mCi.iccTransmitApduBasicChannel(cla, command, p1, p2, p3,
+ data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, response));
+ }
+
+ /**
+ * Exposes {@link CommandsInterface#iccIO}
+ */
+ public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
+ String pathID, Message response) {
+ mCi.iccIO(command, fileID, pathID, p1, p2, p3, null, null,
+ mHandler.obtainMessage(EVENT_SIM_IO_DONE, response));
+ }
+
+ /**
+ * Exposes {@link CommandsInterface#sendEnvelopeWithStatus}
+ */
+ public void sendEnvelopeWithStatus(String contents, Message response) {
+ mCi.sendEnvelopeWithStatus(contents, response);
+ }
+
+ /**
+ * Returns number of applications on this card
+ */
+ public int getNumApplications() {
+ int count = 0;
+ for (UiccCardApplication a : mUiccApplications) {
+ if (a != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns the id of the phone which is associated with this profile.
+ */
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ /**
+ * Returns true iff carrier privileges rules are null (dont need to be loaded) or loaded.
+ */
+ public boolean areCarrierPriviligeRulesLoaded() {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ || carrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
+ }
+
+ /**
+ * Returns true if there are some carrier privilege rules loaded and specified.
+ */
+ public boolean hasCarrierPrivilegeRules() {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules != null && carrierPrivilegeRules.hasCarrierPrivilegeRules();
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ */
+ public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+ carrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ */
+ public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+ carrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
+ */
+ public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+ carrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatusForCurrentTransaction}.
+ */
+ public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+ carrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(
+ packageManager);
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatusForUid}.
+ */
+ public int getCarrierPrivilegeStatusForUid(PackageManager packageManager, int uid) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+ carrierPrivilegeRules.getCarrierPrivilegeStatusForUid(packageManager, uid);
+ }
+
+ /**
+ * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
+ */
+ public List<String> getCarrierPackageNamesForIntent(
+ PackageManager packageManager, Intent intent) {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ return carrierPrivilegeRules == null ? null :
+ carrierPrivilegeRules.getCarrierPackageNamesForIntent(
+ packageManager, intent);
+ }
+
+ /** Returns a reference to the current {@link UiccCarrierPrivilegeRules}. */
+ private UiccCarrierPrivilegeRules getCarrierPrivilegeRules() {
+ synchronized (mLock) {
+ return mCarrierPrivilegeRules;
+ }
+ }
+
+ /**
+ * Sets the overridden operator brand.
+ */
+ public boolean setOperatorBrandOverride(String brand) {
+ log("setOperatorBrandOverride: " + brand);
+ log("current iccId: " + SubscriptionInfo.givePrintableIccid(getIccId()));
+
+ String iccId = getIccId();
+ if (TextUtils.isEmpty(iccId)) {
+ return false;
+ }
+
+ SharedPreferences.Editor spEditor =
+ PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+ String key = OPERATOR_BRAND_OVERRIDE_PREFIX + iccId;
+ if (brand == null) {
+ spEditor.remove(key).commit();
+ } else {
+ spEditor.putString(key, brand).commit();
+ }
+ mOperatorBrandOverrideRegistrants.notifyRegistrants();
+ return true;
+ }
+
+ /**
+ * Returns the overridden operator brand.
+ */
+ public String getOperatorBrandOverride() {
+ String iccId = getIccId();
+ if (TextUtils.isEmpty(iccId)) {
+ return null;
+ }
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ return sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
+ }
+
+ /**
+ * Returns the iccid of the profile.
+ */
+ public String getIccId() {
+ // ICCID should be same across all the apps.
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ IccRecords ir = app.getIccRecords();
+ if (ir != null && ir.getIccId() != null) {
+ return ir.getIccId();
+ }
+ }
+ }
+ return null;
+ }
+
+ private static void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(LOG_TAG, msg);
+ }
+
+ private void loglocal(String msg) {
+ if (DBG) UiccController.sLocalLog.log("UiccProfile[" + mPhoneId + "]: " + msg);
+ }
+
+ /**
+ * Dump
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("UiccProfile:");
+ pw.println(" mCi=" + mCi);
+ pw.println(" mCatService=" + mCatService);
+ for (int i = 0; i < mCarrierPrivilegeRegistrants.size(); i++) {
+ pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
+ + ((Registrant) mCarrierPrivilegeRegistrants.get(i)).getHandler());
+ }
+ for (int i = 0; i < mOperatorBrandOverrideRegistrants.size(); 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);
+ for (int i = 0; i < mUiccApplications.length; i++) {
+ if (mUiccApplications[i] == null) {
+ pw.println(" mUiccApplications[" + i + "]=" + null);
+ } else {
+ pw.println(" mUiccApplications[" + i + "]="
+ + mUiccApplications[i].getType() + " " + mUiccApplications[i]);
+ }
+ }
+ pw.println();
+ // Print details of all applications
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ app.dump(fd, pw, args);
+ pw.println();
+ }
+ }
+ // Print details of all IccRecords
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null) {
+ IccRecords ir = app.getIccRecords();
+ if (ir != null) {
+ ir.dump(fd, pw, args);
+ pw.println();
+ }
+ }
+ }
+ // Print UiccCarrierPrivilegeRules and registrants.
+ if (mCarrierPrivilegeRules == null) {
+ pw.println(" mCarrierPrivilegeRules: null");
+ } else {
+ pw.println(" mCarrierPrivilegeRules: " + mCarrierPrivilegeRules);
+ mCarrierPrivilegeRules.dump(fd, pw, args);
+ }
+ pw.println(" mCarrierPrivilegeRegistrants: size=" + mCarrierPrivilegeRegistrants.size());
+ for (int i = 0; i < mCarrierPrivilegeRegistrants.size(); i++) {
+ pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
+ + ((Registrant) mCarrierPrivilegeRegistrants.get(i)).getHandler());
+ }
+ pw.flush();
+
+ 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.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
new file mode 100644
index 0000000..25ef637
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 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.uicc;
+
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.euicc.EuiccCard;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * This class represents a physical slot on the device.
+ */
+public class UiccSlot extends Handler {
+ private static final String TAG = "UiccSlot";
+ private static final boolean DBG = true;
+
+ public static final String EXTRA_ICC_CARD_ADDED =
+ "com.android.internal.telephony.uicc.ICC_CARD_ADDED";
+ public static final int INVALID_PHONE_ID = -1;
+
+ private final Object mLock = new Object();
+ private boolean mActive;
+ private boolean mStateIsUnknown = true;
+ private CardState mCardState;
+ private Context mContext;
+ private CommandsInterface mCi;
+ private UiccCard mUiccCard;
+ private RadioState mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+ private boolean mIsEuicc;
+ private String mIccId;
+ private AnswerToReset mAtr;
+ private int mPhoneId = INVALID_PHONE_ID;
+
+ private static final int EVENT_CARD_REMOVED = 13;
+ private static final int EVENT_CARD_ADDED = 14;
+
+ public UiccSlot(Context c, boolean isActive) {
+ if (DBG) log("Creating");
+ mContext = c;
+ mActive = isActive;
+ mCardState = null;
+ }
+
+ /**
+ * Update slot. The main trigger for this is a change in the ICC Card status.
+ */
+ public void update(CommandsInterface ci, IccCardStatus ics, int phoneId) {
+ if (DBG) log("cardStatus update: " + ics.toString());
+ synchronized (mLock) {
+ CardState oldState = mCardState;
+ mCardState = ics.mCardState;
+ mIccId = ics.iccid;
+ mPhoneId = phoneId;
+ parseAtr(ics.atr);
+ mCi = ci;
+
+ RadioState radioState = mCi.getRadioState();
+ if (DBG) {
+ log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
+ }
+
+ if (absentStateUpdateNeeded(oldState)) {
+ updateCardStateAbsent();
+ // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
+ // create a new UiccCard instance in two scenarios:
+ // 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) {
+ // No notifications while radio is off or we just powering up
+ if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+ if (DBG) log("update: notify card added");
+ sendMessage(obtainMessage(EVENT_CARD_ADDED, null));
+ }
+
+ // card is present in the slot now; create new mUiccCard
+ if (mUiccCard != null) {
+ loge("update: mUiccCard != null when card was present; disposing it now");
+ mUiccCard.dispose();
+ }
+
+ if (!mIsEuicc) {
+ mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
+ } else {
+ mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
+ }
+ } else {
+ if (mUiccCard != null) {
+ mUiccCard.update(mContext, mCi, ics);
+ }
+ }
+ mLastRadioState = radioState;
+ }
+ }
+
+ /**
+ * Update slot based on IccSlotStatus.
+ */
+ public void update(CommandsInterface ci, IccSlotStatus iss) {
+ if (DBG) log("slotStatus update: " + iss.toString());
+ synchronized (mLock) {
+ CardState oldState = mCardState;
+ mCi = ci;
+ parseAtr(iss.atr);
+ mCardState = iss.cardState;
+ mIccId = iss.iccid;
+ if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) {
+ // TODO: (b/79432584) evaluate whether should broadcast card state change
+ // even if it's inactive.
+ if (mActive) {
+ mActive = false;
+ mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+ mPhoneId = INVALID_PHONE_ID;
+ if (mUiccCard != null) mUiccCard.dispose();
+ nullifyUiccCard(true /* sim state is unknown */);
+ }
+ } else {
+ mActive = true;
+ mPhoneId = iss.logicalSlotIndex;
+ if (absentStateUpdateNeeded(oldState)) {
+ updateCardStateAbsent();
+ }
+ // TODO: (b/79432584) Create UiccCard or EuiccCard object here.
+ // Right now It's OK not creating it because Card status update will do it.
+ // But we should really make them symmetric.
+ }
+ }
+ }
+
+ private boolean absentStateUpdateNeeded(CardState oldState) {
+ return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null)
+ && mCardState == CardState.CARDSTATE_ABSENT;
+ }
+
+ private void updateCardStateAbsent() {
+ RadioState radioState =
+ (mCi == null) ? RadioState.RADIO_UNAVAILABLE : mCi.getRadioState();
+ // No notifications while radio is off or we just powering up
+ if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+ if (DBG) log("update: notify card removed");
+ sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
+ }
+
+ UiccController.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId);
+
+ // no card present in the slot now; dispose card and make mUiccCard null
+ if (mUiccCard != null) {
+ mUiccCard.dispose();
+ }
+ nullifyUiccCard(false /* sim state is not unknown */);
+ mLastRadioState = radioState;
+ }
+
+ // whenever we set mUiccCard to null, we lose the ability to differentiate between absent and
+ // unknown states. To mitigate this, we will us mStateIsUnknown to keep track. The sim is only
+ // unknown if we haven't heard from the radio or if the radio has become unavailable.
+ private void nullifyUiccCard(boolean stateUnknown) {
+ mStateIsUnknown = stateUnknown;
+ mUiccCard = null;
+ }
+
+ public boolean isStateUnknown() {
+ return (mCardState == null || mCardState == CardState.CARDSTATE_ABSENT) && mStateIsUnknown;
+ }
+
+ private void checkIsEuiccSupported() {
+ if (mAtr != null && mAtr.isEuiccSupported()) {
+ mIsEuicc = true;
+ } else {
+ mIsEuicc = false;
+ }
+ }
+
+ private void parseAtr(String atr) {
+ mAtr = AnswerToReset.parseAtr(atr);
+ if (mAtr == null) {
+ return;
+ }
+ checkIsEuiccSupported();
+ }
+
+ public boolean isEuicc() {
+ return mIsEuicc;
+ }
+
+ public boolean isActive() {
+ return mActive;
+ }
+
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ public String getIccId() {
+ if (mIccId != null) {
+ return mIccId;
+ } else if (mUiccCard != null) {
+ return mUiccCard.getIccId();
+ } else {
+ return null;
+ }
+ }
+
+ public boolean isExtendedApduSupported() {
+ return (mAtr != null && mAtr.isExtendedApduSupported());
+ }
+
+ @Override
+ protected void finalize() {
+ if (DBG) log("UiccSlot finalized");
+ }
+
+ private void onIccSwap(boolean isAdded) {
+
+ boolean isHotSwapSupported = mContext.getResources().getBoolean(
+ R.bool.config_hotswapCapable);
+
+ if (isHotSwapSupported) {
+ log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting");
+ return;
+ }
+ log("onIccSwap: isHotSwapSupported is false, prompt for rebooting");
+
+ promptForRestart(isAdded);
+ }
+
+ private void promptForRestart(boolean isAdded) {
+ synchronized (mLock) {
+ final Resources res = mContext.getResources();
+ final String dialogComponent = res.getString(
+ R.string.config_iccHotswapPromptForRestartDialogComponent);
+ if (dialogComponent != null) {
+ Intent intent = new Intent().setComponent(ComponentName.unflattenFromString(
+ dialogComponent)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(EXTRA_ICC_CARD_ADDED, isAdded);
+ try {
+ mContext.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ loge("Unable to find ICC hotswap prompt for restart activity: " + e);
+ }
+ }
+
+ // TODO: Here we assume the device can't handle SIM hot-swap
+ // and has to reboot. We may want to add a property,
+ // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
+ // hot-swap.
+ DialogInterface.OnClickListener listener = null;
+
+
+ // TODO: SimRecords is not reset while SIM ABSENT (only reset while
+ // Radio_off_or_not_available). Have to reset in both both
+ // added or removed situation.
+ listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ synchronized (mLock) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (DBG) log("Reboot due to SIM swap");
+ PowerManager pm = (PowerManager) mContext
+ .getSystemService(Context.POWER_SERVICE);
+ pm.reboot("SIM is added.");
+ }
+ }
+ }
+
+ };
+
+ Resources r = Resources.getSystem();
+
+ String title = (isAdded) ? r.getString(R.string.sim_added_title) :
+ r.getString(R.string.sim_removed_title);
+ String message = (isAdded) ? r.getString(R.string.sim_added_message) :
+ r.getString(R.string.sim_removed_message);
+ String buttonTxt = r.getString(R.string.sim_restart_button);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(buttonTxt, listener)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_CARD_REMOVED:
+ onIccSwap(false);
+ break;
+ case EVENT_CARD_ADDED:
+ onIccSwap(true);
+ break;
+ default:
+ loge("Unknown Event " + msg.what);
+ }
+ }
+
+ /**
+ * Returns the state of the UiccCard in the slot.
+ * @return
+ */
+ public CardState getCardState() {
+ synchronized (mLock) {
+ if (mCardState == null) {
+ return CardState.CARDSTATE_ABSENT;
+ } else {
+ return mCardState;
+ }
+ }
+ }
+
+ /**
+ * Returns the UiccCard in the slot.
+ */
+ public UiccCard getUiccCard() {
+ synchronized (mLock) {
+ return mUiccCard;
+ }
+ }
+
+ /**
+ * Processes radio state unavailable event
+ */
+ public void onRadioStateUnavailable() {
+ if (mUiccCard != null) {
+ mUiccCard.dispose();
+ }
+ nullifyUiccCard(true /* sim state is unknown */);
+
+ if (mPhoneId != INVALID_PHONE_ID) {
+ UiccController.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, mPhoneId);
+ }
+
+ mCardState = CardState.CARDSTATE_ABSENT;
+ mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+ }
+
+ private void log(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
+
+ /**
+ * Dump
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("UiccSlot:");
+ pw.println(" mCi=" + mCi);
+ pw.println(" mActive=" + mActive);
+ pw.println(" mLastRadioState=" + mLastRadioState);
+ pw.println(" mCardState=" + mCardState);
+ if (mUiccCard != null) {
+ pw.println(" mUiccCard=" + mUiccCard);
+ mUiccCard.dump(fd, pw, args);
+ } else {
+ pw.println(" mUiccCard=null");
+ }
+ pw.println();
+ pw.flush();
+ pw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
index a5ab60b..3ef421a 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
@@ -65,11 +65,12 @@
mIsRestricted = new boolean[TelephonyManager.getDefault().getPhoneCount()];
shouldNotify = true;
}
- UiccCard[] cards = mUiccController.getUiccCards();
- for (int i = 0; cards != null && i < cards.length; ++i) {
+ for (int i = 0; i < mIsRestricted.length; ++i) {
// Update only if restricted state changes.
- if ((cards[i] == null
- || cards[i].getCardState() != CardState.CARDSTATE_RESTRICTED)
+
+ UiccCard uiccCard = mUiccController.getUiccCardForPhone(i);
+ if ((uiccCard == null
+ || uiccCard.getCardState() != CardState.CARDSTATE_RESTRICTED)
!= mIsRestricted[i]) {
mIsRestricted[i] = !mIsRestricted[i];
shouldNotify = true;
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
new file mode 100644
index 0000000..103c75b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -0,0 +1,1253 @@
+/*
+ * 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.uicc.euicc;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.UiccAccessRule;
+import android.telephony.euicc.EuiccCardManager;
+import android.telephony.euicc.EuiccNotification;
+import android.telephony.euicc.EuiccRulesAuthTable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
+import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
+import com.android.internal.telephony.uicc.euicc.EuiccCardErrorException.OperationCode;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduException;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+import com.android.internal.telephony.uicc.euicc.apdu.RequestBuilder;
+import com.android.internal.telephony.uicc.euicc.apdu.RequestProvider;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This represents an eUICC card to perform profile management operations asynchronously. This class
+ * includes methods defined by different versions of GSMA Spec (SGP.22).
+ */
+public class EuiccCard extends UiccCard {
+ private static final String LOG_TAG = "EuiccCard";
+ private static final boolean DBG = true;
+
+ private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100";
+ private static final int ICCID_LENGTH = 20;
+
+ // APDU status for SIM refresh
+ private static final int APDU_ERROR_SIM_REFRESH = 0x6F00;
+
+ // These error codes are defined in GSMA SGP.22. 0 is the code for success.
+ private static final int CODE_OK = 0;
+
+ // Error code for profile not in expected state for the operation. This error includes the case
+ // that profile is not in disabled state when being enabled or deleted, and that profile is not
+ // in enabled state when being disabled.
+ private static final int CODE_PROFILE_NOT_IN_EXPECTED_STATE = 2;
+
+ // Error code for nothing to delete when resetting eUICC memory or removing notifications.
+ private static final int CODE_NOTHING_TO_DELETE = 1;
+
+ // Error code for no result available when retrieving notifications.
+ private static final int CODE_NO_RESULT_AVAILABLE = 1;
+
+ private static final EuiccSpecVersion SGP_2_0 = new EuiccSpecVersion(2, 0, 0);
+
+ // Device capabilities.
+ private static final String DEV_CAP_GSM = "gsm";
+ private static final String DEV_CAP_UTRAN = "utran";
+ private static final String DEV_CAP_CDMA_1X = "cdma_1x";
+ private static final String DEV_CAP_HRPD = "hrpd";
+ private static final String DEV_CAP_EHRPD = "ehrpd";
+ private static final String DEV_CAP_EUTRAN = "eutran";
+ private static final String DEV_CAP_NFC = "nfc";
+ private static final String DEV_CAP_CRL = "crl";
+
+ // These interfaces are used for simplifying the code by leveraging lambdas.
+ private interface ApduRequestBuilder {
+ void build(RequestBuilder requestBuilder)
+ throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException;
+ }
+
+ private interface ApduResponseHandler<T> {
+ T handleResult(byte[] response)
+ throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException;
+ }
+
+ private interface ApduExceptionHandler {
+ void handleException(Throwable e);
+ }
+
+ private final ApduSender mApduSender;
+ private final Object mLock = new Object();
+ private RegistrantList mEidReadyRegistrants;
+ private EuiccSpecVersion mSpecVersion;
+ private volatile String mEid;
+
+ public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
+ super(c, ci, ics, phoneId, lock);
+ // TODO: Set supportExtendedApdu based on ATR.
+ mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
+
+ loadEidAndNotifyRegistrants();
+ }
+
+ /**
+ * Registers to be notified when EID is ready. If the EID is ready when this method is called,
+ * the registrant will be notified immediately.
+ */
+ public void registerForEidReady(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ if (mEid != null) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ } else {
+ if (mEidReadyRegistrants == null) {
+ mEidReadyRegistrants = new RegistrantList();
+ }
+ mEidReadyRegistrants.add(r);
+ }
+ }
+
+ /**
+ * Unregisters to be notified when EID is ready.
+ */
+ public void unregisterForEidReady(Handler h) {
+ if (mEidReadyRegistrants != null) {
+ mEidReadyRegistrants.remove(h);
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void loadEidAndNotifyRegistrants() {
+ Handler euiccMainThreadHandler = new Handler();
+ AsyncResultCallback<String> cardCb = new AsyncResultCallback<String>() {
+ @Override
+ public void onResult(String result) {
+ if (mEidReadyRegistrants != null) {
+ mEidReadyRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ // Not notifying registrants if getting eid fails.
+ Rlog.e(LOG_TAG, "Failed loading eid", e);
+ }
+ };
+ getEid(cardCb, euiccMainThreadHandler);
+ }
+
+ /**
+ * Gets the GSMA RSP specification version supported by this eUICC. This may return null if the
+ * version cannot be read.
+ */
+ public void getSpecVersion(AsyncResultCallback<EuiccSpecVersion> callback, Handler handler) {
+ if (mSpecVersion != null) {
+ AsyncResultHelper.returnResult(mSpecVersion, callback, handler);
+ return;
+ }
+
+ sendApdu(newRequestProvider((RequestBuilder requestBuilder) -> { /* Do nothing */ }),
+ (byte[] response) -> mSpecVersion, callback, handler);
+ }
+
+ /**
+ * Gets a list of user-visible profiles.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES)
+ .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS)
+ .build().toHex())),
+ (byte[] response) -> {
+ List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode()
+ .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO);
+ int size = profileNodes.size();
+ EuiccProfileInfo[] profiles = new EuiccProfileInfo[size];
+ int profileCount = 0;
+ for (int i = 0; i < size; i++) {
+ Asn1Node profileNode = profileNodes.get(i);
+ if (!profileNode.hasChild(Tags.TAG_ICCID)) {
+ loge("Profile must have an ICCID.");
+ continue;
+ }
+ String strippedIccIdString =
+ stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes());
+ EuiccProfileInfo.Builder profileBuilder =
+ new EuiccProfileInfo.Builder(strippedIccIdString);
+ buildProfile(profileNode, profileBuilder);
+
+ EuiccProfileInfo profile = profileBuilder.build();
+ profiles[profileCount++] = profile;
+ }
+ return profiles;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Gets a profile.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public final void getProfile(String iccid, AsyncResultCallback<EuiccProfileInfo> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES)
+ .addChildAsBytes(Tags.TAG_ICCID, IccUtils.bcdToBytes(iccid))
+ .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS)
+ .build().toHex())),
+ (byte[] response) -> {
+ List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode()
+ .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO);
+ if (profileNodes.isEmpty()) {
+ return null;
+ }
+ Asn1Node profileNode = profileNodes.get(0);
+ String strippedIccIdString =
+ stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes());
+ EuiccProfileInfo.Builder profileBuilder =
+ new EuiccProfileInfo.Builder(strippedIccIdString);
+ buildProfile(profileNode, profileBuilder);
+ return profileBuilder.build();
+ },
+ callback, handler);
+ }
+
+ /**
+ * Disables a profile of the given {@code iccid}.
+ *
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void disableProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback,
+ Handler handler) {
+ sendApduWithSimResetErrorWorkaround(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid));
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_DISABLE_PROFILE)
+ .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
+ .addChildAsBytes(Tags.TAG_ICCID, iccidBytes))
+ .addChildAsBoolean(Tags.TAG_CTX_1, refresh)
+ .build().toHex());
+ }),
+ (byte[] response) -> {
+ int result;
+ // SGP.22 v2.0 DisableProfileResponse
+ result = parseSimpleResult(response);
+ switch (result) {
+ case CODE_OK:
+ return null;
+ case CODE_PROFILE_NOT_IN_EXPECTED_STATE:
+ logd("Profile is already disabled, iccid: "
+ + SubscriptionInfo.givePrintableIccid(iccid));
+ return null;
+ default:
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_DISABLE_PROFILE, result);
+ }
+ },
+ callback, handler);
+ }
+
+ /**
+ * Switches from the current profile to another profile. The current profile will be disabled
+ * and the specified profile will be enabled.
+ *
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param callback The callback to get the EuiccProfile enabled.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void switchToProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback,
+ Handler handler) {
+ sendApduWithSimResetErrorWorkaround(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid));
+ requestBuilder.addStoreData(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());
+ }),
+ (byte[] response) -> {
+ int result;
+ // SGP.22 v2.0 EnableProfileResponse
+ result = parseSimpleResult(response);
+ switch (result) {
+ case CODE_OK:
+ return null;
+ case CODE_PROFILE_NOT_IN_EXPECTED_STATE:
+ logd("Profile is already enabled, iccid: "
+ + SubscriptionInfo.givePrintableIccid(iccid));
+ return null;
+ default:
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_SWITCH_TO_PROFILE, result);
+ }
+ },
+ callback, handler);
+ }
+
+ /**
+ * Gets the EID synchronously.
+ * @return The EID string. Returns null if it is not ready yet.
+ */
+ public String getEid() {
+ return mEid;
+ }
+
+ /**
+ * Gets the EID of the eUICC and overwrites mCardId in UiccCard.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void getEid(AsyncResultCallback<String> callback, Handler handler) {
+ if (mEid != null) {
+ AsyncResultHelper.returnResult(mEid, callback, handler);
+ return;
+ }
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EID)
+ .addChildAsBytes(Tags.TAG_TAG_LIST, new byte[] {Tags.TAG_EID})
+ .build().toHex())),
+ (byte[] response) -> {
+ String eid = IccUtils.bytesToHexString(parseResponse(response)
+ .getChild(Tags.TAG_EID).asBytes());
+ synchronized (mLock) {
+ mEid = eid;
+ mCardId = eid;
+ }
+ return eid;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Sets the nickname of a profile.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void setNickname(String iccid, String nickname, AsyncResultCallback<Void> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_SET_NICKNAME)
+ .addChildAsBytes(Tags.TAG_ICCID,
+ IccUtils.bcdToBytes(padTrailingFs(iccid)))
+ .addChildAsString(Tags.TAG_NICKNAME, nickname)
+ .build().toHex())),
+ (byte[] response) -> {
+ // SGP.22 v2.0 SetNicknameResponse
+ int result = parseSimpleResult(response);
+ if (result != CODE_OK) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_SET_NICKNAME, result);
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Deletes a profile from eUICC.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void deleteProfile(String iccid, AsyncResultCallback<Void> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid));
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_DELETE_PROFILE)
+ .addChildAsBytes(Tags.TAG_ICCID, iccidBytes)
+ .build().toHex());
+ }),
+ (byte[] response) -> {
+ // SGP.22 v2.0 DeleteProfileRequest
+ int result = parseSimpleResult(response);
+ if (result != CODE_OK) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_DELETE_PROFILE, result);
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Resets the eUICC memory (e.g., remove all profiles).
+ *
+ * @param options Bits of the options of resetting which parts of the eUICC memory.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 1.1.0 [GSMA SGP.22]
+ */
+ public void resetMemory(@EuiccCardManager.ResetOption int options,
+ AsyncResultCallback<Void> callback, Handler handler) {
+ sendApduWithSimResetErrorWorkaround(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_EUICC_MEMORY_RESET)
+ .addChildAsBits(Tags.TAG_CTX_2, options)
+ .build().toHex())),
+ (byte[] response) -> {
+ int result = parseSimpleResult(response);
+ if (result != CODE_OK && result != CODE_NOTHING_TO_DELETE) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_RESET_MEMORY, result);
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Gets the default SM-DP+ address from eUICC.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getDefaultSmdpAddress(AsyncResultCallback<String> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_GET_CONFIGURED_ADDRESSES)
+ .build().toHex())),
+ (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_0).asString(),
+ callback, handler);
+ }
+
+ /**
+ * Gets the SM-DS address from eUICC.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getSmdsAddress(AsyncResultCallback<String> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_GET_CONFIGURED_ADDRESSES)
+ .build().toHex())),
+ (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_1).asString(),
+ callback, handler);
+ }
+
+ /**
+ * Sets the default SM-DP+ address of eUICC.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void setDefaultSmdpAddress(String defaultSmdpAddress, AsyncResultCallback<Void> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_SET_DEFAULT_SMDP_ADDRESS)
+ .addChildAsString(Tags.TAG_CTX_0, defaultSmdpAddress)
+ .build().toHex())),
+ (byte[] response) -> {
+ // SGP.22 v2.0 SetDefaultDpAddressResponse
+ int result = parseSimpleResult(response);
+ if (result != CODE_OK) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_SET_DEFAULT_SMDP_ADDRESS, result);
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Gets Rules Authorisation Table.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getRulesAuthTable(AsyncResultCallback<EuiccRulesAuthTable> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_RAT)
+ .build().toHex())),
+ (byte[] response) -> {
+ Asn1Node root = parseResponse(response);
+ List<Asn1Node> nodes = root.getChildren(Tags.TAG_CTX_COMP_0);
+ EuiccRulesAuthTable.Builder builder =
+ new EuiccRulesAuthTable.Builder(nodes.size());
+ int size = nodes.size();
+ for (int i = 0; i < size; i++) {
+ Asn1Node node = nodes.get(i);
+ List<Asn1Node> opIdNodes = node.getChild(Tags.TAG_CTX_COMP_1).getChildren();
+ int opIdSize = opIdNodes.size();
+ CarrierIdentifier[] opIds = new CarrierIdentifier[opIdSize];
+ for (int j = 0; j < opIdSize; j++) {
+ opIds[j] = buildCarrierIdentifier(opIdNodes.get(j));
+ }
+ builder.add(node.getChild(Tags.TAG_CTX_0).asBits(), Arrays.asList(opIds),
+ node.getChild(Tags.TAG_CTX_2).asBits());
+ }
+ return builder.build();
+ },
+ callback, handler);
+ }
+
+ /**
+ * Gets the eUICC challenge for new profile downloading.
+ *
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getEuiccChallenge(AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_GET_EUICC_CHALLENGE)
+ .build().toHex())),
+ (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_0).asBytes(),
+ callback, handler);
+ }
+
+ /**
+ * Gets the eUICC info1 for new profile downloading.
+ *
+ * @param callback The callback to get the result, which represents an {@code EUICCInfo1}
+ * defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getEuiccInfo1(AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EUICC_INFO_1)
+ .build().toHex())),
+ (response) -> response,
+ callback, handler);
+ }
+
+ /**
+ * Gets the eUICC info2 for new profile downloading.
+ *
+ * @param callback The callback to get the result, which represents an {@code EUICCInfo2}
+ * defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void getEuiccInfo2(AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EUICC_INFO_2)
+ .build().toHex())),
+ (response) -> response,
+ callback, handler);
+ }
+
+ /**
+ * Authenticates the SM-DP+ server by the eUICC. The parameters {@code serverSigned1}, {@code
+ * serverSignature1}, {@code euiccCiPkIdToBeUsed}, and {@code serverCertificate} are the ASN.1
+ * data returned by SM-DP+ server.
+ *
+ * @param matchingId The activation code or an empty string.
+ * @param callback The callback to get the result, which represents an {@code
+ * AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void authenticateServer(String matchingId, byte[] serverSigned1, byte[] serverSignature1,
+ byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+ AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ byte[] imeiBytes = getDeviceId();
+ // TAC is the first 8 digits (4 bytes) of IMEI.
+ byte[] tacBytes = new byte[4];
+ System.arraycopy(imeiBytes, 0, tacBytes, 0, 4);
+
+ Asn1Node.Builder devCapsBuilder = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1);
+ String[] devCapsStrings = getResources().getStringArray(
+ com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities);
+ if (devCapsStrings != null) {
+ for (String devCapItem : devCapsStrings) {
+ addDeviceCapability(devCapsBuilder, devCapItem);
+ }
+ } else {
+ if (DBG) logd("No device capabilities set.");
+ }
+
+ Asn1Node.Builder ctxParams1Builder = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
+ .addChildAsString(Tags.TAG_CTX_0, matchingId)
+ .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1)
+ .addChildAsBytes(Tags.TAG_CTX_0, tacBytes)
+ .addChild(devCapsBuilder)
+ .addChildAsBytes(Tags.TAG_CTX_2, imeiBytes));
+
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_AUTHENTICATE_SERVER)
+ .addChild(new Asn1Decoder(serverSigned1).nextNode())
+ .addChild(new Asn1Decoder(serverSignature1).nextNode())
+ .addChild(new Asn1Decoder(euiccCiPkIdToBeUsed).nextNode())
+ .addChild(new Asn1Decoder(serverCertificate).nextNode())
+ .addChild(ctxParams1Builder)
+ .build().toHex());
+ }),
+ (byte[] response) ->
+ parseResponseAndCheckSimpleError(response,
+ EuiccCardErrorException.OPERATION_AUTHENTICATE_SERVER).toBytes(),
+ callback, handler);
+ }
+
+ /**
+ * Prepares the profile download request sent to SM-DP+. The parameters {@code smdpSigned2},
+ * {@code smdpSignature2}, and {@code smdpCertificate} are the ASN.1 data returned by SM-DP+
+ * server.
+ *
+ * @param hashCc The hash of confirmation code. It can be null if there is no confirmation code
+ * required.
+ * @param callback The callback to get the result, which represents an {@code
+ * PrepareDownloadResponse} defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2, byte[] smdpSignature2,
+ byte[] smdpCertificate, AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ Asn1Node.Builder builder = Asn1Node.newBuilder(Tags.TAG_PREPARE_DOWNLOAD)
+ .addChild(new Asn1Decoder(smdpSigned2).nextNode())
+ .addChild(new Asn1Decoder(smdpSignature2).nextNode());
+ if (hashCc != null) {
+ builder.addChildAsBytes(Tags.TAG_UNI_4, hashCc);
+ }
+ requestBuilder.addStoreData(
+ builder.addChild(new Asn1Decoder(smdpCertificate).nextNode())
+ .build().toHex());
+ }),
+ (byte[] response) -> {
+ Asn1Node root = parseResponse(response);
+ if (root.hasChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2)) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_PREPARE_DOWNLOAD,
+ root.getChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2).asInteger());
+ }
+ return root.toBytes();
+ },
+ callback, handler);
+ }
+
+ /**
+ * Loads a downloaded bound profile package onto the eUICC.
+ *
+ * @param boundProfilePackage The Bound Profile Package data returned by SM-DP+ server.
+ * @param callback The callback to get the result, which represents an {@code
+ * LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void loadBoundProfilePackage(byte[] boundProfilePackage,
+ AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) -> {
+ Asn1Node bppNode = new Asn1Decoder(boundProfilePackage).nextNode();
+ // initialiseSecureChannelRequest (ES8+.InitialiseSecureChannel)
+ Asn1Node initialiseSecureChannelRequest = bppNode.getChild(
+ Tags.TAG_INITIALISE_SECURE_CHANNEL);
+ // firstSequenceOf87 (ES8+.ConfigureISDP)
+ Asn1Node firstSequenceOf87 = bppNode.getChild(Tags.TAG_CTX_COMP_0);
+ // sequenceOf88 (ES8+.StoreMetadata)
+ Asn1Node sequenceOf88 = bppNode.getChild(Tags.TAG_CTX_COMP_1);
+ List<Asn1Node> metaDataSeqs = sequenceOf88.getChildren(Tags.TAG_CTX_8);
+ // sequenceOf86 (ES8+.LoadProfileElements #1)
+ Asn1Node sequenceOf86 = bppNode.getChild(Tags.TAG_CTX_COMP_3);
+ List<Asn1Node> elementSeqs = sequenceOf86.getChildren(Tags.TAG_CTX_6);
+
+ requestBuilder.addStoreData(bppNode.getHeadAsHex()
+ + initialiseSecureChannelRequest.toHex());
+
+ requestBuilder.addStoreData(firstSequenceOf87.toHex());
+
+ requestBuilder.addStoreData(sequenceOf88.getHeadAsHex());
+ int size = metaDataSeqs.size();
+ for (int i = 0; i < size; i++) {
+ requestBuilder.addStoreData(metaDataSeqs.get(i).toHex());
+ }
+
+ if (bppNode.hasChild(Tags.TAG_CTX_COMP_2)) {
+ requestBuilder.addStoreData(bppNode.getChild(Tags.TAG_CTX_COMP_2).toHex());
+ }
+
+ requestBuilder.addStoreData(sequenceOf86.getHeadAsHex());
+ size = elementSeqs.size();
+ for (int i = 0; i < size; i++) {
+ requestBuilder.addStoreData(elementSeqs.get(i).toHex());
+ }
+ }),
+ (byte[] response) -> {
+ // SGP.22 v2.0 ErrorResult
+ Asn1Node root = parseResponse(response);
+ if (root.hasChild(Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA,
+ Tags.TAG_CTX_COMP_2, Tags.TAG_CTX_COMP_1, Tags.TAG_CTX_1)) {
+ Asn1Node errorNode = root.getChild(
+ Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA, Tags.TAG_CTX_COMP_2,
+ Tags.TAG_CTX_COMP_1, Tags.TAG_CTX_1);
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_LOAD_BOUND_PROFILE_PACKAGE,
+ errorNode.asInteger(), errorNode);
+ }
+ return root.toBytes();
+ },
+ callback, handler);
+ }
+
+ /**
+ * Cancels the current profile download session.
+ *
+ * @param transactionId The transaction ID returned by SM-DP+ server.
+ * @param callback The callback to get the result, which represents an {@code
+ * CancelSessionResponse} defined in GSMA RSP v2.0+.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void cancelSession(byte[] transactionId, @EuiccCardManager.CancelReason int reason,
+ AsyncResultCallback<byte[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_CANCEL_SESSION)
+ .addChildAsBytes(Tags.TAG_CTX_0, transactionId)
+ .addChildAsInteger(Tags.TAG_CTX_1, reason)
+ .build().toHex())),
+ (byte[] response) ->
+ parseResponseAndCheckSimpleError(response,
+ EuiccCardErrorException.OPERATION_CANCEL_SESSION).toBytes(),
+ callback, handler);
+ }
+
+ /**
+ * Lists all notifications of the given {@code notificationEvents}.
+ *
+ * @param events Bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void listNotifications(@EuiccNotification.Event int events,
+ AsyncResultCallback<EuiccNotification[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_LIST_NOTIFICATION)
+ .addChildAsBits(Tags.TAG_CTX_1, events)
+ .build().toHex())),
+ (byte[] response) -> {
+ Asn1Node root = parseResponseAndCheckSimpleError(response,
+ EuiccCardErrorException.OPERATION_LIST_NOTIFICATIONS);
+ List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren();
+ EuiccNotification[] notifications = new EuiccNotification[nodes.size()];
+ for (int i = 0; i < notifications.length; ++i) {
+ notifications[i] = createNotification(nodes.get(i));
+ }
+ return notifications;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Retrieves contents of all notification of the given {@code events}.
+ *
+ * @param events Bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void retrieveNotificationList(@EuiccNotification.Event int events,
+ AsyncResultCallback<EuiccNotification[]> callback, Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_RETRIEVE_NOTIFICATIONS_LIST)
+ .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
+ .addChildAsBits(Tags.TAG_CTX_1, events))
+ .build().toHex())),
+ (byte[] response) -> {
+ Asn1Node root = parseResponse(response);
+ if (root.hasChild(Tags.TAG_CTX_1)) {
+ // SGP.22 v2.0 RetrieveNotificationsListResponse
+ int error = root.getChild(Tags.TAG_CTX_1).asInteger();
+ switch (error) {
+ case CODE_NO_RESULT_AVAILABLE:
+ return new EuiccNotification[0];
+ default:
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_RETRIEVE_NOTIFICATION,
+ error);
+ }
+ }
+ List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren();
+ EuiccNotification[] notifications = new EuiccNotification[nodes.size()];
+ for (int i = 0; i < notifications.length; ++i) {
+ notifications[i] = createNotification(nodes.get(i));
+ }
+ return notifications;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Retrieves the content of a notification of the given {@code seqNumber}.
+ *
+ * @param seqNumber The sequence number of the notification.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void retrieveNotification(int seqNumber, AsyncResultCallback<EuiccNotification> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_RETRIEVE_NOTIFICATIONS_LIST)
+ .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
+ .addChildAsInteger(Tags.TAG_CTX_0, seqNumber))
+ .build().toHex())),
+ (byte[] response) -> {
+ Asn1Node root = parseResponseAndCheckSimpleError(response,
+ EuiccCardErrorException.OPERATION_RETRIEVE_NOTIFICATION);
+ List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren();
+ if (nodes.size() > 0) {
+ return createNotification(nodes.get(0));
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Removes a notification from eUICC.
+ *
+ * @param seqNumber The sequence number of the notification.
+ * @param callback The callback to get the result.
+ * @param handler The handler to run the callback.
+ * @since 2.0.0 [GSMA SGP.22]
+ */
+ public void removeNotificationFromList(int seqNumber, AsyncResultCallback<Void> callback,
+ Handler handler) {
+ sendApdu(
+ newRequestProvider((RequestBuilder requestBuilder) ->
+ requestBuilder.addStoreData(
+ Asn1Node.newBuilder(Tags.TAG_REMOVE_NOTIFICATION_FROM_LIST)
+ .addChildAsInteger(Tags.TAG_CTX_0, seqNumber)
+ .build().toHex())),
+ (byte[] response) -> {
+ // SGP.22 v2.0 NotificationSentResponse
+ int result = parseSimpleResult(response);
+ if (result != CODE_OK && result != CODE_NOTHING_TO_DELETE) {
+ throw new EuiccCardErrorException(
+ EuiccCardErrorException.OPERATION_REMOVE_NOTIFICATION_FROM_LIST,
+ result);
+ }
+ return null;
+ },
+ callback, handler);
+ }
+
+ /**
+ * Sets a device capability version as the child of the given device capability ASN1 node
+ * builder.
+ *
+ * @param devCapBuilder The ASN1 node builder to modify.
+ * @param devCapItem The device capability and its supported version in pair.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void addDeviceCapability(Asn1Node.Builder devCapBuilder, String devCapItem) {
+ String[] split = devCapItem.split(",");
+ if (split.length != 2) {
+ loge("Invalid device capability item: " + Arrays.toString(split));
+ return;
+ }
+
+ String devCap = split[0].trim();
+ Integer version;
+ try {
+ version = Integer.parseInt(split[1].trim());
+ } catch (NumberFormatException e) {
+ loge("Invalid device capability version number.", e);
+ return;
+ }
+
+ byte[] versionBytes = new byte[] { version.byteValue(), 0, 0 };
+ switch (devCap) {
+ case DEV_CAP_GSM:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_0, versionBytes);
+ break;
+ case DEV_CAP_UTRAN:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_1, versionBytes);
+ break;
+ case DEV_CAP_CDMA_1X:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_2, versionBytes);
+ break;
+ case DEV_CAP_HRPD:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_3, versionBytes);
+ break;
+ case DEV_CAP_EHRPD:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_4, versionBytes);
+ break;
+ case DEV_CAP_EUTRAN:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_5, versionBytes);
+ break;
+ case DEV_CAP_NFC:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_6, versionBytes);
+ break;
+ case DEV_CAP_CRL:
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_7, versionBytes);
+ break;
+ default:
+ loge("Invalid device capability name: " + devCap);
+ break;
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected byte[] getDeviceId() {
+ byte[] imeiBytes = new byte[8];
+ Phone phone = PhoneFactory.getPhone(getPhoneId());
+ if (phone != null) {
+ IccUtils.bcdToBytes(phone.getDeviceId(), imeiBytes);
+ }
+ return imeiBytes;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected Resources getResources() {
+ return Resources.getSystem();
+ }
+
+ private RequestProvider newRequestProvider(ApduRequestBuilder builder) {
+ return (selectResponse, requestBuilder) -> {
+ EuiccSpecVersion ver = getOrExtractSpecVersion(selectResponse);
+ if (ver == null) {
+ throw new EuiccCardException("Cannot get eUICC spec version.");
+ }
+ try {
+ if (ver.compareTo(SGP_2_0) < 0) {
+ throw new EuiccCardException("eUICC spec version is unsupported: " + ver);
+ }
+ builder.build(requestBuilder);
+ } catch (InvalidAsn1DataException | TagNotFoundException e) {
+ throw new EuiccCardException("Cannot parse ASN1 to build request.", e);
+ }
+ };
+ }
+
+ private EuiccSpecVersion getOrExtractSpecVersion(byte[] selectResponse) {
+ // Uses the cached version.
+ if (mSpecVersion != null) {
+ return mSpecVersion;
+ }
+ // Parses and caches the version.
+ EuiccSpecVersion ver = EuiccSpecVersion.fromOpenChannelResponse(selectResponse);
+ if (ver != null) {
+ synchronized (mLock) {
+ if (mSpecVersion == null) {
+ mSpecVersion = ver;
+ }
+ }
+ }
+ return ver;
+ }
+
+ /**
+ * A wrapper on {@link ApduSender#send(RequestProvider, AsyncResultCallback, Handler)} to
+ * leverage lambda to simplify the sending APDU code.EuiccCardErrorException.
+ *
+ * @param requestBuilder Builds the request of APDU commands.
+ * @param responseHandler Converts the APDU response from bytes to expected result.
+ * @param <T> Type of the originally expected result.
+ */
+ private <T> void sendApdu(RequestProvider requestBuilder,
+ ApduResponseHandler<T> responseHandler, AsyncResultCallback<T> callback,
+ Handler handler) {
+ sendApdu(requestBuilder, responseHandler,
+ (e) -> callback.onException(new EuiccCardException("Cannot send APDU.", e)),
+ callback, handler);
+ }
+
+ /**
+ * This is a workaround solution to the bug that a SIM refresh may interrupt the modem to return
+ * the reset of responses of the original APDU command. This applies to disable profile, switch
+ * profile, and reset eUICC memory.
+ *
+ * <p>TODO: Use
+ * {@link #sendApdu(RequestProvider, ApduResponseHandler, AsyncResultCallback, Handler)} when
+ * this workaround is not needed.
+ */
+ private void sendApduWithSimResetErrorWorkaround(
+ RequestProvider requestBuilder, ApduResponseHandler<Void> responseHandler,
+ AsyncResultCallback<Void> callback, Handler handler) {
+ sendApdu(requestBuilder, responseHandler, (e) -> {
+ if (e instanceof ApduException
+ && ((ApduException) e).getApduStatus() == APDU_ERROR_SIM_REFRESH) {
+ logi("Sim is refreshed after disabling profile, no response got.");
+ callback.onResult(null);
+ } else {
+ callback.onException(new EuiccCardException("Cannot send APDU.", e));
+ }
+ }, callback, handler);
+ }
+
+ private <T> void sendApdu(RequestProvider requestBuilder,
+ ApduResponseHandler<T> responseHandler,
+ ApduExceptionHandler exceptionHandler, AsyncResultCallback<T> callback,
+ Handler handler) {
+ mApduSender.send(requestBuilder, new AsyncResultCallback<byte[]>() {
+ @Override
+ public void onResult(byte[] response) {
+ try {
+ callback.onResult(responseHandler.handleResult(response));
+ } catch (EuiccCardException e) {
+ callback.onException(e);
+ } catch (InvalidAsn1DataException | TagNotFoundException e) {
+ callback.onException(new EuiccCardException(
+ "Cannot parse response: " + IccUtils.bytesToHexString(response), e));
+ }
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ exceptionHandler.handleException(e);
+ }
+ }, handler);
+ }
+
+ private static void buildProfile(Asn1Node profileNode, EuiccProfileInfo.Builder profileBuilder)
+ throws TagNotFoundException, InvalidAsn1DataException {
+ if (profileNode.hasChild(Tags.TAG_NICKNAME)) {
+ profileBuilder.setNickname(profileNode.getChild(Tags.TAG_NICKNAME).asString());
+ }
+
+ if (profileNode.hasChild(Tags.TAG_SERVICE_PROVIDER_NAME)) {
+ profileBuilder.setServiceProviderName(
+ profileNode.getChild(Tags.TAG_SERVICE_PROVIDER_NAME).asString());
+ }
+
+ if (profileNode.hasChild(Tags.TAG_PROFILE_NAME)) {
+ profileBuilder.setProfileName(
+ profileNode.getChild(Tags.TAG_PROFILE_NAME).asString());
+ }
+
+ if (profileNode.hasChild(Tags.TAG_OPERATOR_ID)) {
+ profileBuilder.setCarrierIdentifier(
+ buildCarrierIdentifier(profileNode.getChild(Tags.TAG_OPERATOR_ID)));
+ }
+
+ if (profileNode.hasChild(Tags.TAG_PROFILE_STATE)) {
+ // noinspection WrongConstant
+ profileBuilder.setState(profileNode.getChild(Tags.TAG_PROFILE_STATE).asInteger());
+ } else {
+ profileBuilder.setState(EuiccProfileInfo.PROFILE_STATE_DISABLED);
+ }
+
+ if (profileNode.hasChild(Tags.TAG_PROFILE_CLASS)) {
+ // noinspection WrongConstant
+ profileBuilder.setProfileClass(
+ profileNode.getChild(Tags.TAG_PROFILE_CLASS).asInteger());
+ } else {
+ profileBuilder.setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL);
+ }
+
+ if (profileNode.hasChild(Tags.TAG_PROFILE_POLICY_RULE)) {
+ // noinspection WrongConstant
+ profileBuilder.setPolicyRules(
+ profileNode.getChild(Tags.TAG_PROFILE_POLICY_RULE).asBits());
+ }
+
+ if (profileNode.hasChild(Tags.TAG_CARRIER_PRIVILEGE_RULES)) {
+ List<Asn1Node> refArDoNodes = profileNode.getChild(Tags.TAG_CARRIER_PRIVILEGE_RULES)
+ .getChildren(Tags.TAG_REF_AR_DO);
+ UiccAccessRule[] rules = buildUiccAccessRule(refArDoNodes);
+ List<UiccAccessRule> rulesList = null;
+ if (rules != null) {
+ rulesList = Arrays.asList(rules);
+ }
+ profileBuilder.setUiccAccessRule(rulesList);
+ }
+ }
+
+ private static CarrierIdentifier buildCarrierIdentifier(Asn1Node node)
+ throws InvalidAsn1DataException, TagNotFoundException {
+ String gid1 = null;
+ if (node.hasChild(Tags.TAG_CTX_1)) {
+ gid1 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_1).asBytes());
+ }
+ String gid2 = null;
+ if (node.hasChild(Tags.TAG_CTX_2)) {
+ gid2 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_2).asBytes());
+ }
+ return new CarrierIdentifier(node.getChild(Tags.TAG_CTX_0).asBytes(), gid1, gid2);
+ }
+
+ @Nullable
+ private static UiccAccessRule[] buildUiccAccessRule(List<Asn1Node> nodes)
+ throws InvalidAsn1DataException, TagNotFoundException {
+ if (nodes.isEmpty()) {
+ return null;
+ }
+ int count = nodes.size();
+ UiccAccessRule[] rules = new UiccAccessRule[count];
+ for (int i = 0; i < count; i++) {
+ Asn1Node node = nodes.get(i);
+ Asn1Node refDoNode = node.getChild(Tags.TAG_REF_DO);
+ byte[] signature = refDoNode.getChild(Tags.TAG_DEVICE_APP_ID_REF_DO).asBytes();
+
+ String packageName = null;
+ if (refDoNode.hasChild(Tags.TAG_PKG_REF_DO)) {
+ packageName = refDoNode.getChild(Tags.TAG_PKG_REF_DO).asString();
+ }
+ long accessType = 0;
+ if (node.hasChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO)) {
+ Asn1Node permArDoNode = node.getChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO);
+ accessType = permArDoNode.asRawLong();
+ }
+ rules[i] = new UiccAccessRule(signature, packageName, accessType);
+ }
+ return rules;
+ }
+
+ /**
+ * Creates an instance from the ASN.1 data.
+ *
+ * @param node This should be either {@code NotificationMetadata} or {@code PendingNotification}
+ * defined by SGP.22 v2.0.
+ * @throws TagNotFoundException If no notification tag is found in the bytes.
+ * @throws InvalidAsn1DataException If no valid data is found in the bytes.
+ */
+ private static EuiccNotification createNotification(Asn1Node node)
+ throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node metadataNode;
+ if (node.getTag() == Tags.TAG_NOTIFICATION_METADATA) {
+ metadataNode = node;
+ } else if (node.getTag() == Tags.TAG_PROFILE_INSTALLATION_RESULT) {
+ metadataNode = node.getChild(Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA,
+ Tags.TAG_NOTIFICATION_METADATA);
+ } else {
+ // Other signed notification
+ metadataNode = node.getChild(Tags.TAG_NOTIFICATION_METADATA);
+ }
+ // noinspection WrongConstant
+ return new EuiccNotification(metadataNode.getChild(Tags.TAG_SEQ).asInteger(),
+ metadataNode.getChild(Tags.TAG_TARGET_ADDR).asString(),
+ metadataNode.getChild(Tags.TAG_EVENT).asBits(),
+ node.getTag() == Tags.TAG_NOTIFICATION_METADATA ? null : node.toBytes());
+ }
+
+ /** Returns the first CONTEXT [0] as an integer. */
+ private static int parseSimpleResult(byte[] response)
+ throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException {
+ return parseResponse(response).getChild(Tags.TAG_CTX_0).asInteger();
+ }
+
+ private static Asn1Node parseResponse(byte[] response)
+ throws EuiccCardException, InvalidAsn1DataException {
+ Asn1Decoder decoder = new Asn1Decoder(response);
+ if (!decoder.hasNextNode()) {
+ throw new EuiccCardException("Empty response", null);
+ }
+ return decoder.nextNode();
+ }
+
+ /**
+ * Parses the bytes into an ASN1 node and check if there is an error code represented at the
+ * context 1 tag. If there is an error code, an {@link EuiccCardErrorException} will be thrown
+ * with the given operation code.
+ */
+ private static Asn1Node parseResponseAndCheckSimpleError(byte[] response,
+ @OperationCode int opCode)
+ throws EuiccCardException, InvalidAsn1DataException, TagNotFoundException {
+ Asn1Node root = parseResponse(response);
+ if (root.hasChild(Tags.TAG_CTX_1)) {
+ throw new EuiccCardErrorException(opCode, root.getChild(Tags.TAG_CTX_1).asInteger());
+ }
+ return root;
+ }
+
+ /** Strip all the trailing 'F' characters of an iccId. */
+ private static String stripTrailingFs(byte[] iccId) {
+ return IccUtils.stripTrailingFs(IccUtils.bchToString(iccId, 0, iccId.length));
+ }
+
+ /** Pad an iccId with trailing 'F' characters until the length is 20. */
+ private static String padTrailingFs(String iccId) {
+ if (!TextUtils.isEmpty(iccId) && iccId.length() < ICCID_LENGTH) {
+ iccId += new String(new char[20 - iccId.length()]).replace('\0', 'F');
+ }
+ return iccId;
+ }
+
+ private static void loge(String message) {
+ Rlog.e(LOG_TAG, message);
+ }
+
+ private static void loge(String message, Throwable tr) {
+ Rlog.e(LOG_TAG, message, tr);
+ }
+
+ private static void logi(String message) {
+ Rlog.i(LOG_TAG, message);
+ }
+
+ private static void logd(String message) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, message);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardErrorException.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardErrorException.java
new file mode 100644
index 0000000..4984485
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardErrorException.java
@@ -0,0 +1,123 @@
+/*
+ * 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.uicc.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The exception which is thrown when an error is returned in a successfully executed APDU command
+ * sent to eUICC. This exception means the response status is no-error
+ * ({@link ApduSender#STATUS_NO_ERROR}), but the action is failed due to eUICC specific logic.
+ */
+public class EuiccCardErrorException extends EuiccCardException {
+ /** Operations */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "OPERATION_", value = {
+ OPERATION_UNKNOWN,
+ OPERATION_GET_PROFILE,
+ OPERATION_PREPARE_DOWNLOAD,
+ OPERATION_AUTHENTICATE_SERVER,
+ OPERATION_CANCEL_SESSION,
+ OPERATION_LOAD_BOUND_PROFILE_PACKAGE,
+ OPERATION_LIST_NOTIFICATIONS,
+ OPERATION_SET_NICKNAME,
+ OPERATION_RETRIEVE_NOTIFICATION,
+ OPERATION_REMOVE_NOTIFICATION_FROM_LIST,
+ OPERATION_SWITCH_TO_PROFILE,
+ OPERATION_DISABLE_PROFILE,
+ OPERATION_DELETE_PROFILE,
+ OPERATION_RESET_MEMORY,
+ OPERATION_SET_DEFAULT_SMDP_ADDRESS,
+ })
+ public @interface OperationCode {}
+
+ public static final int OPERATION_UNKNOWN = 0;
+ public static final int OPERATION_GET_PROFILE = 1;
+ public static final int OPERATION_PREPARE_DOWNLOAD = 2;
+ public static final int OPERATION_AUTHENTICATE_SERVER = 3;
+ public static final int OPERATION_CANCEL_SESSION = 4;
+ public static final int OPERATION_LOAD_BOUND_PROFILE_PACKAGE = 5;
+ public static final int OPERATION_LIST_NOTIFICATIONS = 6;
+ public static final int OPERATION_SET_NICKNAME = 7;
+ public static final int OPERATION_RETRIEVE_NOTIFICATION = 8;
+ public static final int OPERATION_REMOVE_NOTIFICATION_FROM_LIST = 9;
+ public static final int OPERATION_SWITCH_TO_PROFILE = 10;
+ public static final int OPERATION_DISABLE_PROFILE = 11;
+ public static final int OPERATION_DELETE_PROFILE = 12;
+ public static final int OPERATION_RESET_MEMORY = 13;
+ public static final int OPERATION_SET_DEFAULT_SMDP_ADDRESS = 14;
+
+ private final @OperationCode int mOperationCode;
+ private final int mErrorCode;
+ private final @Nullable Asn1Node mErrorDetails;
+
+ /**
+ * Creates an exception with an error code in the response of an APDU command.
+ *
+ * @param errorCode The meaning of the code depends on each APDU command. It should always be
+ * non-negative.
+ */
+ public EuiccCardErrorException(@OperationCode int operationCode, int errorCode) {
+ mOperationCode = operationCode;
+ mErrorCode = errorCode;
+ mErrorDetails = null;
+ }
+
+ /**
+ * Creates an exception with an error code and the error details in the response of an APDU
+ * command.
+ *
+ * @param errorCode The meaning of the code depends on each APDU command. It should always be
+ * non-negative.
+ * @param errorDetails The content of the details depends on each APDU command.
+ */
+ public EuiccCardErrorException(@OperationCode int operationCode, int errorCode,
+ @Nullable Asn1Node errorDetails) {
+ mOperationCode = operationCode;
+ mErrorCode = errorCode;
+ mErrorDetails = errorDetails;
+ }
+
+ /** @return The error code. The meaning of the code depends on each APDU command. */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /** @return The operation code. */
+ public int getOperationCode() {
+ return mOperationCode;
+ }
+
+ /** @return The error details. The meaning of the details depends on each APDU command. */
+ @Nullable
+ public Asn1Node getErrorDetails() {
+ return mErrorDetails;
+ }
+
+ @Override
+ public String getMessage() {
+ return "EuiccCardError: mOperatorCode=" + mOperationCode + ", mErrorCode=" + mErrorCode
+ + ", errorDetails=" + (mErrorDetails == null ? "null" : mErrorDetails.toHex());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardException.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardException.java
new file mode 100644
index 0000000..5b98955
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCardException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.uicc.euicc;
+
+/**
+ * The base exception class of all exceptions thrown by the methods of an {@link EuiccCard}
+ * instance.
+ */
+public class EuiccCardException extends Exception {
+ public EuiccCardException() {}
+
+ public EuiccCardException(String message) {
+ super(message);
+ }
+
+ public EuiccCardException(String message, Throwable throwable) {
+ super(message, throwable);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccSpecVersion.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccSpecVersion.java
new file mode 100644
index 0000000..b038716
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccSpecVersion.java
@@ -0,0 +1,144 @@
+/*
+ * 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.uicc.euicc;
+
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
+import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
+
+import java.util.Arrays;
+
+/**
+ * This represents the version of GSMA SGP.22 spec in the form of 3 numbers: major, minor, and
+ * revision.
+ */
+public final class EuiccSpecVersion implements Comparable<EuiccSpecVersion> {
+ private static final String LOG_TAG = "EuiccSpecVer";
+
+ // ASN.1 Tags
+ private static final int TAG_ISD_R_APP_TEMPLATE = 0xE0;
+ private static final int TAG_VERSION = 0x82;
+
+ private final int[] mVersionValues = new int[3];
+
+ /**
+ * Parses the response of opening a logical channel to get spec version of the eUICC card.
+ *
+ * @return Parsed spec version. If any error is encountered, null will be returned.
+ */
+ public static EuiccSpecVersion fromOpenChannelResponse(byte[] response) {
+ Asn1Node node;
+ try {
+ Asn1Decoder decoder = new Asn1Decoder(response);
+ if (!decoder.hasNextNode()) {
+ return null;
+ }
+ node = decoder.nextNode();
+ } catch (InvalidAsn1DataException e) {
+ Rlog.e(LOG_TAG, "Cannot parse the select response of ISD-R.", e);
+ return null;
+ }
+ try {
+ byte[] versionType;
+ if (node.getTag() == TAG_ISD_R_APP_TEMPLATE) {
+ versionType = node.getChild(TAG_VERSION).asBytes();
+ } else {
+ versionType =
+ node.getChild(TAG_ISD_R_APP_TEMPLATE, TAG_VERSION).asBytes();
+ }
+ if (versionType.length == 3) {
+ return new EuiccSpecVersion(versionType);
+ } else {
+ Rlog.e(LOG_TAG, "Cannot parse select response of ISD-R: " + node.toHex());
+ }
+ } catch (InvalidAsn1DataException | TagNotFoundException e) {
+ Rlog.e(LOG_TAG, "Cannot parse select response of ISD-R: " + node.toHex());
+ }
+ return null;
+ }
+
+ public EuiccSpecVersion(int major, int minor, int revision) {
+ mVersionValues[0] = major;
+ mVersionValues[1] = minor;
+ mVersionValues[2] = revision;
+ }
+
+ /**
+ * @param version The version bytes from ASN1 data. The length must be 3.
+ */
+ public EuiccSpecVersion(byte[] version) {
+ mVersionValues[0] = version[0] & 0xFF;
+ mVersionValues[1] = version[1] & 0xFF;
+ mVersionValues[2] = version[2] & 0xFF;
+ }
+
+ public int getMajor() {
+ return mVersionValues[0];
+ }
+
+ public int getMinor() {
+ return mVersionValues[1];
+ }
+
+ public int getRevision() {
+ return mVersionValues[2];
+ }
+
+ @Override
+ public int compareTo(EuiccSpecVersion that) {
+ if (getMajor() > that.getMajor()) {
+ return 1;
+ } else if (getMajor() < that.getMajor()) {
+ return -1;
+ }
+ if (getMinor() > that.getMinor()) {
+ return 1;
+ } else if (getMinor() < that.getMinor()) {
+ return -1;
+ }
+ if (getRevision() > that.getRevision()) {
+ return 1;
+ } else if (getRevision() < that.getRevision()) {
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ return Arrays.equals(mVersionValues, ((EuiccSpecVersion) obj).mVersionValues);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mVersionValues);
+ }
+
+ @Override
+ public String toString() {
+ return mVersionValues[0] + "." + mVersionValues[1] + "." + mVersionValues[2];
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/Tags.java b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
new file mode 100644
index 0000000..11c1cd2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
@@ -0,0 +1,108 @@
+/*
+ * 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.uicc.euicc;
+
+/**
+ * ASN1 tags used by {@link EuiccCard} implementation.
+ */
+class Tags {
+ // ASN.1 tags for commands
+ static final int TAG_GET_PROFILES = 0xBF2D;
+ static final int TAG_DISABLE_PROFILE = 0xBF32;
+ static final int TAG_ENABLE_PROFILE = 0xBF31;
+ static final int TAG_GET_EID = 0xBF3E;
+ static final int TAG_SET_NICKNAME = 0xBF29;
+ static final int TAG_DELETE_PROFILE = 0xBF33;
+ static final int TAG_GET_CONFIGURED_ADDRESSES = 0xBF3C;
+ static final int TAG_SET_DEFAULT_SMDP_ADDRESS = 0xBF3F;
+ static final int TAG_GET_RAT = 0xBF43;
+ static final int TAG_EUICC_MEMORY_RESET = 0xBF34;
+ static final int TAG_GET_EUICC_CHALLENGE = 0xBF2E;
+ static final int TAG_GET_EUICC_INFO_1 = 0xBF20;
+ static final int TAG_GET_EUICC_INFO_2 = 0xBF22;
+ static final int TAG_LIST_NOTIFICATION = 0xBF28;
+ static final int TAG_RETRIEVE_NOTIFICATIONS_LIST = 0xBF2B;
+ static final int TAG_REMOVE_NOTIFICATION_FROM_LIST = 0xBF30;
+ static final int TAG_AUTHENTICATE_SERVER = 0xBF38;
+ static final int TAG_PREPARE_DOWNLOAD = 0xBF21;
+ static final int TAG_INITIALISE_SECURE_CHANNEL = 0xBF23;
+
+ // Universal tags
+ static final int TAG_UNI_2 = 0x02;
+ static final int TAG_UNI_4 = 0x04;
+ // Context tags for primitive types
+ static final int TAG_CTX_0 = 0x80;
+ static final int TAG_CTX_1 = 0x81;
+ static final int TAG_CTX_2 = 0x82;
+ static final int TAG_CTX_3 = 0x83;
+ static final int TAG_CTX_4 = 0x84;
+ static final int TAG_CTX_5 = 0x85;
+ static final int TAG_CTX_6 = 0x86;
+ static final int TAG_CTX_7 = 0x87;
+ static final int TAG_CTX_8 = 0x88;
+ // Context tags for constructed (compound) types
+ static final int TAG_CTX_COMP_0 = 0xA0;
+ static final int TAG_CTX_COMP_1 = 0xA1;
+ static final int TAG_CTX_COMP_2 = 0xA2;
+ static final int TAG_CTX_COMP_3 = 0xA3;
+
+ // Command data tags
+ static final int TAG_PROFILE_INSTALLATION_RESULT = 0xBF37;
+ static final int TAG_PROFILE_INSTALLATION_RESULT_DATA = 0xBF27;
+ static final int TAG_NOTIFICATION_METADATA = 0xBF2F;
+ static final int TAG_SEQ = TAG_CTX_0;
+ static final int TAG_TARGET_ADDR = 0x0C;
+ static final int TAG_EVENT = TAG_CTX_1;
+ static final int TAG_CANCEL_SESSION = 0xBF41;
+ static final int TAG_PROFILE_INFO = 0xE3;
+ static final int TAG_TAG_LIST = 0x5C;
+ static final int TAG_EID = 0x5A;
+ static final int TAG_NICKNAME = 0x90;
+ static final int TAG_ICCID = 0x5A;
+ static final int TAG_PROFILE_STATE = 0x9F70;
+ static final int TAG_SERVICE_PROVIDER_NAME = 0x91;
+ static final int TAG_PROFILE_CLASS = 0x95;
+ static final int TAG_PROFILE_POLICY_RULE = 0x99;
+ static final int TAG_PROFILE_NAME = 0x92;
+ static final int TAG_OPERATOR_ID = 0xB7;
+ static final int TAG_CARRIER_PRIVILEGE_RULES = 0xBF76;
+
+ // Tags from the RefArDo data standard - https://source.android.com/devices/tech/config/uicc
+ static final int TAG_REF_AR_DO = 0xE2;
+ static final int TAG_REF_DO = 0xE1;
+ static final int TAG_DEVICE_APP_ID_REF_DO = 0xC1;
+ static final int TAG_PKG_REF_DO = 0xCA;
+ static final int TAG_AR_DO = 0xE3;
+ static final int TAG_PERM_AR_DO = 0xDB;
+
+ // TAG list for Euicc Profile
+ static final byte[] EUICC_PROFILE_TAGS = new byte[] {
+ TAG_ICCID,
+ (byte) TAG_NICKNAME,
+ (byte) TAG_SERVICE_PROVIDER_NAME,
+ (byte) TAG_PROFILE_NAME,
+ (byte) TAG_OPERATOR_ID,
+ (byte) (TAG_PROFILE_STATE / 256),
+ (byte) (TAG_PROFILE_STATE % 256),
+ (byte) TAG_PROFILE_CLASS,
+ (byte) TAG_PROFILE_POLICY_RULE,
+ (byte) (TAG_CARRIER_PRIVILEGE_RULES / 256),
+ (byte) (TAG_CARRIER_PRIVILEGE_RULES % 256),
+ };
+
+ private Tags() {}
+}
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
new file mode 100644
index 0000000..7a8978f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduCommand.java
@@ -0,0 +1,62 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+/**
+ * Parts of an APDU command.
+ *
+ * @hide
+ */
+class ApduCommand {
+ /** Channel of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int channel;
+
+ /** Class of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int cla;
+
+ /** Instruction of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int ins;
+
+ /** Parameter 1 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int p1;
+
+ /** Parameter 2 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int p2;
+
+ /** Parameter 3 of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final int p3;
+
+ /** Command data of an APDU as defined in GlobalPlatform Card Specification v.2.3. */
+ public final String cmdHex;
+
+ /** 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;
+ this.cla = cla;
+ this.ins = ins;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.p3 = p3;
+ this.cmdHex = cmdHex;
+ }
+
+ @Override
+ public String toString() {
+ return "ApduCommand(channel=" + channel + ", cla=" + cla + ", ins=" + ins + ", p1=" + p1
+ + ", p2=" + p2 + ", p3=" + p3 + ", cmd=" + cmdHex + ")";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduException.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduException.java
new file mode 100644
index 0000000..9c68d44
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduException.java
@@ -0,0 +1,63 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import android.telephony.IccOpenLogicalChannelResponse;
+
+/**
+ * The exception of failing to execute an APDU command. It can be caused by an error happening on
+ * opening the basic or logical channel, or the response of the APDU command is not success
+ * ({@link ApduSender#STATUS_NO_ERROR}).
+ *
+ * @hide
+ */
+public class ApduException extends Exception {
+ private final int mApduStatus;
+
+ /** Creates an exception with the apduStatus code of the response of an APDU command. */
+ public ApduException(int apduStatus) {
+ super();
+ mApduStatus = apduStatus;
+ }
+
+ public ApduException(String message) {
+ super(message);
+ mApduStatus = 0;
+ }
+
+ /**
+ * @return The error status of the response of an APDU command. An error status can be any
+ * positive 16-bit integer (i.e., SW1 & SW2) other than
+ * {@link ApduSender#STATUS_NO_ERROR} which means no error. For an error encountered
+ * when opening a logical channel before the APDU command gets sent, this is not the
+ * status defined in {@link IccOpenLogicalChannelResponse}. In this caes, 0 will be
+ * returned and the message of this exception will have the detailed error information.
+ */
+ public int getApduStatus() {
+ return mApduStatus;
+ }
+
+ /** @return The hex string of the error status. */
+ public String getStatusHex() {
+ return Integer.toHexString(mApduStatus);
+ }
+
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " (apduStatus=" + getStatusHex() + ")";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
new file mode 100644
index 0000000..05cc78c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -0,0 +1,253 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * This class sends a list of APDU commands to an AID on a UICC. A logical channel will be opened
+ * before sending and closed after all APDU commands are sent. The complete response of the last
+ * APDU command will be returned. If any APDU command returns an error status (other than
+ * {@link #STATUS_NO_ERROR}) or causing an exception, an {@link ApduException} will be returned
+ * immediately without sending the rest of commands. This class is thread-safe.
+ *
+ * @hide
+ */
+public class ApduSender {
+ private static final String LOG_TAG = "ApduSender";
+
+ // Parameter and response used by the command to get extra responses of an APDU command.
+ private static final int INS_GET_MORE_RESPONSE = 0xC0;
+ private static final int SW1_MORE_RESPONSE = 0x61;
+
+ // Status code of APDU response
+ private static final int STATUS_NO_ERROR = 0x9000;
+
+ private static void logv(String msg) {
+ Rlog.v(LOG_TAG, msg);
+ }
+
+ private final String mAid;
+ private final boolean mSupportExtendedApdu;
+ private final OpenLogicalChannelInvocation mOpenChannel;
+ private final CloseLogicalChannelInvocation mCloseChannel;
+ private final TransmitApduLogicalChannelInvocation mTransmitApdu;
+
+ // Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
+ // time for an AID.
+ private final Object mChannelLock = new Object();
+ private boolean mChannelOpened;
+
+ /**
+ * @param aid The AID that will be used to open a logical channel to.
+ */
+ public ApduSender(CommandsInterface ci, String aid, boolean supportExtendedApdu) {
+ mAid = aid;
+ mSupportExtendedApdu = supportExtendedApdu;
+ mOpenChannel = new OpenLogicalChannelInvocation(ci);
+ mCloseChannel = new CloseLogicalChannelInvocation(ci);
+ mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
+ }
+
+ /**
+ * Sends APDU commands.
+ *
+ * @param requestProvider Will be called after a logical channel is opened successfully. This is
+ * in charge of building a request with all APDU commands to be sent. This won't be called
+ * if any error happens when opening a logical channel.
+ * @param resultCallback Will be called after an error or the last APDU command has been
+ * executed. The result will be the full response of the last APDU command. Error will be
+ * returned as an {@link ApduException} exception.
+ * @param handler The handler that {@code requestProvider} and {@code resultCallback} will be
+ * executed on.
+ */
+ public void send(
+ RequestProvider requestProvider,
+ AsyncResultCallback<byte[]> resultCallback,
+ Handler handler) {
+ synchronized (mChannelLock) {
+ if (mChannelOpened) {
+ AsyncResultHelper.throwException(
+ new ApduException("Logical channel has already been opened."),
+ resultCallback, handler);
+ return;
+ }
+ mChannelOpened = true;
+ }
+
+ mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
+ @Override
+ public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
+ int channel = openChannelResponse.getChannel();
+ int status = openChannelResponse.getStatus();
+ if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
+ || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
+ synchronized (mChannelLock) {
+ mChannelOpened = false;
+ }
+ resultCallback.onException(
+ new ApduException("Failed to open logical channel opened for AID: "
+ + mAid + ", with status: " + status));
+ return;
+ }
+
+ RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
+ Throwable requestException = null;
+ try {
+ requestProvider.buildRequest(openChannelResponse.getSelectResponse(), builder);
+ } catch (Throwable e) {
+ requestException = e;
+ }
+ if (builder.getCommands().isEmpty() || requestException != null) {
+ // Just close the channel if we don't have commands to send or an error
+ // was encountered.
+ closeAndReturn(channel, null /* response */, requestException, resultCallback,
+ handler);
+ return;
+ }
+ sendCommand(builder.getCommands(), 0 /* index */, resultCallback, handler);
+ }
+ }, handler);
+ }
+
+ /**
+ * Sends the current command and then continue to send the next one. If this is the last
+ * command or any error happens, {@code resultCallback} will be called.
+ *
+ * @param commands All commands to be sent.
+ * @param index The current command index.
+ */
+ private void sendCommand(
+ List<ApduCommand> commands,
+ int index,
+ AsyncResultCallback<byte[]> resultCallback,
+ Handler handler) {
+ ApduCommand command = commands.get(index);
+ mTransmitApdu.invoke(command, new AsyncResultCallback<IccIoResult>() {
+ @Override
+ public void onResult(IccIoResult response) {
+ // A long response may need to be fetched by multiple following-up APDU
+ // commands. Makes sure that we get the complete response.
+ getCompleteResponse(command.channel, response, null /* responseBuilder */,
+ new AsyncResultCallback<IccIoResult>() {
+ @Override
+ public void onResult(IccIoResult fullResponse) {
+ logv("Full APDU response: " + fullResponse);
+
+ int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
+ if (status != STATUS_NO_ERROR) {
+ closeAndReturn(command.channel, null /* response */,
+ new ApduException(status), resultCallback, handler);
+ return;
+ }
+
+ // Last command
+ if (index == commands.size() - 1) {
+ closeAndReturn(command.channel, fullResponse.payload,
+ null /* exception */, resultCallback, handler);
+ return;
+ }
+
+ // Sends the next command
+ sendCommand(commands, index + 1, resultCallback, handler);
+ }
+ }, handler);
+ }
+ }, handler);
+ }
+
+ /**
+ * Gets the full response.
+ *
+ * @param lastResponse Will be checked to see if we need to fetch more.
+ * @param responseBuilder For continuously building the full response. It should not contain the
+ * last response. If it's null, a new builder will be created.
+ * @param resultCallback Error will be included in the result and no exception will be returned.
+ */
+ private void getCompleteResponse(
+ int channel,
+ IccIoResult lastResponse,
+ @Nullable ByteArrayOutputStream responseBuilder,
+ AsyncResultCallback<IccIoResult> resultCallback,
+ Handler handler) {
+ ByteArrayOutputStream resultBuilder =
+ responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
+ try {
+ resultBuilder.write(lastResponse.payload);
+ } catch (IOException e) {
+ // Should never reach here.
+ }
+ if (lastResponse.sw1 != SW1_MORE_RESPONSE) {
+ lastResponse.payload = resultBuilder.toByteArray();
+ resultCallback.onResult(lastResponse);
+ return;
+ }
+
+ mTransmitApdu.invoke(
+ new ApduCommand(channel, 0 /* cls */, INS_GET_MORE_RESPONSE, 0 /* p1 */,
+ 0 /* p2 */, lastResponse.sw2, "" /* cmdHex */),
+ new AsyncResultCallback<IccIoResult>() {
+ @Override
+ public void onResult(IccIoResult response) {
+ getCompleteResponse(
+ channel, response, resultBuilder, resultCallback, handler);
+ }
+ }, handler);
+ }
+
+ /**
+ * Closes the opened logical channel.
+ *
+ * @param response If {@code exception} is null, this will be returned to {@code resultCallback}
+ * after the channel has been closed.
+ * @param exception If not null, this will be returned to {@code resultCallback} after the
+ * channel has been closed.
+ */
+ private void closeAndReturn(
+ int channel,
+ @Nullable byte[] response,
+ @Nullable Throwable exception,
+ AsyncResultCallback<byte[]> resultCallback,
+ Handler handler) {
+ mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() {
+ @Override
+ public void onResult(Boolean aBoolean) {
+ synchronized (mChannelLock) {
+ mChannelOpened = false;
+ }
+
+ if (exception == null) {
+ resultCallback.onResult(response);
+ } else {
+ resultCallback.onException(exception);
+ }
+ }
+ }, handler);
+ }
+}
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
new file mode 100644
index 0000000..edd89d6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/CloseLogicalChannelInvocation.java
@@ -0,0 +1,61 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;
+
+/**
+ * Invokes {@link CommandsInterface#iccCloseLogicalChannel(int, Message)}. This takes a channel id
+ * (Integer) as the input and return a boolean to indicate if closing the logical channel is
+ * succeeded. No exception will be returned to the result callback.
+ *
+ * @hide
+ */
+class CloseLogicalChannelInvocation extends AsyncMessageInvocation<Integer, Boolean> {
+ private static final String LOG_TAG = "CloseChan";
+
+ private final CommandsInterface mCi;
+
+ CloseLogicalChannelInvocation(CommandsInterface ci) {
+ mCi = ci;
+ }
+
+ @Override
+ protected void sendRequestMessage(Integer channel, Message msg) {
+ Rlog.v(LOG_TAG, "Channel: " + channel);
+ mCi.iccCloseLogicalChannel(channel, msg);
+ }
+
+ @Override
+ protected Boolean parseResult(AsyncResult ar) {
+ if (ar.exception == null) {
+ return true;
+ }
+ if (ar.exception instanceof CommandException) {
+ Rlog.e(LOG_TAG, "CommandException", ar.exception);
+ } else {
+ Rlog.e(LOG_TAG, "Unknown exception", ar.exception);
+ }
+ return false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/OpenLogicalChannelInvocation.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/OpenLogicalChannelInvocation.java
new file mode 100644
index 0000000..15b0c43
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/OpenLogicalChannelInvocation.java
@@ -0,0 +1,93 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;
+
+/**
+ * Invokes {@link CommandsInterface#iccOpenLogicalChannel(String, int, Message)}. This takes AID
+ * (String) as the input and return the response of opening a logical channel. Error will be
+ * included in the {@link IccOpenLogicalChannelResponse} result and no exception will be returned to
+ * the result callback.
+ *
+ * @hide
+ */
+class OpenLogicalChannelInvocation
+ extends AsyncMessageInvocation<String, IccOpenLogicalChannelResponse> {
+ private static final String LOG_TAG = "OpenChan";
+
+ private final CommandsInterface mCi;
+
+ OpenLogicalChannelInvocation(CommandsInterface ci) {
+ mCi = ci;
+ }
+
+ @Override
+ protected void sendRequestMessage(String aid, Message msg) {
+ mCi.iccOpenLogicalChannel(aid, 0, msg);
+ }
+
+ @Override
+ protected IccOpenLogicalChannelResponse parseResult(AsyncResult ar) {
+ IccOpenLogicalChannelResponse openChannelResp;
+ // The code below is copied from PhoneInterfaceManager.java.
+ // TODO: move this code into IccOpenLogicalChannelResponse so that it can be shared.
+ if (ar.exception == null && ar.result != null) {
+ int[] result = (int[]) ar.result;
+ int channel = result[0];
+ byte[] selectResponse = null;
+ if (result.length > 1) {
+ selectResponse = new byte[result.length - 1];
+ for (int i = 1; i < result.length; ++i) {
+ selectResponse[i - 1] = (byte) result[i];
+ }
+ }
+ openChannelResp = new IccOpenLogicalChannelResponse(
+ channel, IccOpenLogicalChannelResponse.STATUS_NO_ERROR, selectResponse);
+ } else {
+ if (ar.result == null) {
+ Rlog.e(LOG_TAG, "Empty response");
+ }
+ if (ar.exception != null) {
+ Rlog.e(LOG_TAG, "Exception", ar.exception);
+ }
+
+ int errorCode = IccOpenLogicalChannelResponse.STATUS_UNKNOWN_ERROR;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.MISSING_RESOURCE) {
+ errorCode = IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE;
+ } else if (error == CommandException.Error.NO_SUCH_ELEMENT) {
+ errorCode = IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT;
+ }
+ }
+ openChannelResp = new IccOpenLogicalChannelResponse(
+ IccOpenLogicalChannelResponse.INVALID_CHANNEL, errorCode, null);
+ }
+
+ Rlog.v(LOG_TAG, "Response: " + openChannelResp);
+ return openChannelResp;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java
new file mode 100644
index 0000000..c2a63ee
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java
@@ -0,0 +1,101 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class to build a request for sending APDU commands. It contains a few convenient methods
+ * to add different APDU commands to the request.
+ *
+ * @hide
+ */
+public class RequestBuilder {
+ // The maximum bytes of the data of an APDU command. If more bytes need to be sent than this
+ // limit, we need to separate them into multiple commands.
+ private static final int MAX_APDU_DATA_LEN = 0xFF;
+ // The maximum bytes of the data of an extended APDU command. If more bytes need to be sent than
+ // this limit, we need to separate them into multiple commands.
+ private static final int MAX_EXT_APDU_DATA_LEN = 0xFFFF;
+
+ // Parameters used by STORE DATA command sent to ISD-R, defined by SGP.22 v2.0.
+ private static final int CLA_STORE_DATA = 0x80;
+ private static final int INS_STORE_DATA = 0xE2;
+ private static final int P1_STORE_DATA_INTERM = 0x11;
+ private static final int P1_STORE_DATA_END = 0x91;
+
+ private final int mChannel;
+ private final int mMaxApduDataLen;
+ private final List<ApduCommand> mCommands = new ArrayList<>();
+
+ /**
+ * Adds an APDU command by specifying every parts. The parameters are defined as in
+ * GlobalPlatform Card Specification v.2.3.
+ */
+ public void addApdu(int cla, int ins, int p1, int p2, int p3, String cmdHex) {
+ mCommands.add(new ApduCommand(mChannel, cla, ins, p1, p2, p3, cmdHex));
+ }
+
+ /**
+ * Adds an APDU command with given command data. P3 will be the length of the command data
+ * bytes. The parameters are defined as in GlobalPlatform Card Specification v.2.3.
+ */
+ public void addApdu(int cla, int ins, int p1, int p2, String cmdHex) {
+ mCommands.add(new ApduCommand(mChannel, cla, ins, p1, p2, cmdHex.length() / 2, cmdHex));
+ }
+
+ /**
+ * Adds an APDU command with empty command data. The parameters are defined as in GlobalPlatform
+ * Card Specification v.2.3.
+ */
+ public void addApdu(int cla, int ins, int p1, int p2) {
+ mCommands.add(new ApduCommand(mChannel, cla, ins, p1, p2, 0, ""));
+ }
+
+ /**
+ * Adds a STORE DATA command. Long command length of which is larger than {@link
+ * #mMaxApduDataLen} will be automatically split into multiple ones.
+ *
+ * @param cmdHex The STORE DATA command in a hex string as defined in GlobalPlatform Card
+ * Specification v.2.3.
+ */
+ public void addStoreData(String cmdHex) {
+ final int cmdLen = mMaxApduDataLen * 2;
+ int startPos = 0;
+ int totalLen = cmdHex.length() / 2;
+ int totalSubCmds = totalLen == 0 ? 1 : (totalLen + mMaxApduDataLen - 1) / mMaxApduDataLen;
+ for (int i = 1; i < totalSubCmds; ++i) {
+ String data = cmdHex.substring(startPos, startPos + cmdLen);
+ addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_INTERM, i - 1, mMaxApduDataLen,
+ data);
+ startPos += cmdLen;
+ }
+ String data = cmdHex.substring(startPos);
+ addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_END, totalSubCmds - 1,
+ totalLen % mMaxApduDataLen, data);
+ }
+
+ List<ApduCommand> getCommands() {
+ return mCommands;
+ }
+
+ RequestBuilder(int channel, boolean supportExtendedApdu) {
+ mChannel = channel;
+ mMaxApduDataLen = supportExtendedApdu ? MAX_EXT_APDU_DATA_LEN : MAX_APDU_DATA_LEN;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestProvider.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestProvider.java
new file mode 100644
index 0000000..e3f6bf7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+/**
+ * Request provider provides a request via the given request builder. This interface allows the
+ * caller of {@link ApduSender} to build different requests based on the response of opening a
+ * logical channel.
+ *
+ * @hide
+ */
+public interface RequestProvider {
+ /**
+ * Builds a request based on the response of opening the logical channel.
+ *
+ * @param selectResponse Response of selecting the channel.
+ * @throws Throwable If an exception is thrown, the result callback {@code onException} will
+ * be called to return the exception and no commands will be sent.
+ */
+ void buildRequest(byte[] selectResponse, RequestBuilder requestBuilder) throws Throwable;
+}
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
new file mode 100644
index 0000000..296fe47
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/TransmitApduLogicalChannelInvocation.java
@@ -0,0 +1,73 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.euicc.async.AsyncMessageInvocation;
+
+/**
+ * Invokes {@link CommandsInterface#iccTransmitApduLogicalChannel(int, int, int, int, int, int,
+ * String, Message)}. This takes an APDU command as the input and return the response. The status of
+ * returned response will be 0x6F00 if any error happens. No exception will be returned to the
+ * result callback.
+ *
+ * @hide
+ */
+public class TransmitApduLogicalChannelInvocation
+ extends AsyncMessageInvocation<ApduCommand, IccIoResult> {
+ private static final String LOG_TAG = "TransApdu";
+ private static final int SW1_ERROR = 0x6F;
+
+ private final CommandsInterface mCi;
+
+ TransmitApduLogicalChannelInvocation(CommandsInterface ci) {
+ mCi = ci;
+ }
+
+ @Override
+ 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);
+ }
+
+ @Override
+ protected IccIoResult parseResult(AsyncResult ar) {
+ IccIoResult response;
+ if (ar.exception == null && ar.result != null) {
+ response = (IccIoResult) ar.result;
+ } else {
+ if (ar.result == null) {
+ Rlog.e(LOG_TAG, "Empty response");
+ } else if (ar.exception instanceof CommandException) {
+ Rlog.e(LOG_TAG, "CommandException", ar.exception);
+ } else {
+ Rlog.e(LOG_TAG, "CommandException", ar.exception);
+ }
+ response = new IccIoResult(SW1_ERROR, 0 /* sw2 */, (byte[]) null /* payload */);
+ }
+
+ Rlog.v(LOG_TAG, "Response: " + response);
+ return response;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncMessageInvocation.java b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncMessageInvocation.java
new file mode 100644
index 0000000..61761cc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncMessageInvocation.java
@@ -0,0 +1,72 @@
+/*
+ * 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.uicc.euicc.async;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+
+/**
+ * This class wraps an invocation to an asynchronous method using {@link Message} to be working with
+ * {@link AsyncResultCallback}. With this class, you can use callbacks instead of managing a state
+ * machine to complete a task relying on multiple asynchronous method calls.
+ *
+ * <p>Subclasses should override the abstract methods to invoke the actual asynchronous method and
+ * parse the returned result.
+ *
+ * @param <Request> Class of the request data.
+ * @param <Response> Class of the response data.
+ *
+ * @hide
+ */
+public abstract class AsyncMessageInvocation<Request, Response> implements Handler.Callback {
+ /**
+ * Executes an invocation.
+ *
+ * @param request The request to be sent with the invocation.
+ * @param resultCallback Will be called after result is returned.
+ * @param handler The handler that {@code resultCallback} will be executed on.
+ */
+ public final void invoke(
+ Request request, AsyncResultCallback<Response> resultCallback, Handler handler) {
+ Handler h = new Handler(handler.getLooper(), this);
+ sendRequestMessage(request, h.obtainMessage(0, resultCallback));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean handleMessage(Message msg) {
+ AsyncResult result = (AsyncResult) msg.obj;
+ AsyncResultCallback<Response> resultCallback =
+ (AsyncResultCallback<Response>) result.userObj;
+ try {
+ resultCallback.onResult(parseResult(result));
+ } catch (Throwable t) {
+ resultCallback.onException(t);
+ }
+ return true;
+ }
+
+ /**
+ * Calls the asynchronous method with the given {@code msg}. The implementation should convert
+ * the given {@code request} to the parameters of the asynchronous method.
+ */
+ protected abstract void sendRequestMessage(Request request, Message msg);
+
+ /** Parses the asynchronous result returned by the method to a {@link Response}. */
+ protected abstract Response parseResult(AsyncResult result) throws Throwable;
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java
new file mode 100644
index 0000000..6e93687
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java
@@ -0,0 +1,46 @@
+/*
+ * 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.uicc.euicc.async;
+
+/**
+ * Class to deliver the returned value from an asynchronous call. Either {@link #onResult(Result)}
+ * or {@link #onException(Throwable)} will be called. You can create an anonymous subclass and
+ * override these methods to handle the result or the throwable from an asynchronous call, for
+ * example:
+ *
+ * <pre>
+ * doSomethingAsync(
+ * new AsyncResultCallback<Result>() {
+ * void onResult(Result r) {
+ * Log.i("Got the result: %s", r.toString());
+ * }
+ *
+ * void onException(Throwable e) {...}
+ * });
+ * <pre>
+ *
+ * @param <Result> The returned value of the asynchronous call.
+ * @hide
+ */
+public abstract class AsyncResultCallback<Result> {
+
+ /** This will be called when the result is returned. */
+ public abstract void onResult(Result result);
+
+ /** This will be called when any exception is thrown. */
+ public void onException(Throwable e) {}
+}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultHelper.java b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultHelper.java
new file mode 100644
index 0000000..d677ed8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultHelper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.uicc.euicc.async;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+
+/**
+ * Helper on {@link AsyncResultCallback}.
+ *
+ * @hide
+ */
+public final class AsyncResultHelper {
+ /**
+ * Calls the {@code callback} to return the {@code result} object. The {@code callback} will be
+ * run in the {@code handler}. If the {@code handler} is null, the callback will be called
+ * immediately.
+ *
+ * @param <T> Result type.
+ */
+ public static <T> void returnResult(
+ final T result, final AsyncResultCallback<T> callback, @Nullable Handler handler) {
+ if (handler == null) {
+ callback.onResult(result);
+ } else {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onResult(result);
+ }
+ });
+ }
+ }
+
+ /**
+ * Calls the {@code callback} to return the thrown {@code e} exception. The {@code callback}
+ * will be run in the {@code handler}. If the {@code handler} is null, the callback will be
+ * called immediately.
+ */
+ public static void throwException(
+ final Throwable e, final AsyncResultCallback<?> callback, @Nullable Handler handler) {
+ if (handler == null) {
+ callback.onException(e);
+ } else {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onException(e);
+ }
+ });
+ }
+ }
+
+ private AsyncResultHelper() {}
+}
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
index 88ff4cf..3f6da54 100644
--- a/src/java/com/android/internal/telephony/util/NotificationChannelController.java
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -38,12 +38,12 @@
*/
public static final String CHANNEL_ID_ALERT = "alert";
public static final String CHANNEL_ID_CALL_FORWARD = "callForward";
- public static final String CHANNEL_ID_MOBILE_DATA_ALERT = "mobileDataAlertNew";
+ public static final String CHANNEL_ID_MOBILE_DATA_STATUS = "mobileDataAlertNew";
public static final String CHANNEL_ID_SMS = "sms";
public static final String CHANNEL_ID_VOICE_MAIL = "voiceMail";
public static final String CHANNEL_ID_WFC = "wfc";
- /** deprecated channel, replaced with @see #CHANNEL_ID_MOBILE_DATA_ALERT */
+ /** deprecated channel, replaced with @see #CHANNEL_ID_MOBILE_DATA_STATUS */
private static final String CHANNEL_ID_MOBILE_DATA_ALERT_DEPRECATED = "mobileDataAlert";
/**
@@ -57,22 +57,29 @@
NotificationManager.IMPORTANCE_DEFAULT);
alertChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+ // allow users to block notifications from system
+ alertChannel.setBlockableSystem(true);
+
+ final NotificationChannel mobileDataStatusChannel = new NotificationChannel(
+ CHANNEL_ID_MOBILE_DATA_STATUS,
+ context.getText(R.string.notification_channel_mobile_data_status),
+ NotificationManager.IMPORTANCE_LOW);
+ // allow users to block notifications from system
+ mobileDataStatusChannel.setBlockableSystem(true);
context.getSystemService(NotificationManager.class)
.createNotificationChannels(Arrays.asList(
new NotificationChannel(CHANNEL_ID_CALL_FORWARD,
context.getText(R.string.notification_channel_call_forward),
NotificationManager.IMPORTANCE_LOW),
- new NotificationChannel(CHANNEL_ID_MOBILE_DATA_ALERT,
- context.getText(R.string.notification_channel_mobile_data_alert),
- NotificationManager.IMPORTANCE_LOW),
new NotificationChannel(CHANNEL_ID_SMS,
context.getText(R.string.notification_channel_sms),
NotificationManager.IMPORTANCE_HIGH),
new NotificationChannel(CHANNEL_ID_WFC,
context.getText(R.string.notification_channel_wfc),
NotificationManager.IMPORTANCE_LOW),
- alertChannel));
+ alertChannel,
+ mobileDataStatusChannel));
// only for update
if (getChannel(CHANNEL_ID_VOICE_MAIL, context) != null) {
migrateVoicemailNotificationSettings(context);
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
new file mode 100644
index 0000000..17f2611
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -0,0 +1,276 @@
+/*
+ * 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.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.SmsHeader;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+
+/**
+ * Utilities used by {@link com.android.internal.telephony.SMSDispatcher}'s subclasses.
+ *
+ * These methods can not be moved to {@link CdmaSMSDispatcher} and {@link GsmSMSDispatcher} because
+ * they also need to be called from {@link ImsSmsDispatcher} and the utilities will invoke the cdma
+ * or gsm version of the method based on the format.
+ */
+public final class SMSDispatcherUtil {
+ // Prevent instantiation.
+ private SMSDispatcherUtil() {}
+
+ /**
+ * 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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+ String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader) {
+ if (isCdma) {
+ return getSubmitPduCdma(scAddr, destAddr, message, statusReportRequested, smsHeader);
+ } else {
+ return getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested);
+ }
+ }
+
+ /**
+ * 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.
+ * @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) {
+ if (isCdma) {
+ return getSubmitPduCdma(scAddr, destAddr, message, statusReportRequested, smsHeader,
+ priority);
+ } else {
+ return getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+ validityPeriod);
+ }
+ }
+
+ /**
+ * Gsm implementation for
+ * {@link #getSubmitPdu(boolean, String, String, String, boolean)}
+ *
+ * @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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+ String message, boolean statusReportRequested) {
+ return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr, message,
+ statusReportRequested);
+ }
+
+ /**
+ * 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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, int validityPeriod) {
+ return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr, message,
+ statusReportRequested, validityPeriod);
+ }
+
+ /**
+ * Cdma implementation for
+ * {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
+ *
+ * @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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduCdma(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader) {
+ return com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddr, destAddr,
+ message, statusReportRequested, smsHeader);
+ }
+
+ /**
+ * Cdma implementation for
+ * {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
+ *
+ * @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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPduCdma(String scAddr, String destAddr,
+ String message, boolean statusReportRequested, SmsHeader smsHeader, int priority) {
+ return com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddr, destAddr,
+ message, statusReportRequested, smsHeader, priority);
+ }
+
+ /**
+ * 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.
+ * @return the submit pdu.
+ */
+ public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+ String destAddr, int destPort, byte[] message, boolean statusReportRequested) {
+ if (isCdma) {
+ return getSubmitPduCdma(scAddr, destAddr, destPort, message, statusReportRequested);
+ } else {
+ return getSubmitPduGsm(scAddr, destAddr, destPort, message, statusReportRequested);
+ }
+ }
+
+ /**
+ * Cdma 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 getSubmitPduCdma(String scAddr, String destAddr,
+ int destPort, byte[] message, boolean statusReportRequested) {
+ return com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddr, destAddr,
+ destPort, message, statusReportRequested);
+ }
+
+ /**
+ * 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) {
+ return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr,
+ destPort, message, statusReportRequested);
+
+ }
+
+ /**
+ * Calculate the number of septets needed to encode the message. This function should only be
+ * called for individual segments of multipart message.
+ *
+ * @param isCdma true if cdma format should be used.
+ * @param messageBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
+ */
+ public static TextEncodingDetails calculateLength(boolean isCdma, CharSequence messageBody,
+ boolean use7bitOnly) {
+ if (isCdma) {
+ return calculateLengthCdma(messageBody, use7bitOnly);
+ } else {
+ return calculateLengthGsm(messageBody, use7bitOnly);
+ }
+ }
+
+ /**
+ * Gsm implementation for {@link #calculateLength(boolean, CharSequence, boolean)}
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
+ */
+ public static TextEncodingDetails calculateLengthGsm(CharSequence messageBody,
+ boolean use7bitOnly) {
+ return com.android.internal.telephony.gsm.SmsMessage.calculateLength(messageBody,
+ use7bitOnly);
+
+ }
+
+ /**
+ * Cdma implementation for {@link #calculateLength(boolean, CharSequence, boolean)}
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
+ */
+ public static TextEncodingDetails calculateLengthCdma(CharSequence messageBody,
+ boolean use7bitOnly) {
+ return com.android.internal.telephony.cdma.SmsMessage.calculateLength(messageBody,
+ use7bitOnly, false);
+ }
+}
diff --git a/src/java/com/google/android/mms/pdu/PduComposer.java b/src/java/com/google/android/mms/pdu/PduComposer.java
index bb44bab..582caec 100644
--- a/src/java/com/google/android/mms/pdu/PduComposer.java
+++ b/src/java/com/google/android/mms/pdu/PduComposer.java
@@ -155,7 +155,8 @@
/* make the message */
switch (type) {
case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- if (makeSendReqPdu() != PDU_COMPOSE_SUCCESS) {
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
return null;
}
break;
@@ -564,6 +565,7 @@
case PduHeaders.PRIORITY:
case PduHeaders.DELIVERY_REPORT:
case PduHeaders.READ_REPORT:
+ case PduHeaders.RETRIEVE_STATUS:
int octet = mPduHeader.getOctet(field);
if (0 == octet) {
return PDU_COMPOSE_FIELD_NOT_SET;
@@ -584,6 +586,7 @@
break;
case PduHeaders.SUBJECT:
+ case PduHeaders.RETRIEVE_TEXT:
EncodedStringValue enString =
mPduHeader.getEncodedStringValue(field);
if (null == enString) {
@@ -757,7 +760,7 @@
/**
* Make Send.req.
*/
- private int makeSendReqPdu() {
+ private int makeSendRetrievePdu(int type) {
if (mMessage == null) {
mMessage = new ByteArrayOutputStream();
mPosition = 0;
@@ -765,7 +768,7 @@
// X-Mms-Message-Type
appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ appendOctet(type);
// X-Mms-Transaction-ID
appendOctet(PduHeaders.TRANSACTION_ID);
@@ -831,17 +834,25 @@
// X-Mms-Read-Report Optional
appendHeader(PduHeaders.READ_REPORT);
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ // X-Mms-Retrieve-Status Optional
+ appendHeader(PduHeaders.RETRIEVE_STATUS);
+ // X-Mms-Retrieve-Text Optional
+ appendHeader(PduHeaders.RETRIEVE_TEXT);
+ }
+
+
// Content-Type
appendOctet(PduHeaders.CONTENT_TYPE);
// Message body
- return makeMessageBody();
+ return makeMessageBody(type);
}
/**
* Make message body.
*/
- private int makeMessageBody() {
+ private int makeMessageBody(int type) {
// 1. add body informations
mStack.newbuf(); // Switching buffer because we need to
@@ -858,7 +869,12 @@
appendShortInteger(contentTypeIdentifier.intValue());
// content-type parameter: start
- PduBody body = ((SendReq) mPdu).getBody();
+ PduBody body;
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ body = ((RetrieveConf) mPdu).getBody();
+ } else {
+ body = ((SendReq) mPdu).getBody();
+ }
if (null == body || body.getPartsNum() == 0) {
// empty message
appendUintvarInteger(0);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index fd2cbb2..6709d46 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -7,7 +7,7 @@
#LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := guava \
frameworks-base-testutils \
mockito-target-minus-junit4 \
@@ -16,6 +16,7 @@
legacy-android-test
LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/tests/telephonytests/AndroidManifest.xml b/tests/telephonytests/AndroidManifest.xml
index 028b1e0..35ed2fb 100644
--- a/tests/telephonytests/AndroidManifest.xml
+++ b/tests/telephonytests/AndroidManifest.xml
@@ -38,5 +38,6 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
-
+ <uses-permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"/>
+ <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
</manifest>
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java b/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java
new file mode 100644
index 0000000..caa245f
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsCompatTests.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.telephony.ims;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.internal.IImsMMTelFeature;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsCompatTests {
+
+ TestMMTelFeatureCompat mCompatMmTel;
+ IImsMMTelFeature mCompatMmTelBinder;
+
+ TestImsCallSessionCompat mCompatCallSession;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCompatMmTel = new TestMMTelFeatureCompat();
+ mCompatMmTelBinder = mCompatMmTel.getBinder();
+
+ mCompatCallSession = new TestImsCallSessionCompat();
+ }
+
+ @After
+ public void tearDown() {
+ mCompatMmTel = null;
+ mCompatMmTelBinder = null;
+
+ mCompatCallSession = null;
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateCallSessionCompat() throws Exception {
+ mCompatMmTelBinder.createCallSession(0, new ImsCallProfile());
+ assertTrue(mCompatMmTel.isCreateCallSessionCalled);
+ }
+
+ @SmallTest
+ @Test
+ public void testListenerCompatLayer() throws Exception {
+ IImsCallSessionListener listener = mock(IImsCallSessionListener.class);
+ ImsReasonInfo info = new ImsReasonInfo();
+ mCompatCallSession.setListener(listener);
+
+ mCompatCallSession.mListener.callSessionTerminated(null, info);
+
+ verify(listener).callSessionTerminated(eq(info));
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index acd4e19..07d3223 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,8 +16,18 @@
package android.telephony.ims;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -27,94 +37,198 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
@RunWith(AndroidJUnit4.class)
public class ImsFeatureTest {
- private TestImsFeature mTestImsService;
+ private TestImsFeature mTestImsFeature;
+ private ImsFeature.CapabilityCallback mCapabilityCallback;
@Mock
private IImsFeatureStatusCallback mTestStatusCallback;
@Mock
private IImsFeatureStatusCallback mTestStatusCallback2;
- @Mock
- private ImsFeature.INotifyFeatureRemoved mTestRemovedCallback;
-
- private class TestImsFeature extends ImsFeature {
-
- public boolean featureRemovedCalled = false;
-
- @Override
- public void onFeatureRemoved() {
- featureRemovedCalled = true;
- }
-
- public void testSetFeatureState(int featureState) {
- setFeatureState(featureState);
- }
- }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTestImsService = new TestImsFeature();
+ mTestImsFeature = new TestImsFeature();
+ mCapabilityCallback = Mockito.spy(new ImsFeature.CapabilityCallback());
+ mTestImsFeature.addCapabilityCallback(mCapabilityCallback);
}
@After
public void tearDown() {
- mTestImsService = null;
+ mTestImsFeature = null;
+ mCapabilityCallback = null;
}
@Test
@SmallTest
public void testSetCallbackAndNotify() throws Exception {
- mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
- mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+ mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
+ mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
- verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
- verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
+ verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
+ verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_UNAVAILABLE));
}
@Test
@SmallTest
public void testSetFeatureAndCheckCallback() throws Exception {
- mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
- mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+ mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback);
+ mTestImsFeature.addImsFeatureStatusCallback(mTestStatusCallback2);
- mTestImsService.testSetFeatureState(ImsFeature.STATE_READY);
+ mTestImsFeature.testSetFeatureState(ImsFeature.STATE_READY);
verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
- assertEquals(ImsFeature.STATE_READY, mTestImsService.getFeatureState());
+ assertEquals(ImsFeature.STATE_READY, mTestImsFeature.getFeatureState());
}
- @Test
@SmallTest
- public void testRegisterAndNotifyRemoveFeature() {
- mTestImsService.addFeatureRemovedListener(mTestRemovedCallback);
+ @Test
+ public void testCapabilityConfigAdd() throws Exception {
+ ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
- mTestImsService.notifyFeatureRemoved(0);
-
- verify(mTestRemovedCallback).onFeatureRemoved(eq(0));
- assertTrue(mTestImsService.featureRemovedCalled);
+ assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_1));
}
- @Test
@SmallTest
- public void testRegisterAndUnregisterNotify() {
- mTestImsService.addFeatureRemovedListener(mTestRemovedCallback);
- mTestImsService.removeFeatureRemovedListener(mTestRemovedCallback);
+ @Test
+ public void testCapabilityConfigAddMultiple() throws Exception {
+ ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
- mTestImsService.notifyFeatureRemoved(0);
+ assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
+ }
- verify(mTestRemovedCallback, never()).onFeatureRemoved(eq(0));
- assertTrue(mTestImsService.featureRemovedCalled);
+ @SmallTest
+ @Test
+ public void testCapabilityConfigHasMultiple() throws Exception {
+ ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+ assertTrue(c.isCapable(
+ TestImsFeature.CAPABILITY_TEST_1 | TestImsFeature.CAPABILITY_TEST_2));
+ }
+
+ @SmallTest
+ @Test
+ public void testCapabilityConfigRemove() throws Exception {
+ ImsFeature.Capabilities c = new ImsFeature.Capabilities();
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+ c.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+ c.removeCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+
+ assertTrue(c.isCapable(TestImsFeature.CAPABILITY_TEST_2));
+ }
+
+ @SmallTest
+ @Test
+ public void testSetCapabilityConfig() throws Exception {
+ CapabilityChangeRequest request = new CapabilityChangeRequest();
+ request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+ mTestImsFeature.requestChangeEnabledCapabilities(request, null);
+
+ assertEquals(request, mTestImsFeature.lastRequest);
+ }
+
+
+ @SmallTest
+ @Test
+ public void testSetCapabilityConfigError() throws Exception {
+ CapabilityChangeRequest request = new CapabilityChangeRequest();
+ request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+ mTestImsFeature.setCapabilitiesResult = ImsFeature.CAPABILITY_ERROR_GENERIC;
+ mTestImsFeature.requestChangeEnabledCapabilities(request, mCapabilityCallback);
+
+ verify(mCapabilityCallback).onChangeCapabilityConfigurationError(
+ eq(TestImsFeature.CAPABILITY_TEST_1),
+ eq(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN),
+ eq(ImsFeature.CAPABILITY_ERROR_GENERIC));
+ assertEquals(request, mTestImsFeature.lastRequest);
+ }
+
+ @SmallTest
+ @Test
+ public void testNotifyCapabilityStatusChanged() throws Exception {
+ ImsFeature.Capabilities status =
+ new ImsFeature.Capabilities();
+ status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+ status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+ mTestImsFeature.capabilitiesStatusChanged(status);
+
+ assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
+ }
+
+ @SmallTest
+ @Test
+ public void testNotifyCapabilityStatusChangedCallback() throws Exception {
+ ImsFeature.Capabilities status =
+ new ImsFeature.Capabilities();
+ status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+ status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+
+ mTestImsFeature.capabilitiesStatusChanged(status);
+
+ assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
+ verify(mCapabilityCallback).onCapabilitiesStatusChanged(eq(status));
+ }
+
+ @SmallTest
+ @Test
+ public void testCapabilityChangeContainsFullSets() throws Exception {
+ CapabilityChangeRequest request = new CapabilityChangeRequest();
+ request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1
+ | TestImsFeature.CAPABILITY_TEST_2,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_2,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ request.addCapabilitiesToDisableForTech(TestImsFeature.CAPABILITY_TEST_1,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+ mTestImsFeature.changeEnabledCapabilities(request, /*Callback*/null);
+
+ assertTrue(request.getCapabilitiesToDisable().containsAll(
+ mTestImsFeature.lastRequest.getCapabilitiesToDisable()));
+ assertTrue(request.getCapabilitiesToEnable().containsAll(
+ mTestImsFeature.lastRequest.getCapabilitiesToEnable()));
+ }
+
+ @SmallTest
+ @Test
+ public void testCapabilityChangeRequestParcel() throws Exception {
+ CapabilityChangeRequest request = new CapabilityChangeRequest();
+ // add some capabilities
+ request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ request.addCapabilitiesToEnableForTech(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ request.addCapabilitiesToDisableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+ Parcel p = Parcel.obtain();
+ request.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ CapabilityChangeRequest result =
+ CapabilityChangeRequest.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ assertEquals(request, result);
}
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
new file mode 100644
index 0000000..3810f76
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
@@ -0,0 +1,231 @@
+/*
+ * 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 android.telephony.ims;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ServiceState;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsRegistrationTests {
+
+ @Spy private IImsRegistrationCallback.Stub mCallback;
+ @Spy private IImsRegistrationCallback.Stub mCallback2;
+ private ImsRegistrationImplBase mRegistration;
+ private IImsRegistration mRegBinder;
+
+ @Before
+ public void setup() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mRegistration = new ImsRegistrationImplBase();
+ mRegBinder = mRegistration.getBinder();
+ mRegBinder.addRegistrationCallback(mCallback);
+ }
+
+ @After
+ public void tearDown() {
+ mRegistration = null;
+ mRegBinder = null;
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationConfigParcel() {
+ ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .build();
+ Parcel p = Parcel.obtain();
+ testConfig.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ImsFeatureConfiguration result =
+ ImsFeatureConfiguration.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ assertEquals(testConfig, result);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationConfigPermutationEqual() {
+ ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .build();
+
+ // Permute field insertion ordering to ensure order doesn't matter.
+ ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .build();
+
+ assertEquals(testConfig, testConfig2);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationConfigConstructorsEqual() {
+ // Permute field insertion ordering to ensure order doesn't matter.
+ ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .build();
+
+ // Permute field insertion ordering to ensure order doesn't matter.
+ ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .build();
+
+ assertEquals(testConfig, testConfig2);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnRegistered() throws RemoteException {
+ mRegistration.onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+
+ verify(mCallback).onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnRegistering() throws RemoteException {
+ mRegistration.onRegistering(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+
+ verify(mCallback).onRegistering(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnDeregistered() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo();
+ mRegistration.onDeregistered(info);
+
+ verify(mCallback).onDeregistered(eq(info));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackOnTechChangeFailed() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo();
+ mRegistration.onTechnologyChangeFailed(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ info);
+
+ verify(mCallback).onTechnologyChangeFailed(
+ eq(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN), eq(info));
+ }
+
+ @SmallTest
+ @Test
+ public void testSubscriberUrisChanged() throws RemoteException {
+ Uri[] uris = new Uri[1];
+ uris[0] = Uri.fromParts("tel", "5555551212", null);
+
+ mRegistration.onSubscriberAssociatedUriChanged(uris);
+
+ verify(mCallback).onSubscriberAssociatedUriChanged(eq(uris));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackAfterUnregistered() throws RemoteException {
+ mRegBinder.removeRegistrationCallback(mCallback);
+
+ mRegistration.onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+
+ verify(mCallback, never()).onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackSendCurrentState() throws RemoteException {
+ mRegistration.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+ mRegBinder.addRegistrationCallback(mCallback2);
+
+ verify(mCallback2).onRegistered(eq(ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackGetRegistrationTech() throws RemoteException {
+ mRegistration.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+ assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ mRegBinder.getRegistrationTechnology());
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackSendCurrentStateDisconnected() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, 0);
+ mRegistration.onDeregistered(info);
+
+ mRegBinder.addRegistrationCallback(mCallback2);
+
+ // The original callback that has been registered should get LTE tech in disconnected
+ // message
+ verify(mCallback).onDeregistered(eq(info));
+ // A callback that has just been registered should get NONE for tech in disconnected
+ // message
+ verify(mCallback2).onDeregistered(eq(info));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackGetRegistrationTechDisconnected() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, 0);
+
+ mRegistration.onDeregistered(info);
+
+ verify(mCallback).onDeregistered(eq(info));
+ assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
+ mRegBinder.getRegistrationTechnology());
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackNoCallbackIfUnknown() throws RemoteException {
+ mRegBinder.addRegistrationCallback(mCallback2);
+ // Verify that if we have never set the registration state, we do not callback immediately
+ // with onDeregistered.
+ verify(mCallback2, never()).onDeregistered(any(ImsReasonInfo.class));
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
index 8887027..34febcb 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,49 +16,36 @@
package android.telephony.ims;
-import static android.Manifest.permission.MODIFY_PHONE_STATE;
-import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-
-import static com.android.internal.telephony.ims.ImsResolver.SERVICE_INTERFACE;
-
import static junit.framework.Assert.assertEquals;
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.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.nullable;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.RemoteException;
-import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseArray;
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsServiceController;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Unit tests for ImsService
@@ -72,21 +59,22 @@
private TestImsService mTestImsService;
private IImsServiceController mTestImsServiceBinder;
- @Mock
private Context mMockContext;
- @Mock
private IImsFeatureStatusCallback mTestCallback;
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ mMockContext = mock(Context.class);
+ mTestCallback = mock(IImsFeatureStatusCallback.class);
mTestImsService = new TestImsService(mMockContext);
mTestImsServiceBinder = (IImsServiceController) mTestImsService.onBind(
- new Intent(SERVICE_INTERFACE));
+ new Intent(ImsService.SERVICE_INTERFACE));
}
@After
public void tearDown() throws Exception {
+ mMockContext = null;
+ mTestCallback = null;
mTestImsService = null;
mTestImsServiceBinder = null;
}
@@ -94,88 +82,46 @@
@Test
@SmallTest
public void testCreateMMTelFeature() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
- when(mTestImsService.mSpyMMTelFeature.getFeatureState()).thenReturn(
- ImsFeature.STATE_READY);
+ IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
+ mTestImsService.mTestMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
- SparseArray<ImsFeature> features = mTestImsService.getImsFeatureMap(TEST_SLOT_0);
- assertEquals(mTestImsService.mSpyMMTelFeature,
- mTestImsService.getImsFeatureFromType(features, ImsFeature.MMTEL));
+ SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
+ ImsFeature featureToVerify = features.get(ImsFeature.FEATURE_MMTEL);
+ MmTelFeature testMMTelFeature = null;
+ if (featureToVerify instanceof MmTelFeature) {
+ testMMTelFeature = (MmTelFeature) featureToVerify;
+ } else {
+ fail();
+ }
+ assertEquals(mTestImsService.mSpyMmTelFeature, testMMTelFeature);
// Verify that upon creating a feature, we assign the callback and get the set feature state
// when querying it.
- verify(mTestImsService.mSpyMMTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
- assertEquals(ImsFeature.STATE_READY, mTestImsServiceBinder.getFeatureStatus(TEST_SLOT_0,
- ImsFeature.MMTEL));
+ verify(mTestImsService.mSpyMmTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
+ assertEquals(ImsFeature.STATE_READY, f.getFeatureState());
}
@Test
@SmallTest
public void testRemoveMMTelFeature() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+ mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
- mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+ mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+ mTestCallback);
- verify(mTestImsService.mSpyMMTelFeature).notifyFeatureRemoved(eq(0));
- verify(mTestImsService.mSpyMMTelFeature).removeImsFeatureStatusCallback(mTestCallback);
- SparseArray<ImsFeature> features = mTestImsService.getImsFeatureMap(TEST_SLOT_0);
- assertNull(mTestImsService.getImsFeatureFromType(features, ImsFeature.MMTEL));
+ verify(mTestImsService.mSpyMmTelFeature).onFeatureRemoved();
+ verify(mTestImsService.mSpyMmTelFeature).removeImsFeatureStatusCallback(mTestCallback);
+ SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
+ assertNull(features.get(ImsFeature.FEATURE_MMTEL));
}
@Test
@SmallTest
public void testCallMethodOnCreatedFeature() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+ IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
- mTestImsServiceBinder.isConnected(TEST_SLOT_0, ImsFeature.MMTEL, 0 /*callSessionType*/,
- 0 /*callType*/);
+ f.getUtInterface();
- verify(mTestImsService.mSpyMMTelFeature).isConnected(anyInt(), anyInt());
- }
-
- @Test
- @SmallTest
- public void testCallMethodWithNoCreatedFeature() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
-
- mTestImsServiceBinder.isConnected(TEST_SLOT_1, ImsFeature.MMTEL, 0 /*callSessionType*/,
- 0 /*callType*/);
-
- verify(mTestImsService.mSpyMMTelFeature, never()).isConnected(anyInt(), anyInt());
- }
-
- @Test
- @SmallTest
- public void testCreateFeatureWithNoPermissions() throws RemoteException {
- doThrow(new SecurityException()).when(mMockContext).enforceCallingOrSelfPermission(
- eq(MODIFY_PHONE_STATE), anyString());
-
- try {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
- fail();
- } catch (SecurityException e) {
- // Expected
- }
- }
-
- @FlakyTest
- @Ignore
- @Test
- public void testMethodWithNoPermissions() throws RemoteException {
- when(mMockContext.checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)).thenReturn(
- PackageManager.PERMISSION_DENIED);
- doThrow(new SecurityException()).when(mMockContext).enforceCallingOrSelfPermission(
- eq(READ_PHONE_STATE), nullable(String.class));
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
-
- try {
- mTestImsServiceBinder.isConnected(TEST_SLOT_1, ImsFeature.MMTEL, 0 /*callSessionType*/,
- 0 /*callType*/);
- fail();
- } catch (SecurityException e) {
- // Expected
- }
-
- verify(mTestImsService.mSpyMMTelFeature, never()).isConnected(anyInt(), anyInt());
+ assertTrue(mTestImsService.mTestMmTelFeature.isUtInterfaceCalled);
}
/**
@@ -185,9 +131,9 @@
@Test
@SmallTest
public void testImsServiceUpSentCompat() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+ mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
- mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
+ mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext).sendBroadcast(intentCaptor.capture());
@@ -207,9 +153,9 @@
@Test
@SmallTest
public void testImsServiceDownSentCompatInitializing() throws RemoteException {
- mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+ mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
- mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
+ mTestImsService.mSpyMmTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext).sendBroadcast(intentCaptor.capture());
@@ -222,6 +168,23 @@
}
}
+ /**
+ * Tests that the ImsService will return the correct ImsFeatureConfiguration when queried.
+ */
+ @Test
+ @SmallTest
+ public void testQuerySupportedImsFeatures() throws RemoteException {
+ ImsFeatureConfiguration config = new ImsFeatureConfiguration.Builder()
+ .addFeature(0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(0, ImsFeature.FEATURE_RCS)
+ .build();
+ mTestImsService.testFeatureConfig = config;
+
+ ImsFeatureConfiguration result = mTestImsServiceBinder.querySupportedImsFeatures();
+
+ assertEquals(config, result);
+ }
+
private void verifyServiceDownSent(Intent testIntent) {
assertEquals(ImsManager.ACTION_IMS_SERVICE_DOWN, testIntent.getAction());
assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
new file mode 100644
index 0000000..6be2fb0
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -0,0 +1,153 @@
+/*
+ * 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 android.telephony.ims;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.internal.telephony.ims.ImsTestBase;
+
+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;
+
+@RunWith(AndroidJUnit4.class)
+public class MmTelFeatureTests extends ImsTestBase {
+
+ private static final int TEST_CAPABILITY = 1;
+ private static final int TEST_RADIO_TECH = 0;
+
+ private static final int TEST_TTY_RESULT = 0;
+ private static final int TEST_SEND_DTMF_RESULT = 1;
+ private static final int TEST_RESULT_MAX = 2;
+
+ private static final int TEST_RESULT_DELAY_MS = 5000;
+
+ private android.telephony.ims.TestMmTelFeature mFeature;
+ private IImsMmTelFeature mFeatureBinder;
+ private ImsFeature.CapabilityCallback mCapabilityCallback;
+ private MmTelFeature.Listener mListener;
+
+ // set to true when the handler receives a message back from the Feature.
+ private boolean[] mHandlerResults;
+
+ private class TestHandler extends Handler {
+
+ TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case TEST_TTY_RESULT:
+ mHandlerResults[TEST_TTY_RESULT] = true;
+ break;
+ case TEST_SEND_DTMF_RESULT:
+ mHandlerResults[TEST_SEND_DTMF_RESULT] = true;
+ break;
+ }
+ }
+ }
+ private final Handler mHandler = new TestHandler(Looper.getMainLooper());
+ private final Messenger mHandlerMessenger = new Messenger(mHandler);
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ mFeature = new TestMmTelFeature();
+ mFeatureBinder = mFeature.getBinder();
+ mCapabilityCallback = spy(new ImsFeature.CapabilityCallback());
+ mListener = spy(new MmTelFeature.Listener());
+ mFeatureBinder.setListener(mListener);
+ mHandlerResults = new boolean[TEST_RESULT_MAX];
+ }
+
+ @After
+ public void tearDown() {
+ mFeature = null;
+ mFeatureBinder = null;
+ }
+
+ @SmallTest
+ @Test
+ public void testQueryCapabilityConfiguration() throws Exception {
+ mFeature.queryConfigurationResult = true;
+
+ mFeatureBinder.queryCapabilityConfiguration(TEST_CAPABILITY, TEST_RADIO_TECH,
+ mCapabilityCallback);
+
+ verify(mCapabilityCallback).onQueryCapabilityConfiguration(eq(TEST_CAPABILITY),
+ eq(TEST_RADIO_TECH), eq(true));
+ }
+
+ @SmallTest
+ @Test
+ public void testNewIncomingCall() throws Exception {
+ IImsCallSession sessionBinder = Mockito.mock(IImsCallSession.class);
+ ImsCallSessionImplBase session = new ImsCallSessionImplBase();
+ session.setServiceImpl(sessionBinder);
+
+ mFeature.incomingCall(session);
+ ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
+ verify(mListener).onIncomingCall(captor.capture(), any());
+
+ assertEquals(sessionBinder, captor.getValue());
+ }
+
+ @SmallTest
+ @Test
+ public void testSetTtyMessageMessenger() throws Exception {
+ Message resultMessage = Message.obtain(mHandler, TEST_TTY_RESULT);
+ resultMessage.replyTo = mHandlerMessenger;
+ mFeatureBinder.setUiTtyMode(TelecomManager.TTY_MODE_FULL, resultMessage);
+ waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+ assertTrue(mHandlerResults[TEST_TTY_RESULT]);
+ }
+
+ @SmallTest
+ @Test
+ public void testSendDtmfMessageMessenger() throws Exception {
+ Message resultMessage = Message.obtain(mHandler, TEST_SEND_DTMF_RESULT);
+ resultMessage.replyTo = mHandlerMessenger;
+ IImsCallSession callSession = mFeatureBinder.createCallSession(null);
+ callSession.sendDtmf('0', resultMessage);
+ waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+ assertTrue(mHandlerResults[TEST_SEND_DTMF_RESULT]);
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java
similarity index 62%
rename from tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
rename to tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java
index 6a41c2d..b3c5e00 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsCallSessionCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,20 +16,17 @@
package android.telephony.ims;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
-/**
- * MMTelFeature implementation used by mockito to test functionality.
- */
+import com.android.ims.internal.IImsCallSessionListener;
-public class TestMMTelFeature extends MMTelFeature {
+public class TestImsCallSessionCompat extends ImsCallSessionImplBase {
+
+
+ IImsCallSessionListener mListener;
@Override
- public void onFeatureRemoved() {
-
- }
-
- public void sendSetFeatureState(int state) {
- setFeatureState(state);
+ public void setListener(IImsCallSessionListener listener) {
+ mListener = listener;
}
}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsFeature.java b/tests/telephonytests/src/android/telephony/ims/TestImsFeature.java
new file mode 100644
index 0000000..ec09b4a
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsFeature.java
@@ -0,0 +1,69 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.IInterface;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
+
+public class TestImsFeature extends ImsFeature {
+
+
+ public static final int CAPABILITY_TEST_1 = 1 << 0;
+ public static final int CAPABILITY_TEST_2 = 1 << 1;
+
+ public int setCapabilitiesResult = ImsFeature.CAPABILITY_SUCCESS;
+ public CapabilityChangeRequest lastRequest;
+ public int onFeatureRemovedCount = 0;
+ public int onFeatureReadyCount = 0;
+
+ public void testSetFeatureState(int featureState) {
+ setFeatureState(featureState);
+ }
+
+ public void capabilitiesStatusChanged(Capabilities c) {
+ notifyCapabilitiesStatusChanged(c);
+ }
+
+ @Override
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ lastRequest = request;
+ if (setCapabilitiesResult != ImsFeature.CAPABILITY_SUCCESS) {
+ // Take the first value to enable and return it as an error.
+ CapabilityChangeRequest.CapabilityPair capPair = request.getCapabilitiesToEnable()
+ .get(0);
+ c.onChangeCapabilityConfigurationError(capPair.getCapability(), capPair.getRadioTech(),
+ ImsFeature.CAPABILITY_ERROR_GENERIC);
+ }
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+ onFeatureRemovedCount++;
+ }
+
+ @Override
+ public void onFeatureReady() {
+ onFeatureReadyCount++;
+ }
+
+ @Override
+ public IInterface getBinder() {
+ return null;
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsService.java b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
index 66e03fd..944514a 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestImsService.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
@@ -16,44 +16,46 @@
package android.telephony.ims;
+import static org.mockito.Mockito.spy;
+
import android.content.Context;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-
-import static org.mockito.Mockito.spy;
/**
* Test ImsService used by mockito to verify functionality.
*/
-public class TestImsService extends ImsService {
+public class TestImsService extends android.telephony.ims.ImsService {
- public TestMMTelFeature mSpyMMTelFeature;
- private TestMMTelFeature mTestMMTelFeature;
+ public TestMmTelFeature mSpyMmTelFeature;
+ public TestMmTelFeature mTestMmTelFeature;
+
+ public ImsFeatureConfiguration testFeatureConfig;
public TestImsService(Context context) {
attachBaseContext(context);
MockitoAnnotations.initMocks(this);
// Must create real MMTelFeature to initialize ImsFeature objects.
- mTestMMTelFeature = new TestMMTelFeature();
- mSpyMMTelFeature = spy(mTestMMTelFeature);
+ mTestMmTelFeature = new TestMmTelFeature();
+ mSpyMmTelFeature = spy(mTestMmTelFeature);
}
@Override
- public MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+ public MmTelFeature createMmTelFeature(int slotId) {
+ return mSpyMmTelFeature;
+ }
+
+ @Override
+ public RcsFeature createRcsFeature(int slotId) {
return null;
}
@Override
- public MMTelFeature onCreateMMTelImsFeature(int slotId) {
- return mSpyMMTelFeature;
- }
-
- @Override
- public RcsFeature onCreateRcsFeature(int slotId) {
- return null;
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ return testFeatureConfig;
}
}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java
new file mode 100644
index 0000000..ee9ac82
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeatureCompat.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.telephony.ims.compat.feature.MMTelFeature;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * MMTelFeature implementation used by mockito to test functionality.
+ */
+
+public class TestMMTelFeatureCompat extends MMTelFeature {
+
+ public boolean isConnectedCalled = false;
+ public boolean isCreateCallSessionCalled = false;
+
+ @Override
+ public boolean isConnected(int callSessinType, int callType) {
+ isConnectedCalled = true;
+ return true;
+ }
+
+ @Override
+ public void onFeatureReady() {
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+ }
+
+ public void sendSetFeatureState(int state) {
+ setFeatureState(state);
+ }
+
+ // Compatibility Method
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) {
+ isCreateCallSessionCalled = true;
+ return null;
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
new file mode 100644
index 0000000..6414403
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -0,0 +1,124 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+
+public class TestMmTelFeature extends MmTelFeature {
+
+ public boolean queryConfigurationResult = false;
+ public int setCapabilitiesResult = ImsFeature.CAPABILITY_SUCCESS;
+ public CapabilityChangeRequest lastRequest;
+ public boolean isUtInterfaceCalled = false;
+
+ private final TestImsCallSession mCallSession = new TestImsCallSession();
+ private class TestImsCallSession extends ImsCallSessionImplBase {
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ // Just call result to signify complete for test.
+ if (result.replyTo != null) {
+ try {
+ result.replyTo.send(result);
+ } catch (RemoteException e) {
+ // eat error, test will fail.
+ }
+ }
+ }
+ }
+
+ public void incomingCall(ImsCallSessionImplBase c) throws RemoteException {
+ notifyIncomingCall(c, null);
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ return super.createCallProfile(callSessionType, callType);
+ }
+
+ @Override
+ public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+ return mCallSession;
+ }
+
+ @Override
+ public ImsUtImplBase getUt() {
+ isUtInterfaceCalled = true;
+ return super.getUt();
+ }
+
+ @Override
+ public ImsEcbmImplBase getEcbm() {
+ return super.getEcbm();
+ }
+
+ @Override
+ public ImsMultiEndpointImplBase getMultiEndpoint() {
+ return super.getMultiEndpoint();
+ }
+
+ @Override
+ public void setUiTtyMode(int mode, Message onCompleteMessage) {
+ try {
+ // just send complete message.
+ onCompleteMessage.replyTo.send(onCompleteMessage);
+ } catch (RemoteException e) {
+ // do nothing, test will fail.
+ }
+ }
+
+ @Override
+ public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ // Base implementation - Override to provide functionality
+ return queryConfigurationResult;
+ }
+
+ @Override
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ lastRequest = request;
+ if (setCapabilitiesResult != ImsFeature.CAPABILITY_SUCCESS) {
+ // Take the first value to enable and return it as an error.
+ CapabilityChangeRequest.CapabilityPair capPair = request.getCapabilitiesToEnable()
+ .get(0);
+ c.onChangeCapabilityConfigurationError(capPair.getCapability(), capPair.getRadioTech(),
+ ImsFeature.CAPABILITY_ERROR_GENERIC);
+ }
+ }
+
+ public void sendSetFeatureState(int state) {
+ setFeatureState(state);
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+ }
+
+ @Override
+ public void onFeatureReady() {
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java b/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java
new file mode 100644
index 0000000..46cf4f7
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.telephony.mbms;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MbmsReceiverTest {
+ @Test
+ @SmallTest
+ public void testFileHierarchyRecreation() throws Exception {
+ String rootPath = "http://www.example.com/files/";
+ assertEquals("subdir1/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(rootPath, rootPath + "/subdir1/file.txt"));
+ assertEquals("subdir1/subdir2/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath,
+ rootPath + "/subdir1/subdir2/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir1/file.*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir1/*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("subdir1/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath,
+ rootPath + "/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/*",
+ rootPath + "/file.txt"));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
index 8231d06..bda6d95 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
@@ -144,11 +144,12 @@
CallManager.getInstance().dial(mPhone,
PhoneNumberUtils.stripSeparators("+17005554141"), 0);
ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor<Integer> mCaptorInt = ArgumentCaptor.forClass(Integer.class);
- verify(mPhone, times(1)).dial(mCaptorString.capture(), mCaptorInt.capture());
+ ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor =
+ ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
+ verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture());
assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"),
mCaptorString.getValue());
- assertEquals(0, mCaptorInt.getValue().intValue());
+ assertEquals(0, dialArgsCaptor.getValue().videoState);
}
@SmallTest @Test
@@ -286,12 +287,13 @@
CallManager.getInstance().dial(mPhone,
PhoneNumberUtils.stripSeparators("+17005554141"), 0);
ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor<Integer> mCaptorInt = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor =
+ ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
- verify(mPhone, times(1)).dial(mCaptorString.capture(), mCaptorInt.capture());
+ verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture());
assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"),
mCaptorString.getValue());
- assertEquals(0, mCaptorInt.getValue().intValue());
+ assertEquals(0, dialArgsCaptor.getValue().videoState);
}
@Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
index 2294dec..a584f87 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
@@ -139,6 +139,48 @@
assertEquals(true, ((AsyncResult) message.getValue().obj).result);
}
+ @Test
+ @SmallTest
+ public void testCarrierActionResetOnAPNChange() {
+ // Setting observer register at sim loading
+ final Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+ IccCardConstants.INTENT_VALUE_ICC_LOADED);
+ mContext.sendBroadcast(intent);
+ waitForMs(200);
+
+ // no carrier actions triggered from sim loading since there are same as the current one
+ ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class);
+ verify(mDataActionHandler, times(0)).sendMessageAtTime(message.capture(), anyLong());
+ verify(mRadioActionHandler, times(0)).sendMessageAtTime(message.capture(), anyLong());
+
+ // disable metered apns and radio
+ mCarrierActionAgentUT.carrierActionSetRadioEnabled(false);
+ mCarrierActionAgentUT.carrierActionSetMeteredApnsEnabled(false);
+ waitForMs(200);
+
+ verify(mDataActionHandler, times(1)).sendMessageAtTime(message.capture(), anyLong());
+ assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
+ assertEquals(false, ((AsyncResult) message.getValue().obj).result);
+
+ verify(mRadioActionHandler, times(1)).sendMessageAtTime(message.capture(), anyLong());
+ assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
+ assertEquals(false, ((AsyncResult) message.getValue().obj).result);
+
+ // Simulate APN change
+ mFakeContentResolver.notifyChange(Telephony.Carriers.CONTENT_URI, null);
+ waitForMs(200);
+
+ // Carrier actions triggered from APN change
+ verify(mDataActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+ assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
+ assertEquals(true, ((AsyncResult) message.getValue().obj).result);
+
+ verify(mRadioActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+ assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
+ assertEquals(true, ((AsyncResult) message.getValue().obj).result);
+ }
+
@After
public void tearDown() throws Exception {
Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java
new file mode 100644
index 0000000..6ce6d20
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.provider.Telephony.CarrierId;
+import android.provider.Telephony.Carriers;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class CarrierIdentifierTest extends TelephonyTest {
+ private static final String MCCMNC = "311480";
+ private static final String NAME = "VZW";
+ private static final int CID_VZW = 1;
+
+ private static final String SPN_FI = "PROJECT FI";
+ private static final String NAME_FI = "FI";
+ private static final int CID_FI = 2;
+
+ private static final String NAME_DOCOMO = "DOCOMO";
+ private static final String APN_DOCOMO = "mopera.net";
+ private static final int CID_DOCOMO = 3;
+
+ private static final String NAME_TMO = "TMO";
+ private static final String GID1 = "ae";
+ private static final int CID_TMO = 4;
+
+ private static final int CID_UNKNOWN = -1;
+
+ // events to trigger carrier identification
+ private static final int SIM_LOAD_EVENT = 1;
+ private static final int SIM_ABSENT_EVENT = 2;
+ private static final int SPN_OVERRIDE_EVENT = 3;
+ private static final int PREFER_APN_SET_EVENT = 5;
+
+ private CarrierIdentifier mCarrierIdentifier;
+ private CarrierIdentifierHandler mCarrierIdentifierHandler;
+
+ private class CarrierIdentifierHandler extends HandlerThread {
+ private CarrierIdentifierHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierIdentifier = new CarrierIdentifier(mPhone);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("CarrierIdentifierTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+ ((MockContentResolver) mContext.getContentResolver()).addProvider(
+ CarrierId.AUTHORITY, new CarrierIdContentProvider());
+ // start handler thread
+ mCarrierIdentifierHandler = new CarrierIdentifierHandler(getClass().getSimpleName());
+ mCarrierIdentifierHandler.start();
+ waitUntilReady();
+ logd("CarrierIdentifierTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd("CarrierIdentifier -tearDown");
+ mCarrierIdentifier.removeCallbacksAndMessages(null);
+ mCarrierIdentifier = null;
+ mCarrierIdentifierHandler.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierMatch() {
+ int phoneId = mPhone.getPhoneId();
+ doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+ // trigger sim loading event
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME, mCarrierIdentifier.getCarrierName());
+
+ doReturn(SPN_FI).when(mTelephonyManager).getSimOperatorNameForPhone(eq(phoneId));
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_FI, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME_FI, mCarrierIdentifier.getCarrierName());
+
+ doReturn(GID1).when(mPhone).getGroupIdLevel1();
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_TMO, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME_TMO, mCarrierIdentifier.getCarrierName());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierMatchSpnOverride() {
+ int phoneId = mPhone.getPhoneId();
+ doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+ // trigger sim loading event
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME, mCarrierIdentifier.getCarrierName());
+ // spn override
+ doReturn(SPN_FI).when(mTelephonyManager).getSimOperatorNameForPhone(eq(phoneId));
+ mCarrierIdentifier.sendEmptyMessage(SPN_OVERRIDE_EVENT);
+ waitForMs(200);
+ assertEquals(CID_FI, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME_FI, mCarrierIdentifier.getCarrierName());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierMatchSimAbsent() {
+ int phoneId = mPhone.getPhoneId();
+ doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+ // trigger sim loading event
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME, mCarrierIdentifier.getCarrierName());
+ // trigger sim absent event
+ mCarrierIdentifier.sendEmptyMessage(SIM_ABSENT_EVENT);
+ waitForMs(200);
+ assertEquals(CID_UNKNOWN, mCarrierIdentifier.getCarrierId());
+ assertNull(mCarrierIdentifier.getCarrierName());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierNoMatch() {
+ // un-configured MCCMNC
+ int phoneId = mPhone.getPhoneId();
+ doReturn("12345").when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+ // trigger sim loading event
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_UNKNOWN, mCarrierIdentifier.getCarrierId());
+ assertNull(mCarrierIdentifier.getCarrierName());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierMatchPreferApnChange() {
+ int phoneId = mPhone.getPhoneId();
+ doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+ // trigger sim loading event
+ mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
+ waitForMs(200);
+ assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME, mCarrierIdentifier.getCarrierName());
+ // mock apn
+ ((MockContentResolver) mContext.getContentResolver()).addProvider(
+ Carriers.CONTENT_URI.getAuthority(), new CarrierIdContentProvider());
+ mCarrierIdentifier.sendEmptyMessage(PREFER_APN_SET_EVENT);
+ waitForMs(200);
+ assertEquals(CID_DOCOMO, mCarrierIdentifier.getCarrierId());
+ assertEquals(NAME_DOCOMO, mCarrierIdentifier.getCarrierName());
+ }
+
+ private class CarrierIdContentProvider extends MockContentProvider {
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ logd("CarrierIdContentProvider: query");
+ logd(" uri = " + uri);
+ logd(" projection = " + Arrays.toString(projection));
+ logd(" selection = " + selection);
+ logd(" selectionArgs = " + Arrays.toString(selectionArgs));
+ logd(" sortOrder = " + sortOrder);
+
+ if (CarrierId.All.CONTENT_URI.getAuthority().equals(
+ uri.getAuthority())) {
+ MatrixCursor mc = new MatrixCursor(
+ new String[]{CarrierId._ID,
+ CarrierId.All.MCCMNC,
+ CarrierId.All.GID1,
+ CarrierId.All.GID2,
+ CarrierId.All.PLMN,
+ CarrierId.All.IMSI_PREFIX_XPATTERN,
+ CarrierId.All.ICCID_PREFIX,
+ CarrierId.All.SPN,
+ CarrierId.All.APN,
+ CarrierId.CARRIER_NAME,
+ CarrierId.CARRIER_ID});
+
+ mc.addRow(new Object[] {
+ 1, // id
+ MCCMNC, // mccmnc
+ null, // gid1
+ null, // gid2
+ null, // plmn
+ null, // imsi_prefix
+ null, // iccid_prefix
+ null, // spn
+ null, // apn
+ NAME, // carrier name
+ CID_VZW, // cid
+ });
+ mc.addRow(new Object[] {
+ 2, // id
+ MCCMNC, // mccmnc
+ GID1, // gid1
+ null, // gid2
+ null, // plmn
+ null, // imsi_prefix
+ null, // iccid_prefix
+ null, // spn
+ null, // apn
+ NAME_TMO, // carrier name
+ CID_TMO, // cid
+ });
+ mc.addRow(new Object[] {
+ 3, // id
+ MCCMNC, // mccmnc
+ null, // gid1
+ null, // gid2
+ null, // plmn
+ null, // imsi_prefix
+ null, // iccid_prefix
+ SPN_FI, // spn
+ null, // apn
+ NAME_FI, // carrier name
+ CID_FI, // cid
+ });
+ mc.addRow(new Object[] {
+ 4, // id
+ MCCMNC, // mccmnc
+ null, // gid1
+ null, // gid2
+ null, // plmn
+ null, // imsi_prefix
+ null, // iccid_prefix
+ null, // spn
+ APN_DOCOMO, // apn
+ NAME_DOCOMO, // carrier name
+ CID_DOCOMO, // cid
+ });
+ return mc;
+ } else if (Carriers.CONTENT_URI.getAuthority().equals(uri.getAuthority())) {
+ MatrixCursor mc = new MatrixCursor(new String[]{Carriers._ID, Carriers.APN});
+ mc.addRow(new Object[] {
+ 1, // id
+ APN_DOCOMO // apn
+ });
+ return mc;
+ }
+ return null;
+ }
+ @Override
+ public int update(android.net.Uri uri, android.content.ContentValues values,
+ java.lang.String selection, java.lang.String[] selectionArgs) {
+ return 0;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
new file mode 100644
index 0000000..623cf13
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -0,0 +1,326 @@
+/*
+ * 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;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.HandlerThread;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ImsiEncryptionInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.org.bouncycastle.util.io.pem.PemReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.PublicKey;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CarrierKeyDownloadMgrTest extends TelephonyTest {
+
+ private static final String LOG_TAG = "CarrierKeyDownloadManager";
+
+ private CarrierKeyDownloadManager mCarrierKeyDM;
+ private CarrierActionAgentHandler mCarrierActionAgentHandler;
+
+ private 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-----";
+
+
+ private String mJsonStr = "{ \"carrier-keys\": [ { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
+
+ private String mJsonStr1 = "{ \"carrier-keys\": [ { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
+
+ private class CarrierActionAgentHandler extends HandlerThread {
+
+ private CarrierActionAgentHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("CarrierActionAgentTest +Setup!");
+ MockitoAnnotations.initMocks(this);
+ super.setUp(getClass().getSimpleName());
+ mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
+ mCarrierActionAgentHandler.start();
+ waitUntilReady();
+ logd("CarrierActionAgentTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCarrierActionAgentHandler.quit();
+ super.tearDown();
+ }
+
+ /* Checks if the expiration date is calculated correctly
+ * In this case the expiration date should be the next day.
+ */
+ @Test
+ @SmallTest
+ public void testExpirationDate1Day() {
+ java.security.PublicKey publicKey = null;
+ mCarrierKeyDM.mKeyAvailability = 3;
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, 6);
+ Date date = cal.getTime();
+ Calendar expectedCal = new GregorianCalendar();
+ expectedCal.add(Calendar.DATE, 1);
+ String dateExpected = dt.format(expectedCal.getTime());
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
+ "keyIdentifier", publicKey, date);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(dt.format(expirationDate).equals(dateExpected));
+ }
+
+ /**
+ * Checks if the expiration date is calculated correctly
+ * In this case the expiration date should be within the window (7 to 21 days).
+ **/
+ @Test
+ @SmallTest
+ public void testExpirationDate7Day() {
+ java.security.PublicKey publicKey = null;
+ mCarrierKeyDM.mKeyAvailability = 3;
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, 30);
+ Date date = cal.getTime();
+ Calendar minExpirationCal = new GregorianCalendar();
+ Calendar maxExpirationCal = new GregorianCalendar();
+ minExpirationCal.add(Calendar.DATE, 23);
+ maxExpirationCal.add(Calendar.DATE, 9);
+ Date minExpirationDate = minExpirationCal.getTime();
+ Date maxExpirationDate = maxExpirationCal.getTime();
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
+ "keyIdentifier", publicKey, date);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(expirationDate.before(minExpirationDate));
+ assertTrue(expirationDate.after(maxExpirationDate));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Verify that setCarrierInfoForImsiEncryption is called with the right params
+ **/
+ @Test
+ @SmallTest
+ public void testParseJson() {
+ ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
+ Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
+ PemReader reader = new PemReader(fRd);
+ Pair<PublicKey, Long> keyInfo = null;
+ try {
+ keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ } catch (Exception e) {
+ fail(LOG_TAG + "exception creating public key");
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
+ "key1=value", keyInfo.first, new Date(keyInfo.second));
+ String mccMnc = "310:270";
+ mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr, mccMnc);
+ verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
+ (Matchers.refEq(imsiEncryptionInfo)));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Same as testParseJason, except that the test looks for the "public-key" field.
+ **/
+ @Test
+ @SmallTest
+ public void testParseJsonPublicKey() {
+ ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
+ Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
+ PemReader reader = new PemReader(fRd);
+ Pair<PublicKey, Long> keyInfo = null;
+ try {
+ keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ } catch (Exception e) {
+ fail(LOG_TAG + "exception creating public key");
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
+ "key1=value", keyInfo.first, new Date(keyInfo.second));
+ String mccMnc = "310:270";
+ mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr1, mccMnc);
+ verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
+ (Matchers.refEq(imsiEncryptionInfo)));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Since the json is bad, we want to verify that savePublicKey is not called.
+ **/
+ @Test
+ @SmallTest
+ public void testParseBadJsonFail() {
+ String mccMnc = "310:290";
+ String badJsonStr = "{badJsonString}";
+ mCarrierKeyDM.parseJsonAndPersistKey(badJsonStr, mccMnc);
+ verify(mPhone, times(0)).setCarrierInfoForImsiEncryption(any());
+ }
+
+ /**
+ * Checks if the download is valid.
+ * returns true since the mnc/mcc is valid.
+ **/
+ @Test
+ @SmallTest
+ public void testIsValidDownload() {
+ String mccMnc = "310:260";
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ assertTrue(mCarrierKeyDM.isValidDownload(mccMnc));
+ }
+
+ /**
+ * Checks if the download is valid.
+ * returns false since the mnc/mcc is in-valid.
+ **/
+ @Test
+ @SmallTest
+ public void testIsValidDownloadFail() {
+ String mccMnc = "310:290";
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ assertFalse(mCarrierKeyDM.isValidDownload(mccMnc));
+ }
+
+ /**
+ * Tests if the key is enabled.
+ * tests for all bit-mask value.
+ **/
+ @Test
+ @SmallTest
+ public void testIsKeyEnabled() {
+ mCarrierKeyDM.mKeyAvailability = 3;
+ assertTrue(mCarrierKeyDM.isKeyEnabled(1));
+ assertTrue(mCarrierKeyDM.isKeyEnabled(2));
+ mCarrierKeyDM.mKeyAvailability = 2;
+ assertFalse(mCarrierKeyDM.isKeyEnabled(1));
+ assertTrue(mCarrierKeyDM.isKeyEnabled(2));
+ mCarrierKeyDM.mKeyAvailability = 1;
+ assertTrue(mCarrierKeyDM.isKeyEnabled(1));
+ assertFalse(mCarrierKeyDM.isKeyEnabled(2));
+ }
+
+ /**
+ * Tests sending the ACTION_DOWNLOAD_COMPLETE intent.
+ * Verify that the alarm will kick-off the next day.
+ **/
+ @Test
+ @SmallTest
+ public void testDownloadComplete() {
+ SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
+ String mccMnc = "310:260";
+ int slotId = mPhone.getPhoneId();
+ editor.putString("CARRIER_KEY_DM_MCC_MNC" + slotId, mccMnc);
+ editor.commit();
+
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar expectedCal = new GregorianCalendar();
+ expectedCal.add(Calendar.DATE, 1);
+ String dateExpected = dt.format(expectedCal.getTime());
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.sendBroadcast(mIntent);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(dt.format(expirationDate).equals(dateExpected));
+ }
+
+ /**
+ * Test sending the ACTION_CARRIER_CONFIG_CHANGED intent.
+ * Verify that the right mnc/mcc gets stored in the preferences.
+ **/
+ @Test
+ @SmallTest
+ public void testCarrierConfigChanged() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ int slotId = mPhone.getPhoneId();
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
+ bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ mContext.sendBroadcast(mIntent);
+ SharedPreferences preferences = getDefaultSharedPreferences(mContext);
+ String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
+ assertTrue(mccMnc.equals("310:260"));
+ }
+
+ /**
+ * Tests sending the INTENT_KEY_RENEWAL_ALARM_PREFIX intent.
+ * Verify that the right mnc/mcc gets stored in the preferences.
+ **/
+ @Test
+ @SmallTest
+ public void testAlarmRenewal() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ int slotId = mPhone.getPhoneId();
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
+ bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm"
+ + slotId);
+ mContext.sendBroadcast(mIntent);
+ SharedPreferences preferences = getDefaultSharedPreferences(mContext);
+ String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
+ assertTrue(mccMnc.equals("310:260"));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
new file mode 100644
index 0000000..59d8872
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
+ */
+@SmallTest
+public class CarrierServiceStateTrackerTest extends TelephonyTest {
+ public static final String LOG_TAG = "CSST";
+ public static final int TEST_TIMEOUT = 5000;
+
+ private CarrierServiceStateTracker mSpyCarrierSST;
+ private CarrierServiceStateTracker mCarrierSST;
+ private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
+ private FakeContentResolver mFakeContentResolver;
+
+ NotificationManager mNotificationManager;
+ PersistableBundle mBundle;
+
+ private class FakeContentResolver extends MockContentResolver {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ super.notifyChange(uri, observer, syncToNetwork);
+ logd("onChanged(uri=" + uri + ")" + observer);
+ if (observer != null) {
+ observer.dispatchChange(false, uri);
+ }
+ }
+ }
+
+ private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
+ private CarrierServiceStateTrackerTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+ mSpyCarrierSST = spy(mCarrierSST);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ logd(LOG_TAG + "Setup!");
+ super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mPhone.getSubId()).thenReturn(1);
+ mCarrierServiceStateTrackerTestHandler =
+ new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
+ mCarrierServiceStateTrackerTestHandler.start();
+ mFakeContentResolver = new CarrierServiceStateTrackerTest.FakeContentResolver();
+
+ when(mPhone.getContext().getContentResolver()).thenReturn(mFakeContentResolver);
+
+ mNotificationManager = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+
+ setDefaultValues();
+ waitUntilReady();
+ }
+
+ private void setDefaultValues() {
+ mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
+ 0);
+ mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+ 0);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCarrierServiceStateTrackerTestHandler.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testCancelBothNotifications() {
+ logd(LOG_TAG + ":testCancelBothNotifications()");
+ Message notificationMsg = mSpyCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
+ doReturn(false).when(mSpyCarrierSST).evaluateSendingMessage(any());
+ doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ verify(mNotificationManager).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendBothNotifications() {
+ logd(LOG_TAG + ":testSendBothNotifications()");
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ Message notificationMsg = mSpyCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
+ doReturn(true).when(mSpyCarrierSST).evaluateSendingMessage(any());
+ doReturn(false).when(mSpyCarrierSST).isRadioOffOrAirplaneMode();
+ doReturn(0).when(mSpyCarrierSST).getDelay(any());
+ doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
+ doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendPrefNetworkNotification() {
+ logd(LOG_TAG + ":testSendPrefNetworkNotification()");
+ Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+ mCarrierSST.getNotificationTypeMap();
+ CarrierServiceStateTracker.NotificationType prefNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ CarrierServiceStateTracker.NotificationType spyPrefNetworkNotification = spy(
+ prefNetworkNotification);
+ notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK,
+ spyPrefNetworkNotification);
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getDataRegState();
+ doReturn(true).when(mSST).isRadioOn();
+ doReturn(mNotificationBuilder).when(spyPrefNetworkNotification).getNotificationBuilder();
+
+ String prefNetworkMode = Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId();
+ Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+ waitForMs(500);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+
+ Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+ waitForMs(500);
+ verify(mNotificationManager, atLeast(1)).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendEmergencyNetworkNotification() {
+ logd(LOG_TAG + ":testSendEmergencyNetworkNotification()");
+ Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+ mCarrierSST.getNotificationTypeMap();
+ CarrierServiceStateTracker.NotificationType emergencyNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ CarrierServiceStateTracker.NotificationType spyEmergencyNetworkNotification = spy(
+ emergencyNetworkNotification);
+ notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK,
+ spyEmergencyNetworkNotification);
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+ doReturn(mNotificationBuilder).when(spyEmergencyNetworkNotification)
+ .getNotificationBuilder();
+
+ doReturn(true).when(mPhone).isWifiCallingEnabled();
+ Message notificationMsg = mSpyCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK),
+ isA(Notification.class));
+
+ doReturn(false).when(mPhone).isWifiCallingEnabled();
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager, atLeast(2)).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
index 7cb7d45..96a5e7f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
@@ -15,8 +15,23 @@
*/
package com.android.internal.telephony;
+import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE;
+import static com.android.internal.telephony.TelephonyIntents
+ .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.HandlerThread;
import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -27,43 +42,48 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.compat.ArgumentMatcher;
import java.util.ArrayList;
import java.util.Arrays;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED;
-import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
+import java.util.Objects;
public class CarrierSignalAgentTest extends TelephonyTest {
private CarrierSignalAgent mCarrierSignalAgentUT;
private PersistableBundle mBundle;
+ private CarrierSignalAgentHandler mCarrierSignalAgentHandler;
private static final String PCO_RECEIVER = "pak/PCO_RECEIVER";
private static final String DC_ERROR_RECEIVER = "pak/DC_ERROR_RECEIVER";
@Mock
ResolveInfo mResolveInfo;
+ private class CarrierSignalAgentHandler extends HandlerThread {
+
+ private CarrierSignalAgentHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierSignalAgentUT = new CarrierSignalAgent(mPhone);
+ setReady(true);
+ }
+ }
+
@Before
public void setUp() throws Exception {
logd("CarrierSignalAgentTest +Setup!");
super.setUp(getClass().getSimpleName());
- mCarrierSignalAgentUT = new CarrierSignalAgent(mPhone);
mBundle = mContextFixture.getCarrierConfigBundle();
+ mCarrierSignalAgentHandler = new CarrierSignalAgentHandler(getClass().getSimpleName());
+ mCarrierSignalAgentHandler.start();
+ waitUntilReady();
logd("CarrierSignalAgentTest -Setup!");
}
@After
public void tearDown() throws Exception {
+ mCarrierSignalAgentHandler.quit();
super.tearDown();
}
@@ -161,11 +181,8 @@
// Only wake signal is declared in the manifest
doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
.when(mPackageManager).queryBroadcastReceivers(
- argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matchesObject(Object o) {
- return o instanceof Intent && ((Intent) o).getAction()
- .equals(ACTION_CARRIER_SIGNAL_PCO_VALUE); }}), anyInt());
+ argThat(o -> Objects.equals(o.getAction(), ACTION_CARRIER_SIGNAL_PCO_VALUE)),
+ anyInt());
mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
count++;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityCdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityCdmaTest.java
new file mode 100644
index 0000000..2d7e29e
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityCdmaTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telephony.CellIdentityCdma;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellIdentityCdma}. */
+
+public class CellIdentityCdmaTest extends AndroidTestCase {
+
+ // Network Id ranges from 0 to 65535.
+ private static final int NETWORK_ID = 65535;
+ // CDMA System Id ranges from 0 to 32767
+ private static final int SYSTEM_ID = 32767;
+ // Base Station Id ranges from 0 to 65535
+ private static final int BASESTATION_ID = 65535;
+ // Longitude ranges from -2592000 to 2592000.
+ private static final int LONGITUDE = 2592000;
+ // Latitude ranges from -1296000 to 1296000.
+ private static final int LATITUDE = 1296000;
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+ @SmallTest
+ public void testConstructor() {
+ CellIdentityCdma ci =
+ new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(NETWORK_ID, ci.getNetworkId());
+ assertEquals(LATITUDE, ci.getLatitude());
+ assertEquals(LONGITUDE, ci.getLongitude());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testNullIsland() {
+ CellIdentityCdma ci =
+ new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, -1, 0,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getLatitude());
+ assertEquals(Integer.MAX_VALUE, ci.getLongitude());
+ }
+
+ @SmallTest
+ public void testEquals() {
+ CellIdentityCdma ciA =
+ new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+ CellIdentityCdma ciB =
+ new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ null, null);
+ ciB = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ null, null);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ null, null);
+
+ assertFalse(ciA.equals(ciB));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentityCdma ci =
+ new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentityCdma newCi = CellIdentityCdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java
new file mode 100644
index 0000000..cb003b7
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellIdentityGsm}. */
+
+public class CellIdentityGsmTest extends AndroidTestCase {
+
+ // Location Area Code ranges from 0 to 65535.
+ private static final int LAC = 65535;
+ // GSM Cell Identity ranges from 0 to 65535.
+ private static final int CID = 65535;
+ // GSM Absolute RF Channel Number ranges from 0 to 65535.
+ private static final int ARFCN = 65535;
+ // Base Station Identity Code ranges from 0 to 63.
+ private static final int BSIC = 63;
+ private static final int MCC = 120;
+ private static final int MNC = 260;
+ private static final String MCC_STR = "120";
+ private static final String MNC_STR = "260";
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+
+ @SmallTest
+ public void testDefaultConstructor() {
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(LAC, ci.getLac());
+ assertEquals(CID, ci.getCid());
+ assertEquals(ARFCN, ci.getArfcn());
+ assertEquals(ARFCN, ci.getChannelNumber());
+ assertEquals(BSIC, ci.getBsic());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testConstructorWithThreeDigitMnc() {
+ final String mncWithThreeDigit = "061";
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR, mncWithThreeDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithThreeDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithThreeDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithTwoDigitMnc() {
+ final String mncWithTwoDigit = "61";
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR, mncWithTwoDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithTwoDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithTwoDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithEmptyMccMnc() {
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, "", "", ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testFormerConstructor() {
+ CellIdentityGsm ci =
+ new CellIdentityGsm(MCC, MNC, LAC, CID);
+
+ assertEquals(LAC, ci.getLac());
+ assertEquals(CID, ci.getCid());
+ assertEquals(Integer.MAX_VALUE, ci.getArfcn());
+ assertEquals(Integer.MAX_VALUE, ci.getBsic());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertNull(ci.getOperatorAlphaLong());
+ assertNull(ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testEquals() {
+ CellIdentityGsm ciA = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellIdentityGsm ciB = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertFalse(ciA.equals(ciB));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentityGsm newCi = CellIdentityGsm.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithUnknowMccMnc() {
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentityGsm.TYPE_GSM);
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(ARFCN);
+ p.writeInt(BSIC);
+ p.setDataPosition(0);
+
+ CellIdentityGsm newCi = CellIdentityGsm.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithInvalidMccMnc() {
+ final String invalidMcc = "randomStuff";
+ final String invalidMnc = "randomStuff";
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_GSM);
+ p.writeString(invalidMcc);
+ p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(ARFCN);
+ p.writeInt(BSIC);
+ p.setDataPosition(0);
+
+ CellIdentityGsm newCi = CellIdentityGsm.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
new file mode 100644
index 0000000..bbd9078
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityLte;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellIdentityLte}. */
+
+public class CellIdentityLteTest extends AndroidTestCase {
+
+ // Cell identity ranges from 0 to 268435456.
+ private static final int CI = 268435456;
+ // Physical cell id ranges from 0 to 503.
+ private static final int PCI = 503;
+ // Tracking area code ranges from 0 to 65535.
+ private static final int TAC = 65535;
+ // Absolute RF Channel Number ranges from 0 to 262140.
+ private static final int EARFCN = 262140;
+ private static final int MCC = 120;
+ private static final int MNC = 260;
+ private static final int BANDWIDTH = 5000; // kHz
+ private static final String MCC_STR = "120";
+ private static final String MNC_STR = "260";
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+ @SmallTest
+ public void testDefaultConstructor() {
+ CellIdentityLte ci =
+ new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(CI, ci.getCi());
+ assertEquals(PCI, ci.getPci());
+ assertEquals(TAC, ci.getTac());
+ assertEquals(EARFCN, ci.getEarfcn());
+ assertEquals(EARFCN, ci.getChannelNumber());
+ assertEquals(BANDWIDTH, ci.getBandwidth());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testConstructorWithThreeDigitMnc() {
+ final String mncWithThreeDigit = "061";
+ CellIdentityLte ci =
+ new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, mncWithThreeDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithThreeDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithThreeDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithTwoDigitMnc() {
+ final String mncWithTwoDigit = "61";
+ CellIdentityLte ci =
+ new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, mncWithTwoDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithTwoDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithTwoDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithEmptyMccMnc() {
+ CellIdentityLte ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, "", "", ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testFormerConstructor() {
+ CellIdentityLte ci =
+ new CellIdentityLte(MCC, MNC, CI, PCI, TAC);
+
+ assertEquals(CI, ci.getCi());
+ assertEquals(PCI, ci.getPci());
+ assertEquals(TAC, ci.getTac());
+ assertEquals(Integer.MAX_VALUE, ci.getEarfcn());
+ assertEquals(Integer.MAX_VALUE, ci.getBandwidth());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertNull(ci.getOperatorAlphaLong());
+ assertNull(ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testEquals() {
+ CellIdentityLte ciA = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellIdentityLte ciB = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, null, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertFalse(ciA.equals(ciB));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentityLte ci =
+ new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentityLte newCi = CellIdentityLte.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithUnknownMccMnc() {
+ CellIdentityLte ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_LTE);
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(CI);
+ p.writeInt(PCI);
+ p.writeInt(TAC);
+ p.writeInt(EARFCN);
+ p.writeInt(BANDWIDTH);
+ p.setDataPosition(0);
+
+ CellIdentityLte newCi = CellIdentityLte.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithInvalidMccMnc() {
+ final String invalidMcc = "randomStuff";
+ final String invalidMnc = "randomStuff";
+ CellIdentityLte ci = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_LTE);
+ p.writeString(invalidMcc);
+ p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(CI);
+ p.writeInt(PCI);
+ p.writeInt(TAC);
+ p.writeInt(EARFCN);
+ p.writeInt(BANDWIDTH);
+ p.setDataPosition(0);
+
+ CellIdentityLte newCi = CellIdentityLte.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
new file mode 100644
index 0000000..5915062
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityTdscdma;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellIdentityTdscdma}. */
+
+public class CellIdentityTdscdmaTest extends AndroidTestCase {
+
+ // Cell identity ranges from 0 to 268435456.
+ private static final int CI = 268435456;
+ // Physical cell id ranges from 0 to 503.
+ private static final int PCI = 503;
+ // Tracking area code ranges from 0 to 65535.
+ private static final int TAC = 65535;
+ // Absolute RF Channel Number ranges from 0 to 262140.
+ private static final int UARFCN = 262140;
+ private static final int MCC = 120;
+ private static final int MNC = 260;
+ private static final String MCC_STR = "120";
+ private static final String MNC_STR = "260";
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+ // Location Area Code ranges from 0 to 65535.
+ private static final int LAC = 65535;
+ // UMTS Cell Identity ranges from 0 to 268435455.
+ private static final int CID = 268435455;
+
+ private static final int CPID = 12345;
+
+
+ @SmallTest
+ public void testDefaultConstructor() {
+ CellIdentityTdscdma ci =
+ new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(LAC, ci.getLac());
+ assertEquals(CID, ci.getCid());
+ assertEquals(CPID, ci.getCpid());
+ assertEquals(UARFCN, ci.getChannelNumber());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testConstructorWithEmptyMccMnc() {
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, "", "");
+
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+
+ ci = new CellIdentityTdscdma(MCC_STR, null, LAC, CID, CPID, UARFCN, "", "");
+
+ assertEquals(MCC_STR, ci.getMccString());
+ assertNull(ci.getMncString());
+
+ ci = new CellIdentityTdscdma(null, MNC_STR, LAC, CID, CPID, UARFCN, "", "");
+
+ assertEquals(MNC_STR, ci.getMncString());
+ assertNull(ci.getMccString());
+
+ ci = new CellIdentityTdscdma("", "", LAC, CID, CPID, UARFCN, "", "");
+
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, UARFCN, CPID, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithUnknowMccMnc() {
+ CellIdentityTdscdma ci =
+ new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_TDSCDMA);
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(CPID);
+ p.writeInt(UARFCN);
+ p.setDataPosition(0);
+
+ CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithInvalidMccMnc() {
+ final String invalidMcc = "randomStuff";
+ final String invalidMnc = "randomStuff";
+ CellIdentityTdscdma ci =
+ new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_TDSCDMA);
+ p.writeString(invalidMcc);
+ p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(CPID);
+ p.writeInt(UARFCN);
+ p.setDataPosition(0);
+
+ CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java
new file mode 100644
index 0000000..bb73f92
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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;
+
+import android.os.Parcel;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityLte;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class CellIdentityTest extends AndroidTestCase {
+
+ // Cell identity ranges from 0 to 268435456.
+ private static final int CI = 268435456;
+ // Physical cell id ranges from 0 to 503.
+ private static final int PCI = 503;
+ // Tracking area code ranges from 0 to 65535.
+ private static final int TAC = 65535;
+ // Absolute RF Channel Number ranges from 0 to 262140.
+ private static final int EARFCN = 262140;
+ private static final int BANDWIDTH = 5000; // kHz
+ private static final int MCC = 120;
+ private static final int MNC = 260;
+ private static final String MCC_STR = "120";
+ private static final String MNC_STR = "260";
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+ // Network Id ranges from 0 to 65535.
+ private static final int NETWORK_ID = 65535;
+ // CDMA System Id ranges from 0 to 32767
+ private static final int SYSTEM_ID = 32767;
+ // Base Station Id ranges from 0 to 65535
+ private static final int BASESTATION_ID = 65535;
+ // Longitude ranges from -2592000 to 2592000.
+ private static final int LONGITUDE = 2592000;
+ // Latitude ranges from -1296000 to 1296000.
+ private static final int LATITUDE = 1296000;
+
+ @SmallTest
+ public void testEquals() {
+ CellIdentity ciA = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellIdentity ciB = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG,
+ ALPHA_SHORT);
+ ciB = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG,
+ ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, null, ALPHA_LONG,
+ ALPHA_SHORT);
+ ciB = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG,
+ ALPHA_SHORT);
+
+ assertFalse(ciA.equals(ciB));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentity ci = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentity newCi = CellIdentity.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+
+ ci = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ newCi = CellIdentity.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java
new file mode 100644
index 0000000..d0e30b4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityWcdma;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellIdentityWcdma}. */
+
+public class CellIdentityWcdmaTest extends AndroidTestCase {
+
+ // Location Area Code ranges from 0 to 65535.
+ private static final int LAC = 65535;
+ // UMTS Cell Identity ranges from 0 to 268435455.
+ private static final int CID = 268435455;
+ // Primary Scrambling Coderanges from 0 to 511.
+ private static final int PSC = 511;
+ // UMTS Absolute RF Channel Number ranges from 0 to 65535.
+ private static final int UARFCN = 65535;
+ private static final int MCC = 120;
+ private static final int MNC = 260;
+ private static final String MCC_STR = "120";
+ private static final String MNC_STR = "260";
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+
+ @SmallTest
+ public void testDefaultConstructor() {
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(LAC, ci.getLac());
+ assertEquals(CID, ci.getCid());
+ assertEquals(PSC, ci.getPsc());
+ assertEquals(UARFCN, ci.getUarfcn());
+ assertEquals(UARFCN, ci.getChannelNumber());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testConstructorWithThreeDigitMnc() {
+ final String mncWithThreeDigit = "061";
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, mncWithThreeDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithThreeDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithThreeDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithTwoDigitMnc() {
+ final String mncWithTwoDigit = "61";
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, mncWithTwoDigit,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(61, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(mncWithTwoDigit, ci.getMncString());
+ assertEquals(MCC_STR + mncWithTwoDigit, ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testConstructorWithEmptyMccMnc() {
+ final String integerMaxValue = String.valueOf(Integer.MAX_VALUE);
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMobileNetworkOperator());
+
+ ci = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, "", "", ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(Integer.MAX_VALUE, ci.getMcc());
+ assertEquals(Integer.MAX_VALUE, ci.getMnc());
+ assertNull(ci.getMccString());
+ assertNull(ci.getMncString());
+ assertNull(ci.getMobileNetworkOperator());
+ }
+
+ @SmallTest
+ public void testFormerConstructor() {
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(MCC, MNC, LAC, CID, PSC);
+
+ assertEquals(LAC, ci.getLac());
+ assertEquals(CID, ci.getCid());
+ assertEquals(PSC, ci.getPsc());
+ assertEquals(MCC, ci.getMcc());
+ assertEquals(MNC, ci.getMnc());
+ assertEquals(MCC_STR, ci.getMccString());
+ assertEquals(MNC_STR, ci.getMncString());
+ assertEquals(MCC_STR + MNC_STR, ci.getMobileNetworkOperator());
+ assertNull(ci.getOperatorAlphaLong());
+ assertNull(ci.getOperatorAlphaShort());
+ }
+
+ @SmallTest
+ public void testEquals() {
+ CellIdentityWcdma ciA = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellIdentityWcdma ciB = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertTrue(ciA.equals(ciB));
+
+ ciA = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, null, ALPHA_LONG, ALPHA_SHORT);
+ ciB = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ assertFalse(ciA.equals(ciB));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR,
+ ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellIdentityWcdma newCi = CellIdentityWcdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithUnknowMccMnc() {
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_WCDMA);
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(PSC);
+ p.writeInt(UARFCN);
+ p.setDataPosition(0);
+
+ CellIdentityWcdma newCi = CellIdentityWcdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+
+ @SmallTest
+ public void testParcelWithInvalidMccMnc() {
+ final String invalidMcc = "randomStuff";
+ final String invalidMnc = "randomStuff";
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(CellIdentity.TYPE_WCDMA);
+ p.writeString(invalidMcc);
+ p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
+ p.writeInt(LAC);
+ p.writeInt(CID);
+ p.writeInt(PSC);
+ p.writeInt(UARFCN);
+ p.setDataPosition(0);
+
+ CellIdentityWcdma newCi = CellIdentityWcdma.CREATOR.createFromParcel(p);
+ assertEquals(ci, newCi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthCdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthCdmaTest.java
new file mode 100644
index 0000000..7ad38fc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthCdmaTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.telephony.CellSignalStrengthCdma;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/** Unit tests for {@link CellSignalStrengthCdma}. */
+
+public class CellSignalStrengthCdmaTest extends AndroidTestCase {
+
+ private static final int CDMA_DBM = 74;
+ private static final int CDMA_ECIO = 124;
+ private static final int EVDO_DBM = 23;
+ private static final int EVDO_ECIO = 108;
+ private static final int EVDO_SNR = 7;
+
+ @SmallTest
+ public void testConstructor() {
+ CellSignalStrengthCdma css = new CellSignalStrengthCdma(
+ CDMA_DBM, CDMA_ECIO, EVDO_DBM, EVDO_ECIO, EVDO_SNR);
+ assertEquals(-CDMA_DBM, css.getCdmaDbm());
+ assertEquals(-CDMA_ECIO, css.getCdmaEcio());
+ assertEquals(-EVDO_DBM, css.getEvdoDbm());
+ assertEquals(-EVDO_ECIO, css.getEvdoEcio());
+ assertEquals(EVDO_SNR, css.getEvdoSnr());
+ }
+
+ @SmallTest
+ public void testInvalidConstructor() {
+ CellSignalStrengthCdma css = new CellSignalStrengthCdma(-1, -1, -1, -1, -1);
+ assertEquals(Integer.MAX_VALUE, css.getCdmaDbm());
+ assertEquals(Integer.MAX_VALUE, css.getCdmaEcio());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoDbm());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoEcio());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoSnr());
+ }
+
+ @SmallTest
+ public void testDefaultConstructor() {
+ CellSignalStrengthCdma css = new CellSignalStrengthCdma();
+ assertEquals(Integer.MAX_VALUE, css.getCdmaDbm());
+ assertEquals(Integer.MAX_VALUE, css.getCdmaEcio());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoDbm());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoEcio());
+ assertEquals(Integer.MAX_VALUE, css.getEvdoSnr());
+ }
+
+ @SmallTest
+ public void testEquals() {
+ assertTrue(new CellSignalStrengthCdma(
+ CDMA_DBM, CDMA_ECIO, EVDO_DBM, EVDO_ECIO, EVDO_SNR).equals(
+ new CellSignalStrengthCdma(
+ CDMA_DBM, CDMA_ECIO, EVDO_DBM, EVDO_ECIO, EVDO_SNR)));
+ assertFalse(new CellSignalStrengthCdma(
+ CDMA_DBM, CDMA_ECIO, EVDO_DBM, EVDO_ECIO, EVDO_SNR).equals(
+ new CellSignalStrengthCdma(CDMA_DBM, CDMA_ECIO, -1, EVDO_ECIO, EVDO_SNR)));
+ }
+
+ @SmallTest
+ public void testParcel() {
+ CellSignalStrengthCdma css = new CellSignalStrengthCdma(
+ CDMA_DBM, CDMA_ECIO, EVDO_DBM, EVDO_ECIO, EVDO_SNR);
+
+ Parcel p = Parcel.obtain();
+ css.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ CellSignalStrengthCdma newCss = CellSignalStrengthCdma.CREATOR.createFromParcel(p);
+ assertEquals(css, newCss);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
new file mode 100644
index 0000000..a595c36
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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 com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.INetworkService;
+import android.telephony.INetworkServiceCallback;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.NetworkServiceCallback;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class CellularNetworkServiceTest extends TelephonyTest {
+ CellularNetworkService mCellularNetworkService;
+
+ @Mock
+ private INetworkServiceCallback mCallback;
+
+ private void addNetworkService() {
+ mCellularNetworkService = new CellularNetworkService();
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = "com.android.phone";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
+ IntentFilter filter = new IntentFilter();
+ mContextFixture.addService(
+ NetworkService.NETWORK_SERVICE_INTERFACE,
+ null,
+ "com.android.phone",
+ mCellularNetworkService.mBinder,
+ serviceInfo,
+ filter);
+ }
+ INetworkService.Stub mBinder;
+
+ @Before
+ public void setUp() throws Exception {
+
+ logd("CellularNetworkServiceTest +Setup!");
+ super.setUp("CellularNetworkServiceTest");
+
+ mContextFixture.putResource(R.string.config_wwan_network_service_package,
+ "com.android.phone");
+ addNetworkService();
+ mBinder = mCellularNetworkService.mBinder;
+ mBinder.createNetworkServiceProvider(0);
+
+ int dds = SubscriptionManager.getDefaultDataSubscriptionId();
+ doReturn(dds).when(mPhone).getSubId();
+
+ logd("CellularNetworkServiceTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @MediumTest
+ public void testGetNetworkRegistrationState() {
+ int voiceRegState = NetworkRegistrationState.REG_STATE_HOME;
+ int dataRegState = NetworkRegistrationState.REG_STATE_HOME;
+ int voiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ int dataRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ int domain = NetworkRegistrationState.DOMAIN_CS;
+
+ boolean cssSupported = true;
+ int roamingIndicator = 1;
+ int systemIsInPrl = 2;
+ int defaultRoamingIndicator = 3;
+ int reasonForDenial = 0;
+ int maxDataCalls = 4;
+ int[] availableServices = new int[] {
+ NetworkRegistrationState.SERVICE_TYPE_VOICE,
+ NetworkRegistrationState.SERVICE_TYPE_SMS,
+ NetworkRegistrationState.SERVICE_TYPE_VIDEO
+ };
+
+ mSimulatedCommands.setVoiceRegState(voiceRegState);
+ mSimulatedCommands.setVoiceRadioTech(voiceRadioTech);
+ mSimulatedCommands.setDataRegState(dataRegState);
+ mSimulatedCommands.setDataRadioTech(dataRadioTech);
+ mSimulatedCommands.mCssSupported = cssSupported;
+ mSimulatedCommands.mRoamingIndicator = roamingIndicator;
+ mSimulatedCommands.mSystemIsInPrl = systemIsInPrl;
+ mSimulatedCommands.mDefaultRoamingIndicator = defaultRoamingIndicator;
+ mSimulatedCommands.mReasonForDenial = reasonForDenial;
+ mSimulatedCommands.mMaxDataCalls = maxDataCalls;
+
+ mSimulatedCommands.notifyNetworkStateChanged();
+
+ try {
+ mBinder.getNetworkRegistrationState(0, domain, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ waitForMs(1000);
+
+ NetworkRegistrationState expectedState = new NetworkRegistrationState(
+ domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
+ ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
+ false, availableServices, null, cssSupported,
+ roamingIndicator, systemIsInPrl, defaultRoamingIndicator);
+
+ try {
+ verify(mCallback, times(1)).onGetNetworkRegistrationStateComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ domain = NetworkRegistrationState.DOMAIN_PS;
+ availableServices = new int[] {NetworkRegistrationState.SERVICE_TYPE_DATA};
+ try {
+ mBinder.getNetworkRegistrationState(0, domain, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ waitForMs(1000);
+
+ expectedState = new NetworkRegistrationState(
+ domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
+ ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
+ false, availableServices, null, maxDataCalls);
+
+ try {
+ verify(mCallback, times(1)).onGetNetworkRegistrationStateComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 7e8a439..d2e28ca 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -27,6 +27,7 @@
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.DownloadManager;
import android.app.NotificationManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
@@ -39,9 +40,11 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
@@ -240,6 +243,8 @@
return mEuiccManager;
case Context.TELECOM_SERVICE:
return mTelecomManager;
+ case Context.DOWNLOAD_SERVICE:
+ return mDownloadManager;
case Context.DISPLAY_SERVICE:
case Context.POWER_SERVICE:
// PowerManager and DisplayManager are final classes so cannot be mocked,
@@ -251,16 +256,34 @@
}
@Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass == SubscriptionManager.class) {
+ return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
+ }
+ return super.getSystemServiceName(serviceClass);
+ }
+
+ @Override
public int getUserId() {
return 0;
}
@Override
+ public AssetManager getAssets() {
+ return mAssetManager;
+ }
+
+ @Override
public Resources getResources() {
return mResources;
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
+
+ @Override
public String getOpPackageName() {
return "com.android.internal.telephony";
}
@@ -271,6 +294,11 @@
}
@Override
+ public Resources.Theme getTheme() {
+ return null;
+ }
+
+ @Override
public void unregisterReceiver(BroadcastReceiver receiver) {
}
@@ -441,6 +469,11 @@
}
@Override
+ public void enforcePermission(String permission, int pid, int uid, String message) {
+ enforceCallingOrSelfPermission(permission, message);
+ }
+
+ @Override
public int checkCallingOrSelfPermission(String permission) {
if (mPermissionTable.contains(permission)
|| mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
@@ -453,6 +486,11 @@
}
@Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return checkCallingOrSelfPermission(permission);
+ }
+
+ @Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mSharedPreferences;
}
@@ -494,14 +532,17 @@
// when(...) logic to be used to add specific little responses where needed.
private final Resources mResources = mock(Resources.class);
+ private final ApplicationInfo mApplicationInfo = mock(ApplicationInfo.class);
private final PackageManager mPackageManager = mock(PackageManager.class);
private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ private final DownloadManager mDownloadManager = mock(DownloadManager.class);
private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
private final NotificationManager mNotificationManager = mock(NotificationManager.class);
private final UserManager mUserManager = mock(UserManager.class);
private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
private final AlarmManager mAlarmManager = mock(AlarmManager.class);
+ private final AssetManager mAssetManager = new AssetManager();
private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
private final UsageStatsManager mUsageStatManager = null;
private final WifiManager mWifiManager = mock(WifiManager.class);
@@ -582,6 +623,10 @@
doReturn(values).when(mResources).getIntArray(eq(id));
}
+ public void putIntResource(int id, int value) {
+ doReturn(value).when(mResources).getInteger(eq(id));
+ }
+
public PersistableBundle getCarrierConfigBundle() {
return mBundle;
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java b/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java
new file mode 100644
index 0000000..f9a883a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ExponentialBackoffTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.ims.ImsTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+@RunWith(AndroidJUnit4.class)
+public class ExponentialBackoffTest extends ImsTestBase {
+
+ private static final int START_DELAY_MS = 10;
+ private static final int MAXIMUM_DELAY_MS = 1000;
+ private static final int MULTIPLIER = 2;
+
+ private ExponentialBackoff mBackoffUnderTest;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ @Mock private Runnable mRunnable;
+ @Mock private ExponentialBackoff.HandlerAdapter mHandlerAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mBackoffUnderTest = new ExponentialBackoff(
+ START_DELAY_MS, MAXIMUM_DELAY_MS, MULTIPLIER, mHandler, mRunnable);
+ mBackoffUnderTest.setHandlerAdapter(mHandlerAdapter);
+ doAnswer(invocation -> mHandler.postDelayed((Runnable) invocation.getArguments()[0],
+ (long) invocation.getArguments()[1])
+ ).when(mHandlerAdapter).postDelayed(any(Runnable.class), anyLong());
+ doAnswer(invocation -> {
+ mHandler.removeCallbacks((Runnable) invocation.getArguments()[0]);
+ return null;
+ }).when(mHandlerAdapter).removeCallbacks(any(Runnable.class));
+ }
+
+ @After
+ public void tearDown() {
+ mBackoffUnderTest.stop();
+ }
+
+ @Test
+ public void testStartBackoff() {
+ mBackoffUnderTest.start();
+ long delay = mBackoffUnderTest.getCurrentDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
+
+ // The runnable is executed after timeout event occurred.
+ verify(mRunnable).run();
+ }
+
+ @Test
+ public void testStopBackoff() {
+ mBackoffUnderTest.start();
+
+ mBackoffUnderTest.stop();
+ // removeCallbacks is called during start() and stop()
+ verify(mHandlerAdapter, times(2)).removeCallbacks(mRunnable);
+ }
+
+ @Test
+ public void testDelayIncreasedExponentially() {
+ mBackoffUnderTest.start();
+ // guarantee START_DELAY_MS * 2 ^ i <= MAXIMUM_DELAY_MS
+ for (int i = 1; i < 5; i++) {
+ mBackoffUnderTest.notifyFailed();
+ long delay = mBackoffUnderTest.getCurrentDelay();
+ long minDelay = (long) (START_DELAY_MS * Math.pow(MULTIPLIER, i - 1));
+ long maxDelay = (long) (START_DELAY_MS * Math.pow(MULTIPLIER, i));
+ assertTrue("delay = " + delay + " minDelay = " + minDelay, delay >= minDelay);
+ assertTrue("delay = " + delay + " maxDelay = " + maxDelay, delay <= maxDelay);
+ }
+ }
+
+ @Test
+ public void testDelayShouldNotExceededTheMaximumLimit() {
+ mBackoffUnderTest.start();
+ // guarantee START_DELAY_MS * 2 ^ 30 > MAXIMUM_DELAY_MS
+ for (int i = 1; i < 30; i++) {
+ mBackoffUnderTest.notifyFailed();
+ }
+ long delay = mBackoffUnderTest.getCurrentDelay();
+ assertTrue(
+ "delay = " + delay + " maximumDelay = " + MAXIMUM_DELAY_MS,
+ delay <= MAXIMUM_DELAY_MS);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index 4c1f5c7..f3066f4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -185,6 +185,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMOCallPendingHangUp() {
@@ -295,6 +296,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMOCallSwitchHangupForeGround() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 309568a..77eb7dc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -31,6 +31,7 @@
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;
@@ -57,8 +58,11 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.test.SimulatedCommands;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccException;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import org.junit.After;
import org.junit.Before;
@@ -79,6 +83,7 @@
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;
private class GsmCdmaPhoneTestHandler extends HandlerThread {
@@ -149,14 +154,19 @@
@Test
@SmallTest
public void testHandleActionCarrierConfigChanged() {
+ Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(50);
+ verify(mSST, times(1)).pollState();
+
// set voice radio tech in RIL to 1xRTT. ACTION_CARRIER_CONFIG_CHANGED should trigger a
// query and change phone type
mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
assertTrue(mPhoneUT.isPhoneTypeGsm());
- Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mContext.sendBroadcast(intent);
waitForMs(50);
assertTrue(mPhoneUT.isPhoneTypeCdmaLte());
+ verify(mSST, times(2)).pollState();
}
@Test
@@ -298,7 +308,8 @@
mCT.mRingingCall = mGsmCdmaCall;
doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();
- Connection connection = mPhoneUT.dial("1234567890", 0);
+ Connection connection = mPhoneUT.dial("1234567890",
+ new PhoneInternalInterface.DialArgs.Builder().build());
verify(mCT).dial("1234567890", null, null);
} catch (CallStateException e) {
fail();
@@ -377,8 +388,9 @@
assertEquals("*86", mPhoneUT.getVoiceMailNumber());
// config_telephony_use_own_number_for_voicemail
- mContextFixture.putBooleanResource(
- com.android.internal.R.bool.config_telephony_use_own_number_for_voicemail, true);
+ mContextFixture.getCarrierConfigBundle()
+ .putBoolean(CarrierConfigManager
+ .KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, true);
doReturn(voiceMailNumber).when(mSST).getMdnNumber();
assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
@@ -391,7 +403,7 @@
// voicemail number from sharedPreference
mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mRuimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
+ verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
messageArgumentCaptor.capture());
Message msg = messageArgumentCaptor.getValue();
@@ -468,8 +480,8 @@
doReturn(imsi).when(mSimRecords).getIMSI();
mPhoneUT.getCallForwardingOption(CF_REASON_UNCONDITIONAL, null);
verify(mSimulatedCommandsVerifier).queryCallForwardStatus(
- eq(CF_REASON_UNCONDITIONAL), anyInt(), nullable(String.class),
- nullable(Message.class));
+ eq(CF_REASON_UNCONDITIONAL), eq(CommandsInterface.SERVICE_CLASS_VOICE),
+ nullable(String.class), nullable(Message.class));
waitForMs(50);
verify(mSimRecords).setVoiceCallForwardingFlag(anyInt(), anyBoolean(),
nullable(String.class));
@@ -513,6 +525,7 @@
* received when obj is created and that are received on phone type switch
*/
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testHandleInitialMessages() {
@@ -804,4 +817,60 @@
waitForMs(100);
verify(mEriManager, times(1)).loadEriFile();
}
+
+ @Test
+ @SmallTest
+ public void testGetIccCardUnknownAndAbsent() {
+ // If UiccSlot.isStateUnknown is true, we should return a dummy IccCard with the state
+ // set to UNKNOWN
+ doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
+ UiccSlot mockSlot = mock(UiccSlot.class);
+ doReturn(mockSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
+ doReturn(true).when(mockSlot).isStateUnknown();
+
+ IccCard iccCard = mPhoneUT.getIccCard();
+ assertEquals(IccCardConstants.State.UNKNOWN, iccCard.getState());
+
+ // if isStateUnknown is false, we should return a dummy IccCard with the state set to
+ // ABSENT
+ doReturn(false).when(mockSlot).isStateUnknown();
+ iccCard = mPhoneUT.getIccCard();
+ assertEquals(IccCardConstants.State.ABSENT, iccCard.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetEmptyIccCard() {
+ doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
+
+ IccCard iccCard = mPhoneUT.getIccCard();
+
+ // The iccCard should be a dummy object, not null.
+ assertTrue(!(iccCard instanceof UiccProfile));
+
+ assertTrue(iccCard != null);
+ assertEquals(IccCardConstants.State.UNKNOWN, iccCard.getState());
+ assertEquals(null, iccCard.getIccRecords());
+ assertEquals(false, iccCard.getIccLockEnabled());
+ assertEquals(false, iccCard.getIccFdnEnabled());
+ assertEquals(false, iccCard.isApplicationOnIcc(
+ IccCardApplicationStatus.AppType.APPTYPE_SIM));
+ assertEquals(false, iccCard.hasIccCard());
+ assertEquals(false, iccCard.getIccPin2Blocked());
+ assertEquals(false, iccCard.getIccPuk2Blocked());
+
+ Message onComplete = mTestHandler.obtainMessage(EVENT_SET_ICC_LOCK_ENABLED);
+ iccCard.setIccLockEnabled(true, "password", onComplete);
+
+ waitForMs(100);
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ // Verify that message is sent back with exception.
+ verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
+ anyLong());
+ Message message = messageArgumentCaptor.getAllValues().get(0);
+ AsyncResult ret = (AsyncResult) message.obj;
+ assertEquals(EVENT_SET_ICC_LOCK_ENABLED, message.what);
+ assertTrue(ret.exception != null);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
new file mode 100644
index 0000000..3bc2c1b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.UserManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class IccSmsInterfaceManagerTest {
+ private static final String PACKAGE = "com.example.package";
+ private static final String MESSAGE = "msg";
+
+ private HandlerThread mHandlerThread;
+
+ @Mock
+ private Phone mMockPhone;
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private AppOpsManager mMockAppOps;
+ @Mock
+ private UserManager mMockUserManager;
+ @Mock
+ private SmsDispatchersController mMockDispatchersController;
+
+ private IccSmsInterfaceManager mIccSmsInterfaceManager;
+
+ private boolean mCallerHasCarrierPrivileges;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mHandlerThread = new HandlerThread("IccSmsInterfaceManagerTest");
+ mHandlerThread.start();
+ final CountDownLatch initialized = new CountDownLatch(1);
+ new Handler(mHandlerThread.getLooper()).post(() -> {
+ mIccSmsInterfaceManager = new IccSmsInterfaceManager(
+ mMockPhone, mMockContext, mMockAppOps, mMockUserManager,
+ mMockDispatchersController) {
+ @Override
+ public void enforceCallerIsImsAppOrCarrierApp(String message) {
+ if (!mCallerHasCarrierPrivileges) {
+ throw new SecurityException(message);
+ }
+ }
+ };
+ initialized.countDown();
+ });
+ // Wait for object to initialize.
+ if (!initialized.await(30, TimeUnit.SECONDS)) {
+ fail("Could not initialize IccSmsInterfaceManager");
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_grant() {
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_noGrant() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_noAppOps() {
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_grantViaCarrierApp() {
+ mCallerHasCarrierPrivileges = true;
+ // Other permissions shouldn't matter.
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE);
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_grantViaModifyAndSend() {
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noModify() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noSendSmsPermission() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noAppOps() {
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
new file mode 100644
index 0000000..f052a9a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.HandlerThread;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class ImsSmsDispatcherTest extends TelephonyTest {
+ @Mock private SmsDispatchersController mSmsDispatchersController;
+ @Mock private SMSDispatcher.SmsTracker mSmsTracker;
+ private ImsSmsDispatcher mImsSmsDispatcher;
+ private ImsSmsDispatcherTestHandler mImsSmsDispatcherTestHandler;
+
+
+ private class ImsSmsDispatcherTestHandler extends HandlerThread {
+
+ private ImsSmsDispatcherTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mImsSmsDispatcher = spy(new ImsSmsDispatcher(mPhone, mSmsDispatchersController));
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mImsSmsDispatcherTestHandler = new ImsSmsDispatcherTestHandler(TAG);
+ mImsSmsDispatcherTestHandler.start();
+ waitUntilReady();
+ }
+
+ @Test @SmallTest
+ public void testSendSms() {
+ int token = mImsSmsDispatcher.mNextToken.get();
+ int trackersSize = mImsSmsDispatcher.mTrackers.size();
+
+ mImsSmsDispatcher.sendSms(mSmsTracker);
+
+ assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+ assertEquals(trackersSize + 1, mImsSmsDispatcher.mTrackers.size());
+ }
+
+ @Test @SmallTest
+ public void testFallbackToPstn() {
+ int token = 0;
+ mImsSmsDispatcher.mTrackers.put(token, mSmsTracker);
+ doNothing().when(mSmsDispatchersController).sendRetrySms(mSmsTracker);
+
+ mImsSmsDispatcher.fallbackToPstn(token, mSmsTracker);
+
+ verify(mSmsDispatchersController).sendRetrySms(mSmsTracker);
+ assertNull(mImsSmsDispatcher.mTrackers.get(token));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mImsSmsDispatcher = null;
+ mImsSmsDispatcherTestHandler.quit();
+ super.tearDown();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
index 761bd5f..e24dd21 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
@@ -64,6 +64,7 @@
@Before
public void setUp() throws Exception {
+
mPublicKey = createPublicKey(TEST_CERT);
mImsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", TelephonyManager.KEY_TYPE_WLAN,
"key1=value", mPublicKey, mDate);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
new file mode 100644
index 0000000..bcc1730
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellInfoGsm;
+import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class LocaleTrackerTest extends TelephonyTest {
+
+ private static final String US_MCC = "310";
+ private static final String FAKE_MNC = "123";
+ private static final String US_COUNTRY_CODE = "us";
+ private static final String COUNTRY_CODE_UNAVAILABLE = "";
+
+ private LocaleTracker mLocaleTracker;
+ private LocaleTrackerTestHandler mLocaleTrackerTestHandler;
+
+ private CellInfoGsm mCellInfo;
+ private WifiManager mWifiManager;
+
+ private class LocaleTrackerTestHandler extends HandlerThread {
+
+ private LocaleTrackerTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mLocaleTracker = new LocaleTracker(mPhone, this.getLooper());
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("LocaleTrackerTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+
+ // This is a workaround to bypass setting system properties, which causes access violation.
+ doReturn(-1).when(mPhone).getPhoneId();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+
+ mCellInfo = new CellInfoGsm();
+ mCellInfo.setCellIdentity(new CellIdentityGsm(Integer.parseInt(US_MCC),
+ Integer.parseInt(FAKE_MNC), 0, 0));
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ doReturn(true).when(mSST).getDesiredPowerState();
+
+ mLocaleTrackerTestHandler = new LocaleTrackerTestHandler(getClass().getSimpleName());
+ mLocaleTrackerTestHandler.start();
+ waitUntilReady();
+ logd("LocaleTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mLocaleTracker.removeCallbacksAndMessages(null);
+ mLocaleTrackerTestHandler.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateOperatorNumericSync() throws Exception {
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateOperatorNumericAsync() throws Exception {
+ mLocaleTracker.updateOperatorNumericAsync(US_MCC + FAKE_MNC);
+ waitForMs(100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testNoSim() throws Exception {
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testBootupInAirplaneModeOn() throws Exception {
+ doReturn(false).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testTogglingAirplaneMode() throws Exception {
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+
+ doReturn(false).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(true).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testCellInfoUnavailableRetry() throws Exception {
+ doReturn(null).when(mPhone).getAllCellInfo(isNull());
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ waitForHandlerActionDelayed(mLocaleTracker, 100, 2500);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testOutOfAirplaneMode() throws Exception {
+ doReturn(null).when(mPhone).getAllCellInfo(isNull());
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+ AsyncResult ar = new AsyncResult(null, ss, null);
+ mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(3, ar));
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
new file mode 100644
index 0000000..eb52e7c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CellIdentityLte;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.TelephonyManager;
+
+import org.junit.Test;
+
+/** Unit tests for {@link NetworkRegistrationState}. */
+
+public class NetworkRegistrationStateTest {
+
+ @Test
+ @SmallTest
+ public void testParcel() {
+ NetworkRegistrationState nrs = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_CS,
+ TransportType.WWAN,
+ NetworkRegistrationState.REG_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ 0,
+ false,
+ new int[]{NetworkRegistrationState.SERVICE_TYPE_DATA},
+ new CellIdentityLte());
+
+ Parcel p = Parcel.obtain();
+ nrs.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ NetworkRegistrationState newNrs = NetworkRegistrationState.CREATOR.createFromParcel(p);
+ assertEquals(nrs, newNrs);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
index 7284083..ed1a723 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
@@ -20,11 +20,11 @@
import android.os.Parcel;
import android.support.test.filters.SmallTest;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.EutranBand;
+import android.telephony.AccessNetworkConstants.GeranBand;
import android.telephony.NetworkScanRequest;
import android.telephony.RadioAccessSpecifier;
-import android.telephony.RadioNetworkConstants.EutranBands;
-import android.telephony.RadioNetworkConstants.GeranBands;
-import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
import org.junit.Test;
@@ -37,12 +37,12 @@
@Test
@SmallTest
public void testParcel() {
- int ranGsm = RadioAccessNetworks.GERAN;
- int[] gsmBands = {GeranBands.BAND_T380, GeranBands.BAND_T410};
+ int ranGsm = AccessNetworkType.GERAN;
+ int[] gsmBands = {GeranBand.BAND_T380, GeranBand.BAND_T410};
int[] gsmChannels = {1, 2, 3, 4};
RadioAccessSpecifier gsm = new RadioAccessSpecifier(ranGsm, gsmBands, gsmChannels);
- int ranLte = RadioAccessNetworks.EUTRAN;
- int[] lteBands = {EutranBands.BAND_10, EutranBands.BAND_11};
+ int ranLte = AccessNetworkType.EUTRAN;
+ int[] lteBands = {EutranBand.BAND_10, EutranBand.BAND_11};
int[] lteChannels = {5, 6, 7, 8};
RadioAccessSpecifier lte = new RadioAccessSpecifier(ranLte, lteBands, lteChannels);
RadioAccessSpecifier[] ras = {gsm, lte};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
index f149211..47a9979 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
@@ -42,8 +42,7 @@
ArrayList<CellInfo> infos = new ArrayList<CellInfo>();
CellIdentityGsm cig = new CellIdentityGsm(310, 310, 1, 2, 3, 4);
- CellSignalStrengthGsm cssg = new CellSignalStrengthGsm();
- cssg.initialize(5, 6, 7);
+ CellSignalStrengthGsm cssg = new CellSignalStrengthGsm(5, 6, 7);
CellInfoGsm gsm = new CellInfoGsm();
gsm.setRegistered(true);
gsm.setTimeStampType(8);
@@ -53,8 +52,7 @@
infos.add(gsm);
CellIdentityLte cil = new CellIdentityLte(320, 320, 11, 12, 13, 14);
- CellSignalStrengthLte cssl = new CellSignalStrengthLte();
- cssl.initialize(15, 16, 17, 18, 19, 20);
+ CellSignalStrengthLte cssl = new CellSignalStrengthLte(15, 16, 17, 18, 19, 20);
CellInfoLte lte = new CellInfoLte();
lte.setRegistered(false);
lte.setTimeStampType(21);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
new file mode 100644
index 0000000..028cbcc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright 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;
+
+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.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+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.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class NewNitzStateMachineTest extends TelephonyTest {
+
+ // A country with a single zone : the zone can be guessed from the country.
+ // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals.
+ private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/London")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("gb")
+ .build();
+
+ // A country that has multiple zones, but there is only one matching time zone at the time :
+ // the zone cannot be guessed from the country alone, but can be guessed from the country +
+ // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("America/Los_Angeles")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("us")
+ .build();
+
+ // A country with a single zone: the zone can be guessed from the country alone. CZ never uses
+ // UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/Prague")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("cz")
+ .build();
+
+ @Mock
+ private NewNitzStateMachine.DeviceState mDeviceState;
+
+ @Mock
+ private NewTimeServiceHelper mTimeServiceHelper;
+
+ private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
+
+ private NewNitzStateMachine mNitzStateMachine;
+
+ @Before
+ public void setUp() throws Exception {
+ logd("NitzStateMachineTest +Setup!");
+ super.setUp("NitzStateMachineTest");
+
+ // In tests we use the real TimeZoneLookupHelper.
+ mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
+ mNitzStateMachine = new NewNitzStateMachine(
+ mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
+
+ logd("ServiceStateTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ checkNoUnverifiedSetOperations(mTimeServiceHelper);
+
+ super.tearDown();
+ }
+
+ @Test
+ public void test_uniqueUsZone_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "America/New_York", false /* allZonesHaveSameOffset */,
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ // isOnlyMatch == true, so the combination of country + NITZ should be enough.
+ OffsetResult expectedLookupResult =
+ new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_unitedKingdom_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
+ // the zone knowing only the country.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "Europe/London", true /* allZonesHaveSameOffset */,
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ OffsetResult expectedLookupResult =
+ new OffsetResult("Europe/London", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to guess the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country is not enough to guess the time zone and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be suggested from NITZ.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone would be enough for time zone detection, but it's disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be suggested from NITZ.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ + country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+
+ // The NITZ + country is enough to detect the time zone.
+ // NOTE: setting the time zone happens twice because of a quirk in NitzStateMachine: it
+ // handles the country lookup / set, then combines the country with the NITZ state and does
+ // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
+ script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone, but the NITZ + country is
+ // also sufficient so we expect the time zone to be set twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The time will be suggested from the NITZ signal.
+ // The combination of NITZ + country will cause the time zone to be set.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out it is bogus.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ. If the NITZ signal
+ // wasn't obviously bogus we'd try to set it twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be suggested.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be suggested.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+
+ // Create an NITZ signal with an explicit time zone (as can happen on emulators)
+ NitzData originalNitzData = originalNitzSignal.getValue();
+ // A time zone that is obviously not in the US, but it should not be questioned.
+ String emulatorTimeZoneId = "Europe/London";
+ NitzData emulatorNitzData = NitzData.createForTests(
+ originalNitzData.getLocalOffsetMillis(),
+ originalNitzData.getDstAdjustmentMillis(),
+ originalNitzData.getCurrentTimeInMillis(),
+ java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+ originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+ // Simulate receiving the emulator NITZ signal.
+ script.nitzReceived(emulatorNitzSignal)
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), emulatorTimeZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Nothing should be set. The country is not valid.
+ script.countryReceived("").verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyTimeSuggestedAndZoneSetAndReset(scenario.getNitzSignal(), expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // The time zone should be set (but the country is not valid so it's unlikely to be
+ // correct).
+ script.countryReceived("").verifyOnlyTimeZoneWasSetAndReset(expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ /**
+ * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
+ * usually multiple zones that will share the same UTC offset so we get a low quality / low
+ * confidence answer, but the zone we find should at least have the correct offset.
+ */
+ private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
+ OffsetResult result =
+ mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
+ String expectedZoneId = result.zoneId;
+ // All our scenarios should return multiple matches. The only cases where this wouldn't be
+ // true are places that use offsets like XX:15, XX:30 and XX:45.
+ assertFalse(result.isOnlyMatch);
+ assertSameOffset(scenario.getActualTimeMillis(), expectedZoneId, scenario.getTimeZoneId());
+ return expectedZoneId;
+ }
+
+ private static void assertSameOffset(long timeMillis, String zoneId1, String zoneId2) {
+ assertEquals(TimeZone.getTimeZone(zoneId1).getOffset(timeMillis),
+ TimeZone.getTimeZone(zoneId2).getOffset(timeMillis));
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * A helper class for common test operations involving a device.
+ */
+ class Script {
+ private final Device mDevice;
+
+ Script(Device device) {
+ this.mDevice = device;
+ }
+
+ Script countryReceived(String countryIsoCode) {
+ mDevice.networkCountryKnown(countryIsoCode);
+ return this;
+ }
+
+ Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.nitzSignalReceived(nitzSignal);
+ return this;
+ }
+
+ Script verifyNothingWasSetAndReset() {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasNotSuggested();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId, times);
+ mDevice.verifyTimeWasNotSuggested();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
+ return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
+ }
+
+ Script verifyOnlyTimeWasSuggestedAndReset(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.verifyTimeZoneWasNotSet();
+
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ mDevice.verifyTimeWasSuggested(time);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyTimeSuggestedAndZoneSetAndReset(
+ TimestampedValue<NitzData> nitzSignal, String timeZoneId) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId);
+
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ mDevice.verifyTimeWasSuggested(time);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script reset() {
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+ }
+
+ /**
+ * An abstraction of a device for use in telephony time zone detection tests. It can be used to
+ * retrieve device state, modify device state and verify changes.
+ */
+ class Device {
+
+ private final long mInitialSystemClockMillis;
+ private final long mInitialRealtimeMillis;
+ private final boolean mTimeZoneDetectionEnabled;
+ private final boolean mTimeZoneSettingInitialized;
+
+ Device(long initialSystemClockMillis, long initialRealtimeMillis,
+ boolean timeZoneDetectionEnabled, boolean timeZoneSettingInitialized) {
+ mInitialSystemClockMillis = initialSystemClockMillis;
+ mInitialRealtimeMillis = initialRealtimeMillis;
+ mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
+ mTimeZoneSettingInitialized = timeZoneSettingInitialized;
+ }
+
+ void initialize() {
+ // Set initial configuration.
+ when(mDeviceState.getIgnoreNitz()).thenReturn(false);
+ when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
+ when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
+
+ // Simulate the country not being known.
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
+
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
+ when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
+ .thenReturn(mTimeZoneDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneSettingInitialized())
+ .thenReturn(mTimeZoneSettingInitialized);
+ }
+
+ void networkCountryKnown(String countryIsoCode) {
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
+ mNitzStateMachine.handleNetworkCountryCodeSet(true);
+ }
+
+ void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
+ mNitzStateMachine.handleNitzReceived(nitzSignal);
+ }
+
+ void verifyTimeZoneWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId) {
+ verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId, int times) {
+ verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
+ }
+
+ void verifyTimeWasNotSuggested() {
+ verify(mTimeServiceHelper, times(0)).suggestDeviceTime(any());
+ }
+
+ void verifyTimeWasSuggested(TimestampedValue<Long> expectedTime) {
+ verify(mTimeServiceHelper, times(1)).suggestDeviceTime(eq(expectedTime));
+ }
+
+ /**
+ * Used after calling verify... methods to reset expectations.
+ */
+ void resetInvocations() {
+ clearInvocations(mTimeServiceHelper);
+ }
+
+ void checkNoUnverifiedSetOperations() {
+ NewNitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
+ }
+ }
+
+ /** A class used to construct a Device. */
+ class DeviceBuilder {
+
+ private long mInitialSystemClock;
+ private long mInitialRealtimeMillis;
+ private boolean mTimeZoneDetectionEnabled;
+ private boolean mTimeZoneSettingInitialized;
+
+ Device initialize() {
+ Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
+ mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
+ device.initialize();
+ return device;
+ }
+
+ DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
+ mTimeZoneSettingInitialized = initialized;
+ return this;
+ }
+
+ DeviceBuilder setClocksFromScenario(Scenario scenario) {
+ mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
+ mInitialSystemClock = scenario.getInitialSystemClockMillis();
+ return this;
+ }
+ }
+
+ /**
+ * A scenario used during tests. Describes a fictional reality.
+ */
+ static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+ private final TimeZone mZone;
+ private final String mNetworkCountryIsoCode;
+
+ private TimestampedValue<NitzData> mNitzSignal;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
+ String zoneId, String countryIsoCode) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ mZone = TimeZone.getTimeZone(zoneId);
+ mNetworkCountryIsoCode = countryIsoCode;
+ }
+
+ TimestampedValue<NitzData> getNitzSignal() {
+ if (mNitzSignal == null) {
+ int[] offsets = new int[2];
+ mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
+ int zoneOffsetMillis = offsets[0] + offsets[1];
+ NitzData nitzData = NitzData.createForTests(
+ zoneOffsetMillis, offsets[1], mActualTimeMillis,
+ null /* emulatorHostTimeZone */);
+ mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
+ }
+ return mNitzSignal;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ String getNetworkCountryIsoCode() {
+ return mNetworkCountryIsoCode;
+ }
+
+ String getTimeZoneId() {
+ return mZone.getID();
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+ private String mZoneId;
+ private String mCountryIsoCode;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
+ second);
+ return this;
+ }
+
+ Builder setTimeZone(String zoneId) {
+ mZoneId = zoneId;
+ return this;
+ }
+
+ Builder setCountryIso(String isoCode) {
+ mCountryIsoCode = isoCode;
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis, mZoneId, mCountryIsoCode);
+ }
+ }
+ }
+
+ /**
+ * Confirms all mTimeServiceHelper side effects were verified.
+ */
+ private static void checkNoUnverifiedSetOperations(NewTimeServiceHelper mTimeServiceHelper) {
+ // We don't care about current auto time / time zone state retrievals / listening so we can
+ // use "at least 0" times to indicate they don't matter.
+ verify(mTimeServiceHelper, atLeast(0)).setListener(any());
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+ verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+ verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
+ verifyNoMoreInteractions(mTimeServiceHelper);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java
new file mode 100644
index 0000000..e9d0b44
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzDataTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+public class NitzDataTest {
+
+ @Test
+ public void testParse_dateOutsideAllowedRange() {
+ assertNull(NitzData.parse("38/06/20,00:00:00+0"));
+ }
+
+ @Test
+ public void testParse_missingRequiredFields() {
+ // "yy/mm/dd,hh:mm:ss(+/-)tz[,dt[,tzid]]"
+
+ // No tz field.
+ assertNull(NitzData.parse("38/06/20,00:00:00"));
+ }
+
+ @Test
+ public void testParse_withDst() {
+ // "yy/mm/dd,hh:mm:ss(+/-)tz[,dt[,tzid]]"
+ // tz, dt are in number of quarter-hours
+ {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03-1,0");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(-1 * 15), nitz.getLocalOffsetMillis());
+ assertEquals(0, nitz.getDstAdjustmentMillis().longValue());
+ assertNull(nitz.getEmulatorHostTimeZone());
+ }
+ {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03+8,4");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(8 * 15), nitz.getLocalOffsetMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(4 * 15),
+ nitz.getDstAdjustmentMillis().longValue());
+ assertNull(nitz.getEmulatorHostTimeZone());
+ }
+ {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03-8,4");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(-8 * 15), nitz.getLocalOffsetMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(4 * 15),
+ nitz.getDstAdjustmentMillis().longValue());
+ assertNull(nitz.getEmulatorHostTimeZone());
+ }
+ }
+
+ @Test
+ public void testParse_noDstField() {
+ {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03+4");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(4 * 15), nitz.getLocalOffsetMillis());
+ assertNull(nitz.getDstAdjustmentMillis());
+ assertNull(nitz.getEmulatorHostTimeZone());
+ }
+ {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03-4");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(-4 * 15), nitz.getLocalOffsetMillis());
+ assertNull(nitz.getDstAdjustmentMillis());
+ assertNull(nitz.getEmulatorHostTimeZone());
+ }
+ }
+
+ @Test
+ public void testParse_androidEmulatorTimeZoneExtension() {
+ NitzData nitz = NitzData.parse("15/06/20,01:02:03-32,4,America!Los_Angeles");
+ assertEquals(createUtcTime(2015, 6, 20, 1, 2, 3), nitz.getCurrentTimeInMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(-32 * 15), nitz.getLocalOffsetMillis());
+ assertEquals(TimeUnit.MINUTES.toMillis(4 * 15),
+ nitz.getDstAdjustmentMillis().longValue());
+ assertEquals("America/Los_Angeles", nitz.getEmulatorHostTimeZone().getID());
+ }
+
+ @Test
+ public void testToString() {
+ assertNotNull(NitzData.parse("15/06/20,01:02:03-32").toString());
+ assertNotNull(NitzData.parse("15/06/20,01:02:03-32,4").toString());
+ assertNotNull(NitzData.parse("15/06/20,01:02:03-32,4,America!Los_Angeles")
+ .toString());
+ }
+
+ private static long createUtcTime(
+ int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minute, int second) {
+ GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ calendar.clear(); // Clear millis, etc.
+ calendar.set(year, monthOfYear - 1, dayOfMonth, hourOfDay, minute, second);
+ return calendar.getTimeInMillis();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
new file mode 100644
index 0000000..2fc864a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
@@ -0,0 +1,1110 @@
+/*
+ * Copyright 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;
+
+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.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+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.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class OldNitzStateMachineTest extends TelephonyTest {
+
+ // A country with a single zone : the zone can be guessed from the country.
+ // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals.
+ private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/London")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("gb")
+ .build();
+
+ // A country that has multiple zones, but there is only one matching time zone at the time :
+ // the zone cannot be guessed from the country alone, but can be guessed from the country +
+ // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("America/Los_Angeles")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("us")
+ .build();
+
+ // A country with a single zone: the zone can be guessed from the country alone. CZ never uses
+ // UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/Prague")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("cz")
+ .build();
+
+ @Mock
+ private OldNitzStateMachine.DeviceState mDeviceState;
+
+ @Mock
+ private OldTimeServiceHelper mTimeServiceHelper;
+
+ private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
+
+ private OldNitzStateMachine mNitzStateMachine;
+
+ @Before
+ public void setUp() throws Exception {
+ logd("NitzStateMachineTest +Setup!");
+ super.setUp("NitzStateMachineTest");
+
+ // In tests we use the real TimeZoneLookupHelper.
+ mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
+ mNitzStateMachine = new OldNitzStateMachine(
+ mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
+
+ logd("ServiceStateTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ checkNoUnverifiedSetOperations(mTimeServiceHelper);
+
+ super.tearDown();
+ }
+
+ @Test
+ public void test_uniqueUsZone_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "America/New_York", false /* allZonesHaveSameOffset */,
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ // isOnlyMatch == true, so the combination of country + NITZ should be enough.
+ OffsetResult expectedLookupResult =
+ new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_unitedKingdom_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
+ // the zone knowing only the country.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "Europe/London", true /* allZonesHaveSameOffset */,
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ OffsetResult expectedLookupResult =
+ new OffsetResult("Europe/London", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to guess the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeEnabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country is not enough to guess the time zone and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be set from NITZ.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeEnabledTimeZoneDisabled_countryThenNitz()
+ throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone would be enough for time zone detection, but it's disabled.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be set from NITZ.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time detection is disabled, but time zone should be detected from country + NITZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to detect time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ .nitzReceived(scenario.getNitzSignal())
+ // Time detection is disabled, so we don't set the clock from NITZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ + country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+
+ // The NITZ + country is enough to detect the time zone.
+ // NOTE: setting the timezone happens twice because of a quirk in NitzStateMachine: it
+ // handles the country lookup / set, then combines the country with the NITZ state and does
+ // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
+ script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone, but the NITZ + country is
+ // also sufficient so we expect the time zone to be set twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The time will be set from the NITZ signal.
+ // The combination of NITZ + country will cause the time zone to be set.
+ .verifyTimeAndZoneSetAndReset(
+ scenario.getActualTimeMillis(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ. If the NITZ signal
+ // wasn't obviously bogus we'd try to set it twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be set.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be set.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+
+ // Create an NITZ signal with an explicit time zone (as can happen on emulators)
+ NitzData originalNitzData = originalNitzSignal.getValue();
+ // A time zone that is obviously not in the US, but it should not be questioned.
+ String emulatorTimeZoneId = "Europe/London";
+ NitzData emulatorNitzData = NitzData.createForTests(
+ originalNitzData.getLocalOffsetMillis(),
+ originalNitzData.getDstAdjustmentMillis(),
+ originalNitzData.getCurrentTimeInMillis(),
+ java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+ originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+ // Simulate receiving the emulator NITZ signal.
+ script.nitzReceived(emulatorNitzSignal)
+ .verifyOnlyTimeZoneWasSetAndReset(emulatorTimeZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Nothing should be set. The country is not valid.
+ script.countryReceived("").verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyTimeAndZoneSetAndReset(scenario.getActualTimeMillis(), expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // The time zone should be set (but the country is not valid so it's unlikely to be
+ // correct).
+ script.countryReceived("").verifyOnlyTimeZoneWasSetAndReset(expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ /**
+ * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
+ * usually multiple zones that will share the same UTC offset so we get a low quality / low
+ * confidence answer, but the zone we find should at least have the correct offset.
+ */
+ private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
+ OffsetResult result =
+ mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
+ String expectedZoneId = result.zoneId;
+ // All our scenarios should return multiple matches. The only cases where this wouldn't be
+ // true are places that use offsets like XX:15, XX:30 and XX:45.
+ assertFalse(result.isOnlyMatch);
+ assertSameOffset(scenario.getActualTimeMillis(), expectedZoneId, scenario.getTimeZoneId());
+ return expectedZoneId;
+ }
+
+ private static void assertSameOffset(long timeMillis, String zoneId1, String zoneId2) {
+ assertEquals(TimeZone.getTimeZone(zoneId1).getOffset(timeMillis),
+ TimeZone.getTimeZone(zoneId2).getOffset(timeMillis));
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * A helper class for common test operations involving a device.
+ */
+ class Script {
+ private final Device mDevice;
+
+ Script(Device device) {
+ this.mDevice = device;
+ }
+
+ Script countryReceived(String countryIsoCode) {
+ mDevice.networkCountryKnown(countryIsoCode);
+ return this;
+ }
+
+ Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.nitzSignalReceived(nitzSignal);
+ return this;
+ }
+
+ Script incrementClocks(int clockIncrement) {
+ mDevice.incrementClocks(clockIncrement);
+ return this;
+ }
+
+ Script verifyNothingWasSetAndReset() {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasNotSet();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId, times);
+ mDevice.verifyTimeWasNotSet();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
+ return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
+ }
+
+ Script verifyOnlyTimeWasSetAndReset(long expectedTimeMillis) {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasSet(expectedTimeMillis);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyTimeAndZoneSetAndReset(long expectedTimeMillis, String timeZoneId) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId);
+ mDevice.verifyTimeWasSet(expectedTimeMillis);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script reset() {
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+ }
+
+ /**
+ * An abstraction of a device for use in telephony time zone detection tests. It can be used to
+ * retrieve device state, modify device state and verify changes.
+ */
+ class Device {
+
+ private final long mInitialSystemClockMillis;
+ private final long mInitialRealtimeMillis;
+ private final boolean mTimeDetectionEnabled;
+ private final boolean mTimeZoneDetectionEnabled;
+ private final boolean mTimeZoneSettingInitialized;
+
+ Device(long initialSystemClockMillis, long initialRealtimeMillis,
+ boolean timeDetectionEnabled, boolean timeZoneDetectionEnabled,
+ boolean timeZoneSettingInitialized) {
+ mInitialSystemClockMillis = initialSystemClockMillis;
+ mInitialRealtimeMillis = initialRealtimeMillis;
+ mTimeDetectionEnabled = timeDetectionEnabled;
+ mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
+ mTimeZoneSettingInitialized = timeZoneSettingInitialized;
+ }
+
+ void initialize() {
+ // Set initial configuration.
+ when(mDeviceState.getIgnoreNitz()).thenReturn(false);
+ when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
+ when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
+
+ // Simulate the country not being known.
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
+
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
+ when(mTimeServiceHelper.isTimeDetectionEnabled()).thenReturn(mTimeDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
+ .thenReturn(mTimeZoneDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneSettingInitialized())
+ .thenReturn(mTimeZoneSettingInitialized);
+ }
+
+ void networkCountryKnown(String countryIsoCode) {
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
+ mNitzStateMachine.handleNetworkCountryCodeSet(true);
+ }
+
+ void incrementClocks(int millis) {
+ long currentElapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentElapsedRealtime + millis);
+ long currentTimeMillis = mTimeServiceHelper.currentTimeMillis();
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(currentTimeMillis + millis);
+ }
+
+ void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
+ mNitzStateMachine.handleNitzReceived(nitzSignal);
+ }
+
+ void verifyTimeZoneWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId) {
+ verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId, int times) {
+ verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
+ }
+
+ void verifyTimeWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTime(anyLong());
+ }
+
+ void verifyTimeWasSet(long expectedTimeMillis) {
+ ArgumentCaptor<Long> timeServiceTimeCaptor = ArgumentCaptor.forClass(Long.TYPE);
+ verify(mTimeServiceHelper, times(1)).setDeviceTime(timeServiceTimeCaptor.capture());
+ assertEquals(expectedTimeMillis, (long) timeServiceTimeCaptor.getValue());
+ }
+
+ /**
+ * Used after calling verify... methods to reset expectations.
+ */
+ void resetInvocations() {
+ clearInvocations(mTimeServiceHelper);
+ }
+
+ void checkNoUnverifiedSetOperations() {
+ OldNitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
+ }
+ }
+
+ /** A class used to construct a Device. */
+ class DeviceBuilder {
+
+ private long mInitialSystemClock;
+ private long mInitialRealtimeMillis;
+ private boolean mTimeDetectionEnabled;
+ private boolean mTimeZoneDetectionEnabled;
+ private boolean mTimeZoneSettingInitialized;
+
+ Device initialize() {
+ Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
+ mTimeDetectionEnabled, mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
+ device.initialize();
+ return device;
+ }
+
+ DeviceBuilder setTimeDetectionEnabled(boolean enabled) {
+ mTimeDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
+ mTimeZoneSettingInitialized = initialized;
+ return this;
+ }
+
+ DeviceBuilder setClocksFromScenario(Scenario scenario) {
+ mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
+ mInitialSystemClock = scenario.getInitialSystemClockMillis();
+ return this;
+ }
+ }
+
+ /**
+ * A scenario used during tests. Describes a fictional reality.
+ */
+ static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+ private final TimeZone mZone;
+ private final String mNetworkCountryIsoCode;
+
+ private TimestampedValue<NitzData> mNitzSignal;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
+ String zoneId, String countryIsoCode) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ mZone = TimeZone.getTimeZone(zoneId);
+ mNetworkCountryIsoCode = countryIsoCode;
+ }
+
+ TimestampedValue<NitzData> getNitzSignal() {
+ if (mNitzSignal == null) {
+ int[] offsets = new int[2];
+ mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
+ int zoneOffsetMillis = offsets[0] + offsets[1];
+ NitzData nitzData = NitzData.createForTests(
+ zoneOffsetMillis, offsets[1], mActualTimeMillis,
+ null /* emulatorHostTimeZone */);
+ mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
+ }
+ return mNitzSignal;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ String getNetworkCountryIsoCode() {
+ return mNetworkCountryIsoCode;
+ }
+
+ String getTimeZoneId() {
+ return mZone.getID();
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+ private String mZoneId;
+ private String mCountryIsoCode;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
+ second);
+ return this;
+ }
+
+ Builder setTimeZone(String zoneId) {
+ mZoneId = zoneId;
+ return this;
+ }
+
+ Builder setCountryIso(String isoCode) {
+ mCountryIsoCode = isoCode;
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis, mZoneId, mCountryIsoCode);
+ }
+ }
+ }
+
+ /**
+ * Confirms all mTimeServiceHelper side effects were verified.
+ */
+ private static void checkNoUnverifiedSetOperations(OldTimeServiceHelper mTimeServiceHelper) {
+ // We don't care about current auto time / time zone state retrievals / listening so we can
+ // use "at least 0" times to indicate they don't matter.
+ verify(mTimeServiceHelper, atLeast(0)).setListener(any());
+ verify(mTimeServiceHelper, atLeast(0)).isTimeDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+ verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+ verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
+ verifyNoMoreInteractions(mTimeServiceHelper);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index faeabd5..b9c8163 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -26,6 +26,7 @@
import android.telephony.PhoneNumberUtils;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableStringBuilder;
+import android.text.style.TtsSpan;
import org.junit.Ignore;
import org.junit.Test;
@@ -442,6 +443,7 @@
// To run this test, the device has to be registered with network
@FlakyTest
+ @Ignore
public void testCheckAndProcessPlusCode() {
assertEquals("0118475797000",
PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
@@ -764,4 +766,21 @@
assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
}
+
+ @SmallTest
+ @Test
+ public void testCreateTtsSpan() {
+ checkTtsNumber("650 555 1212", "650-555-1212");
+ checkTtsNumber("6505551212", "+1-650-555-1212");
+ checkTtsNumber("232", "232");
+ checkTtsNumber("*232", "*232");
+ checkTtsNumber("*232#", "*232#");
+ checkTtsNumber("*650 555 1212#", "*650-555-1212#");
+ }
+
+ private void checkTtsNumber(String expected, String sourceNumber) {
+ TtsSpan ttsSpan = PhoneNumberUtils.createTtsSpan(sourceNumber);
+ assertEquals(TtsSpan.TYPE_TELEPHONE, ttsSpan.getType());
+ assertEquals(expected, ttsSpan.getArgs().getString(TtsSpan.ARG_NUMBER_PARTS));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
index fdda80f..1d4b173 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
@@ -15,20 +15,31 @@
*/
package com.android.internal.telephony;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
import android.os.HandlerThread;
import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.test.suitebuilder.annotation.SmallTest;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+
import java.lang.reflect.Field;
-import static org.mockito.Mockito.verify;
+import java.util.Collections;
+import java.util.List;
public class PhoneStateListenerTest extends TelephonyTest {
private PhoneStateListener mPhoneStateListenerUT;
private PhoneStateListenerHandler mPhoneStateListenerHandler;
+ private boolean mUserMobileDataState = false;
+ private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
private class PhoneStateListenerHandler extends HandlerThread {
private PhoneStateListenerHandler(String name) {
@@ -45,6 +56,21 @@
mServiceState.setDataRegState(serviceState.getDataRegState());
setReady(true);
}
+
+ @Override
+ public void onUserMobileDataStateChanged(boolean state) {
+ logd("User Mobile Data State Changed");
+ mUserMobileDataState = true;
+ setReady(true);
+ }
+
+ @Override
+ public void onPhysicalChannelConfigurationChanged(
+ List<PhysicalChannelConfig> configs) {
+ logd("PhysicalChannelConfig Changed");
+ mPhysicalChannelConfigs = configs;
+ setReady(true);
+ }
};
setReady(true);
}
@@ -81,4 +107,37 @@
verify(mServiceState).setVoiceRegState(ServiceState.STATE_EMERGENCY_ONLY);
}
+ @Test @SmallTest
+ public void testTriggerUserMobileDataStateChanged() throws Exception {
+ Field field = PhoneStateListener.class.getDeclaredField("callback");
+ field.setAccessible(true);
+
+ assertFalse(mUserMobileDataState);
+
+ setReady(false);
+ ((IPhoneStateListener) field.get(mPhoneStateListenerUT)).onUserMobileDataStateChanged(true);
+ waitUntilReady();
+
+ assertTrue(mUserMobileDataState);
+ }
+
+ @Test @SmallTest
+ public void testTriggerPhysicalChannelConfigurationChanged() throws Exception {
+ Field field = PhoneStateListener.class.getDeclaredField("callback");
+ field.setAccessible(true);
+
+ assertNull(mPhysicalChannelConfigs);
+
+ PhysicalChannelConfig config = new PhysicalChannelConfig(
+ PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING, 20000 /* bandwidth */);
+
+ List<PhysicalChannelConfig> configs = Collections.singletonList(config);
+
+ setReady(false);
+ ((IPhoneStateListener) field.get(mPhoneStateListenerUT))
+ .onPhysicalChannelConfigurationChanged(configs);
+ waitUntilReady();
+
+ assertTrue(mPhysicalChannelConfigs.equals(configs));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index ec52c79..49ab00b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -15,15 +15,10 @@
*/
package com.android.internal.telephony;
-import android.app.AppOpsManager;
-import android.content.Context;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import static android.Manifest.permission.READ_SMS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -31,12 +26,17 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
public class PhoneSubInfoControllerTest extends TelephonyTest {
private PhoneSubInfoController mPhoneSubInfoControllerUT;
private AppOpsManager mAppOsMgr;
@@ -92,7 +92,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceId"));
}
try {
@@ -100,7 +100,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceId"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -142,7 +142,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getNai", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getNai"));
}
try {
@@ -150,7 +150,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getNai", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getNai"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -192,7 +192,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getImei", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getImei"));
}
try {
@@ -200,7 +200,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getImei", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getImei"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -242,7 +242,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceSvn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceSvn"));
}
try {
@@ -250,7 +250,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceSvn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceSvn"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -295,7 +295,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getSubscriberId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getSubscriberId"));
}
try {
@@ -303,7 +303,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getSubscriberId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getSubscriberId"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -350,7 +350,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getIccSerialNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getIccSerialNumber"));
}
try {
@@ -358,7 +358,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getIccSerialNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getIccSerialNumber"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -483,7 +483,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getLine1AlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getLine1AlphaTag"));
}
try {
@@ -491,7 +491,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getLine1AlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getLine1AlphaTag"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -535,7 +535,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getMsisdn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getMsisdn"));
}
try {
@@ -543,7 +543,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getMsisdn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getMsisdn"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -587,7 +587,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailNumber"));
}
try {
@@ -595,7 +595,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailNumber"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -641,7 +641,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailAlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailAlphaTag"));
}
try {
@@ -649,7 +649,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailAlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailAlphaTag"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
new file mode 100644
index 0000000..10dd6f2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -0,0 +1,1729 @@
+/*
+ * 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;
+
+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;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CHANGE_SIM_PIN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CHANGE_SIM_PIN2;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CONFERENCE;
+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_DTMF;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PIN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PIN2;
+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_GET_ACTIVITY_INFO;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_CURRENT_CALLS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HARDWARE_CONFIG;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMSI;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_RADIO_CAPABILITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SMSC_ADDRESS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
+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_LAST_CALL_FAIL_CAUSE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_READ_ITEM;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_RESET_CONFIG;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_WRITE_ITEM;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OPERATOR;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PULL_LCEDATA;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_SMS_MEMORY_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING;
+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;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
+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_UNSOLICITED_RESPONSE_FILTER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SHUTDOWN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIGNAL_STRENGTH;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_AUTHENTICATION;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_CLOSE_CHANNEL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_OPEN_CHANNEL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_REGISTRATION_STATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_WRITE_SMS_TO_SIM;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.pm.ApplicationInfo;
+import android.hardware.radio.V1_0.CdmaSmsMessage;
+import android.hardware.radio.V1_0.DataProfileInfo;
+import android.hardware.radio.V1_0.GsmSmsMessage;
+import android.hardware.radio.V1_0.IRadio;
+import android.hardware.radio.V1_0.ImsSmsMessage;
+import android.hardware.radio.V1_0.NvWriteItem;
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.V1_0.RadioResponseType;
+import android.hardware.radio.V1_0.SmsWriteArgs;
+import android.hardware.radio.deprecated.V1_0.IOemHook;
+import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IPowerManager;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.WorkSource;
+import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityTdscdma;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoTdscdma;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthTdscdma;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
+
+import com.android.internal.telephony.RIL.RilHandler;
+import com.android.internal.telephony.dataconnection.DcTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class RILTest extends TelephonyTest {
+
+ // refer to RIL#DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS
+ private static final int DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS = 2000;
+
+ // refer to RIL#DEFAULT_WAKE_LOCK_TIMEOUT_MS
+ private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MS = 60000;
+
+ @Mock
+ private ConnectivityManager mConnectionManager;
+ @Mock
+ private IRadio mRadioProxy;
+ @Mock
+ private IOemHook mOemHookProxy;
+
+ private RilHandler mRilHandler;
+ private RIL mRILInstance;
+ private RIL mRILUnderTest;
+ private RILTestHandler mTestHandler;
+ ArgumentCaptor<Integer> mSerialNumberCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ // Constants
+ private static final String ALPHA_LONG = "long";
+ private static final String ALPHA_SHORT = "short";
+ private static final int ARFCN = 690;
+ private static final int BASESTATION_ID = 65531;
+ private static final int BIT_ERROR_RATE = 99;
+ private static final int BSIC = 8;
+ private static final int CI = 268435456;
+ private static final int CID = 65535;
+ private static final int CQI = 2147483647;
+ private static final int DBM = 74;
+ private static final int EARFCN = 262140;
+ private static final int BANDWIDTH = 5000;
+ private static final int ECIO = 124;
+ private static final String EMPTY_ALPHA_LONG = "";
+ private static final String EMPTY_ALPHA_SHORT = "";
+ private static final int LAC = 65535;
+ private static final int LATITUDE = 1292000;
+ private static final int LONGITUDE = 1295000;
+ private static final int MCC = 120;
+ private static final String MCC_STR = "120";
+ private static final int MNC = 260;
+ private static final String MNC_STR = "260";
+ private static final int NETWORK_ID = 65534;
+ private static final int PCI = 503;
+ private static final int PSC = 500;
+ private static final int RIL_TIMESTAMP_TYPE_OEM_RIL = 3;
+ private static final int RSSNR = 2147483647;
+ private static final int RSRP = 96;
+ private static final int RSRQ = 10;
+ private static final int RSCP = 94;
+ private static final int ECNO = 5;
+ private static final int SIGNAL_NOISE_RATIO = 6;
+ private static final int SIGNAL_STRENGTH = 24;
+ private static final int SYSTEM_ID = 65533;
+ private static final int TAC = 65535;
+ private static final int TIME_ADVANCE = 4;
+ private static final long TIMESTAMP = 215924934;
+ private static final int UARFCN = 690;
+ private static final int TYPE_CDMA = 2;
+ private static final int TYPE_GSM = 1;
+ private static final int TYPE_LTE = 3;
+ private static final int TYPE_WCDMA = 4;
+ private static final int TYPE_TD_SCDMA = 5;
+
+ private static final int PROFILE_ID = 0;
+ private static final String APN = "apn";
+ private static final String PROTOCOL = "IPV6";
+ private static final int AUTH_TYPE = 0;
+ private static final String USER_NAME = "username";
+ private static final String PASSWORD = "password";
+ private static final int TYPE = 0;
+ private static final int MAX_CONNS_TIME = 1;
+ private static final int MAX_CONNS = 3;
+ private static final int WAIT_TIME = 10;
+ private static final boolean APN_ENABLED = true;
+ private static final int SUPPORTED_APNT_YPES_BITMAP = 123456;
+ private static final String ROAMING_PROTOCOL = "IPV6";
+ private static final int BEARER_BITMAP = 123123;
+ private static final int MTU = 1234;
+ private static final String MVNO_TYPE = "";
+ private static final String MVNO_MATCH_DATA = "";
+ private static final boolean MODEM_COGNITIVE = true;
+
+ private class RILTestHandler extends HandlerThread {
+
+ RILTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ super.onLooperPrepared();
+ createTelephonyDevController();
+ Context context = new ContextFixture().getTestDouble();
+ doReturn(true).when(mConnectionManager)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ doReturn(mConnectionManager).when(context)
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ PowerManager powerManager = createPowerManager(context);
+ doReturn(powerManager).when(context).getSystemService(Context.POWER_SERVICE);
+ doReturn(new ApplicationInfo()).when(context).getApplicationInfo();
+
+ mRILInstance = new RIL(context, RILConstants.PREFERRED_NETWORK_MODE,
+ Phone.PREFERRED_CDMA_SUBSCRIPTION, 0);
+ mRILUnderTest = spy(mRILInstance);
+ doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy(any());
+ doReturn(mOemHookProxy).when(mRILUnderTest).getOemHookProxy(any());
+
+ mRilHandler = mRILUnderTest.getRilHandler();
+
+ setReady(true);
+ }
+
+ private PowerManager createPowerManager(Context context) {
+ return new PowerManager(context, mock(IPowerManager.class), new Handler(getLooper()));
+ }
+
+ private void createTelephonyDevController() {
+ try {
+ TelephonyDevController.create();
+ } catch (RuntimeException e) {
+ }
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(RILTest.class.getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ mTestHandler = new RILTestHandler(getClass().getSimpleName());
+ mTestHandler.start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestHandler.quit();
+ super.tearDown();
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetIccCardStatus() throws Exception {
+ mRILUnderTest.getIccCardStatus(obtainMessage());
+ verify(mRadioProxy).getIccCardStatus(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_SIM_STATUS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSupplyIccPinForApp() throws Exception {
+ String pin = "1234";
+ String aid = "2345";
+ mRILUnderTest.supplyIccPinForApp(pin, aid, obtainMessage());
+ verify(mRadioProxy).supplyIccPinForApp(mSerialNumberCaptor.capture(), eq(pin), eq(aid));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_ENTER_SIM_PIN);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSupplyIccPukForApp() throws Exception {
+ String puk = "pukcode";
+ String newPin = "1314";
+ String aid = "2345";
+ mRILUnderTest.supplyIccPukForApp(puk, newPin, aid, obtainMessage());
+ verify(mRadioProxy)
+ .supplyIccPukForApp(mSerialNumberCaptor.capture(), eq(puk), eq(newPin), eq(aid));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_ENTER_SIM_PUK);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSupplyIccPin2ForApp() throws Exception {
+ String pin = "1234";
+ String aid = "2345";
+ mRILUnderTest.supplyIccPin2ForApp(pin, aid, obtainMessage());
+ verify(mRadioProxy).supplyIccPin2ForApp(
+ mSerialNumberCaptor.capture(), eq(pin), eq(aid));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_ENTER_SIM_PIN2);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSupplyIccPuk2ForApp() throws Exception {
+ String puk = "pukcode";
+ String newPin = "1314";
+ String aid = "2345";
+ mRILUnderTest.supplyIccPuk2ForApp(puk, newPin, aid, obtainMessage());
+ verify(mRadioProxy)
+ .supplyIccPuk2ForApp(mSerialNumberCaptor.capture(), eq(puk), eq(newPin), eq(aid));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_ENTER_SIM_PUK2);
+ }
+
+ @FlakyTest
+ @Test
+ public void testChangeIccPinForApp() throws Exception {
+ String oldPin = "1234";
+ String newPin = "1314";
+ String aid = "2345";
+ mRILUnderTest.changeIccPinForApp(oldPin, newPin, aid, obtainMessage());
+ verify(mRadioProxy).changeIccPinForApp(
+ mSerialNumberCaptor.capture(), eq(oldPin), eq(newPin), eq(aid));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_CHANGE_SIM_PIN);
+ }
+
+ @FlakyTest
+ @Test
+ public void testChangeIccPin2ForApp() throws Exception {
+ String oldPin2 = "1234";
+ String newPin2 = "1314";
+ String aid = "2345";
+ mRILUnderTest.changeIccPin2ForApp(oldPin2, newPin2, aid, obtainMessage());
+ verify(mRadioProxy).changeIccPin2ForApp(
+ mSerialNumberCaptor.capture(), eq(oldPin2), eq(newPin2), eq(aid));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_CHANGE_SIM_PIN2);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSupplyNetworkDepersonalization() throws Exception {
+ String netpin = "1234";
+ mRILUnderTest.supplyNetworkDepersonalization(netpin, obtainMessage());
+ verify(mRadioProxy).supplyNetworkDepersonalization(
+ mSerialNumberCaptor.capture(), eq(netpin));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetCurrentCalls() throws Exception {
+ mRILUnderTest.getCurrentCalls(obtainMessage());
+ verify(mRadioProxy).getCurrentCalls(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_CURRENT_CALLS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetIMSIForApp() throws Exception {
+ String aid = "1234";
+ mRILUnderTest.getIMSIForApp(aid, obtainMessage());
+ verify(mRadioProxy).getImsiForApp(mSerialNumberCaptor.capture(), eq(aid));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_IMSI);
+ }
+
+ @FlakyTest
+ @Test
+ public void testHangupWaitingOrBackground() throws Exception {
+ mRILUnderTest.hangupWaitingOrBackground(obtainMessage());
+ verify(mRadioProxy).hangupWaitingOrBackground(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND);
+ }
+
+ @FlakyTest
+ @Test
+ public void testHangupForegroundResumeBackground() throws Exception {
+ mRILUnderTest.hangupForegroundResumeBackground(obtainMessage());
+ verify(mRadioProxy).hangupForegroundResumeBackground(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND);
+ }
+
+ @FlakyTest
+ @Test
+ public void testHangupConnection() throws Exception {
+ int gsmIndex = 0;
+ mRILUnderTest.hangupConnection(gsmIndex, obtainMessage());
+ verify(mRadioProxy).hangup(mSerialNumberCaptor.capture(), eq(gsmIndex));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_HANGUP);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSwitchWaitingOrHoldingAndActive() throws Exception {
+ mRILUnderTest.switchWaitingOrHoldingAndActive(obtainMessage());
+ verify(mRadioProxy).switchWaitingOrHoldingAndActive(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testConference() throws Exception {
+ mRILUnderTest.conference(obtainMessage());
+ verify(mRadioProxy).conference(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_CONFERENCE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testRejectCall() throws Exception {
+ mRILUnderTest.rejectCall(obtainMessage());
+ verify(mRadioProxy).rejectCall(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_UDUB);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetLastCallFailCause() throws Exception {
+ mRILUnderTest.getLastCallFailCause(obtainMessage());
+ verify(mRadioProxy).getLastCallFailCause(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_LAST_CALL_FAIL_CAUSE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetSignalStrength() throws Exception {
+ mRILUnderTest.getSignalStrength(obtainMessage());
+ verify(mRadioProxy).getSignalStrength(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SIGNAL_STRENGTH);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetVoiceRegistrationState() throws Exception {
+ mRILUnderTest.getVoiceRegistrationState(obtainMessage());
+ verify(mRadioProxy).getVoiceRegistrationState(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_VOICE_REGISTRATION_STATE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetDataRegistrationState() throws Exception {
+ mRILUnderTest.getDataRegistrationState(obtainMessage());
+ verify(mRadioProxy).getDataRegistrationState(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DATA_REGISTRATION_STATE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetOperator() throws Exception {
+ mRILUnderTest.getOperator(obtainMessage());
+ verify(mRadioProxy).getOperator(mSerialNumberCaptor.capture());
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_OPERATOR);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetRadioPower() throws Exception {
+ boolean on = true;
+ mRILUnderTest.setRadioPower(on, obtainMessage());
+ verify(mRadioProxy).setRadioPower(mSerialNumberCaptor.capture(), eq(on));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_RADIO_POWER);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendDtmf() throws Exception {
+ char c = 'c';
+ mRILUnderTest.sendDtmf(c, obtainMessage());
+ verify(mRadioProxy).sendDtmf(mSerialNumberCaptor.capture(), eq(c + ""));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DTMF);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendSMS() throws Exception {
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ GsmSmsMessage msg = new GsmSmsMessage();
+ msg.smscPdu = smscPdu;
+ msg.pdu = pdu;
+ mRILUnderTest.sendSMS(smscPdu, pdu, obtainMessage());
+ verify(mRadioProxy).sendSms(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SEND_SMS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendSMSExpectMore() throws Exception {
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ GsmSmsMessage msg = new GsmSmsMessage();
+ msg.smscPdu = smscPdu;
+ msg.pdu = pdu;
+ mRILUnderTest.sendSMSExpectMore(smscPdu, pdu, obtainMessage());
+ verify(mRadioProxy).sendSMSExpectMore(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SEND_SMS_EXPECT_MORE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testWriteSmsToSim() throws Exception {
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ int status = SmsManager.STATUS_ON_ICC_READ;
+ SmsWriteArgs args = new SmsWriteArgs();
+ args.status = 1;
+ args.smsc = smscPdu;
+ args.pdu = pdu;
+ mRILUnderTest.writeSmsToSim(status, smscPdu, pdu, obtainMessage());
+ verify(mRadioProxy).writeSmsToSim(mSerialNumberCaptor.capture(), eq(args));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_WRITE_SMS_TO_SIM);
+ }
+
+ @FlakyTest
+ @Test
+ public void testDeleteSmsOnSim() throws Exception {
+ int index = 0;
+ mRILUnderTest.deleteSmsOnSim(index, obtainMessage());
+ verify(mRadioProxy).deleteSmsOnSim(mSerialNumberCaptor.capture(), eq(index));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DELETE_SMS_ON_SIM);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetDeviceIdentity() throws Exception {
+ mRILUnderTest.getDeviceIdentity(obtainMessage());
+ verify(mRadioProxy).getDeviceIdentity(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DEVICE_IDENTITY);
+ }
+
+ @FlakyTest
+ @Test
+ public void testExitEmergencyCallbackMode() throws Exception {
+ mRILUnderTest.exitEmergencyCallbackMode(obtainMessage());
+ verify(mRadioProxy).exitEmergencyCallbackMode(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetSmscAddress() throws Exception {
+ mRILUnderTest.getSmscAddress(obtainMessage());
+ verify(mRadioProxy).getSmscAddress(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_SMSC_ADDRESS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetSmscAddress() throws Exception {
+ String address = "address";
+ mRILUnderTest.setSmscAddress(address, obtainMessage());
+ verify(mRadioProxy).setSmscAddress(mSerialNumberCaptor.capture(), eq(address));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_SMSC_ADDRESS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testReportSmsMemoryStatus() throws Exception {
+ boolean available = true;
+ mRILUnderTest.reportSmsMemoryStatus(available, obtainMessage());
+ verify(mRadioProxy).reportSmsMemoryStatus(mSerialNumberCaptor.capture(), eq(available));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_REPORT_SMS_MEMORY_STATUS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testReportStkServiceIsRunning() throws Exception {
+ mRILUnderTest.reportStkServiceIsRunning(obtainMessage());
+ verify(mRadioProxy).reportStkServiceIsRunning(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetCdmaSubscriptionSource() throws Exception {
+ mRILUnderTest.getCdmaSubscriptionSource(obtainMessage());
+ verify(mRadioProxy).getCdmaSubscriptionSource(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testAcknowledgeIncomingGsmSmsWithPdu() throws Exception {
+ boolean success = true;
+ String ackPdu = "ackPdu";
+ mRILUnderTest.acknowledgeIncomingGsmSmsWithPdu(success, ackPdu, obtainMessage());
+ verify(mRadioProxy).acknowledgeIncomingGsmSmsWithPdu(
+ mSerialNumberCaptor.capture(), eq(success), eq(ackPdu));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetVoiceRadioTechnology() throws Exception {
+ mRILUnderTest.getVoiceRadioTechnology(obtainMessage());
+ verify(mRadioProxy).getVoiceRadioTechnology(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_VOICE_RADIO_TECH);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetCellInfoList() throws Exception {
+ mRILUnderTest.getCellInfoList(obtainMessage(), null);
+ verify(mRadioProxy).getCellInfoList(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_CELL_INFO_LIST);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetCellInfoListRate() throws Exception {
+ int rateInMillis = 1000;
+ mRILUnderTest.setCellInfoListRate(rateInMillis, obtainMessage(), null);
+ verify(mRadioProxy).setCellInfoListRate(mSerialNumberCaptor.capture(), eq(rateInMillis));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetInitialAttachApn() throws Exception {
+ ApnSetting apnSetting = ApnSetting.makeApnSetting(
+ -1, "22210", "Vodafone IT", "web.omnitel.it", null, -1,
+ null, null, -1, "", "", 0, ApnSetting.TYPE_DUN, ApnSetting.PROTOCOL_IP,
+ ApnSetting.PROTOCOL_IP, true, 0, 0, false, 0, 0, 0, 0, -1, "");
+ DataProfile dataProfile = DcTracker.createDataProfile(
+ apnSetting, apnSetting.getProfileId());
+ boolean isRoaming = false;
+
+ mRILUnderTest.setInitialAttachApn(dataProfile, isRoaming, obtainMessage());
+ verify(mRadioProxy).setInitialAttachApn(
+ mSerialNumberCaptor.capture(),
+ eq((DataProfileInfo) invokeMethod(
+ mRILInstance,
+ "convertToHalDataProfile",
+ new Class<?>[] {DataProfile.class},
+ new Object[] {dataProfile})),
+ eq(dataProfile.isModemCognitive()),
+ eq(isRoaming));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_INITIAL_ATTACH_APN);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetImsRegistrationState() throws Exception {
+ mRILUnderTest.getImsRegistrationState(obtainMessage());
+ verify(mRadioProxy).getImsRegistrationState(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_IMS_REGISTRATION_STATE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendRetryImsGsmSms() throws Exception {
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ GsmSmsMessage gsmMsg = new GsmSmsMessage();
+ gsmMsg.smscPdu = smscPdu;
+ gsmMsg.pdu = pdu;
+
+ ImsSmsMessage firstMsg = new ImsSmsMessage();
+ firstMsg.tech = RILConstants.GSM_PHONE;
+ firstMsg.retry = false;
+ firstMsg.messageRef = 0;
+ firstMsg.gsmMessage.add(gsmMsg);
+
+ ImsSmsMessage retryMsg = new ImsSmsMessage();
+ retryMsg.tech = RILConstants.GSM_PHONE;
+ retryMsg.retry = true;
+ retryMsg.messageRef = 0;
+ retryMsg.gsmMessage.add(gsmMsg);
+
+ int maxRetryCount = 3;
+ int firstTransmission = 0;
+ for (int i = 0; i <= maxRetryCount; i++) {
+ mRILUnderTest.sendImsGsmSms(smscPdu, pdu, i, 0, obtainMessage());
+ if (i == firstTransmission) {
+ verify(mRadioProxy, times(1)).sendImsSms(mSerialNumberCaptor.capture(),
+ eq(firstMsg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_IMS_SEND_SMS);
+ } else {
+ verify(mRadioProxy, times(i)).sendImsSms(mSerialNumberCaptor.capture(),
+ eq(retryMsg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_IMS_SEND_SMS);
+ }
+ }
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendRetryImsCdmaSms() throws Exception {
+ CdmaSmsMessage cdmaMsg = new CdmaSmsMessage();
+
+ ImsSmsMessage firstMsg = new ImsSmsMessage();
+ firstMsg.tech = RILConstants.CDMA_PHONE;
+ firstMsg.retry = false;
+ firstMsg.messageRef = 0;
+ firstMsg.cdmaMessage.add(cdmaMsg);
+
+ ImsSmsMessage retryMsg = new ImsSmsMessage();
+ retryMsg.tech = RILConstants.CDMA_PHONE;
+ retryMsg.retry = true;
+ retryMsg.messageRef = 0;
+ retryMsg.cdmaMessage.add(cdmaMsg);
+
+ int maxRetryCount = 3;
+ int firstTransmission = 0;
+ byte pdu[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ for (int i = 0; i <= maxRetryCount; i++) {
+ mRILUnderTest.sendImsCdmaSms(pdu, i, 0, obtainMessage());
+ if (i == firstTransmission) {
+ verify(mRadioProxy, times(1)).sendImsSms(mSerialNumberCaptor.capture(),
+ eq(firstMsg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_IMS_SEND_SMS);
+ } else {
+ verify(mRadioProxy, times(i)).sendImsSms(mSerialNumberCaptor.capture(),
+ eq(retryMsg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_IMS_SEND_SMS);
+ }
+ }
+ }
+
+ @Test
+ public void testIccOpenLogicalChannel() throws Exception {
+ String aid = "aid";
+ int p2 = 0;
+ mRILUnderTest.iccOpenLogicalChannel(aid, p2, obtainMessage());
+ verify(mRadioProxy).iccOpenLogicalChannel(mSerialNumberCaptor.capture(), eq(aid), eq(p2));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SIM_OPEN_CHANNEL);
+ }
+
+ @FlakyTest
+ @Test
+ public void testIccCloseLogicalChannel() throws Exception {
+ int channel = 1;
+ mRILUnderTest.iccCloseLogicalChannel(channel, obtainMessage());
+ verify(mRadioProxy).iccCloseLogicalChannel(mSerialNumberCaptor.capture(), eq(channel));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SIM_CLOSE_CHANNEL);
+ }
+
+ @FlakyTest
+ @Test
+ public void testNvWriteItem() throws Exception {
+ int itemId = 1;
+ String itemValue = "value";
+ mRILUnderTest.nvWriteItem(itemId, itemValue, obtainMessage());
+ NvWriteItem item = new NvWriteItem();
+ item.itemId = itemId;
+ item.value = itemValue;
+ verify(mRadioProxy).nvWriteItem(mSerialNumberCaptor.capture(), eq(item));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_NV_WRITE_ITEM);
+ }
+
+ @FlakyTest
+ @Test
+ public void testNvReadItem() throws Exception {
+ int itemId = 1;
+ mRILUnderTest.nvReadItem(itemId, obtainMessage());
+ verify(mRadioProxy).nvReadItem(mSerialNumberCaptor.capture(), eq(itemId));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_NV_READ_ITEM);
+ }
+
+ @FlakyTest
+ @Test
+ public void testNvResetConfig() throws Exception {
+ int resetType = 1;
+ mRILUnderTest.nvResetConfig(resetType, obtainMessage());
+ verify(mRadioProxy).nvResetConfig(
+ mSerialNumberCaptor.capture(),
+ eq((Integer) invokeMethod(
+ mRILInstance,
+ "convertToHalResetNvType",
+ new Class<?>[] {Integer.TYPE},
+ new Object[] {resetType})));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_NV_RESET_CONFIG);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetDataAllowed() throws Exception {
+ boolean allowed = true;
+ mRILUnderTest.setDataAllowed(allowed, obtainMessage());
+ verify(mRadioProxy).setDataAllowed(mSerialNumberCaptor.capture(), eq(allowed));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_ALLOW_DATA);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetHardwareConfig() throws Exception {
+ mRILUnderTest.getHardwareConfig(obtainMessage());
+ verify(mRadioProxy).getHardwareConfig(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_HARDWARE_CONFIG);
+ }
+
+ @FlakyTest
+ @Test
+ public void testRequestIccSimAuthentication() throws Exception {
+ int authContext = 1;
+ String data = "data";
+ String aid = "aid";
+ mRILUnderTest.requestIccSimAuthentication(authContext, data, aid, obtainMessage());
+ verify(mRadioProxy).requestIccSimAuthentication(
+ mSerialNumberCaptor.capture(), eq(authContext), eq(data), eq(aid));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SIM_AUTHENTICATION);
+ }
+
+ @FlakyTest
+ @Test
+ public void testRequestShutdown() throws Exception {
+ mRILUnderTest.requestShutdown(obtainMessage());
+ verify(mRadioProxy).requestShutdown(mSerialNumberCaptor.capture());
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SHUTDOWN);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetRadioCapability() throws Exception {
+ mRILUnderTest.getRadioCapability(obtainMessage());
+ verify(mRadioProxy).getRadioCapability(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_RADIO_CAPABILITY);
+ }
+
+ @FlakyTest
+ @Test
+ public void testStartLceService() throws Exception {
+ int reportIntervalMs = 1000;
+ boolean pullMode = false;
+ mRILUnderTest.startLceService(reportIntervalMs, pullMode, obtainMessage());
+ verify(mRadioProxy).startLceService(
+ mSerialNumberCaptor.capture(), eq(reportIntervalMs), eq(pullMode));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_START_LCE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testStopLceService() throws Exception {
+ mRILUnderTest.stopLceService(obtainMessage());
+ verify(mRadioProxy).stopLceService(mSerialNumberCaptor.capture());
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_STOP_LCE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testPullLceData() throws Exception {
+ mRILUnderTest.pullLceData(obtainMessage());
+ verify(mRadioProxy).pullLceData(mSerialNumberCaptor.capture());
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_PULL_LCEDATA);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetModemActivityInfo() throws Exception {
+ mRILUnderTest.getModemActivityInfo(obtainMessage());
+ verify(mRadioProxy).getModemActivityInfo(mSerialNumberCaptor.capture());
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_ACTIVITY_INFO);
+ }
+
+ @FlakyTest
+ @Test
+ public void testGetModemActivityInfoTimeout() {
+ mRILUnderTest.getModemActivityInfo(obtainMessage());
+ assertEquals(1, mRILUnderTest.getRilRequestList().size());
+ waitForHandlerActionDelayed(mRilHandler, 10, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
+ assertEquals(0, mRILUnderTest.getRilRequestList().size());
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendDeviceState() throws Exception {
+ int stateType = 1;
+ boolean state = false;
+ mRILUnderTest.sendDeviceState(stateType, state, obtainMessage());
+ verify(mRadioProxy).sendDeviceState(
+ mSerialNumberCaptor.capture(), eq(stateType), eq(state));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SEND_DEVICE_STATE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetUnsolResponseFilter() throws Exception {
+ int filter = 1;
+ mRILUnderTest.setUnsolResponseFilter(filter, obtainMessage());
+ verify(mRadioProxy).setIndicationFilter(mSerialNumberCaptor.capture(), eq(filter));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetSimCardPowerForPowerDownState() throws Exception {
+ mRILUnderTest.setSimCardPower(TelephonyManager.CARD_POWER_DOWN, obtainMessage());
+ verify(mRadioProxy).setSimCardPower(mSerialNumberCaptor.capture(), eq(false));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_SIM_CARD_POWER);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSetSimCardPowerForPowerUpState() throws Exception {
+ mRILUnderTest.setSimCardPower(TelephonyManager.CARD_POWER_UP, obtainMessage());
+ verify(mRadioProxy).setSimCardPower(mSerialNumberCaptor.capture(), eq(true));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_SIM_CARD_POWER);
+ }
+
+ @FlakyTest
+ @Test
+ public void testHandleCallSetupRequestFromSim() throws Exception {
+ boolean accept = true;
+ mRILUnderTest.handleCallSetupRequestFromSim(accept, obtainMessage());
+ verify(mRadioProxy).handleStkCallSetupRequestFromSim(
+ mSerialNumberCaptor.capture(), eq(accept));
+ verifyRILResponse(
+ mRILUnderTest,
+ mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM);
+ }
+
+ @FlakyTest
+ @Test
+ public void testWakeLockTimeout() throws Exception {
+ invokeMethod(
+ mRILInstance,
+ "obtainRequest",
+ new Class<?>[] {Integer.TYPE, Message.class, WorkSource.class},
+ new Object[] {RIL_REQUEST_GET_SIM_STATUS, obtainMessage(), null});
+
+ // The wake lock should be held when obtain a RIL request.
+ assertTrue(mRILInstance.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+
+ waitForHandlerActionDelayed(mRilHandler, 10, DEFAULT_WAKE_LOCK_TIMEOUT_MS);
+
+ // The wake lock should be released after processed the time out event.
+ assertFalse(mRILInstance.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+ }
+
+ @Test
+ public void testInvokeOemRilRequestStrings() throws Exception {
+ String[] strings = new String[]{"a", "b", "c"};
+ mRILUnderTest.invokeOemRilRequestStrings(strings, obtainMessage());
+ verify(mOemHookProxy).sendRequestStrings(
+ mSerialNumberCaptor.capture(), eq(new ArrayList<>(Arrays.asList(strings))));
+ }
+
+ @Test
+ public void testInvokeOemRilRequestRaw() throws Exception {
+ byte[] data = new byte[]{1, 2, 3};
+ mRILUnderTest.invokeOemRilRequestRaw(data, obtainMessage());
+ verify(mOemHookProxy).sendRequestRaw(
+ mSerialNumberCaptor.capture(), eq(mRILUnderTest.primitiveArrayToArrayList(data)));
+ }
+
+ private Message obtainMessage() {
+ return mTestHandler.getThreadHandler().obtainMessage();
+ }
+
+ private static void verifyRILResponse(RIL ril, int serial, int requestType) {
+ RadioResponseInfo responseInfo =
+ createFakeRadioResponseInfo(serial, RadioError.NONE, RadioResponseType.SOLICITED);
+
+ RILRequest rr = ril.processResponse(responseInfo);
+ assertNotNull(rr);
+
+ assertEquals(serial, rr.getSerial());
+ assertEquals(requestType, rr.getRequest());
+ assertTrue(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+
+ ril.processResponseDone(rr, responseInfo, null);
+ assertEquals(0, ril.getRilRequestList().size());
+ assertFalse(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+ }
+
+ private static RadioResponseInfo createFakeRadioResponseInfo(int serial, int error, int type) {
+ RadioResponseInfo respInfo = new RadioResponseInfo();
+ respInfo.serial = serial;
+ respInfo.error = error;
+ respInfo.type = type;
+ return respInfo;
+ }
+
+ @Test
+ public void testConvertHalCellInfoListForLTE() throws Exception {
+ android.hardware.radio.V1_0.CellInfoLte lte = new android.hardware.radio.V1_0.CellInfoLte();
+ lte.cellIdentityLte.ci = CI;
+ lte.cellIdentityLte.pci = PCI;
+ lte.cellIdentityLte.tac = TAC;
+ lte.cellIdentityLte.earfcn = EARFCN;
+ lte.cellIdentityLte.mcc = MCC_STR;
+ lte.cellIdentityLte.mnc = MNC_STR;
+ lte.signalStrengthLte.signalStrength = SIGNAL_STRENGTH;
+ lte.signalStrengthLte.rsrp = RSRP;
+ lte.signalStrengthLte.rsrq = RSRQ;
+ lte.signalStrengthLte.rssnr = RSSNR;
+ lte.signalStrengthLte.cqi = CQI;
+ lte.signalStrengthLte.timingAdvance = TIME_ADVANCE;
+ android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
+ record.cellInfoType = TYPE_LTE;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.lte.add(lte);
+ ArrayList<android.hardware.radio.V1_0.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_0.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(records);
+
+ assertEquals(1, ret.size());
+ CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
+ CellInfoLte expected = new CellInfoLte();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, Integer.MAX_VALUE, MCC_STR,
+ MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthLte css = new CellSignalStrengthLte(
+ SIGNAL_STRENGTH, -RSRP, -RSRQ, RSSNR, CQI, TIME_ADVANCE);
+ expected.setCellIdentity(cil);
+ expected.setCellSignalStrength(css);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+ assertEquals(expected, cellInfoLte);
+ }
+
+ @Test
+ public void testConvertHalCellInfoListForGSM() throws Exception {
+ android.hardware.radio.V1_0.CellInfoGsm cellinfo =
+ new android.hardware.radio.V1_0.CellInfoGsm();
+ cellinfo.cellIdentityGsm.lac = LAC;
+ cellinfo.cellIdentityGsm.cid = CID;
+ cellinfo.cellIdentityGsm.bsic = BSIC;
+ cellinfo.cellIdentityGsm.arfcn = ARFCN;
+ cellinfo.cellIdentityGsm.mcc = MCC_STR;
+ cellinfo.cellIdentityGsm.mnc = MNC_STR;
+ cellinfo.signalStrengthGsm.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthGsm.bitErrorRate = BIT_ERROR_RATE;
+ cellinfo.signalStrengthGsm.timingAdvance = TIME_ADVANCE;
+ android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
+ record.cellInfoType = TYPE_GSM;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.gsm.add(cellinfo);
+ ArrayList<android.hardware.radio.V1_0.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_0.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(records);
+
+ assertEquals(1, ret.size());
+ CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
+ CellInfoGsm expected = new CellInfoGsm();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityGsm ci = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+ assertEquals(expected, cellInfoGsm);
+ }
+
+ @Test
+ public void testConvertHalCellInfoListForWcdma() throws Exception {
+ android.hardware.radio.V1_0.CellInfoWcdma cellinfo =
+ new android.hardware.radio.V1_0.CellInfoWcdma();
+ cellinfo.cellIdentityWcdma.lac = LAC;
+ cellinfo.cellIdentityWcdma.cid = CID;
+ cellinfo.cellIdentityWcdma.psc = PSC;
+ cellinfo.cellIdentityWcdma.uarfcn = UARFCN;
+ cellinfo.cellIdentityWcdma.mcc = MCC_STR;
+ cellinfo.cellIdentityWcdma.mnc = MNC_STR;
+ cellinfo.signalStrengthWcdma.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthWcdma.bitErrorRate = BIT_ERROR_RATE;
+ android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
+ record.cellInfoType = TYPE_WCDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.wcdma.add(cellinfo);
+ ArrayList<android.hardware.radio.V1_0.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_0.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(records);
+
+ assertEquals(1, ret.size());
+ CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
+ CellInfoWcdma expected = new CellInfoWcdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityWcdma ci = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+ assertEquals(expected, cellInfoWcdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoListForTdscdma() throws Exception {
+ android.hardware.radio.V1_2.CellInfoTdscdma cellinfo =
+ new android.hardware.radio.V1_2.CellInfoTdscdma();
+ cellinfo.cellIdentityTdscdma.base.lac = LAC;
+ cellinfo.cellIdentityTdscdma.base.cid = CID;
+ cellinfo.cellIdentityTdscdma.base.cpid = PSC;
+ cellinfo.cellIdentityTdscdma.uarfcn = UARFCN;
+ cellinfo.cellIdentityTdscdma.base.mcc = MCC_STR;
+ cellinfo.cellIdentityTdscdma.base.mnc = MNC_STR;
+ cellinfo.signalStrengthTdscdma.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthTdscdma.bitErrorRate = BIT_ERROR_RATE;
+ cellinfo.signalStrengthTdscdma.rscp = RSCP;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_TD_SCDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.tdscdma.add(cellinfo);
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList_1_2(records);
+
+ assertEquals(1, ret.size());
+ CellInfoTdscdma cellInfoTdscdma = (CellInfoTdscdma) ret.get(0);
+ CellInfoTdscdma expected = new CellInfoTdscdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthTdscdma cs = new CellSignalStrengthTdscdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ assertEquals(expected, cellInfoTdscdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoListForCdma() throws Exception {
+ android.hardware.radio.V1_0.CellInfoCdma cellinfo =
+ new android.hardware.radio.V1_0.CellInfoCdma();
+ cellinfo.cellIdentityCdma.networkId = NETWORK_ID;
+ cellinfo.cellIdentityCdma.systemId = SYSTEM_ID;
+ cellinfo.cellIdentityCdma.baseStationId = BASESTATION_ID;
+ cellinfo.cellIdentityCdma.longitude = LONGITUDE;
+ cellinfo.cellIdentityCdma.latitude = LATITUDE;
+ cellinfo.signalStrengthCdma.dbm = DBM;
+ cellinfo.signalStrengthCdma.ecio = ECIO;
+ cellinfo.signalStrengthEvdo.dbm = DBM;
+ cellinfo.signalStrengthEvdo.ecio = ECIO;
+ cellinfo.signalStrengthEvdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
+ android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
+ record.cellInfoType = TYPE_CDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.cdma.add(cellinfo);
+ ArrayList<android.hardware.radio.V1_0.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_0.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(records);
+
+ assertEquals(1, ret.size());
+ CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
+ CellInfoCdma expected = new CellInfoCdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityCdma ci = new CellIdentityCdma(
+ NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
+ DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+ assertEquals(expected, cellInfoCdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForLTE() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForLTE(MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
+ CellInfoLte expected = new CellInfoLte();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityLte cil = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthLte css = new CellSignalStrengthLte(
+ SIGNAL_STRENGTH, -RSRP, -RSRQ, RSSNR, CQI, TIME_ADVANCE);
+ expected.setCellIdentity(cil);
+ expected.setCellSignalStrength(css);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoLte);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2_ForLTEWithEmptyOperatorInfo() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForLTE(
+ MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
+ CellInfoLte expected = new CellInfoLte();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR,
+ EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthLte css = new CellSignalStrengthLte(
+ SIGNAL_STRENGTH, -RSRP, -RSRQ, RSSNR, CQI, TIME_ADVANCE);
+ expected.setCellIdentity(cil);
+ expected.setCellSignalStrength(css);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoLte);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForLTEWithEmptyMccMnc() throws Exception {
+ // MCC/MNC will be set as INT_MAX if unknown
+ ArrayList<CellInfo> ret = getCellInfoListForLTE(
+ String.valueOf(Integer.MAX_VALUE), String.valueOf(Integer.MAX_VALUE),
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
+ CellInfoLte expected = new CellInfoLte();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityLte cil = new CellIdentityLte(
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthLte css = new CellSignalStrengthLte(
+ SIGNAL_STRENGTH, -RSRP, -RSRQ, RSSNR, CQI, TIME_ADVANCE);
+ expected.setCellIdentity(cil);
+ expected.setCellSignalStrength(css);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoLte);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForGSM() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForGSM(MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
+ CellInfoGsm expected = new CellInfoGsm();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityGsm ci = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoGsm);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForGSMWithEmptyOperatorInfo() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForGSM(
+ MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
+ CellInfoGsm expected = new CellInfoGsm();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityGsm ci = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoGsm);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForGSMWithEmptyMccMnc() throws Exception {
+ // MCC/MNC will be set as INT_MAX if unknown
+ ArrayList<CellInfo> ret = getCellInfoListForGSM(
+ String.valueOf(Integer.MAX_VALUE), String.valueOf(Integer.MAX_VALUE),
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
+ CellInfoGsm expected = new CellInfoGsm();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityGsm ci = new CellIdentityGsm(
+ LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+ expected.setCellIdentity(ci);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ expected.setCellSignalStrength(cs);
+ assertEquals(expected, cellInfoGsm);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForWcdma() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForWcdma(
+ MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
+ CellInfoWcdma expected = new CellInfoWcdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityWcdma ci = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthWcdma cs =
+ new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoWcdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForWcdmaWithEmptyOperatorInfo() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForWcdma(
+ MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
+ CellInfoWcdma expected = new CellInfoWcdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityWcdma ci = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoWcdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForWcdmaWithEmptyMccMnc() throws Exception {
+ // MCC/MNC will be set as INT_MAX if unknown
+ ArrayList<CellInfo> ret = getCellInfoListForWcdma(
+ String.valueOf(Integer.MAX_VALUE), String.valueOf(Integer.MAX_VALUE),
+ ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
+ CellInfoWcdma expected = new CellInfoWcdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityWcdma ci = new CellIdentityWcdma(
+ LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoWcdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForCdma() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForCdma(ALPHA_LONG, ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
+ CellInfoCdma expected = new CellInfoCdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityCdma ci = new CellIdentityCdma(
+ NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ ALPHA_LONG, ALPHA_SHORT);
+ CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
+ DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoCdma);
+ }
+
+ @Test
+ public void testConvertHalCellInfoList_1_2ForCdmaWithEmptyOperatorInfo() throws Exception {
+ ArrayList<CellInfo> ret = getCellInfoListForCdma(EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+
+ assertEquals(1, ret.size());
+ CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
+ CellInfoCdma expected = new CellInfoCdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ CellIdentityCdma ci = new CellIdentityCdma(
+ NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
+ EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
+ DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ assertEquals(expected, cellInfoCdma);
+ }
+
+ private ArrayList<CellInfo> getCellInfoListForLTE(
+ String mcc, String mnc, String alphaLong, String alphaShort) {
+ android.hardware.radio.V1_2.CellInfoLte lte = new android.hardware.radio.V1_2.CellInfoLte();
+ lte.cellIdentityLte.base.ci = CI;
+ lte.cellIdentityLte.base.pci = PCI;
+ lte.cellIdentityLte.base.tac = TAC;
+ lte.cellIdentityLte.base.earfcn = EARFCN;
+ lte.cellIdentityLte.bandwidth = BANDWIDTH;
+ lte.cellIdentityLte.base.mcc = mcc;
+ lte.cellIdentityLte.base.mnc = mnc;
+ lte.cellIdentityLte.operatorNames.alphaLong = alphaLong;
+ lte.cellIdentityLte.operatorNames.alphaShort = alphaShort;
+ lte.signalStrengthLte.signalStrength = SIGNAL_STRENGTH;
+ lte.signalStrengthLte.rsrp = RSRP;
+ lte.signalStrengthLte.rsrq = RSRQ;
+ lte.signalStrengthLte.rssnr = RSSNR;
+ lte.signalStrengthLte.cqi = CQI;
+ lte.signalStrengthLte.timingAdvance = TIME_ADVANCE;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_LTE;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.lte.add(lte);
+ record.connectionStatus = 0;
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+ return RIL.convertHalCellInfoList_1_2(records);
+ }
+
+ private ArrayList<CellInfo> getCellInfoListForGSM(
+ String mcc, String mnc, String alphaLong, String alphaShort) {
+ android.hardware.radio.V1_2.CellInfoGsm cellinfo =
+ new android.hardware.radio.V1_2.CellInfoGsm();
+ cellinfo.cellIdentityGsm.base.lac = LAC;
+ cellinfo.cellIdentityGsm.base.cid = CID;
+ cellinfo.cellIdentityGsm.base.bsic = BSIC;
+ cellinfo.cellIdentityGsm.base.arfcn = ARFCN;
+ cellinfo.cellIdentityGsm.base.mcc = mcc;
+ cellinfo.cellIdentityGsm.base.mnc = mnc;
+ cellinfo.cellIdentityGsm.operatorNames.alphaLong = alphaLong;
+ cellinfo.cellIdentityGsm.operatorNames.alphaShort = alphaShort;
+ cellinfo.signalStrengthGsm.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthGsm.bitErrorRate = BIT_ERROR_RATE;
+ cellinfo.signalStrengthGsm.timingAdvance = TIME_ADVANCE;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_GSM;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.gsm.add(cellinfo);
+ record.connectionStatus = 0;
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+
+ return RIL.convertHalCellInfoList_1_2(records);
+ }
+
+ private ArrayList<CellInfo> getCellInfoListForWcdma(
+ String mcc, String mnc, String alphaLong, String alphaShort) {
+ android.hardware.radio.V1_2.CellInfoWcdma cellinfo =
+ new android.hardware.radio.V1_2.CellInfoWcdma();
+ cellinfo.cellIdentityWcdma.base.lac = LAC;
+ cellinfo.cellIdentityWcdma.base.cid = CID;
+ cellinfo.cellIdentityWcdma.base.psc = PSC;
+ cellinfo.cellIdentityWcdma.base.uarfcn = UARFCN;
+ cellinfo.cellIdentityWcdma.base.mcc = mcc;
+ cellinfo.cellIdentityWcdma.base.mnc = mnc;
+ cellinfo.cellIdentityWcdma.operatorNames.alphaLong = alphaLong;
+ cellinfo.cellIdentityWcdma.operatorNames.alphaShort = alphaShort;
+ cellinfo.signalStrengthWcdma.base.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthWcdma.base.bitErrorRate = BIT_ERROR_RATE;
+ cellinfo.signalStrengthWcdma.rscp = RSCP;
+ cellinfo.signalStrengthWcdma.ecno = ECNO;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_WCDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.wcdma.add(cellinfo);
+ record.connectionStatus = 0;
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+
+ return RIL.convertHalCellInfoList_1_2(records);
+ }
+
+ private ArrayList<CellInfo> getCellInfoListForCdma(String alphaLong, String alphaShort) {
+ android.hardware.radio.V1_2.CellInfoCdma cellinfo =
+ new android.hardware.radio.V1_2.CellInfoCdma();
+ cellinfo.cellIdentityCdma.base.networkId = NETWORK_ID;
+ cellinfo.cellIdentityCdma.base.systemId = SYSTEM_ID;
+ cellinfo.cellIdentityCdma.base.baseStationId = BASESTATION_ID;
+ cellinfo.cellIdentityCdma.base.longitude = LONGITUDE;
+ cellinfo.cellIdentityCdma.base.latitude = LATITUDE;
+ cellinfo.cellIdentityCdma.operatorNames.alphaLong = alphaLong;
+ cellinfo.cellIdentityCdma.operatorNames.alphaShort = alphaShort;
+ cellinfo.signalStrengthCdma.dbm = DBM;
+ cellinfo.signalStrengthCdma.ecio = ECIO;
+ cellinfo.signalStrengthEvdo.dbm = DBM;
+ cellinfo.signalStrengthEvdo.ecio = ECIO;
+ cellinfo.signalStrengthEvdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_CDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.cdma.add(cellinfo);
+ record.connectionStatus = 0;
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+
+ return RIL.convertHalCellInfoList_1_2(records);
+ }
+
+ public android.telephony.SignalStrength getTdScdmaSignalStrength_1_0(int tdscdmaNegDbm) {
+ android.hardware.radio.V1_0.SignalStrength halSs =
+ new android.hardware.radio.V1_0.SignalStrength();
+ halSs.lte.signalStrength = SIGNAL_STRENGTH;
+ halSs.lte.rsrp = RSRP;
+ halSs.lte.rsrq = RSRQ;
+ halSs.lte.rssnr = RSSNR;
+ halSs.gw.signalStrength = SIGNAL_STRENGTH;
+ halSs.gw.bitErrorRate = BIT_ERROR_RATE;
+ halSs.cdma.dbm = DBM;
+ halSs.cdma.ecio = ECIO;
+ halSs.evdo.dbm = DBM;
+ halSs.evdo.ecio = ECIO;
+ halSs.evdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
+ halSs.tdScdma.rscp = tdscdmaNegDbm;
+ android.telephony.SignalStrength ss = RIL.convertHalSignalStrength(halSs);
+ // FIXME: We should not need to call validateInput here b/74115980.
+ ss.validateInput();
+ return ss;
+ }
+
+ public android.telephony.SignalStrength getTdScdmaSignalStrength_1_2(int tdscdmaAsu) {
+ android.hardware.radio.V1_2.SignalStrength halSs =
+ new android.hardware.radio.V1_2.SignalStrength();
+ halSs.lte.signalStrength = SIGNAL_STRENGTH;
+ halSs.lte.rsrp = RSRP;
+ halSs.lte.rsrq = RSRQ;
+ halSs.lte.rssnr = RSSNR;
+ halSs.gsm.signalStrength = SIGNAL_STRENGTH;
+ halSs.gsm.bitErrorRate = BIT_ERROR_RATE;
+ halSs.cdma.dbm = DBM;
+ halSs.cdma.ecio = ECIO;
+ halSs.evdo.dbm = DBM;
+ halSs.evdo.ecio = ECIO;
+ halSs.evdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
+ halSs.wcdma.base.signalStrength = 99;
+ halSs.wcdma.rscp = 255;
+ halSs.tdScdma.rscp = tdscdmaAsu;
+ android.telephony.SignalStrength ss = RIL.convertHalSignalStrength_1_2(halSs);
+ // FIXME: We should not need to call validateInput here b/74115980
+ // but unless we call it, we have to pass Integer.MAX_VALUE for wcdma RSCP,
+ // which is outside the allowable range for the HAL. This value is being
+ // coerced inside SignalStrength.validateInput().
+ ss.validateInput();
+ return ss;
+ }
+
+ @Test
+ public void testHalSignalStrengthTdScdma() throws Exception {
+ // Check that the minimum value is the same.
+ assertEquals(getTdScdmaSignalStrength_1_0(120), getTdScdmaSignalStrength_1_2(0));
+ // Check that the maximum common value is the same.
+ assertEquals(getTdScdmaSignalStrength_1_0(25), getTdScdmaSignalStrength_1_2(95));
+ // Check that an invalid value is the same.
+ assertEquals(getTdScdmaSignalStrength_1_0(-1), getTdScdmaSignalStrength_1_2(255));
+ }
+
+ @Test
+ public void testSetupDataCall() throws Exception {
+
+ DataProfile dp = new DataProfile(PROFILE_ID, APN, PROTOCOL, AUTH_TYPE, USER_NAME, PASSWORD,
+ TYPE, MAX_CONNS_TIME, MAX_CONNS, WAIT_TIME, APN_ENABLED, SUPPORTED_APNT_YPES_BITMAP,
+ ROAMING_PROTOCOL, BEARER_BITMAP, MTU, MVNO_TYPE, MVNO_MATCH_DATA, MODEM_COGNITIVE);
+ mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
+ false, 0, null, obtainMessage());
+ ArgumentCaptor<DataProfileInfo> dpiCaptor = ArgumentCaptor.forClass(DataProfileInfo.class);
+ verify(mRadioProxy).setupDataCall(
+ mSerialNumberCaptor.capture(), eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ dpiCaptor.capture(), eq(true), eq(false), eq(false));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SETUP_DATA_CALL);
+ DataProfileInfo dpi = dpiCaptor.getValue();
+ assertEquals(PROFILE_ID, dpi.profileId);
+ assertEquals(APN, dpi.apn);
+ assertEquals(PROTOCOL, dpi.protocol);
+ assertEquals(AUTH_TYPE, dpi.authType);
+ assertEquals(USER_NAME, dpi.user);
+ assertEquals(PASSWORD, dpi.password);
+ assertEquals(TYPE, dpi.type);
+ assertEquals(MAX_CONNS_TIME, dpi.maxConnsTime);
+ assertEquals(MAX_CONNS, dpi.maxConns);
+ assertEquals(WAIT_TIME, dpi.waitTime);
+ assertEquals(APN_ENABLED, dpi.enabled);
+ assertEquals(SUPPORTED_APNT_YPES_BITMAP, dpi.supportedApnTypesBitmap);
+ assertEquals(ROAMING_PROTOCOL, dpi.protocol);
+ assertEquals(BEARER_BITMAP, dpi.bearerBitmap);
+ assertEquals(MTU, dpi.mtu);
+ assertEquals(0, dpi.mvnoType);
+ assertEquals(MVNO_MATCH_DATA, dpi.mvnoMatchData);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java
index 653c357..511b5c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java
@@ -20,9 +20,9 @@
import android.os.Parcel;
import android.support.test.filters.SmallTest;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.GeranBand;
import android.telephony.RadioAccessSpecifier;
-import android.telephony.RadioNetworkConstants.GeranBands;
-import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
import org.junit.Test;
@@ -33,8 +33,8 @@
@Test
@SmallTest
public void testParcel() {
- int ranGsm = RadioAccessNetworks.GERAN;
- int[] gsmBands = {GeranBands.BAND_T380, GeranBands.BAND_T410};
+ int ranGsm = AccessNetworkType.GERAN;
+ int[] gsmBands = {GeranBand.BAND_T380, GeranBand.BAND_T410};
int[] gsmChannels = {1, 2, 3, 4};
RadioAccessSpecifier ras = new RadioAccessSpecifier(ranGsm, gsmBands, gsmChannels);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
new file mode 100644
index 0000000..56cd8db
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ServiceState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/** Tests for RatRatcheter. */
+public class RatRatcheterTest {
+
+ private ServiceState mServiceState;
+
+ @Before
+ public void setUp() {
+ mServiceState = new ServiceState();
+ }
+
+ @Test
+ public void testUpdateBandwidthsSuccess() {
+ int[] bandwidths = new int[] {1400, 5000};
+ mServiceState.setCellBandwidths(new int[] {5000});
+
+ boolean updated = RatRatcheter.updateBandwidths(bandwidths, mServiceState);
+
+ assertTrue(updated);
+ assertTrue(Arrays.equals(mServiceState.getCellBandwidths(), bandwidths));
+ }
+
+ @Test
+ public void testUpdateBandwidthsFailure() {
+ int[] originalBandwidths = {5000, 10000};
+ int[] newBandwidths = {1400, 5000};
+ mServiceState.setCellBandwidths(originalBandwidths);
+
+ boolean updated = RatRatcheter.updateBandwidths(newBandwidths, mServiceState);
+
+ assertFalse(updated);
+ assertTrue(Arrays.equals(mServiceState.getCellBandwidths(), originalBandwidths));
+ }
+
+ @Test
+ public void testUpdateBandwidthsNull() {
+ int[] originalBandwidths = {5000, 10000};
+ mServiceState.setCellBandwidths(originalBandwidths);
+
+ boolean updated = RatRatcheter.updateBandwidths(null, mServiceState);
+
+ assertFalse(updated);
+ assertTrue(Arrays.equals(mServiceState.getCellBandwidths(), originalBandwidths));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 5697a45..55fde49 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -16,7 +16,10 @@
package com.android.internal.telephony;
+import android.os.Bundle;
import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationState;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
@@ -25,6 +28,7 @@
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Arrays;
public class ServiceStateTest extends TestCase {
@@ -68,6 +72,55 @@
}
@SmallTest
+ public void testBitmaskFromString() {
+ String networkTypeList = "4|7|5|6|12|13|14|19";
+ int networkTypeBitmask = 1 << (4 - 1) | 1 << (7 - 1) | 1 << (5 - 1) | 1 << (6 - 1)
+ | 1 << (12 - 1) | 1 << (14 - 1) | 1 << (13 - 1) | 1 << (19 - 1);
+ assertEquals(networkTypeBitmask,
+ ServiceState.getBitmaskFromString(networkTypeList));
+
+ networkTypeList = "13";
+ networkTypeBitmask = 1 << (13 - 1);
+ assertEquals(networkTypeBitmask,
+ ServiceState.getBitmaskFromString(networkTypeList));
+
+ networkTypeList = "";
+ networkTypeBitmask = 0;
+ assertEquals(networkTypeBitmask,
+ ServiceState.getBitmaskFromString(networkTypeList));
+ }
+
+ @SmallTest
+ public void testConvertNetworkTypeBitmaskToBearerBitmask() {
+ // The value was calculated by adding "4|4|7|5|6|12|14|13|19".
+ int networkTypeBitmask = 276600;
+ // The value was calculated by adding "4|5|6|7|8|12|13|14|19".
+ int bearerBitmask = 276728;
+ assertEquals(bearerBitmask,
+ ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask));
+
+ networkTypeBitmask = 0;
+ bearerBitmask = 0;
+ assertEquals(bearerBitmask,
+ ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask));
+ }
+
+ @SmallTest
+ public void testConvertBearerBitmaskToNetworkTypeBitmask() {
+ // The value was calculated by adding "4|4|7|5|6|12|14|13|19".
+ int networkTypeBitmask = 276600;
+ // The value was calculated by adding "4|5|6|7|8|12|13|14|19".
+ int bearerBitmask = 276728;
+ assertEquals(networkTypeBitmask,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask));
+
+ networkTypeBitmask = 0;
+ bearerBitmask = 0;
+ assertEquals(networkTypeBitmask,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask));
+ }
+
+ @SmallTest
public void testRAT() {
ServiceState ss = new ServiceState();
@@ -113,6 +166,17 @@
}
}
}
+ @SmallTest
+ public void testGetCellBandwidths() {
+ ServiceState ss = new ServiceState();
+
+ ss.setCellBandwidths(null);
+ assertTrue(Arrays.equals(ss.getCellBandwidths(), new int[0]));
+
+ int[] cellBandwidths = new int[]{5000, 10000};
+ ss.setCellBandwidths(cellBandwidths);
+ assertTrue(Arrays.equals(ss.getCellBandwidths(), cellBandwidths));
+ }
@SmallTest
public void testOperatorName() {
@@ -147,9 +211,9 @@
ss.setIsManualSelection(true);
assertTrue(ss.getIsManualSelection());
- ss.setSystemAndNetworkId(123, 456);
- assertEquals(123, ss.getSystemId());
- assertEquals(456, ss.getNetworkId());
+ ss.setCdmaSystemAndNetworkId(123, 456);
+ assertEquals(123, ss.getCdmaSystemId());
+ assertEquals(456, ss.getCdmaNetworkId());
ss.setEmergencyOnly(true);
assertTrue(ss.isEmergencyOnly());
@@ -168,13 +232,15 @@
ss.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
ss.setCssIndicator(1);
- ss.setSystemAndNetworkId(2, 3);
+ ss.setCdmaSystemAndNetworkId(2, 3);
ss.setCdmaRoamingIndicator(4);
ss.setCdmaDefaultRoamingIndicator(5);
ss.setCdmaEriIconIndex(6);
ss.setCdmaEriIconMode(7);
ss.setEmergencyOnly(true);
ss.setDataRoamingFromRegistration(true);
+ ss.setChannelNumber(2100);
+ ss.setCellBandwidths(new int[]{1400, 5000, 10000});
Parcel p = Parcel.obtain();
ss.writeToParcel(p, 0);
@@ -183,4 +249,113 @@
ServiceState newSs = new ServiceState(p);
assertEquals(ss, newSs);
}
-}
\ No newline at end of file
+
+ @SmallTest
+ public void testBundle() {
+ ServiceState ss = new ServiceState();
+ ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+ ss.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+ ss.setVoiceRoamingType(ServiceState.ROAMING_TYPE_INTERNATIONAL);
+ ss.setDataRoamingType(ServiceState.ROAMING_TYPE_UNKNOWN);
+ ss.setOperatorName("long", "short", "numeric");
+ ss.setIsManualSelection(true);
+ ss.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
+ ss.setCssIndicator(1);
+ ss.setCdmaSystemAndNetworkId(2, 3);
+ ss.setCdmaRoamingIndicator(4);
+ ss.setCdmaDefaultRoamingIndicator(5);
+ ss.setCdmaEriIconIndex(6);
+ ss.setCdmaEriIconMode(7);
+ ss.setEmergencyOnly(true);
+ ss.setDataRoamingFromRegistration(true);
+ ss.setChannelNumber(2100);
+ ss.setCellBandwidths(new int[]{3, 4, 10});
+
+ Bundle b = new Bundle();
+ ss.fillInNotifierBundle(b);
+ ServiceState newSs = ServiceState.newFromBundle(b);
+
+ assertEquals(ss, newSs);
+ }
+
+ @SmallTest
+ public void testNetworkRegistrationState() {
+ NetworkRegistrationState wwanVoiceRegState = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN,
+ 0, 0, 0, false,
+ null, null, true, 0, 0, 0);
+
+
+ NetworkRegistrationState wwanDataRegState = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
+ 0, 0, 0, false,
+ null, null, 0);
+
+ NetworkRegistrationState wlanRegState = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN,
+ 0, 0, 0, false,
+ null, null);
+
+ ServiceState ss = new ServiceState();
+
+ ss.addNetworkRegistrationState(wwanVoiceRegState);
+ ss.addNetworkRegistrationState(wwanDataRegState);
+ ss.addNetworkRegistrationState(wlanRegState);
+
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_CS,
+ AccessNetworkConstants.TransportType.WWAN), wwanVoiceRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WLAN), wlanRegState);
+
+ wwanDataRegState = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
+ 0, 0, 0, true,
+ null, null, 0);
+ ss.addNetworkRegistrationState(wwanDataRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
+ }
+
+ @SmallTest
+ public void testDuplexMode_notLte() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
+ ss.setChannelNumber(2400);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+ }
+
+ @SmallTest
+ public void testDuplexMode_invalidEarfcn() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(-1);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+
+ ss.setChannelNumber(Integer.MAX_VALUE);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+ }
+
+ @SmallTest
+ public void testDuplexMode_FddChannel() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(2400); // band 5
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_FDD);
+ }
+
+ @SmallTest
+ public void testDuplexMode_TddChannel() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(36000); // band 33
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_TDD);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 5c2b427..603f800 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -23,22 +23,28 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.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.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.IAlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.hardware.radio.V1_0.CellIdentityGsm;
-import android.hardware.radio.V1_0.CellInfoType;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
@@ -47,20 +53,30 @@
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkService;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
+import android.util.TimestampedValue;
+import com.android.internal.R;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.test.SimulatedCommands;
@@ -68,11 +84,14 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -87,6 +106,8 @@
@Mock
protected IAlarmManager mAlarmManager;
+ CellularNetworkService mCellularNetworkService;
+
private ServiceStateTracker sst;
private ServiceStateTrackerTestHandler mSSTTestHandler;
private PersistableBundle mBundle;
@@ -116,12 +137,31 @@
}
}
+ private void addNetworkService() {
+ mCellularNetworkService = new CellularNetworkService();
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = "com.android.phone";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
+ IntentFilter filter = new IntentFilter();
+ mContextFixture.addService(
+ NetworkService.NETWORK_SERVICE_INTERFACE,
+ null,
+ "com.android.phone",
+ mCellularNetworkService.mBinder,
+ serviceInfo,
+ filter);
+ }
+
@Before
public void setUp() throws Exception {
logd("ServiceStateTrackerTest +Setup!");
super.setUp("ServiceStateTrackerTest");
+ mContextFixture.putResource(R.string.config_wwan_network_service_package,
+ "com.android.phone");
+ addNetworkService();
+
doReturn(true).when(mDct).isDisconnected();
mPhone.mDcTracker = mDct;
@@ -133,9 +173,13 @@
mBundle.putStringArray(
CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
+ mBundle.putStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES,
+ // UMTS < GPRS < EDGE
+ new String[]{"3,1,2"});
+
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.setDataRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
int dds = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -239,10 +283,11 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testSpnUpdateShowPlmnOnly() {
- doReturn(0x02).when(mSimRecords).getDisplayRule(anyString());
+ doReturn(0x02).when(mSimRecords).getDisplayRule(new ServiceState());
doReturn(IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN).
when(mUiccCardApplication3gpp).getState();
@@ -372,8 +417,7 @@
SignalStrength.INVALID, // lteRsrq
SignalStrength.INVALID, // lteRssnr
SignalStrength.INVALID, // lteCqi
- SignalStrength.INVALID, // tdScdmaRscp
- true // gsmFlag
+ SignalStrength.INVALID // tdScdmaRscp
);
mSimulatedCommands.setSignalStrength(ss);
@@ -403,14 +447,188 @@
}
@Test
+ public void testSetsNewSignalStrengthReportingCriteria() {
+ int[] wcdmaThresholds = {
+ -110, /* SIGNAL_STRENGTH_POOR */
+ -100, /* SIGNAL_STRENGTH_MODERATE */
+ -90, /* SIGNAL_STRENGTH_GOOD */
+ -80 /* SIGNAL_STRENGTH_GREAT */
+ };
+ mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+ wcdmaThresholds);
+
+ int[] lteThresholds = {
+ -130, /* SIGNAL_STRENGTH_POOR */
+ -120, /* SIGNAL_STRENGTH_MODERATE */
+ -110, /* SIGNAL_STRENGTH_GOOD */
+ -100, /* SIGNAL_STRENGTH_GREAT */
+ };
+ mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ lteThresholds);
+
+ 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);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ verify(mPhone).setSignalStrengthReportingCriteria(eq(wcdmaThresholds),
+ eq(AccessNetworkType.UTRAN));
+ verify(mPhone).setSignalStrengthReportingCriteria(eq(lteThresholds),
+ eq(AccessNetworkType.EUTRAN));
+ }
+
+ @Test
+ @MediumTest
+ public void testSignalLevelWithWcdmaRscpThresholds() {
+ mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -110, /* SIGNAL_STRENGTH_POOR */
+ -100, /* SIGNAL_STRENGTH_MODERATE */
+ -90, /* SIGNAL_STRENGTH_GOOD */
+ -80 /* SIGNAL_STRENGTH_GREAT */
+ });
+ mBundle.putString(
+ CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
+ "rscp");
+
+ SignalStrength ss = new SignalStrength(
+ 30, // gsmSignalStrength
+ 0, // gsmBitErrorRate
+ -1, // cdmaDbm
+ -1, // cdmaEcio
+ -1, // evdoDbm
+ -1, // evdoEcio
+ -1, // evdoSnr
+ 99, // lteSignalStrength
+ SignalStrength.INVALID, // lteRsrp
+ SignalStrength.INVALID, // lteRsrq
+ SignalStrength.INVALID, // lteRssnr
+ SignalStrength.INVALID, // lteCqi
+ SignalStrength.INVALID, // tdScdmaRscp
+ 99, // wcdmaSignalStrength
+ 45 // wcdmaRscpAsu
+ );
+ mSimulatedCommands.setSignalStrength(ss);
+ mSimulatedCommands.notifySignalStrength();
+ waitForMs(300);
+ assertEquals(sst.getSignalStrength(), ss);
+ assertEquals(sst.getSignalStrength().getWcdmaLevel(), SignalStrength.SIGNAL_STRENGTH_GREAT);
+ assertEquals(sst.getSignalStrength().getWcdmaAsuLevel(), 45);
+ assertEquals(sst.getSignalStrength().getWcdmaDbm(), -75);
+
+ ss = new SignalStrength(
+ 30, // gsmSignalStrength
+ 0, // gsmBitErrorRate
+ -1, // cdmaDbm
+ -1, // cdmaEcio
+ -1, // evdoDbm
+ -1, // evdoEcio
+ -1, // evdoSnr
+ 99, // lteSignalStrength
+ SignalStrength.INVALID, // lteRsrp
+ SignalStrength.INVALID, // lteRsrq
+ SignalStrength.INVALID, // lteRssnr
+ SignalStrength.INVALID, // lteCqi
+ SignalStrength.INVALID, // tdScdmaRscp
+ 99, // wcdmaSignalStrength
+ 35 // wcdmaRscpAsu
+ );
+ mSimulatedCommands.setSignalStrength(ss);
+ mSimulatedCommands.notifySignalStrength();
+ waitForMs(300);
+ assertEquals(sst.getSignalStrength(), ss);
+ assertEquals(sst.getSignalStrength().getWcdmaLevel(), SignalStrength.SIGNAL_STRENGTH_GOOD);
+ assertEquals(sst.getSignalStrength().getWcdmaAsuLevel(), 35);
+ assertEquals(sst.getSignalStrength().getWcdmaDbm(), -85);
+
+ ss = new SignalStrength(
+ 30, // gsmSignalStrength
+ 0, // gsmBitErrorRate
+ -1, // cdmaDbm
+ -1, // cdmaEcio
+ -1, // evdoDbm
+ -1, // evdoEcio
+ -1, // evdoSnr
+ 99, // lteSignalStrength
+ SignalStrength.INVALID, // lteRsrp
+ SignalStrength.INVALID, // lteRsrq
+ SignalStrength.INVALID, // lteRssnr
+ SignalStrength.INVALID, // lteCqi
+ SignalStrength.INVALID, // tdScdmaRscp
+ 99, // wcdmaSignalStrength
+ 25 // wcdmaRscpAsu
+ );
+ mSimulatedCommands.setSignalStrength(ss);
+ mSimulatedCommands.notifySignalStrength();
+ waitForMs(300);
+ assertEquals(sst.getSignalStrength(), ss);
+ assertEquals(sst.getSignalStrength().getWcdmaLevel(),
+ SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ assertEquals(sst.getSignalStrength().getWcdmaAsuLevel(), 25);
+ assertEquals(sst.getSignalStrength().getWcdmaDbm(), -95);
+
+ ss = new SignalStrength(
+ 30, // gsmSignalStrength
+ 0, // gsmBitErrorRate
+ -1, // cdmaDbm
+ -1, // cdmaEcio
+ -1, // evdoDbm
+ -1, // evdoEcio
+ -1, // evdoSnr
+ 99, // lteSignalStrength
+ SignalStrength.INVALID, // lteRsrp
+ SignalStrength.INVALID, // lteRsrq
+ SignalStrength.INVALID, // lteRssnr
+ SignalStrength.INVALID, // lteCqi
+ SignalStrength.INVALID, // tdScdmaRscp
+ 99, // wcdmaSignalStrength
+ 15 // wcdmaRscpAsu
+ );
+ mSimulatedCommands.setSignalStrength(ss);
+ mSimulatedCommands.notifySignalStrength();
+ waitForMs(300);
+ assertEquals(sst.getSignalStrength(), ss);
+ assertEquals(sst.getSignalStrength().getWcdmaLevel(), SignalStrength.SIGNAL_STRENGTH_POOR);
+ assertEquals(sst.getSignalStrength().getWcdmaAsuLevel(), 15);
+ assertEquals(sst.getSignalStrength().getWcdmaDbm(), -105);
+
+ ss = new SignalStrength(
+ 30, // gsmSignalStrength
+ 0, // gsmBitErrorRate
+ -1, // cdmaDbm
+ -1, // cdmaEcio
+ -1, // evdoDbm
+ -1, // evdoEcio
+ -1, // evdoSnr
+ 99, // lteSignalStrength
+ SignalStrength.INVALID, // lteRsrp
+ SignalStrength.INVALID, // lteRsrq
+ SignalStrength.INVALID, // lteRssnr
+ SignalStrength.INVALID, // lteCqi
+ SignalStrength.INVALID, // tdScdmaRscp
+ 99, // wcdmaSignalStrength
+ 5 // wcdmaRscpAsu
+ );
+ mSimulatedCommands.setSignalStrength(ss);
+ mSimulatedCommands.notifySignalStrength();
+ waitForMs(300);
+ assertEquals(sst.getSignalStrength(), ss);
+ assertEquals(sst.getSignalStrength().getWcdmaLevel(),
+ SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ assertEquals(sst.getSignalStrength().getWcdmaAsuLevel(), 5);
+ assertEquals(sst.getSignalStrength().getWcdmaDbm(), -115);
+ }
+
+ @Test
@MediumTest
public void testGsmCellLocation() {
-
- VoiceRegStateResult result = new VoiceRegStateResult();
- result.cellIdentity.cellInfoType = CellInfoType.GSM;
- result.cellIdentity.cellIdentityGsm.add(new CellIdentityGsm());
- result.cellIdentity.cellIdentityGsm.get(0).lac = 2;
- result.cellIdentity.cellIdentityGsm.get(0).cid = 3;
+ CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(0, 0, 2, 3);
+ NetworkRegistrationState result = new NetworkRegistrationState(
+ 0, 0, 0, 0, 0, false, null, cellIdentityGsm);
sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_GET_LOC_DONE,
new AsyncResult(null, result, null)));
@@ -473,8 +691,8 @@
// Enable roaming and trigger events to notify handler registered
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -485,8 +703,8 @@
assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
// Disable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -495,8 +713,8 @@
sst.unregisterForVoiceRoamingOn(mTestHandler);
// Enable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -510,8 +728,8 @@
public void testRegAndUnregForVoiceRoamingOff() throws Exception {
// Enable roaming
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -520,8 +738,8 @@
// Disable roaming
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -532,8 +750,8 @@
assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
// Enable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -542,8 +760,8 @@
sst.unregisterForVoiceRoamingOff(mTestHandler);
// Disable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -559,8 +777,8 @@
// Enable roaming and trigger events to notify handler registered
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -571,8 +789,8 @@
assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
// Disable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -581,8 +799,8 @@
sst.unregisterForDataRoamingOn(mTestHandler);
// Enable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -596,8 +814,8 @@
public void testRegAndUnregForDataRoamingOff() throws Exception {
// Enable roaming
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -606,8 +824,8 @@
// Disable roaming
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -618,8 +836,8 @@
assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
// Enable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -628,8 +846,8 @@
sst.unregisterForDataRoamingOff(mTestHandler);
// Disable roaming
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -652,8 +870,8 @@
sst.registerForDataConnectionAttached(mTestHandler, EVENT_DATA_CONNECTION_ATTACHED, null);
// set service state in service and trigger events to post message on handler
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -674,8 +892,8 @@
sst.unregisterForDataConnectionAttached(mTestHandler);
// set service state in service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -689,8 +907,8 @@
public void testRegAndUnregForDataConnAttach() throws Exception {
// Initially set service state out of service
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -698,8 +916,8 @@
sst.registerForDataConnectionAttached(mTestHandler, EVENT_DATA_CONNECTION_ATTACHED, null);
// set service state in service and trigger events to post message on handler
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -710,8 +928,8 @@
assertEquals(EVENT_DATA_CONNECTION_ATTACHED, messageArgumentCaptor.getValue().what);
// set service state out of service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -720,8 +938,8 @@
sst.unregisterForDataConnectionAttached(mTestHandler);
// set service state in service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -735,15 +953,15 @@
public void testRegAndUnregForDataConnDetach() throws Exception {
// Initially set service state in service
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
// set service state out of service and trigger events to post message on handler
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -754,8 +972,8 @@
assertEquals(EVENT_DATA_CONNECTION_DETACHED, messageArgumentCaptor.getValue().what);
// set service state in service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -764,8 +982,8 @@
sst.unregisterForDataConnectionDetached(mTestHandler);
// set service state out of service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -777,7 +995,7 @@
@Test
@MediumTest
public void testRegisterForDataRegStateOrRatChange() {
- int drs = sst.mSS.RIL_REG_STATE_HOME;
+ int drs = NetworkRegistrationState.REG_STATE_HOME;
int rat = sst.mSS.RIL_RADIO_TECHNOLOGY_LTE;
sst.mSS.setRilDataRadioTechnology(rat);
sst.mSS.setDataRegState(drs);
@@ -798,8 +1016,8 @@
public void testRegAndUnregForNetworkAttached() throws Exception {
// Initially set service state out of service
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -807,8 +1025,8 @@
sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
// set service state in service and trigger events to post message on handler
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -819,8 +1037,8 @@
assertEquals(EVENT_REGISTERED_TO_NETWORK, messageArgumentCaptor.getValue().what);
// set service state out of service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_UNKNOWN);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -829,8 +1047,8 @@
sst.unregisterForNetworkAttached(mTestHandler);
// set service state in service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -853,8 +1071,8 @@
sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
// set service state in service and trigger events to post message on handler
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -880,8 +1098,8 @@
sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
// set service state in service
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(100);
@@ -996,6 +1214,127 @@
assertEquals(intArgumentCaptor.getValue().intValue(), restrictedState[1]);
}
+ private boolean notificationHasTitleSet(Notification n) {
+ // Notification has no methods to check the actual title, but #toString() includes the
+ // word "tick" if the title is set so we check this as a workaround
+ return n.toString().contains("tick");
+ }
+
+ private String getNotificationTitle(Notification n) {
+ return n.extras.getString(Notification.EXTRA_TITLE);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetPsNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnDataTitle, "test1");
+ sst.setNotification(ServiceStateTracker.PS_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test1", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.PS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnAllVoiceTitle,
+ "test2");
+ sst.setNotification(ServiceStateTracker.CS_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test2", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsNormalNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnNormalTitle, "test3");
+ sst.setNotification(ServiceStateTracker.CS_NORMAL_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test3", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsEmergencyNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnEmergencyTitle,
+ "test4");
+ sst.setNotification(ServiceStateTracker.CS_EMERGENCY_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test4", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ sst.setNotification(ServiceStateTracker.CS_REJECT_CAUSE_ENABLED);
+ }
+
@Test
@MediumTest
public void testRegisterForSubscriptionInfoReady() {
@@ -1020,8 +1359,8 @@
// Enable roaming
doReturn(true).when(mPhone).isPhoneTypeGsm();
- mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
- mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+ mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_ROAMING);
+ mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_ROAMING);
mSimulatedCommands.notifyNetworkStateChanged();
waitForMs(200);
@@ -1114,20 +1453,275 @@
@Test
@SmallTest
+ public void testShuttingDownRequest() throws Exception {
+ sst.setRadioPower(true);
+ waitForMs(100);
+
+ sst.requestShutdown();
+ waitForMs(100);
+ assertFalse(mSimulatedCommands.getRadioState().isAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testShuttingDownRequestWithRadioPowerFailResponse() throws Exception {
+ sst.setRadioPower(true);
+ waitForMs(100);
+
+ // Simulate RIL fails the radio power settings.
+ mSimulatedCommands.setRadioPowerFailResponse(true);
+ sst.setRadioPower(false);
+ waitForMs(100);
+ assertTrue(mSimulatedCommands.getRadioState().isOn());
+ sst.requestShutdown();
+ waitForMs(100);
+ assertFalse(mSimulatedCommands.getRadioState().isAvailable());
+ }
+
+ @Test
+ @SmallTest
public void testSetTimeFromNITZStr() throws Exception {
- doReturn(mAlarmManager).when(mIBinder).queryLocalInterface(anyString());
- mServiceManagerMockedServices.put(Context.ALARM_SERVICE, mIBinder);
+ {
+ // Mock sending incorrect nitz str from RIL
+ mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0");
+ waitForMs(200);
+ verify(mNitzStateMachine, times(0)).handleNitzReceived(any());
+ }
+ {
+ // Mock sending correct nitz str from RIL
+ String nitzStr = "15/06/20,00:00:00+0";
+ NitzData expectedNitzData = NitzData.parse(nitzStr);
+ mSimulatedCommands.triggerNITZupdate(nitzStr);
+ waitForMs(200);
- // Mock sending incorrect nitz str from RIL
- mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0");
- waitForMs(200);
- // AlarmManger.setTime is triggered by SystemClock.setCurrentTimeMillis().
- // Verify system time is not set to incorrect NITZ time
- verify(mAlarmManager, times(0)).setTime(anyLong());
+ ArgumentCaptor<TimestampedValue<NitzData>> argumentsCaptor =
+ ArgumentCaptor.forClass(TimestampedValue.class);
+ verify(mNitzStateMachine, times(1))
+ .handleNitzReceived(argumentsCaptor.capture());
- // Mock sending correct nitz str from RIL
- mSimulatedCommands.triggerNITZupdate("15/06/20,00:00:00+0");
+ // Confirm the argument was what we expected.
+ TimestampedValue<NitzData> actualNitzSignal = argumentsCaptor.getValue();
+ assertEquals(expectedNitzData, actualNitzSignal.getValue());
+ assertTrue(actualNitzSignal.getReferenceTimeMillis() <= SystemClock.elapsedRealtime());
+ }
+ }
+
+ // Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
+ // Expect no rat update when move from E to G.
+ @Test
+ public void testRatRatchet() throws Exception {
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, -1, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
waitForMs(200);
- verify(mAlarmManager, times(1)).setTime(anyLong());
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // EDGE -> GPRS
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null,
+ cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ // Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
+ // Bypass rat rachet when cell id changed. Expect rat update from E to G
+ @Test
+ public void testRatRatchetWithCellChange() throws Exception {
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, -1, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: EDGE -> GPRS cell ID: -1 -> 5
+ cellIdentity = new CellIdentityGsm(-1, -1, -1, 5, -1, -1);
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ // Edge, GPRS and UMTS are grouped under the same family where Edge > GPRS > UMTS .
+ // Expect no rat update from E to G immediately following cell id change.
+ // Expect ratratchet (from G to UMTS) for the following rat update within the cell location.
+ @Test
+ public void testRatRatchetWithCellChangeBeforeRatChange() throws Exception {
+ // cell ID update
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, 5, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: EDGE -> GPRS, cell ID unchanged. Expect no rat ratchet following cell Id change.
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: GPRS -> UMTS
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 3, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ private void sendPhyChanConfigChange(int[] bandwidths) {
+ ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
+ int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+ for (int bw : bandwidths) {
+ pc.add(new PhysicalChannelConfig(ssType, bw));
+
+ // All cells after the first are secondary serving cells.
+ ssType = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
+ }
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_PHYSICAL_CHANNEL_CONFIG,
+ new AsyncResult(null, pc, null)));
+ waitForMs(100);
+ }
+
+ private void sendRegStateUpdateForLteCellId(CellIdentityLte cellId) {
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 2, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1);
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 1, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId,
+ false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ }
+
+ @Test
+ public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentity5 =
+ new CellIdentityLte(1, 1, 5, 1, 5000, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000});
+ sendRegStateUpdateForLteCellId(cellIdentity5);
+ assertTrue(Arrays.equals(new int[] {5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthNotUpdatedWhenInvalidInCellIdentity() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentityInv =
+ new CellIdentityLte(1, 1, 5, 1, 12345, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000});
+ sendRegStateUpdateForLteCellId(cellIdentityInv);
+ assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthPrefersCarrierAggregationReport() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentity10 =
+ new CellIdentityLte(1, 1, 5, 1, 10000, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000, 5000});
+ sendRegStateUpdateForLteCellId(cellIdentity10);
+ assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
+ // LTE Cell with bandwidth = 10000
+ CellIdentityLte cellIdentity10 =
+ new CellIdentityLte(1, 1, 1, 1, 10000, "1", "1", "test", "tst");
+
+ sendRegStateUpdateForLteCellId(cellIdentity10);
+ assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+ sendPhyChanConfigChange(new int[] {10000, 5000});
+ assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthResetsOnOos() throws Exception {
+ testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 2, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1);
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 1, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null,
+ false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
new file mode 100644
index 0000000..3cd5239
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.SignalStrength;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecConfig}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class SignalStrengthTest {
+
+ @Test
+ public void testDefaults() throws Exception {
+ SignalStrength s = new SignalStrength();
+ assertEquals(-1, s.getCdmaDbm());
+ assertEquals(-1, s.getCdmaEcio());
+ assertEquals(-1, s.getEvdoDbm());
+ assertEquals(-1, s.getEvdoEcio());
+ assertEquals(-1, s.getEvdoSnr());
+ assertEquals(-1, s.getGsmBitErrorRate());
+ assertEquals(99, s.getGsmSignalStrength());
+ assertEquals(true, s.isGsm());
+ }
+
+ @Test
+ public void testParcelUnparcel() throws Exception {
+ assertParcelingIsLossless(new SignalStrength());
+
+ SignalStrength s = new SignalStrength(
+ 20, // gsmSignalStrength
+ 5, // gsmBitErrorRate
+ -95, // cdmaDbm
+ 10, // cdmaEcio
+ -98, // evdoDbm
+ -5, // evdoEcio
+ -2, // evdoSnr
+ 45, // lteSignalStrength
+ -105, // lteRsrp
+ -110, // lteRsrq
+ -115, // lteRssnr
+ 13, // lteCqi
+ -90, // tdscdmaRscp
+ 45, // wcdmaSignalStrength
+ 20, // wcdmaRscpAsu
+ 2, // lteRsrpBoost
+ false, // gsmFlag
+ true, // lteLevelBaseOnRsrp
+ "rscp"); // wcdmaDefaultMeasurement
+ assertParcelingIsLossless(s);
+ }
+
+ private void assertParcelingIsLossless(SignalStrength ssi) throws Exception {
+ Parcel p = Parcel.obtain();
+ ssi.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ SignalStrength sso = SignalStrength.CREATOR.createFromParcel(p);
+ assertTrue(sso.equals(ssi));
+ }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
index 283b170..cf11d1b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
@@ -16,21 +16,23 @@
package com.android.internal.telephony;
-import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.io.UnsupportedEncodingException;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import android.support.test.filters.FlakyTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.io.UnsupportedEncodingException;
+
+@Ignore
public class Sms7BitEncodingTranslatorTest extends TelephonyTest {
@Mock
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
similarity index 76%
rename from tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 105e0f8..41c033b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.isNull;
@@ -38,6 +39,7 @@
import android.content.IntentFilter;
import android.os.HandlerThread;
import android.os.Message;
+import android.provider.Telephony.Sms.Intents;
import android.test.FlakyTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Singleton;
@@ -49,24 +51,14 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-public class ImsSMSDispatcherTest extends TelephonyTest {
+public class SmsDispatchersControllerTest extends TelephonyTest {
@Mock
private SMSDispatcher.SmsTracker mTracker;
- private ImsSMSDispatcher mImsSmsDispatcher;
+ private SmsDispatchersController mSmsDispatchersController;
private ImsSmsDispatcherTestHandler mImsSmsDispatcherTestHandler;
- private boolean mReceivedTestIntent = false;
- private Object mLock = new Object();
+ private boolean mInjectionCallbackTriggered = false;
private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
- private BroadcastReceiver mTestReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- logd("onReceive");
- synchronized (mLock) {
- mReceivedTestIntent = true;
- }
- }
- };
private class ImsSmsDispatcherTestHandler extends HandlerThread {
@@ -76,7 +68,7 @@
@Override
public void onLooperPrepared() {
- mImsSmsDispatcher = new ImsSMSDispatcher(mPhone, mSmsStorageMonitor,
+ mSmsDispatchersController = new SmsDispatchersController(mPhone, mSmsStorageMonitor,
mSmsUsageMonitor);
//Initial state of RIL is power on, need to wait util RADIO_ON msg get handled
waitForMs(200);
@@ -96,30 +88,30 @@
@After
public void tearDown() throws Exception {
- mImsSmsDispatcher = null;
+ mSmsDispatchersController = null;
mImsSmsDispatcherTestHandler.quit();
super.tearDown();
}
@Test @SmallTest @FlakyTest @Ignore
public void testSmsHandleStateUpdate() throws Exception {
- assertEquals(SmsConstants.FORMAT_UNKNOWN, mImsSmsDispatcher.getImsSmsFormat());
+ assertEquals(SmsConstants.FORMAT_UNKNOWN, mSmsDispatchersController.getImsSmsFormat());
//Mock ImsNetWorkStateChange with GSM phone type
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
- assertEquals(SmsConstants.FORMAT_3GPP, mImsSmsDispatcher.getImsSmsFormat());
- assertTrue(mImsSmsDispatcher.isIms());
+ assertEquals(SmsConstants.FORMAT_3GPP, mSmsDispatchersController.getImsSmsFormat());
+ assertTrue(mSmsDispatchersController.isIms());
//Mock ImsNetWorkStateChange with Cdma Phone type
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_CDMA);
- assertEquals(SmsConstants.FORMAT_3GPP2, mImsSmsDispatcher.getImsSmsFormat());
- assertTrue(mImsSmsDispatcher.isIms());
+ assertEquals(SmsConstants.FORMAT_3GPP2, mSmsDispatchersController.getImsSmsFormat());
+ assertTrue(mSmsDispatchersController.isIms());
}
@Test @SmallTest @FlakyTest @Ignore
public void testSendImsGmsTest() throws Exception {
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
- mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
- null, null, null, null, false);
+ mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier).sendImsGsmSms(eq("038122f2"),
eq("0100038111f1000014c9f67cda9c12d37378983e4697e5d4f29c0e"), eq(0), eq(0),
any(Message.class));
@@ -128,8 +120,8 @@
@Test @SmallTest
public void testSendImsGmsTestWithOutDesAddr() throws Exception {
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
- mImsSmsDispatcher.sendText(null, "222" /*scAddr*/, TAG,
- null, null, null, null, false);
+ mSmsDispatchersController.sendText(null, "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
anyInt(), anyInt(), any(Message.class));
}
@@ -137,8 +129,8 @@
@Test @SmallTest
public void testSendImsCdmaTest() throws Exception {
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_CDMA);
- mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
- null, null, null, null, false);
+ mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier).sendImsCdmaSms((byte[])any(), eq(0), eq(0),
any(Message.class));
}
@@ -151,7 +143,7 @@
replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker,
SmsConstants.FORMAT_3GPP2);
doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
- mImsSmsDispatcher.sendRetrySms(mTracker);
+ mSmsDispatchersController.sendRetrySms(mTracker);
verify(mSimulatedCommandsVerifier).sendImsCdmaSms(captor.capture(), eq(0), eq(0),
any(Message.class));
assertEquals(1, captor.getAllValues().size());
@@ -164,7 +156,7 @@
switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker,
SmsConstants.FORMAT_3GPP);
- mImsSmsDispatcher.sendRetrySms(mTracker);
+ mSmsDispatchersController.sendRetrySms(mTracker);
verify(mSimulatedCommandsVerifier).sendImsGsmSms((String)isNull(), (String)isNull(), eq(0),
eq(0), any(Message.class));
}
@@ -176,18 +168,15 @@
restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
- Context realContext = TestApplication.getAppContext();
- realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
-
- PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
- new Intent(TEST_INTENT), 0);
-
// inject null sms pdu. This should cause intent to be received since pdu is null.
- mImsSmsDispatcher.injectSmsPdu(null, SmsConstants.FORMAT_3GPP, pendingIntent);
+ mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP,
+ (SmsDispatchersController.SmsInjectionCallback) result -> {
+ mInjectionCallbackTriggered = true;
+ assertEquals(Intents.RESULT_SMS_GENERIC_ERROR, result);
+ }
+ );
waitForMs(100);
- synchronized (mLock) {
- assertEquals(true, mReceivedTestIntent);
- }
+ assertEquals(true, mInjectionCallbackTriggered);
}
private void switchImsSmsFormat(int phoneType) {
@@ -196,4 +185,4 @@
/* wait for async msg get handled */
waitForMs(200);
}
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 141ca45..a237010 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -65,7 +65,9 @@
SubscriptionManager.NAME_SOURCE, SubscriptionManager.COLOR,
SubscriptionManager.NUMBER, SubscriptionManager.DISPLAY_NUMBER_FORMAT,
SubscriptionManager.DATA_ROAMING, SubscriptionManager.MCC,
- SubscriptionManager.MNC, SubscriptionManager.CB_EXTREME_THREAT_ALERT,
+ SubscriptionManager.MNC, SubscriptionManager.MCC_STRING,
+ SubscriptionManager.MNC_STRING,
+ SubscriptionManager.CB_EXTREME_THREAT_ALERT,
SubscriptionManager.CB_SEVERE_THREAT_ALERT, SubscriptionManager.CB_AMBER_ALERT,
SubscriptionManager.CB_ALERT_SOUND_DURATION,
SubscriptionManager.CB_ALERT_REMINDER_INTERVAL,
@@ -73,25 +75,32 @@
SubscriptionManager.CB_ETWS_TEST_ALERT, SubscriptionManager.CB_CHANNEL_50_ALERT,
SubscriptionManager.CB_CMAS_TEST_ALERT, SubscriptionManager.CB_OPT_OUT_DIALOG,
SubscriptionManager.SIM_PROVISIONING_STATUS, SubscriptionManager.IS_EMBEDDED,
- SubscriptionManager.ACCESS_RULES};
+ SubscriptionManager.ACCESS_RULES, 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.CARD_ID};
/* internal util function */
- private MatrixCursor convertFromContentToCursor(ContentValues initialValues) {
+ private MatrixCursor convertFromContentToCursor(ContentValues initialValues,
+ String[] projection) {
MatrixCursor cursor = null;
ArrayList<Object> values = new ArrayList<Object>();
-
- if (initialValues != null && mKeyMappingSet.length != 0) {
- cursor = new MatrixCursor(mKeyMappingSet);
+ if (projection == null) {
+ projection = mKeyMappingSet;
+ }
+ if (initialValues != null && projection.length != 0) {
+ cursor = new MatrixCursor(projection);
/* push value from contentValues to matrixCursors */
- for (String key : mKeyMappingSet) {
+ for (String key : projection) {
if (initialValues.containsKey(key)) {
values.add(initialValues.get(key));
} else {
values.add(null);
}
}
- cursor.addRow(values.toArray());
}
+ cursor.addRow(values.toArray());
return cursor;
}
@@ -115,7 +124,7 @@
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
if (mSubscriptionArray.size() > 0) {
- return convertFromContentToCursor(mSubscriptionArray.get(0));
+ return convertFromContentToCursor(mSubscriptionArray.get(0), projection);
}
return null;
}
@@ -243,6 +252,33 @@
}
@Test @SmallTest
+ public void testSetGetDisplayNameSrc() {
+ testInsertSim();
+
+ /* Get SUB ID */
+ int[] subIds = mSubscriptionControllerUT.getActiveSubIdList();
+ assertTrue(subIds != null && subIds.length != 0);
+ int subID = subIds[0];
+
+ /* Setting */
+ String disName = "TESTING";
+ long nameSource = 1;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(disName, subID, nameSource);
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subID, mCallingPackage);
+ 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());
+
+ }
+
+ @Test @SmallTest
public void testCleanUpSIM() {
testInsertSim();
assertFalse(mSubscriptionControllerUT.isActiveSubId(1));
@@ -310,4 +346,82 @@
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();
+ 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));
+
+ assertEquals("0", mSubscriptionControllerUT.getSubscriptionProperty(
+ subID,
+ SubscriptionManager.VT_IMS_ENABLED,
+ mCallingPackage));
+
+ assertEquals("1", mSubscriptionControllerUT.getSubscriptionProperty(
+ subID,
+ SubscriptionManager.WFC_IMS_ENABLED,
+ mCallingPackage));
+
+ assertEquals("2", mSubscriptionControllerUT.getSubscriptionProperty(
+ subID,
+ SubscriptionManager.WFC_IMS_MODE,
+ mCallingPackage));
+
+ assertEquals("3", mSubscriptionControllerUT.getSubscriptionProperty(
+ subID,
+ SubscriptionManager.WFC_IMS_ROAMING_MODE,
+ mCallingPackage));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index ac97937..6770e9a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -15,15 +15,15 @@
*/
package com.android.internal.telephony;
-import android.telephony.SubscriptionManager;
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.SubscriptionInfo;
import android.test.suitebuilder.annotation.SmallTest;
-import static org.junit.Assert.*;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import android.telephony.SubscriptionInfo;
-
public class SubscriptionInfoTest {
private SubscriptionInfo mSubscriptionInfoUT;
@@ -35,7 +35,7 @@
@Before
public void setUp() throws Exception {
mSubscriptionInfoUT = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
- "T-mobile", 0, 255, "12345", 0, null, 310, 260, "156");
+ "T-mobile", 0, 255, "12345", 0, null, "310", "260", "156");
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 32e78e3..33782b2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -39,9 +38,7 @@
import android.content.Intent;
import android.content.pm.UserInfo;
import android.net.Uri;
-import android.os.AsyncResult;
import android.os.HandlerThread;
-import android.os.Message;
import android.service.euicc.EuiccProfileInfo;
import android.service.euicc.EuiccService;
import android.service.euicc.GetEuiccProfileInfoListResult;
@@ -53,10 +50,8 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.euicc.EuiccController;
-import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.IccUtils;
import org.junit.After;
import org.junit.Before;
@@ -126,6 +121,8 @@
replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null, new int[1]);
replaceInstance(SubscriptionInfoUpdater.class, "mContext", null, null);
replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 1);
+ replaceInstance(SubscriptionInfoUpdater.class, "sSimCardState", null, new int[1]);
+ replaceInstance(SubscriptionInfoUpdater.class, "sSimApplicationState", null, new int[1]);
replaceInstance(EuiccController.class, "sInstance", null, mEuiccController);
replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
@@ -152,7 +149,7 @@
SubscriptionManager.CONTENT_URI.getAuthority(),
new FakeSubscriptionContentProvider());
doReturn(new int[]{}).when(mSubscriptionController).getActiveSubIdList();
- mIccRecord = mIccCardProxy.getIccRecords();
+ mIccRecord = mUiccProfile.getIccRecords();
mSubscriptionInfoUpdaterHandlerThread = new SubscriptionInfoUpdaterHandlerThread(TAG);
mSubscriptionInfoUpdaterHandlerThread.start();
@@ -169,14 +166,10 @@
@SmallTest
public void testSimAbsent() throws Exception {
doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController).getActiveSubIdList();
- Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
-
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
waitForMs(100);
verify(mSubscriptionContent).put(eq(SubscriptionManager.SIM_SLOT_INDEX),
@@ -193,12 +186,8 @@
@Test
@SmallTest
public void testSimUnknown() throws Exception {
- Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
-
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, FAKE_SUB_ID_1);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
@@ -213,17 +202,14 @@
@Test
@SmallTest
public void testSimError() throws Exception {
- Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(0),
+ 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();
@@ -232,12 +218,9 @@
@Test
@SmallTest
public void testWrongSimState() throws Exception {
- Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_IMSI);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 2);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_IMSI, null, 2);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
@@ -253,16 +236,13 @@
public void testSimLoaded() throws Exception {
/* mock new sim got loaded and there is no sim loaded before */
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
- doReturn("89012604200000000000").when(mIccRecord).getIccId();
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
+ doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- Intent intentInternalSimStateChanged =
- new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- intentInternalSimStateChanged.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
- mContext.sendBroadcast(intentInternalSimStateChanged);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
+
waitForMs(100);
// verify SIM_STATE_CHANGED broadcast. It should be broadcast twice, once for
@@ -321,16 +301,13 @@
public void testSimLoadedEmptyOperatorNumeric() throws Exception {
/* mock new sim got loaded and there is no sim loaded before */
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
- doReturn("89012604200000000000").when(mIccRecord).getIccId();
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
+ doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
// operator numeric is empty
doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
@@ -350,28 +327,14 @@
public void testSimLockedWithOutIccId() throws Exception {
/* mock no IccId Info present and try to query IccId
after IccId query, update subscriptionDB */
- doReturn(mIccFileHandler).when(mIccCardProxy).getIccFileHandler();
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Message msg = (Message) invocation.getArguments()[1];
- AsyncResult.forMessage(msg).result = IccUtils
- .hexStringToBytes("89012604200000000000");
- msg.sendToTarget();
- return null;
- }
- }).when(mIccFileHandler).loadEFTransparent(anyInt(), any(Message.class));
+ doReturn("98106240020000000000").when(mIccRecord).getFullIccId();
doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
- Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
/* old IccId != new queried IccId */
@@ -402,6 +365,10 @@
replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null,
new int[]{SubscriptionInfoUpdater.SIM_NOT_CHANGE,
SubscriptionInfoUpdater.SIM_NOT_CHANGE});
+ 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();
@@ -412,16 +379,14 @@
doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
// Mock there is no sim inserted before
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(anyInt(), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(anyInt(), anyBoolean());
verify(mSubscriptionController, times(0)).clearSubInfo();
- doReturn("89012604200000000000").when(mIccRecord).getIccId();
+ doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
// Mock sending a sim loaded for SIM 1
- Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
+
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
@@ -430,12 +395,11 @@
verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
// Mock sending a sim loaded for SIM 2
- doReturn("89012604200000000001").when(mIccRecord).getIccId();
- mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_2);
- mContext.sendBroadcast(mIntent);
+ doReturn("89012604200000000001").when(mIccRecord).getFullIccId();
+
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);
+
waitForMs(100);
verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
@@ -455,22 +419,16 @@
replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
new String[]{"89012604200000000000"});
- doReturn(mIccFileHandler).when(mIccCardProxy).getIccFileHandler();
- Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
- verify(mIccFileHandler, times(0)).loadEFTransparent(anyInt(), any(Message.class));
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
- verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(
+ verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
anyString(), eq(FAKE_SUB_ID_1));
- verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
+ verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
verify(mSubscriptionController, times(0)).clearSubInfo();
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -495,11 +453,11 @@
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 */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
// 2: embedded but no longer present.
subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -546,11 +504,11 @@
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 */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
// 2: embedded.
subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -588,7 +546,7 @@
List<SubscriptionInfo> subInfoList = new ArrayList<>();
// 1: not embedded.
subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, 0, 0, "", false /* isEmbedded */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -603,4 +561,25 @@
verify(mContentProvider, never()).update(eq(SubscriptionManager.CONTENT_URI), any(),
any(), isNull());
}
-}
\ No newline at end of file
+
+ @Test
+ @SmallTest
+ public void testHexIccIdSuffix() throws Exception {
+ doReturn(null).when(mSubscriptionController)
+ .getSubInfoUsingSlotIndexPrivileged(anyInt(), anyBoolean());
+ 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);
+
+ waitForMs(100);
+
+ 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();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
new file mode 100644
index 0000000..3a0d375
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class TelephonyPermissionsTest {
+
+ private static final int SUB_ID = 55555;
+ private static final int PID = 12345;
+ private static final int UID = 54321;
+ private static final String PACKAGE = "com.example";
+ private static final String MSG = "message";
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private AppOpsManager mMockAppOps;
+ @Mock
+ private ITelephony mMockTelephony;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOps);
+
+ // By default, assume we have no permissions or app-ops bits.
+ doThrow(new SecurityException()).when(mMockContext)
+ .enforcePermission(anyString(), eq(PID), eq(UID), eq(MSG));
+ doThrow(new SecurityException()).when(mMockContext)
+ .enforcePermission(anyString(), eq(PID), eq(UID), eq(MSG));
+ when(mMockAppOps.noteOp(anyInt(), eq(UID), eq(PACKAGE)))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ }
+
+ @Test
+ public void testCheckReadPhoneState_noPermissions() {
+ try {
+ TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG);
+ fail("Should have thrown SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckReadPhoneState_hasPrivilegedPermission() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, PID, UID, MSG);
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneState_hasPermissionAndAppOp() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_PHONE_STATE, PID, UID, MSG);
+ when(mMockAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, UID, PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneState_hasPermissionWithoutAppOp() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_PHONE_STATE, PID, UID, MSG);
+ assertFalse(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneState_hasCarrierPrivileges() throws Exception {
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneNumber_noPermissions() {
+ try {
+ TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG);
+ fail("Should have thrown SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckReadPhoneNumber_defaultSmsApp() {
+ when(mMockAppOps.noteOp(AppOpsManager.OP_WRITE_SMS, UID, PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneNumber_hasPrivilegedPhoneStatePermission() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, PID, UID, MSG);
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneNumber_hasReadSms() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_SMS, PID, UID, MSG);
+ when(mMockAppOps.noteOp(AppOpsManager.OP_READ_SMS, UID, PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneNumber_hasReadPhoneNumbers() {
+ doNothing().when(mMockContext).enforcePermission(
+ android.Manifest.permission.READ_PHONE_NUMBERS, PID, UID, MSG);
+ when(mMockAppOps.noteOp(AppOpsManager.OP_READ_PHONE_NUMBERS, UID, PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index dbd737e..89d8143 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -27,6 +27,7 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
@@ -38,20 +39,22 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IDeviceIdleController;
+import android.os.Looper;
import android.os.RegistrantList;
import android.os.ServiceManager;
import android.provider.BlockedNumberContract;
+import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsCallProfile;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.util.Log;
import android.util.Singleton;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
import com.android.ims.ImsEcbm;
import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
@@ -62,13 +65,15 @@
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
-import com.android.internal.telephony.uicc.IccCardProxy;
+import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IsimUiccRecords;
import com.android.internal.telephony.uicc.RuimRecords;
import com.android.internal.telephony.uicc.SIMRecords;
+import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -76,6 +81,8 @@
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -102,7 +109,7 @@
@Mock
protected UiccController mUiccController;
@Mock
- protected IccCardProxy mIccCardProxy;
+ protected UiccProfile mUiccProfile;
@Mock
protected CallManager mCallManager;
@Mock
@@ -124,8 +131,6 @@
@Mock
protected ImsCall mImsCall;
@Mock
- protected ImsCallProfile mImsCallProfile;
- @Mock
protected ImsEcbm mImsEcbm;
@Mock
protected SubscriptionController mSubscriptionController;
@@ -185,7 +190,16 @@
protected DeviceStateMonitor mDeviceStateMonitor;
@Mock
protected IntentBroadcaster mIntentBroadcaster;
+ @Mock
+ protected NitzStateMachine mNitzStateMachine;
+ @Mock
+ protected RadioConfig mMockRadioConfig;
+ @Mock
+ protected SubscriptionInfoUpdater mSubInfoRecordUpdater;
+ @Mock
+ protected LocaleTracker mLocaleTracker;
+ protected ImsCallProfile mImsCallProfile;
protected TelephonyManager mTelephonyManager;
protected SubscriptionManager mSubscriptionManager;
protected EuiccManager mEuiccManager;
@@ -197,6 +211,7 @@
private Object mLock = new Object();
private boolean mReady;
protected HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
+ private Phone[] mPhones;
protected HashMap<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
@@ -295,26 +310,8 @@
TAG = tag;
MockitoAnnotations.initMocks(this);
- //Use reflection to mock singletons
- replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
- replaceInstance(TelephonyComponentFactory.class, "sInstance", null,
- mTelephonyComponentFactory);
- replaceInstance(UiccController.class, "mInstance", null, mUiccController);
- replaceInstance(CdmaSubscriptionSourceManager.class, "sInstance", null, mCdmaSSM);
- replaceInstance(ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
- replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
- replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
- replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
- mIActivityManagerSingleton);
- replaceInstance(CdmaSubscriptionSourceManager.class,
- "mCdmaSubscriptionSourceChangedRegistrants", mCdmaSSM, mRegistrantList);
- replaceInstance(SimulatedCommandsVerifier.class, "sInstance", null,
- mSimulatedCommandsVerifier);
- replaceInstance(Singleton.class, "mInstance", mIActivityManagerSingleton,
- mIActivityManager);
- replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
- replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
-
+ mPhones = new Phone[] {mPhone};
+ mImsCallProfile = new ImsCallProfile();
mSimulatedCommands = new SimulatedCommands();
mContextFixture = new ContextFixture();
mContext = mContextFixture.getTestDouble();
@@ -329,16 +326,14 @@
mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
mPackageManager = mContext.getPackageManager();
- replaceInstance(TelephonyManager.class, "sInstance", null,
- mContext.getSystemService(Context.TELEPHONY_SERVICE));
-
//mTelephonyComponentFactory
doReturn(mSST).when(mTelephonyComponentFactory)
.makeServiceStateTracker(nullable(GsmCdmaPhone.class),
nullable(CommandsInterface.class));
- doReturn(mIccCardProxy).when(mTelephonyComponentFactory)
- .makeIccCardProxy(nullable(Context.class), nullable(CommandsInterface.class),
- anyInt());
+ doReturn(mUiccProfile).when(mTelephonyComponentFactory)
+ .makeUiccProfile(nullable(Context.class), nullable(CommandsInterface.class),
+ nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
+ nullable(Object.class));
doReturn(mCT).when(mTelephonyComponentFactory)
.makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
@@ -375,12 +370,16 @@
.makeCarrierActionAgent(nullable(Phone.class));
doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory)
.makeDeviceStateMonitor(nullable(Phone.class));
+ doReturn(mNitzStateMachine).when(mTelephonyComponentFactory)
+ .makeNitzStateMachine(nullable(GsmCdmaPhone.class));
+ doReturn(mLocaleTracker).when(mTelephonyComponentFactory)
+ .makeLocaleTracker(nullable(Phone.class), nullable(Looper.class));
//mPhone
doReturn(mContext).when(mPhone).getContext();
doReturn(mContext).when(mImsPhone).getContext();
doReturn(true).when(mPhone).getUnitTestMode();
- doReturn(mIccCardProxy).when(mPhone).getIccCard();
+ doReturn(mUiccProfile).when(mPhone).getIccCard();
doReturn(mServiceState).when(mPhone).getServiceState();
doReturn(mServiceState).when(mImsPhone).getServiceState();
doReturn(mPhone).when(mImsPhone).getDefaultPhone();
@@ -422,13 +421,21 @@
doReturn(mRuimRecords).when(mUiccCardApplication3gpp2).getIccRecords();
doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
- //mIccCardProxy
- doReturn(mSimRecords).when(mIccCardProxy).getIccRecords();
+ //mUiccProfile
+ doReturn(mSimRecords).when(mUiccProfile).getIccRecords();
doAnswer(new Answer<IccRecords>() {
public IccRecords answer(InvocationOnMock invocation) {
return (mPhone.isPhoneTypeGsm()) ? mSimRecords : mRuimRecords;
}
- }).when(mIccCardProxy).getIccRecords();
+ }).when(mUiccProfile).getIccRecords();
+
+ //mUiccProfile
+ doReturn(mUiccCardApplication3gpp).when(mUiccProfile).getApplication(
+ eq(UiccController.APP_FAM_3GPP));
+ doReturn(mUiccCardApplication3gpp2).when(mUiccProfile).getApplication(
+ eq(UiccController.APP_FAM_3GPP2));
+ doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplication(
+ eq(UiccController.APP_FAM_IMS));
//SMS
doReturn(true).when(mSmsStorageMonitor).isStorageAvailable();
@@ -441,8 +448,8 @@
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS).when(mServiceState).
getRilDataRadioTechnology();
doReturn(mPhone).when(mCT).getPhone();
- mImsManagerInstances.put(mPhone.getPhoneId(), null);
- doReturn(mImsEcbm).when(mImsManager).getEcbmInterface(anyInt());
+ mImsManagerInstances.put(mPhone.getPhoneId(), mImsManager);
+ doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
doReturn(mPhone).when(mInboundSmsHandler).getPhone();
doReturn(mImsCallProfile).when(mImsCall).getCallProfile();
doReturn(mIBinder).when(mIIntentSender).asBinder();
@@ -453,6 +460,45 @@
mSST.mSS = mServiceState;
mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
+ //SIM
+ doReturn(1).when(mTelephonyManager).getSimCount();
+ doReturn(1).when(mTelephonyManager).getPhoneCount();
+
+ //Data
+ //Initial state is: userData enabled, provisioned.
+ ContentResolver resolver = mContext.getContentResolver();
+ Settings.Global.putInt(resolver, Settings.Global.MOBILE_DATA, 1);
+ Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
+ Settings.Global.putInt(resolver,
+ Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
+
+ //Use reflection to mock singletons
+ replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
+ replaceInstance(TelephonyComponentFactory.class, "sInstance", null,
+ mTelephonyComponentFactory);
+ replaceInstance(UiccController.class, "mInstance", null, mUiccController);
+ replaceInstance(CdmaSubscriptionSourceManager.class, "sInstance", null, mCdmaSSM);
+ replaceInstance(ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
+ replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
+ replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
+ replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
+ mIActivityManagerSingleton);
+ replaceInstance(CdmaSubscriptionSourceManager.class,
+ "mCdmaSubscriptionSourceChangedRegistrants", mCdmaSSM, mRegistrantList);
+ replaceInstance(SimulatedCommandsVerifier.class, "sInstance", null,
+ mSimulatedCommandsVerifier);
+ replaceInstance(Singleton.class, "mInstance", mIActivityManagerSingleton,
+ mIActivityManager);
+ replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
+ replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
+ replaceInstance(TelephonyManager.class, "sInstance", null,
+ mContext.getSystemService(Context.TELEPHONY_SERVICE));
+ 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);
+
setReady(false);
}
@@ -522,4 +568,16 @@
}
}
}
+
+ public static Object invokeMethod(
+ Object instance, String methodName, Class<?>[] parameterClasses, Object[] parameters) {
+ try {
+ Method method = instance.getClass().getDeclaredMethod(methodName, parameterClasses);
+ method.setAccessible(true);
+ return method.invoke(instance, parameters);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ fail(instance.getClass() + " " + methodName + " " + e.getClass().getName());
+ }
+ return null;
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
new file mode 100644
index 0000000..d0eeb43
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright 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;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+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 libcore.util.TimeZoneFinder;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+public class TimeZoneLookupHelperTest {
+ // Note: Historical dates are used to avoid the test breaking due to data changes.
+ /* Arbitrary summer date in the Northern hemisphere. */
+ private static final long NH_SUMMER_TIME_MILLIS = createUtcTime(2015, 6, 20, 1, 2, 3);
+ /* Arbitrary winter date in the Northern hemisphere. */
+ private static final long NH_WINTER_TIME_MILLIS = createUtcTime(2015, 1, 20, 1, 2, 3);
+
+ private TimeZoneLookupHelper mTimeZoneLookupHelper;
+
+ @Before
+ public void setUp() {
+ mTimeZoneLookupHelper = new TimeZoneLookupHelper();
+ }
+
+ @Test
+ public void testLookupByNitzdByNitz() {
+ // Historical dates are used to avoid the test breaking due to data changes.
+ // However, algorithm updates may change the exact time zone returned, though it shouldn't
+ // ever be a less exact match.
+ long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
+ long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
+
+ String nhSummerTimeString = "15/06/20,01:02:03";
+ String nhWinterTimeString = "15/01/20,01:02:03";
+
+ // Tests for London, UK.
+ {
+ String lonSummerTimeString = nhSummerTimeString + "+4";
+ int lonSummerOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
+ int lonSummerDstOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
+
+ String lonWinterTimeString = nhWinterTimeString + "+0";
+ int lonWinterOffsetMillis = 0;
+ int lonWinterDstOffsetMillis = 0;
+
+ OffsetResult lookupResult;
+
+ // Summer, known DST state (DST == true).
+ NitzData lonSummerNitzDataWithOffset = NitzData.parse(lonSummerTimeString + ",4");
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithOffset);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis,
+ lonSummerDstOffsetMillis, lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Winter, known DST state (DST == false).
+ NitzData lonWinterNitzDataWithOffset = NitzData.parse(lonWinterTimeString + ",0");
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithOffset);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis,
+ lonWinterDstOffsetMillis, lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Summer, unknown DST state
+ NitzData lonSummerNitzDataWithoutOffset = NitzData.parse(lonSummerTimeString);
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithoutOffset);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis, null,
+ lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Winter, unknown DST state
+ NitzData lonWinterNitzDataWithoutOffset = NitzData.parse(lonWinterTimeString);
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithoutOffset);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis, null,
+ lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+ }
+
+ // Tests for Mountain View, CA, US.
+ {
+ String mtvSummerTimeString = nhSummerTimeString + "-32";
+ int mtvSummerOffsetMillis = (int) TimeUnit.HOURS.toMillis(-8);
+ int mtvSummerDstOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
+
+ String mtvWinterTimeString = nhWinterTimeString + "-28";
+ int mtvWinterOffsetMillis = (int) TimeUnit.HOURS.toMillis(-7);
+ int mtvWinterDstOffsetMillis = 0;
+
+ OffsetResult lookupResult;
+
+ // Summer, known DST state (DST == true).
+ NitzData mtvSummerNitzDataWithOffset = NitzData.parse(mtvSummerTimeString + ",4");
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithOffset);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis,
+ mtvSummerDstOffsetMillis, lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Winter, known DST state (DST == false).
+ NitzData mtvWinterNitzDataWithOffset = NitzData.parse(mtvWinterTimeString + ",0");
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithOffset);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis,
+ mtvWinterDstOffsetMillis, lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Summer, unknown DST state
+ NitzData mtvSummerNitzDataWithoutOffset = NitzData.parse(mtvSummerTimeString);
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithoutOffset);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis, null,
+ lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+
+ // Winter, unknown DST state
+ NitzData mtvWinterNitzDataWithoutOffset = NitzData.parse(mtvWinterTimeString);
+ lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithoutOffset);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis, null,
+ lookupResult);
+ assertOffsetResultMetadata(false, lookupResult);
+ }
+ }
+
+ @Test
+ public void testLookupByNitzCountry() {
+ // Historical dates are used to avoid the test breaking due to data changes.
+ // However, algorithm updates may change the exact time zone returned, though it shouldn't
+ // ever be a less exact match.
+ long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
+ long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
+
+ // Two countries in the northern hemisphere that share the same Winter and Summer DST
+ // offsets at the dates being used.
+ String deIso = "DE"; // Germany
+ String adIso = "AD"; // Andora
+ String summerTimeNitzString = "15/06/20,01:02:03+8";
+ String winterTimeNitzString = "15/01/20,01:02:03+4";
+
+ // Summer, known DST state (DST == true).
+ {
+ String summerTimeNitzStringWithDst = summerTimeNitzString + ",4";
+ NitzData nitzData = NitzData.parse(summerTimeNitzStringWithDst);
+ int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(2);
+ Integer expectedDstOffset = (int) TimeUnit.HOURS.toMillis(1);
+ assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
+ assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
+
+ OffsetResult expectedResult;
+
+ OffsetResult deSummerWithDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+ expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+ assertEquals(expectedResult, deSummerWithDstResult);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
+ deSummerWithDstResult);
+
+ OffsetResult adSummerWithDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+ expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+ assertEquals(expectedResult, adSummerWithDstResult);
+ assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
+ adSummerWithDstResult);
+ assertOffsetResultZoneCountry(adIso, adSummerWithDstResult);
+ }
+
+ // Winter, known DST state (DST == false)
+ {
+ String winterTimeNitzStringWithDst = winterTimeNitzString + ",0";
+ NitzData nitzData = NitzData.parse(winterTimeNitzStringWithDst);
+ int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(1);
+ Integer expectedDstOffset = 0;
+ assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
+ assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
+
+ OffsetResult expectedResult;
+
+ OffsetResult deWinterWithDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+ expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+ assertEquals(expectedResult, deWinterWithDstResult);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+ deWinterWithDstResult);
+
+ OffsetResult adWinterWithDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+ expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+ assertEquals(expectedResult, adWinterWithDstResult);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+ adWinterWithDstResult);
+ }
+
+ // Summer, unknown DST state
+ // For historic reasons, GuessZoneIdByNitzCountry() does not handle unknown DST state - it
+ // assumes that "unknown DST" means "no DST": This leads to no match when DST is actually in
+ // force.
+ {
+ NitzData nitzData = NitzData.parse(summerTimeNitzString);
+ int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(2);
+ Integer expectedDstOffset = null; // Unknown
+ assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
+ assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
+
+ OffsetResult deSummerUnknownDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+ assertNull(deSummerUnknownDstResult);
+
+ OffsetResult adSummerUnknownDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+ assertNull(adSummerUnknownDstResult);
+ }
+
+ // Winter, unknown DST state
+ {
+ NitzData nitzData = NitzData.parse(winterTimeNitzString);
+ int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(1);
+ Integer expectedDstOffset = null; // Unknown
+ assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
+ assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
+
+ OffsetResult expectedResult;
+
+ OffsetResult deWinterUnknownDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
+ expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
+ assertEquals(expectedResult, deWinterUnknownDstResult);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+ deWinterUnknownDstResult);
+
+ OffsetResult adWinterUnknownDstResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
+ expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
+ assertEquals(expectedResult, adWinterUnknownDstResult);
+ assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
+ adWinterUnknownDstResult);
+ }
+ }
+
+ @Test
+ public void testLookupByCountry() {
+ // Historical dates are used to avoid the test breaking due to data changes.
+ long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
+ long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
+
+ CountryResult expectedResult;
+
+ // GB has one time zone.
+ expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
+ nhSummerTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("gb", nhSummerTimeMillis));
+ expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
+ nhWinterTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("gb", nhWinterTimeMillis));
+
+ // DE has two time zones according to data, but they agree on offset.
+ expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
+ nhSummerTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("de", nhSummerTimeMillis));
+ expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
+ nhWinterTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("de", nhWinterTimeMillis));
+
+ // US has many time zones that have different offsets.
+ expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
+ nhSummerTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("us", nhSummerTimeMillis));
+ expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
+ nhWinterTimeMillis);
+ assertEquals(expectedResult,
+ mTimeZoneLookupHelper.lookupByCountry("us", nhWinterTimeMillis));
+ }
+
+ @Test
+ public void testCountryUsesUtc() {
+ assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_SUMMER_TIME_MILLIS));
+ assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_WINTER_TIME_MILLIS));
+ assertFalse(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_SUMMER_TIME_MILLIS));
+ assertTrue(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_WINTER_TIME_MILLIS));
+ }
+
+ private static void assertOffsetResultZoneCountry(
+ String isoCountryCode, OffsetResult lookupResult) {
+ String timeZoneId = lookupResult.zoneId;
+ List<String> zoneIdsByCountry =
+ TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(isoCountryCode);
+ assertTrue(timeZoneId + " must be used in " + isoCountryCode,
+ zoneIdsByCountry.contains(timeZoneId));
+ }
+
+ /**
+ * Assert the time zone in the OffsetResult has the expected properties at the specified time.
+ */
+ private static void assertOffsetResultZoneOffsets(long time, int expectedOffsetAtTime,
+ Integer expectedDstAtTime, OffsetResult lookupResult) {
+
+ TimeZone timeZone = TimeZone.getTimeZone(lookupResult.zoneId);
+ GregorianCalendar calendar = new GregorianCalendar(timeZone);
+ calendar.setTimeInMillis(time);
+ int actualOffsetAtTime =
+ calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+ assertEquals(expectedOffsetAtTime, actualOffsetAtTime);
+
+ if (expectedDstAtTime != null) {
+ Date date = new Date(time);
+ assertEquals(expectedDstAtTime > 0, timeZone.inDaylightTime(date));
+
+ // The code under test assumes DST means +1 in all cases,
+ // This code makes fewer assumptions.
+ assertEquals(expectedDstAtTime.intValue(), calendar.get(Calendar.DST_OFFSET));
+ }
+ }
+
+ private static void assertOffsetResultMetadata(boolean isOnlyMatch, OffsetResult lookupResult) {
+ assertEquals(isOnlyMatch, lookupResult.isOnlyMatch);
+ }
+
+ private static long createUtcTime(
+ int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minute, int second) {
+ GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ calendar.clear(); // Clear millis, etc.
+ calendar.set(year, monthOfYear - 1, dayOfMonth, hourOfDay, minute, second);
+ return calendar.getTimeInMillis();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
index ab7fd7f..55036ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -17,27 +17,24 @@
package com.android.internal.telephony.cdma;
import android.hardware.radio.V1_0.CdmaSmsMessage;
-import android.os.Parcel;
import android.support.test.filters.FlakyTest;
+import android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
import android.telephony.SmsCbMessage;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.test.AndroidTestCase;
-import android.telephony.Rlog;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.cdma.SmsMessageConverter;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
-import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.BitwiseOutputStream;
+import org.junit.Ignore;
import org.junit.Test;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -482,7 +479,9 @@
// VZW requirement is to discard message with unsupported charset. Verify that we return null
// for this unsupported character set.
@FlakyTest
- @Test @SmallTest
+ @Ignore
+ @Test
+ @SmallTest
public void testCmasUnsupportedCharSet() throws Exception {
SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
index a308762..b9d9c46 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
@@ -20,8 +20,8 @@
import android.os.Message;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.telephony.ImsSMSDispatcher;
import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
import com.android.internal.telephony.TelephonyTest;
import static org.mockito.Mockito.*;
@@ -37,7 +37,7 @@
@Mock
private SmsMessage mCdmaSmsMessage;
@Mock
- private ImsSMSDispatcher mImsSmsDispatcher;
+ private SmsDispatchersController mSmsDispatchersController;
@Mock
private SMSDispatcher.SmsTracker mSmsTracker;
@@ -52,8 +52,7 @@
@Override
public void onLooperPrepared() {
- mCdmaSmsDispatcher = new CdmaSMSDispatcher(mPhone, mSmsUsageMonitor,
- mImsSmsDispatcher);
+ mCdmaSmsDispatcher = new CdmaSMSDispatcher(mPhone, mSmsDispatchersController);
setReady(true);
}
}
@@ -63,7 +62,7 @@
super.setUp(this.getClass().getSimpleName());
setupMockPackagePermissionChecks();
-
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
mCdmaSmsDispatcherTestHandler = new CdmaSmsDispatcherTestHandler(TAG);
mCdmaSmsDispatcherTestHandler.start();
waitUntilReady();
@@ -86,14 +85,14 @@
@Test @SmallTest
public void testSendText() {
mCdmaSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
- null, null, null, null, false);
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier).sendCdmaSms(any(byte[].class), any(Message.class));
}
@Test @SmallTest
public void testSendTextWithOutDesAddr() {
mCdmaSmsDispatcher.sendText(null, "222" /*scAddr*/, TAG,
- null, null, null, null, false);
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
anyInt(), anyInt(), any(Message.class));
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsAddressTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsAddressTest.java
new file mode 100644
index 0000000..0c706f4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsAddressTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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.cdma.sms;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.test.AndroidTestCase;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link CdmaSmsAddress}.
+ */
+public class CdmaSmsAddressTest extends AndroidTestCase {
+ @Test
+ public void testNumberAddress() {
+ String address = "3141592653";
+ CdmaSmsAddress cdmaAddress = CdmaSmsAddress.parse(address);
+ assertEquals(address, cdmaAddress.address);
+ assertEquals(CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF, cdmaAddress.digitMode);
+ assertEquals(CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK, cdmaAddress.numberMode);
+ assertArrayEquals(CdmaSmsAddress.parseToDtmf(address), cdmaAddress.origBytes);
+ }
+
+ @Test
+ public void testNumberWithSugar() {
+ String address = "(314)1592653";
+ CdmaSmsAddress cdmaAddress = CdmaSmsAddress.parse(address);
+ assertEquals(address, cdmaAddress.address);
+ assertEquals(CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF, cdmaAddress.digitMode);
+ assertEquals(CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK, cdmaAddress.numberMode);
+ assertArrayEquals(CdmaSmsAddress.parseToDtmf("3141592653"), cdmaAddress.origBytes);
+ }
+
+ @Test
+ public void testInternationalAddress() {
+ String address = "+10068";
+ CdmaSmsAddress cdmaAddress = CdmaSmsAddress.parse(address);
+ assertEquals(address, cdmaAddress.address);
+ assertEquals(CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR, cdmaAddress.digitMode);
+ assertEquals(CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK, cdmaAddress.numberMode);
+ assertEquals(CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY, cdmaAddress.numberPlan);
+ assertEquals(CdmaSmsAddress.TON_INTERNATIONAL_OR_IP, cdmaAddress.ton);
+ assertArrayEquals(UserData.stringToAscii("10068"), cdmaAddress.origBytes);
+ }
+
+ @Test
+ public void testEmailAddress() {
+ String address = "fb769394+10086@tvb.com";
+ CdmaSmsAddress cdmaAddress = CdmaSmsAddress.parse(address);
+ assertEquals(address, cdmaAddress.address);
+ assertEquals(CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR, cdmaAddress.digitMode);
+ assertEquals(CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK, cdmaAddress.numberMode);
+ assertEquals(CdmaSmsAddress.TON_NATIONAL_OR_EMAIL, cdmaAddress.ton);
+ assertArrayEquals(UserData.stringToAscii(address), cdmaAddress.origBytes);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsTest.java
index 6f60e64..2e8c34b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsTest.java
@@ -77,13 +77,13 @@
}
addr = CdmaSmsAddress.parse("(+886) 917 222 555");
assertEquals(addr.ton, CdmaSmsAddress.TON_INTERNATIONAL_OR_IP);
- assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR);
assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
assertEquals(addr.numberOfDigits, 12);
assertEquals(addr.origBytes.length, 12);
- byte[] data3 = {8, 8, 6, 9, 1, 7, 2, 2, 2, 5, 5, 5};
- for (int i = 0; i < data3.length; i++) {
- assertEquals(addr.origBytes[i], data3[i]);
+ String expectedAddr = "886917222555";
+ for (int i = 0; i < addr.numberOfDigits; i++) {
+ assertEquals((int) expectedAddr.charAt(i), addr.origBytes[i]);
}
addr = CdmaSmsAddress.parse("(650) *253-1000 #600");
byte[] data4 = {6, 5, 10, 11, 2, 5, 3, 1, 10, 10, 10, 12, 6, 10, 10};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
index 8c49c21..6722691 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
@@ -16,9 +16,19 @@
package com.android.internal.telephony.dataconnection;
-import android.net.NetworkCapabilities;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.net.NetworkConfig;
import android.net.NetworkRequest;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.LocalLog;
@@ -32,16 +42,6 @@
import org.junit.Test;
import org.mockito.Mock;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-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;
-
public class ApnContextTest extends TelephonyTest {
@Mock
@@ -143,14 +143,14 @@
NetworkRequest nr = new NetworkRequest.Builder().build();
mApnContext.requestNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
mApnContext.requestNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
mApnContext.releaseNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
mApnContext.releaseNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
}
@Test
@@ -176,32 +176,31 @@
public void testProvisionApn() throws Exception {
mContextFixture.putResource(R.string.mobile_provisioning_apn, "fake_apn");
- ApnSetting myApn = new ApnSetting(
+ ApnSetting myApn = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bismask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
mApnContext.setApnSetting(myApn);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index 3624ec1..b9738e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -16,13 +16,6 @@
package com.android.internal.telephony.dataconnection;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_ALL;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_DEFAULT;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_HIPRI;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_IA;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_MMS;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_SUPL;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -30,9 +23,11 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;
+import android.net.Uri;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.PhoneConstants;
@@ -44,6 +39,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
@@ -62,41 +58,40 @@
super.tearDown();
}
- static ApnSetting createApnSetting(String[] apnTypes) {
- return createApnSettingInternal(apnTypes, true);
+ static ApnSetting createApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, true);
}
- private static ApnSetting createDisabledApnSetting(String[] apnTypes) {
- return createApnSettingInternal(apnTypes, false);
+ private static ApnSetting createDisabledApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, false);
}
- private static ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
- return new ApnSetting(
+ private static ApnSetting createApnSettingInternal(int apnTypeBitmask, boolean carrierEnabled) {
+ return ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- apnTypes, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ apnTypeBitmask, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
carrierEnabled, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
}
@@ -108,75 +103,133 @@
}
private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
- assertEquals(a1.carrier, a2.carrier);
- assertEquals(a1.apn, a2.apn);
- assertEquals(a1.proxy, a2.proxy);
- assertEquals(a1.port, a2.port);
- assertEquals(a1.mmsc, a2.mmsc);
- assertEquals(a1.mmsProxy, a2.mmsProxy);
- assertEquals(a1.mmsPort, a2.mmsPort);
- assertEquals(a1.user, a2.user);
- assertEquals(a1.password, a2.password);
- assertEquals(a1.authType, a2.authType);
- assertEquals(a1.id, a2.id);
- assertEquals(a1.numeric, a2.numeric);
- assertEquals(a1.protocol, a2.protocol);
- assertEquals(a1.roamingProtocol, a2.roamingProtocol);
- assertEquals(a1.types.length, a2.types.length);
- int i;
- for (i = 0; i < a1.types.length; i++) {
- assertEquals(a1.types[i], a2.types[i]);
- }
- assertEquals(a1.carrierEnabled, a2.carrierEnabled);
- assertEquals(a1.bearerBitmask, a2.bearerBitmask);
- assertEquals(a1.profileId, a2.profileId);
- assertEquals(a1.modemCognitive, a2.modemCognitive);
- assertEquals(a1.maxConns, a2.maxConns);
- assertEquals(a1.waitTime, a2.waitTime);
- assertEquals(a1.maxConnsTime, a2.maxConnsTime);
- assertEquals(a1.mtu, a2.mtu);
- assertEquals(a1.mvnoType, a2.mvnoType);
- assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
+ 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.getModemCognitive(), a2.getModemCognitive());
+ assertEquals(a1.getMaxConns(), a2.getMaxConns());
+ assertEquals(a1.getWaitTime(), a2.getWaitTime());
+ assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
+ assertEquals(a1.getMtu(), a2.getMtu());
+ assertEquals(a1.getMvnoType(), a2.getMvnoType());
+ assertEquals(a1.getMvnoMatchData(), a2.getMvnoMatchData());
+ assertEquals(a1.getNetworkTypeBitmask(), a2.getNetworkTypeBitmask());
+ assertEquals(a1.getApnSetId(), a2.getApnSetId());
}
@Test
@SmallTest
public void testFromString() throws Exception {
- String[] dunTypes = {"DUN"};
- String[] mmsTypes = {"mms", "*"};
+ final int dunTypesBitmask = ApnSetting.TYPE_DUN;
+ final int mmsTypesBitmask = ApnSetting.TYPE_MMS | ApnSetting.TYPE_ALL;
ApnSetting expectedApn;
String testString;
// A real-world v1 example string.
testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
- expectedApn = new ApnSetting(
- -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
- "", "", "", "", "", 0, dunTypes, "IP", "IP", true, 0, 0,
- 0, false, 0, 0, 0, 0, "", "");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "22210", "Vodafone IT", "web.omnitel.it", "", -1, null, "", -1, "", "", 0,
+ dunTypesBitmask, ApnSetting.PROTOCOL_IP, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v2 string.
testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
- 0, false, 0, 0, 0, 0, "", "");
+ int networkTypeBitmask = 1 << (13 - 1);
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v2 string with spaces.
testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV6, IP,true,14";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
- 0, false, 0, 0, 0, 0, "", "");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v3 string.
testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v4 string with network type bitmask.
+ testString =
+ "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,6";
+ networkTypeBitmask = 1 << (6 - 1);
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ testString =
+ "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,"
+ + "4|5|6|7|8|12|13|14|19";
+ // The value was calculated by adding "4|5|6|7|8|12|13|14|19".
+ networkTypeBitmask = 276728;
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v4 string with network type bitmask and compatible bearer bitmask.
+ testString =
+ "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,8,,,,,,,spn,testspn, 6";
+ networkTypeBitmask = 1 << (6 - 1);
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v4 string with network type bitmask and incompatible bearer bitmask.
+ testString =
+ "[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,9,,,,,,,spn,testspn, 6";
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0,
+ 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v5 string with apnSetId=0
+ testString =
+ "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,0";
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v5 string with apnSetId=3
+ testString =
+ "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,3";
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3);
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// Return no apn if insufficient fields given.
@@ -190,6 +243,7 @@
@Test
@SmallTest
public void testArrayFromString() throws Exception {
+ final int mmsTypesBitmask = ApnSetting.TYPE_MMS;
// Test a multiple v3 string.
String testString =
"[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms,IPV6,IP,true,14,,,,,,,spn,testspn";
@@ -197,30 +251,51 @@
" ;[ApnSettingV3] Name1,apn1,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,gid,testGid";
testString +=
" ;[ApnSettingV3] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,,";
+ testString +=
+ " ;[ApnSettingV5] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,0,,,,,,,,,,3";
List<ApnSetting> expectedApns = new ArrayList<ApnSetting>();
- expectedApns.add(new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn"));
- expectedApns.add(new ApnSetting(
- -1, "12346", "Name1", "apn1", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "gid", "testGid"));
- expectedApns.add(new ApnSetting(
- -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "", ""));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (13 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn"));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn1", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (12 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_GID, "testGid"));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (12 - 1), 0, false, 0, 0, 0, 0, -1, ""));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, -1, "", 3));
assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
}
@Test
@SmallTest
public void testToString() throws Exception {
- String[] types = {"default", "*"};
- ApnSetting apn = new ApnSetting(
- 99, "12345", "Name", "apn", "proxy", "port",
- "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
- types, "IPV6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
- String expected = "[ApnSettingV3] Name, 99, 12345, apn, proxy, " +
- "mmsc, mmsproxy, mmsport, port, 0, default | *, " +
- "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false";
+ // Use default apn_set_id constructor.
+ ApnSetting apn = ApnSetting.makeApnSetting(
+ 99, "12345", "Name", "apn", null, 10,
+ null, null, -1, "user", "password", 0,
+ ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 4096, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "");
+ String expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+ + "null, null, null, 10, 0, default, "
+ + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 4096, 0";
+ assertEquals(expected, apn.toString());
+
+ final int networkTypeBitmask = 1 << (14 - 1);
+ apn = ApnSetting.makeApnSetting(
+ 99, "12345", "Name", "apn", null, 10,
+ null, null, -1, "user", "password", 0,
+ ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "", 3);
+ expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+ + "null, null, null, 10, 0, default, "
+ + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 8192, 3";
assertEquals(expected, apn.toString());
}
@@ -232,50 +307,43 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
}
@Test
@@ -286,42 +354,34 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(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[]{PhoneConstants.APN_TYPE_FOTA});
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
}
@Test
@@ -334,42 +394,34 @@
.getRilDataRadioTechnology();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_FOTA});
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
}
@Test
@@ -380,33 +432,26 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
+
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
}
@Test
@@ -416,42 +461,35 @@
new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(2).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
}
@Test
@@ -463,42 +501,35 @@
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
.getRilDataRadioTechnology();
doReturn(2).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
}
@Test
@@ -510,19 +541,16 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -533,20 +561,16 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -559,19 +583,16 @@
.getRilDataRadioTechnology();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -583,22 +604,17 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
-
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -610,20 +626,17 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -637,20 +650,17 @@
.getRilDataRadioTechnology();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -658,59 +668,52 @@
public void testCanHandleType() throws Exception {
String types[] = {"mms"};
- // empty string replaced with ALL ('*') when loaded to db
- assertFalse(createApnSetting(new String[]{}).
- canHandleType(APN_TYPE_MMS));
+ assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertTrue(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_MMS));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_MMS));
-
- assertTrue(createApnSetting(new String[]{"DEfAULT"}).
- canHandleType("defAult"));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
// Hipri is asymmetric
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_HIPRI));
- assertFalse(createApnSetting(new String[]{APN_TYPE_HIPRI}).
- canHandleType(APN_TYPE_DEFAULT));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_HIPRI));
+ assertFalse(createApnSetting(ApnSetting.TYPE_HIPRI)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_DEFAULT));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_MMS));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_SUPL));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_SUPL));
// special IA case - doesn't match wildcards
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_IA));
- assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_IA));
- assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType("iA"));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
- canHandleType(APN_TYPE_IA));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_IA));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+ .canHandleType(ApnSetting.TYPE_IA));
// check carrier disabled
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_MMS));
- assertFalse(createDisabledApnSetting(new String[]{"DEfAULT"}).
- canHandleType("defAult"));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_HIPRI));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_DEFAULT));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_MMS));
- assertFalse(createDisabledApnSetting(new String[]
- {APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
- canHandleType(APN_TYPE_IA));
+ 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));
}
@Test
@@ -719,8 +722,10 @@
final int dummyInt = 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(new String[] {"mms", "default"});
+ ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
Field[] fields = ApnSetting.class.getDeclaredFields();
for (Field f : fields) {
int modifiers = f.getModifiers();
@@ -730,17 +735,23 @@
f.setAccessible(true);
ApnSetting testApn = null;
if (int.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.setInt(testApn, dummyInt + f.getInt(testApn));
} else if (boolean.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.setBoolean(testApn, !f.getBoolean(testApn));
} else if (String.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.set(testApn, dummyString);
} else if (String[].class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ 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());
}
@@ -753,63 +764,61 @@
@Test
@SmallTest
public void testEqualsRoamingProtocol() throws Exception {
- ApnSetting apn1 = new ApnSetting(
+ ApnSetting apn1 = ApnSetting.makeApnSetting(
1234,
"310260",
"",
"ims",
- "",
- "",
- "",
- "",
- "",
+ null,
+ -1,
+ null,
+ null,
+ -1,
"",
"",
-1,
- new String[]{"ims"},
- "IPV6",
- "",
+ ApnSetting.TYPE_IMS,
+ ApnSetting.PROTOCOL_IPV6,
+ -1,
true,
- 0,
- 131071,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131071),
0,
false,
0,
0,
0,
1440,
- "",
+ -1,
"");
- ApnSetting apn2 = new ApnSetting(
+ ApnSetting apn2 = ApnSetting.makeApnSetting(
1235,
"310260",
"",
"ims",
- "",
- "",
- "",
- "",
- "",
+ null,
+ -1,
+ null,
+ null,
+ -1,
"",
"",
-1,
- new String[]{"ims"},
- "IPV6",
- "IPV6",
+ ApnSetting.TYPE_IMS,
+ ApnSetting.PROTOCOL_IPV6,
+ ApnSetting.PROTOCOL_IPV6,
true,
- 0,
- 131072,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131072),
0,
false,
0,
0,
0,
1440,
- "",
+ -1,
"");
assertTrue(apn1.equals(apn2, false));
assertFalse(apn1.equals(apn2, true));
}
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java
new file mode 100644
index 0000000..a34febc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.telephony.data.DataCallResponse;
+
+import com.android.internal.telephony.dataconnection.CellularDataService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class CellularDataServiceTest extends TelephonyTest {
+
+ private CellularDataService mCellularDataService;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mCellularDataService = new CellularDataService();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testConvertDataCallResult() throws Exception {
+
+ SetupDataCallResult result = new SetupDataCallResult();
+ result.status = 0;
+ result.suggestedRetryTime = -1;
+ result.cid = 1;
+ result.active = 1;
+ result.type = "IP";
+ result.ifname = "eth0";
+ result.addresses = "10.0.2.15";
+ result.dnses = "10.0.2.3";
+ result.gateways = "10.0.2.15 fe80::2";
+ result.pcscf = "";
+ result.mtu = 1500;
+
+ DataCallResponse response = new DataCallResponse(0, -1, 1, 1, "IP",
+ "eth0",
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress("10.0.2.15"), 32)),
+ Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.3")),
+ Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.15"),
+ NetworkUtils.numericToInetAddress("fe80::2")),
+ Arrays.asList(""),
+ 1500);
+
+ assertEquals(response, mCellularDataService.convertDataCallResult(result));
+
+ result.status = 0;
+ result.suggestedRetryTime = -1;
+ result.cid = 0;
+ result.active = 2;
+ result.type = "IPV4V6";
+ result.ifname = "ifname";
+ result.addresses = "2607:fb90:a620:651d:eabe:f8da:c107:44be/64";
+ result.dnses = "fd00:976a::9 fd00:976a::10";
+ result.gateways = "fe80::4c61:1832:7b28:d36c 1.2.3.4";
+ result.pcscf = "fd00:976a:c206:20::6 fd00:976a:c206:20::9 fd00:976a:c202:1d::9";
+ result.mtu = 1500;
+
+ response = new DataCallResponse(0, -1, 0, 2, "IPV4V6",
+ "ifname",
+ Arrays.asList(new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")),
+ Arrays.asList(NetworkUtils.numericToInetAddress("fd00:976a::9"),
+ NetworkUtils.numericToInetAddress("fd00:976a::10")),
+ Arrays.asList(NetworkUtils.numericToInetAddress("fe80::4c61:1832:7b28:d36c"),
+ NetworkUtils.numericToInetAddress("1.2.3.4")),
+ Arrays.asList("fd00:976a:c206:20::6", "fd00:976a:c206:20::9",
+ "fd00:976a:c202:1d::9"),
+ 1500);
+
+ assertEquals(response, mCellularDataService.convertDataCallResult(result));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
index 6a8a49a..3490b57 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 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.
@@ -22,73 +22,62 @@
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 android.net.LinkProperties;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.os.Parcel;
+import android.telephony.data.DataCallResponse;
+import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataCallResponse.SetupResult;
+import java.util.Arrays;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+public class DataCallResponseTest extends AndroidTestCase {
-public class DataCallResponseTest extends TelephonyTest {
-
- DataCallResponse mDcResponse;
-
- @Before
- public void setUp() throws Exception {
- super.setUp(getClass().getSimpleName());
- mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME, FAKE_ADDRESS,
- FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @Test
@SmallTest
- public void testSetLinkProperties() throws Exception {
- LinkProperties linkProperties = new LinkProperties();
- assertEquals(SetupResult.SUCCESS,
- mDcResponse.setLinkProperties(linkProperties, true));
- logd(linkProperties.toString());
- assertEquals(mDcResponse.ifname, linkProperties.getInterfaceName());
- assertEquals(mDcResponse.addresses.length, linkProperties.getAddresses().size());
- for (int i = 0; i < mDcResponse.addresses.length; ++i) {
- assertEquals(mDcResponse.addresses[i],
- linkProperties.getLinkAddresses().get(i).getAddress().getHostAddress());
- }
+ public void testParcel() throws Exception {
+ DataCallResponse response = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
- assertEquals(mDcResponse.dnses.length, linkProperties.getDnsServers().size());
- for (int i = 0; i < mDcResponse.dnses.length; ++i) {
- assertEquals("i = " + i, mDcResponse.dnses[i],
- linkProperties.getDnsServers().get(i).getHostAddress());
- }
+ Parcel p = Parcel.obtain();
+ response.writeToParcel(p, 0);
+ p.setDataPosition(0);
- assertEquals(mDcResponse.gateways.length, linkProperties.getRoutes().size());
- for (int i = 0; i < mDcResponse.gateways.length; ++i) {
- assertEquals("i = " + i, mDcResponse.gateways[i],
- linkProperties.getRoutes().get(i).getGateway().getHostAddress());
- }
-
- assertEquals(mDcResponse.mtu, linkProperties.getMtu());
+ DataCallResponse newResponse = new DataCallResponse(p);
+ assertEquals(response, newResponse);
}
- @Test
@SmallTest
- public void testSetLinkPropertiesInvalidAddress() throws Exception {
+ public void testEquals() throws Exception {
+ DataCallResponse response = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
- // 224.224.224.224 is an invalid address.
- mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME, "224.224.224.224",
- FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+ DataCallResponse response1 = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
- LinkProperties linkProperties = new LinkProperties();
- assertEquals(SetupResult.ERR_UnacceptableParameter,
- mDcResponse.setLinkProperties(linkProperties, true));
+ assertEquals(response, response);
+ assertEquals(response, response1);
+
+ DataCallResponse response2 = new DataCallResponse(1, -1, 1, 3, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS),
+ NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS, FAKE_PCSCF_ADDRESS),
+ 1441);
+ assertNotSame(response1, response2);
+ assertNotSame(response1, null);
+ assertNotSame(response1, new String[1]);
}
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 76380c7..ce5eaeb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -16,6 +16,11 @@
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.NetworkPolicyManager.OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+
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;
@@ -26,28 +31,44 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+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.times;
import static org.mockito.Mockito.verify;
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.net.KeepalivePacketData;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkUtils;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
+import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.R;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.TelephonyTest;
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.util.IState;
import com.android.internal.util.StateMachine;
@@ -59,6 +80,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Arrays;
public class DataConnectionTest extends TelephonyTest {
@@ -77,32 +99,58 @@
private DataConnectionTestHandler mDataConnectionTestHandler;
private DcController mDcc;
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
+ ""); // mnvo_match_data
+
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
+ 2164, // id
+ "44010", // numeric
+ "sp-mode", // name
+ "spmode.ne.jp", // apn
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
+ true, // carrier_enabled
+ 0, // networktype_bitmask
+ 0, // profile_id
+ false, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ -1, // mvno_type
""); // mnvo_match_data
private class DataConnectionTestHandler extends HandlerThread {
@@ -115,12 +163,30 @@
public void onLooperPrepared() {
Handler h = new Handler();
- mDcc = DcController.makeDcc(mPhone, mDcTracker, h);
- mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDcTesterFailBringUpAll,
- mDcc);
+ DataServiceManager manager = new DataServiceManager(mPhone, TransportType.WWAN);
+ mDcc = DcController.makeDcc(mPhone, mDcTracker, manager, h);
+ mDcc.start();
+ mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, manager,
+ mDcTesterFailBringUpAll, mDcc);
}
}
+ private void addDataService() {
+ CellularDataService cellularDataService = 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.DATA_SERVICE_INTERFACE,
+ null,
+ "com.android.phone",
+ cellularDataService.mBinder,
+ serviceInfo,
+ filter);
+ }
+
+
@Before
public void setUp() throws Exception {
logd("+Setup!");
@@ -132,6 +198,7 @@
ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
doReturn(mApn1).when(mApnContext).getApnSetting();
doReturn(PhoneConstants.APN_TYPE_DEFAULT).when(mApnContext).getApnType();
+ doReturn(true).when(mDcTracker).isDataEnabled();
mDcFailBringUp.saveParameters(0, 0, -2);
doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
@@ -149,9 +216,13 @@
"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;
+ addDataService();
+
mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
mDataConnectionTestHandler.start();
@@ -174,12 +245,23 @@
return (IState) method.invoke(mDc);
}
- private long getSuggestedRetryDelay(AsyncResult ar) throws Exception {
+ private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
Class[] cArgs = new Class[1];
- cArgs[0] = AsyncResult.class;
+ cArgs[0] = DataCallResponse.class;
Method method = DataConnection.class.getDeclaredMethod("getSuggestedRetryDelay", cArgs);
method.setAccessible(true);
- return (long) method.invoke(mDc, ar);
+ return (long) method.invoke(mDc, response);
+ }
+
+ 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
@@ -200,13 +282,19 @@
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));
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
+ eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(), any(Message.class));
- assertEquals("spmode.ne.jp", dpCaptor.getValue().apn);
+ assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
assertEquals("DcActiveState", getCurrentState().getName());
}
@@ -219,8 +307,11 @@
mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
waitForMs(100);
+ verify(mSimulatedCommandsVerifier, times(1)).unregisterForLceInfo(any(Handler.class));
+ verify(mSimulatedCommandsVerifier, times(1))
+ .unregisterForNattKeepaliveStatus(any(Handler.class));
verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(eq(1),
- eq(RILConstants.DEACTIVATE_REASON_NONE), any(Message.class));
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
assertEquals("DcInactiveState", getCurrentState().getName());
}
@@ -228,48 +319,72 @@
@Test
@SmallTest
public void testModemSuggestRetry() throws Exception {
- DataCallResponse response = new DataCallResponse(0, 0, 1, 2, "IP",
- FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- AsyncResult ar = new AsyncResult(null, response, null);
- assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
+ DataCallResponse response = new DataCallResponse(0, 0, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
- response = new DataCallResponse(0, 1000, 1, 2, "IP",
- FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- ar = new AsyncResult(null, response, null);
- assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
+ assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
- response = new DataCallResponse(0, 9999, 1, 2, "IP",
- FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- ar = new AsyncResult(null, response, null);
- assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
+ response = new DataCallResponse(0, 1000, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+ assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
+
+ response = new DataCallResponse(0, 9999, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+ assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
}
@Test
@SmallTest
public void testModemNotSuggestRetry() throws Exception {
DataCallResponse response = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- AsyncResult ar = new AsyncResult(null, response, null);
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
response = new DataCallResponse(0, -5, 1, 2, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- ar = new AsyncResult(null, response, null);
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
response = new DataCallResponse(0, Integer.MIN_VALUE, 1, 2, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- ar = new AsyncResult(null, response, null);
- assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
}
@Test
@SmallTest
public void testModemSuggestNoRetry() throws Exception {
DataCallResponse response = new DataCallResponse(0, Integer.MAX_VALUE, 1, 2, "IP",
- FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
- AsyncResult ar = new AsyncResult(null, response, null);
- assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(ar));
+ FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+ assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
}
private NetworkInfo getNetworkInfo() throws Exception {
@@ -286,6 +401,36 @@
@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));
+
+ mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
+ waitForMs(100);
+ doReturn(mApn1).when(mApnContext).getApnSetting();
+ mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
+ waitForMs(200);
+
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
+ }
+
+ @Test
+ @SmallTest
public void testMeteredCapability() throws Exception {
mContextFixture.getCarrierConfigBundle().
@@ -296,7 +441,6 @@
assertFalse(getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
- assertTrue(getNetworkInfo().isMetered());
}
@Test
@@ -312,9 +456,51 @@
assertTrue(getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkInfo().isMetered());
}
+ @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));
+ assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+ mDc.onSubscriptionOverride(OVERRIDE_UNMETERED, OVERRIDE_UNMETERED);
+
+ assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+ mDc.onSubscriptionOverride(OVERRIDE_UNMETERED, 0);
+
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_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));
+ assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+ mDc.onSubscriptionOverride(OVERRIDE_CONGESTED, OVERRIDE_CONGESTED);
+
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+ mDc.onSubscriptionOverride(OVERRIDE_CONGESTED, 0);
+
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ }
+
+ @Test
@SmallTest
public void testIsIpAddress() throws Exception {
// IPv4
@@ -325,4 +511,245 @@
assertTrue(DataConnection.isIpAddress("::1"));
assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
}
+
+ @Test
+ @SmallTest
+ public void testSetLinkProperties() throws Exception {
+
+ DataCallResponse response = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+
+ LinkProperties linkProperties = new LinkProperties();
+ assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
+ logd(linkProperties.toString());
+ assertEquals(response.getIfname(), linkProperties.getInterfaceName());
+ assertEquals(response.getAddresses().size(), linkProperties.getAddresses().size());
+ for (int i = 0; i < response.getAddresses().size(); ++i) {
+ assertEquals(response.getAddresses().get(i).getAddress(),
+ NetworkUtils.numericToInetAddress(linkProperties.getLinkAddresses().get(i)
+ .getAddress().getHostAddress()));
+ }
+
+ assertEquals(response.getDnses().size(), linkProperties.getDnsServers().size());
+ for (int i = 0; i < response.getDnses().size(); ++i) {
+ assertEquals("i = " + i, response.getDnses().get(i), NetworkUtils.numericToInetAddress(
+ linkProperties.getDnsServers().get(i).getHostAddress()));
+ }
+
+ assertEquals(response.getGateways().size(), linkProperties.getRoutes().size());
+ for (int i = 0; i < response.getGateways().size(); ++i) {
+ assertEquals("i = " + i, response.getGateways().get(i),
+ NetworkUtils.numericToInetAddress(linkProperties.getRoutes().get(i)
+ .getGateway().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(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ null,
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+
+ 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(0, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ null,
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+
+ // 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);
+
+ DataServiceManager mockDsm = mock(DataServiceManager.class);
+ doReturn(TransportType.WLAN).when(mockDsm).getTransportType();
+ replaceInstance(DataConnection.class, "mDataServiceManager", mDc, mockDsm);
+
+ 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 = KeepalivePacketData.nattKeepalivePacket(
+ NetworkUtils.numericToInetAddress("1.2.3.4"),
+ 1234,
+ NetworkUtils.numericToInetAddress("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 = KeepalivePacketData.nattKeepalivePacket(
+ NetworkUtils.numericToInetAddress("1.2.3.4"),
+ 1234,
+ NetworkUtils.numericToInetAddress("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 = KeepalivePacketData.nattKeepalivePacket(
+ NetworkUtils.numericToInetAddress("1.2.3.4"),
+ 1234,
+ NetworkUtils.numericToInetAddress("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);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 4df3693..97fcc75 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony.dataconnection;
+import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.RILConstants;
@@ -24,85 +27,130 @@
public class DataProfileTest extends TestCase {
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"user", // user
"passwd", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IPV6", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IPV6, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
1234, // profile_id
false, // modem_cognitive
321, // max_conns
456, // wait_time
789, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn2 = new ApnSetting(
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"user", // user
"passwd", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
1234, // profile_id
false, // modem_cognitive
111, // max_conns
456, // wait_time
789, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
+ ""); // mnvo_match_data
+
+ private ApnSetting mApn3 = ApnSetting.makeApnSetting(
+ 2163, // id
+ "44010", // numeric
+ "sp-mode", // name
+ "fake_apn", // apn
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
+ "user", // user
+ "passwd", // password
+ -1, // authtype
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
+ true, // carrier_enabled
+ 276600, // networktype_bitmask
+ 1234, // profile_id
+ false, // modem_cognitive
+ 111, // max_conns
+ 456, // wait_time
+ 789, // max_conns_time
+ 0, // mtu
+ -1, // mvno_type
""); // mnvo_match_data
@SmallTest
public void testCreateFromApnSetting() throws Exception {
- DataProfile dp = new DataProfile(mApn1);
- assertEquals(mApn1.profileId, dp.profileId);
- assertEquals(mApn1.apn, dp.apn);
- assertEquals(mApn1.protocol, dp.protocol);
- assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.authType);
- assertEquals(mApn1.user, dp.user);
- assertEquals(mApn1.password, dp.password);
- assertEquals(0, dp.type);
- assertEquals(mApn1.maxConnsTime, dp.maxConnsTime);
- assertEquals(mApn1.maxConns, dp.maxConns);
- assertEquals(mApn1.waitTime, dp.waitTime);
- assertEquals(mApn1.carrierEnabled, dp.enabled);
+ DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+ assertEquals(mApn1.getProfileId(), dp.getProfileId());
+ assertEquals(mApn1.getApnName(), dp.getApn());
+ assertEquals(ApnSetting.getProtocolStringFromInt(mApn1.getProtocol()), dp.getProtocol());
+ assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
+ assertEquals(mApn1.getUser(), dp.getUserName());
+ assertEquals(mApn1.getPassword(), dp.getPassword());
+ assertEquals(0, dp.getType()); // TYPE_COMMON
+ assertEquals(mApn1.getMaxConnsTime(), dp.getMaxConnsTime());
+ assertEquals(mApn1.getMaxConns(), dp.getMaxConns());
+ assertEquals(mApn1.getWaitTime(), dp.getWaitTime());
+ assertEquals(mApn1.isEnabled(), dp.isEnabled());
+ }
+
+ @SmallTest
+ public void testCreateFromApnSettingWithNetworkTypeBitmask() throws Exception {
+ DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.getProfileId());
+ assertEquals(mApn3.getProfileId(), dp.getProfileId());
+ assertEquals(mApn3.getApnName(), dp.getApn());
+ assertEquals(ApnSetting.getProtocolStringFromInt(mApn3.getProtocol()), dp.getProtocol());
+ assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
+ assertEquals(mApn3.getUser(), dp.getUserName());
+ assertEquals(mApn3.getPassword(), dp.getPassword());
+ assertEquals(2, dp.getType()); // TYPE_3GPP2
+ assertEquals(mApn3.getMaxConnsTime(), dp.getMaxConnsTime());
+ assertEquals(mApn3.getMaxConns(), dp.getMaxConns());
+ assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
+ assertEquals(mApn3.isEnabled(), dp.isEnabled());
+ int expectedBearerBitmap =
+ ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
+ mApn3.getNetworkTypeBitmask());
+ assertEquals(expectedBearerBitmap, dp.getBearerBitmap());
}
@SmallTest
public void testEquals() throws Exception {
- DataProfile dp1 = new DataProfile(mApn1);
- DataProfile dp2 = new DataProfile(mApn1);
+ DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+ DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
assertEquals(dp1, dp2);
- dp2 = new DataProfile(mApn2);
+ dp2 = DcTracker.createDataProfile(mApn2, mApn2.getProfileId());
assertFalse(dp1.equals(dp2));
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index d209639..c86c911 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -31,10 +31,13 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkUtils;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
+import android.telephony.data.DataCallResponse;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.DctConstants;
@@ -51,6 +54,7 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
public class DcControllerTest extends TelephonyTest {
@@ -62,6 +66,8 @@
DataConnection mDc;
@Mock
HashMap<ApnContext, ConnectionParams> mApnContexts;
+ @Mock
+ DataServiceManager mDataServiceManager;
UpdateLinkPropertyResult mResult;
@@ -79,7 +85,8 @@
@Override
public void onLooperPrepared() {
mHandler = new Handler();
- mDcc = DcController.makeDcc(mPhone, mDcTracker, mHandler);
+ mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, mHandler);
+ mDcc.start();
setReady(true);
}
}
@@ -120,12 +127,17 @@
@Test
@SmallTest
- public void testDataDormant() {
+ public void testDataDormant() throws Exception {
assertEquals("DccDefaultState", getCurrentState().getName());
ArrayList<DataCallResponse> l = new ArrayList<DataCallResponse>();
DataCallResponse dcResponse = new DataCallResponse(0, -1, 1,
DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);
+
l.add(dcResponse);
mDc.mCid = 1;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index fed6f3f..451571e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -37,10 +37,15 @@
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.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -53,16 +58,23 @@
import android.provider.Settings;
import android.provider.Telephony;
import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
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.DataProfile;
+import android.telephony.data.DataService;
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.LocalLog;
+import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.ISub;
import com.android.internal.telephony.Phone;
@@ -79,6 +91,7 @@
import org.mockito.stubbing.Answer;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
@@ -108,6 +121,12 @@
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_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");
@Mock
ISub mIsub;
@@ -136,6 +155,21 @@
private final ApnSettingContentProvider mApnSettingContentProvider =
new ApnSettingContentProvider();
+ private void addDataService() {
+ CellularDataService cellularDataService = 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.DATA_SERVICE_INTERFACE,
+ null,
+ "com.android.phone",
+ cellularDataService.mBinder,
+ serviceInfo,
+ filter);
+ }
+
private class DcTrackerTestHandler extends HandlerThread {
private DcTrackerTestHandler(String name) {
@@ -144,12 +178,13 @@
@Override
public void onLooperPrepared() {
- mDct = new DcTracker(mPhone);
+ mDct = new DcTracker(mPhone, TransportType.WWAN);
setReady(true);
}
}
private class ApnSettingContentProvider extends MockContentProvider {
+ private int mPreferredApnSet = 0;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -161,7 +196,9 @@
logd(" selectionArgs = " + Arrays.toString(selectionArgs));
logd(" sortOrder = " + sortOrder);
- if (uri.compareTo(Telephony.Carriers.CONTENT_URI) == 0) {
+ if (uri.compareTo(Telephony.Carriers.CONTENT_URI) == 0
+ || uri.compareTo(Uri.withAppendedPath(
+ Telephony.Carriers.CONTENT_URI, "filtered")) == 0) {
if (projection == null && selectionArgs == null && selection != null) {
Pattern pattern = Pattern.compile("^numeric = '([0-9]*)'");
@@ -191,7 +228,9 @@
Telephony.Carriers.MAX_CONNS, Telephony.Carriers.WAIT_TIME,
Telephony.Carriers.MAX_CONNS_TIME, Telephony.Carriers.MTU,
Telephony.Carriers.MVNO_TYPE,
- Telephony.Carriers.MVNO_MATCH_DATA});
+ Telephony.Carriers.MVNO_MATCH_DATA,
+ Telephony.Carriers.NETWORK_TYPE_BITMASK,
+ Telephony.Carriers.APN_SET_ID});
mc.addRow(new Object[]{
2163, // id
@@ -219,7 +258,9 @@
0, // max_conns_time
0, // mtu
"", // mvno_type
- "" // mnvo_match_data
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -248,7 +289,9 @@
0, // max_conns_time
0, // mtu
"", // mvno_type
- "" // mnvo_match_data
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -277,7 +320,9 @@
0, // max_conns_time
0, // mtu
"", // mvno_type
- "" // mnvo_match_data
+ "", // mnvo_match_data
+ 0, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -306,7 +351,9 @@
0, // max_conns_time
0, // mtu
"", // mvno_type
- "" // mnvo_match_data
+ "", // mnvo_match_data
+ NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -335,14 +382,31 @@
0, // max_conns_time
0, // mtu
"", // mvno_type
- "" // mnvo_match_data
+ "", // mnvo_match_data
+ 0, // network_type_bitmask
+ 0 // apn_set_id
});
+
return mc;
}
+ } else if (uri.isPathPrefixMatch(
+ 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;
}
return null;
}
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
+ return 1;
+ }
}
@Before
@@ -371,6 +435,9 @@
"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);
@@ -402,7 +469,8 @@
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mBundle = mContextFixture.getCarrierConfigBundle();
- mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+ mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
+ addDataService();
mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
mDcTrackerTestHandler.start();
@@ -421,32 +489,42 @@
}
// Create a successful data response
- public static DataCallResponse createDataCallResponse() {
-
- return new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+ private static SetupDataCallResult createSetupDataCallResult() throws Exception {
+ 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.profileId);
- assertEquals(apn, dp.apn);
- assertEquals("IP", dp.protocol);
- assertEquals(0, dp.authType);
- assertEquals("", dp.user);
- assertEquals("", dp.password);
- assertEquals(type, dp.type);
- assertEquals(0, dp.maxConnsTime);
- assertEquals(0, dp.maxConns);
- assertEquals(0, dp.waitTime);
- assertTrue(dp.enabled);
- assertEquals(supportedApnTypesBitmap, dp.supportedApnTypesBitmap);
- assertEquals("IP", dp.roamingProtocol);
- assertEquals(bearerBitmask, dp.bearerBitmap);
- assertEquals(0, dp.mtu);
- assertEquals("", dp.mvnoType);
- assertEquals("", dp.mvnoMatchData);
- assertFalse(dp.modemCognitive);
+ assertEquals(profileId, dp.getProfileId());
+ assertEquals(apn, dp.getApn());
+ assertEquals("IP", dp.getProtocol());
+ assertEquals(0, dp.getAuthType());
+ assertEquals("", dp.getUserName());
+ assertEquals("", dp.getPassword());
+ assertEquals(type, dp.getType());
+ assertEquals(0, dp.getMaxConnsTime());
+ assertEquals(0, dp.getMaxConns());
+ assertEquals(0, dp.getWaitTime());
+ assertTrue(dp.isEnabled());
+ assertEquals(supportedApnTypesBitmap, dp.getSupportedApnTypesBitmap());
+ assertEquals("IP", dp.getRoamingProtocol());
+ assertEquals(bearerBitmask, dp.getBearerBitmap());
+ assertEquals(0, dp.getMtu());
+ assertEquals("", dp.getMvnoType());
+ assertEquals("", dp.getMvnoMatchData());
+ assertFalse(dp.isModemCognitive());
}
private void verifyDataConnected(final String apnSetting) {
@@ -486,11 +564,11 @@
// Test the normal data call setup scenario.
@Test
@MediumTest
- public void testDataSetup() {
+ public void testDataSetup() throws Exception {
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
- mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+ mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
boolean allowed = isDataAllowed(dataConnectionReasons);
@@ -527,7 +605,7 @@
logd("Sending EVENT_ENABLE_NEW_APN");
// APN id 0 is APN_TYPE_DEFAULT
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
waitForMs(200);
dataConnectionReasons = new DataConnectionReasons();
@@ -537,8 +615,10 @@
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
verifyDataConnected(FAKE_APN1);
@@ -547,15 +627,22 @@
// Test the scenario where the first data call setup is failed, and then retry the setup later.
@Test
@MediumTest
- public void testDataRetry() {
+ public void testDataRetry() throws Exception {
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
// LOST_CONNECTION(0x10004) is a non-permanent failure, so we'll retry data setup later.
- DataCallResponse dcResponse = new DataCallResponse(0x10004, -1, 1, 2, "IP", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+ /*DataCallResponse dcResponse = new DataCallResponse(0x10004, -1, 1, 2, "IP", FAKE_IFNAME,
+ Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress(FAKE_ADDRESS), 0)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_DNS)),
+ Arrays.asList(NetworkUtils.numericToInetAddress(FAKE_GATEWAY)),
+ Arrays.asList(FAKE_PCSCF_ADDRESS),
+ 1440);*/
+ SetupDataCallResult result = createSetupDataCallResult();
+ result.status = 0x10004;
+
// Simulate RIL fails the data call setup
- mSimulatedCommands.setDataCallResponse(false, dcResponse);
+ mSimulatedCommands.setDataCallResult(false, result);
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
boolean allowed = isDataAllowed(dataConnectionReasons);
@@ -592,7 +679,7 @@
logd("Sending EVENT_ENABLE_NEW_APN");
// APN id 0 is APN_TYPE_DEFAULT
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
waitForMs(200);
@@ -603,8 +690,10 @@
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
// Make sure we never notify connected because the data call setup is supposed to fail.
@@ -616,7 +705,7 @@
anyLong(), any(PendingIntent.class));
// This time we'll let RIL command succeed.
- mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+ mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
// Simulate the timer expires.
Intent intent = new Intent("com.android.internal.telephony.data-reconnect.default");
@@ -629,8 +718,10 @@
dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 5, 1, LTE_BEARER_BITMASK);
// Verify connected with APN2 setting.
@@ -644,11 +735,11 @@
public void testUserDisableData() throws Exception {
//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
- boolean dataEnabled = mDct.getDataEnabled();
+ boolean dataEnabled = mDct.isUserDataEnabled();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -659,28 +750,31 @@
waitForMs(200);
logd("Sending DATA_ENABLED_CMD");
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
waitForMs(200);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
logd("Sending DATA_DISABLED_CMD");
- mDct.setDataEnabled(false);
+ mDct.setUserDataEnabled(false);
waitForMs(200);
// expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+ verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+ eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
// reset the setting at the end of this test
- mDct.setDataEnabled(dataEnabled);
+ mDct.setUserDataEnabled(dataEnabled);
waitForMs(200);
}
@@ -694,12 +788,13 @@
//set Default and MMS to be metered in the CarrierConfigManager
boolean roamingEnabled = mDct.getDataRoamingEnabled();
- boolean dataEnabled = mDct.getDataEnabled();
+ boolean dataEnabled = mDct.isUserDataEnabled();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -710,13 +805,15 @@
waitForMs(200);
logd("Sending DATA_ENABLED_CMD");
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
waitForMs(300);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
//user is in roaming
@@ -727,7 +824,8 @@
waitForMs(200);
// expected tear down all metered DataConnections
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+ verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+ eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -735,7 +833,7 @@
// reset roaming settings / data enabled settings at end of this test
mDct.setDataRoamingEnabledByUser(roamingEnabled);
- mDct.setDataEnabled(dataEnabled);
+ mDct.setUserDataEnabled(dataEnabled);
waitForMs(200);
}
@@ -747,17 +845,17 @@
//step 3: only non-metered data call is established
boolean roamingEnabled = mDct.getDataRoamingEnabled();
- boolean dataEnabled = mDct.getDataEnabled();
+ boolean dataEnabled = mDct.isUserDataEnabled();
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[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending DATA_ENABLED_CMD");
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
logd("Sending DISABLE_ROAMING_CMD");
mDct.setDataRoamingEnabledByUser(false);
@@ -773,8 +871,10 @@
waitForMs(200);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
@@ -783,7 +883,7 @@
// reset roaming settings / data enabled settings at end of this test
mDct.setDataRoamingEnabledByUser(roamingEnabled);
- mDct.setDataEnabled(dataEnabled);
+ mDct.setUserDataEnabled(dataEnabled);
waitForMs(200);
}
@@ -792,12 +892,15 @@
@MediumTest
public void testDDSResetAutoAttach() throws Exception {
- mDct.setDataEnabled(true);
+ ContentResolver resolver = mContext.getContentResolver();
+ Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
+
+ mDct.setUserDataEnabled(true);
mContextFixture.putBooleanResource(
com.android.internal.R.bool.config_auto_attach_data_on_creation, true);
- mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+ mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
boolean allowed = isDataAllowed(dataConnectionReasons);
@@ -849,6 +952,7 @@
// Test for API carrierActionSetMeteredApnsEnabled.
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testCarrierActionSetMeteredApnsEnabled() throws Exception {
@@ -859,11 +963,11 @@
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- boolean dataEnabled = mDct.getDataEnabled();
- mDct.setDataEnabled(true);
+ boolean dataEnabled = mDct.isUserDataEnabled();
+ mDct.setUserDataEnabled(true);
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -875,8 +979,10 @@
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
@@ -887,20 +993,22 @@
waitForMs(100);
// Validate all metered data connections have been torn down
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+ verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+ eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
// Reset settings at the end of test
- mDct.setDataEnabled(dataEnabled);
+ mDct.setUserDataEnabled(dataEnabled);
waitForMs(200);
}
private void initApns(String targetApn, String[] canHandleTypes) {
doReturn(targetApn).when(mApnContext).getApnType();
doReturn(true).when(mApnContext).isConnectable();
- ApnSetting apnSetting = createApnSetting(canHandleTypes);
+ ApnSetting apnSetting = createApnSetting(ApnSetting.getApnTypesBitmaskFromString(
+ TextUtils.join(",", canHandleTypes)));
doReturn(apnSetting).when(mApnContext).getNextApnSetting();
doReturn(apnSetting).when(mApnContext).getApnSetting();
doReturn(mDcac).when(mApnContext).getDcAc();
@@ -919,8 +1027,37 @@
waitForMs(200);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDataConnectionState() throws Exception {
+ initApns(PhoneConstants.APN_TYPE_SUPL,
+ new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_DEFAULT});
+ mDct.setUserDataEnabled(false);
+
+ mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+ waitForMs(200);
+
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+ waitForMs(200);
+
+ // 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(PhoneConstants.APN_TYPE_SUPL));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
}
// Test the unmetered APN setup when data is disabled.
@@ -928,7 +1065,7 @@
@SmallTest
public void testTrySetupDataUnmeteredDataDisabled() throws Exception {
initApns(PhoneConstants.APN_TYPE_FOTA, new String[]{PhoneConstants.APN_TYPE_ALL});
- mDct.setDataEnabled(false);
+ mDct.setUserDataEnabled(false);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -945,8 +1082,10 @@
waitForMs(200);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test the metered APN setup when data is disabled.
@@ -954,7 +1093,7 @@
@SmallTest
public void testTrySetupMeteredDataDisabled() throws Exception {
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
- mDct.setDataEnabled(false);
+ mDct.setUserDataEnabled(false);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -970,8 +1109,9 @@
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
- anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test the restricted data request when data is disabled.
@@ -981,7 +1121,7 @@
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
- mDct.setDataEnabled(false);
+ mDct.setUserDataEnabled(false);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -997,8 +1137,40 @@
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
- anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
+ }
+
+ // Test the restricted data request when roaming is disabled.
+ @Test
+ @SmallTest
+ public void testTrySetupRestrictedRoamingDisabled() throws Exception {
+ initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+
+ mDct.setUserDataEnabled(true);
+ mDct.setDataRoamingEnabledByUser(false);
+ mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ //user is in roaming
+ doReturn(true).when(mServiceState).getDataRoaming();
+
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+ waitForMs(200);
+
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+ waitForMs(200);
+
+ // expect no restricted data connection
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test the default data when data is not connectable.
@@ -1007,7 +1179,7 @@
public void testTrySetupNotConnectable() throws Exception {
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
doReturn(false).when(mApnContext).isConnectable();
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -1023,8 +1195,9 @@
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
- anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test the default data on IWLAN.
@@ -1034,7 +1207,7 @@
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
.getRilDataRadioTechnology();
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -1050,8 +1223,9 @@
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
- anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test the default data when the phone is in ECBM.
@@ -1060,7 +1234,7 @@
public void testTrySetupDefaultInECBM() throws Exception {
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
doReturn(true).when(mPhone).isInEcm();
- mDct.setDataEnabled(true);
+ mDct.setUserDataEnabled(true);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
@@ -1076,8 +1250,9 @@
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForMs(200);
- verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
- anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
}
// Test update waiting apn list when on data rat change
@@ -1088,8 +1263,8 @@
.getRilDataRadioTechnology();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- mDct.setEnabled(0, true);
- mDct.setDataEnabled(true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
+ mDct.setUserDataEnabled(true);
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
logd("Sending EVENT_RECORDS_LOADED");
@@ -1103,8 +1278,10 @@
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
@@ -1117,7 +1294,8 @@
// Verify the disconnected data call due to rat change and retry manger schedule another
// data call setup
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+ 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));
@@ -1132,13 +1310,15 @@
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
}
-// Test for fetchDunApn()
+ // Test for fetchDunApns()
@Test
@SmallTest
public void testFetchDunApn() {
@@ -1153,15 +1333,48 @@
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.TETHER_DUN_APN, dunApnString);
// should return APN from Setting
- ApnSetting dunApn = mDct.fetchDunApn();
+ ApnSetting dunApn = mDct.fetchDunApns().get(0);
assertTrue(dunApnExpected.equals(dunApn));
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.TETHER_DUN_APN, null);
// should return APN from db
- dunApn = mDct.fetchDunApn();
- assertEquals(FAKE_APN5, dunApn.apn);
+ dunApn = mDct.fetchDunApns().get(0);
+ assertEquals(FAKE_APN5, dunApn.getApnName());
}
+
+ // Test for fetchDunApns() with apn set id
+ @Test
+ @SmallTest
+ public void testFetchDunApnWithPreferredApnSet() {
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ // apnSetId=1
+ String dunApnString1 = "[ApnSettingV5]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+ + "0,,,,,,,,,,1";
+ // apnSetId=0
+ String dunApnString2 = "[ApnSettingV5]HOT mobile PC,pc.coldm,,,,,,,,,440,10,,DUN,,,true,"
+ + "0,,,,,,,,,,0";
+
+ ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString1);
+
+ ContentResolver cr = mContext.getContentResolver();
+ Settings.Global.putString(cr, Settings.Global.TETHER_DUN_APN,
+ dunApnString1 + ";" + dunApnString2);
+
+ // set that we prefer apn set 1
+ ContentValues values = new ContentValues();
+ values.put(Telephony.Carriers.APN_SET_ID, 1);
+ cr.update(PREFERAPN_URI, values, null, null);
+
+ // return APN from Setting with apnSetId=1
+ ArrayList<ApnSetting> dunApns = mDct.sortApnListByPreferred(mDct.fetchDunApns());
+ assertEquals(2, dunApns.size());
+ assertTrue(dunApnExpected.equals(dunApns.get(0)));
+ }
+
// Test oos
@Test
@SmallTest
@@ -1170,8 +1383,8 @@
.getRilDataRadioTechnology();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- mDct.setEnabled(0, true);
- mDct.setDataEnabled(true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
+ mDct.setUserDataEnabled(true);
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
logd("Sending EVENT_RECORDS_LOADED");
@@ -1185,8 +1398,10 @@
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
// Verify if RIL command was sent properly.
verify(mSimulatedCommandsVerifier).setupDataCall(
- eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
- eq(false), eq(false), any(Message.class));
+ eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+ mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
@@ -1198,7 +1413,8 @@
waitForMs(200);
// Verify data connection is on
- verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(anyInt(), anyInt(),
+ verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(
+ eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
// Data rat resume from unknown to ehrpd
@@ -1211,4 +1427,39 @@
assertEquals(FAKE_APN4, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
}
+
+ // 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);
+
+ 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);
+ waitForMs(200);
+
+ assertTrue(mDct.isDataEnabled());
+ assertTrue(mDct.isUserDataEnabled());
+ assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
index 6b00289..2f452f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
@@ -16,8 +16,12 @@
package com.android.internal.telephony.dataconnection;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.RetryManager;
@@ -31,97 +35,91 @@
import java.util.ArrayList;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
/**
* APN retry manager tests
*/
public class RetryManagerTest extends TelephonyTest {
// This is the real APN data for the Japanese carrier NTT Docomo.
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn2 = new ApnSetting(
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
2164, // id
"44010", // numeric
"mopera U", // name
"mopera.net", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn3 = new ApnSetting(
+ private ApnSetting mApn3 = ApnSetting.makeApnSetting(
2165, // id
"44010", // numeric
"b-mobile for Nexus", // name
"bmobile.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
3, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
private PersistableBundle mBundle;
@@ -173,7 +171,7 @@
new String[]{"default:"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -195,7 +193,7 @@
new String[]{"supl:2000,3000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "supl");
rm.setWaitingApns(waitingApns);
@@ -249,8 +247,8 @@
new String[]{"others:2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -287,8 +285,8 @@
new String[]{"dun:2000,5000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "dun");
rm.setWaitingApns(waitingApns);
@@ -335,8 +333,8 @@
new String[]{"mms: 3000,6000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "mms");
rm.setWaitingApns(waitingApns);
@@ -383,7 +381,7 @@
new String[]{"fota:1000,4000,7000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting apn = new ApnSetting(mApn1);
+ ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
waitingApns.add(apn);
RetryManager rm = new RetryManager(mPhone, "fota");
@@ -416,8 +414,8 @@
new String[]{"xyz : 1000,4000,7000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -473,9 +471,9 @@
new String[]{"default:2000:2000,3000:3000", "ims:1000,4000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
- ApnSetting myApn3 = new ApnSetting(mApn3);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
+ ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
waitingApns.add(myApn3);
@@ -532,8 +530,8 @@
new String[]{"default:1000,4000,7000,9000", "mms:1234,4123"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -587,7 +585,7 @@
new String[]{"default:default_randomization=1000,3000:2000,6000:3000,10000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -624,8 +622,8 @@
new String[]{"default:max_retries=infinite,1000,2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -682,8 +680,8 @@
new String[]{"hipri: max_retries=4,1000,2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "hipri");
rm.setWaitingApns(waitingApns);
@@ -752,8 +750,8 @@
mBundle.putLong(CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 2000);
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -800,8 +798,8 @@
new String[]{"dun:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -845,7 +843,7 @@
// reset the retry manager
- ApnSetting myApn3 = new ApnSetting(mApn3);
+ ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
waitingApns.clear();
waitingApns.add(myApn3);
@@ -881,8 +879,8 @@
new String[]{"others:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -937,8 +935,8 @@
new String[]{"default:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -980,8 +978,8 @@
new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index a60b502..6d1cf8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -22,7 +22,6 @@
import android.net.NetworkRequest;
import android.net.StringNetworkSpecifier;
import android.os.Binder;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
@@ -39,6 +38,7 @@
import com.android.internal.telephony.mocks.SubscriptionMonitorMock;
import com.android.internal.telephony.mocks.TelephonyRegistryMock;
+import org.junit.Ignore;
public class TelephonyNetworkFactoryTest extends AndroidTestCase {
private final static String LOG_TAG = "TelephonyNetworkFactoryTest";
@@ -65,18 +65,29 @@
final Looper looper;
DcTrackerMock dcTrackerMock;
final Context contextMock;
+ private Object mLock = new Object();
+ private static final int MAX_INIT_WAIT_MS = 30000; // 30 seconds
TestSetup(int numPhones) {
- handlerThread = new HandlerThread("TelephonyNetworkFactoryTest");
- handlerThread.start();
- looper = handlerThread.getLooper();
-
- Handler myHandler = new Handler(looper) {
- public void handleMessage(Message msg) {
- if (dcTrackerMock == null) dcTrackerMock = new DcTrackerMock();
+ handlerThread = new HandlerThread("TelephonyNetworkFactoryTest") {
+ @Override
+ public void onLooperPrepared() {
+ synchronized (mLock) {
+ if (dcTrackerMock == null) dcTrackerMock = new DcTrackerMock();
+ mLock.notifyAll();
+ }
}
};
- myHandler.obtainMessage(0).sendToTarget();
+ handlerThread.start();
+ // wait until dct created
+ synchronized (mLock) {
+ try {
+ mLock.wait(MAX_INIT_WAIT_MS);
+ } catch (InterruptedException ie) {
+ }
+ if (dcTrackerMock == null) fail("failed to initialize dct");
+ }
+ looper = handlerThread.getLooper();
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccCardControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccCardControllerTest.java
new file mode 100644
index 0000000..f5a079a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccCardControllerTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.euicc;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccManager;
+
+import com.android.internal.telephony.TelephonyTest;
+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.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class EuiccCardControllerTest extends TelephonyTest {
+ private static final String KEY_LAST_BOOT_COUNT = "last_boot_count";
+
+ private int mBootCount;
+ private int mLastBootCount;
+ private EuiccCardController mEuiccCardController;
+ private SharedPreferences mSp;
+ @Mock
+ private UiccSlot mInactivatedEsimSlot;
+ @Mock
+ private UiccSlot mActivatedEsimSlot;
+ @Mock
+ private UiccSlot mActivatedRemovableSlot;
+ @Mock
+ private EuiccController mEuiccController;
+ @Mock
+ private UiccController mUiccController;
+ private boolean mOtaStarted;
+ private CountDownLatch mOtaLatch;
+
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp("EuiccCardControllerTest");
+ MockitoAnnotations.initMocks(this);
+ mSp = PreferenceManager.getDefaultSharedPreferences(mContext);
+
+ mLastBootCount = mSp.getInt(KEY_LAST_BOOT_COUNT, -1);
+ mBootCount = Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.BOOT_COUNT, -1);
+ mOtaStarted = false;
+ mOtaLatch = new CountDownLatch(1);
+
+ when(mEuiccController.getOtaStatus()).thenReturn(EuiccManager.EUICC_OTA_SUCCEEDED);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mOtaStarted = true;
+ mOtaLatch.countDown();
+ return null;
+ }
+ }).when(mEuiccController).startOtaUpdatingIfNecessary();
+ when(mTelephonyManager.switchSlots(any())).thenReturn(true);
+ when(mTelephonyManager.getPhoneCount()).thenReturn(1);
+ when(mActivatedEsimSlot.isEuicc()).thenReturn(true);
+ when(mActivatedEsimSlot.isActive()).thenReturn(true);
+ when(mInactivatedEsimSlot.isEuicc()).thenReturn(true);
+ when(mInactivatedEsimSlot.isActive()).thenReturn(false);
+ when(mActivatedRemovableSlot.isEuicc()).thenReturn(false);
+ when(mActivatedRemovableSlot.isActive()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mBootCount == -1) {
+ Settings.Global.resetToDefaults(mContext.getContentResolver(), KEY_LAST_BOOT_COUNT);
+ } else {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.BOOT_COUNT, mBootCount);
+ }
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ sp.edit().putInt(KEY_LAST_BOOT_COUNT, mLastBootCount).apply();
+ }
+
+ @Test
+ public void testIsBootUp() {
+ mSp.edit().remove(KEY_LAST_BOOT_COUNT);
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+ assertTrue(EuiccCardController.isBootUp(mContext));
+
+ mSp.edit().putInt(KEY_LAST_BOOT_COUNT, 1).apply();
+ Settings.Global.resetToDefaults(mContext.getContentResolver(), KEY_LAST_BOOT_COUNT);
+ assertTrue(EuiccCardController.isBootUp(mContext));
+
+ mSp.edit().putInt(KEY_LAST_BOOT_COUNT, 1).apply();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 1);
+ assertFalse(EuiccCardController.isBootUp(mContext));
+
+ mSp.edit().putInt(KEY_LAST_BOOT_COUNT, 2).apply();
+ assertTrue(EuiccCardController.isBootUp(mContext));
+ assertEquals(mSp.getInt(KEY_LAST_BOOT_COUNT, -1), 1);
+ }
+
+ @Test
+ public void testIsEmbeddedSlotActivated() {
+ mEuiccCardController =
+ new EuiccCardController(mContext, null, mEuiccController, mUiccController);
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {mActivatedRemovableSlot});
+ assertFalse(mEuiccCardController.isEmbeddedSlotActivated());
+
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {mActivatedEsimSlot});
+ assertTrue(mEuiccCardController.isEmbeddedSlotActivated());
+
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {mInactivatedEsimSlot});
+ assertFalse(mEuiccCardController.isEmbeddedSlotActivated());
+
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {
+ mActivatedEsimSlot, mInactivatedEsimSlot, mActivatedRemovableSlot});
+ assertTrue(mEuiccCardController.isEmbeddedSlotActivated());
+
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {
+ mInactivatedEsimSlot, mActivatedEsimSlot, mActivatedRemovableSlot});
+ assertTrue(mEuiccCardController.isEmbeddedSlotActivated());
+ }
+
+ @Test
+ public void testStartOtaUpdatingIfNecessary_onEmbeddedSlot() {
+ // isBootUp = true
+ mSp.edit().remove(KEY_LAST_BOOT_COUNT);
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+ when(mUiccController.getUiccSlots()).thenReturn(new UiccSlot[] {mActivatedEsimSlot});
+
+ mEuiccCardController =
+ new EuiccCardController(mContext, null, mEuiccController, mUiccController);
+ mContext.sendBroadcast(new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED));
+ try {
+ mOtaLatch.await(5000, TimeUnit.MILLISECONDS);
+ assertTrue(mOtaStarted);
+ } catch (InterruptedException ignore) { }
+ }
+
+ @Test
+ public void testStartOtaUpdatingIfNecessary_notBootUp() {
+ // isBootUp = false
+ mSp.edit().putInt(KEY_LAST_BOOT_COUNT, 1).apply();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 1);
+ when(mUiccController.getUiccSlots()).thenReturn(new UiccSlot[] {mActivatedEsimSlot});
+ mEuiccCardController =
+ new EuiccCardController(mContext, null, mEuiccController, mUiccController);
+
+ mContext.sendBroadcast(new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED));
+ assertFalse(mOtaStarted);
+ }
+
+ @Test
+ public void testStartOtaUpdatingIfNecessary_onRemovableSlot() {
+ // isBootUp = true
+ mSp.edit().remove(KEY_LAST_BOOT_COUNT);
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+ when(mUiccController.getUiccSlots())
+ .thenReturn(new UiccSlot[] {mActivatedRemovableSlot, mInactivatedEsimSlot});
+
+ mEuiccCardController =
+ new EuiccCardController(mContext, null, mEuiccController, mUiccController);
+ mContext.sendBroadcast(new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED));
+ assertFalse(mOtaStarted);
+ }
+}
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 bbad22b..f7d8671 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.telephony.euicc;
+import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -56,6 +58,8 @@
import android.telephony.euicc.EuiccManager;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback;
+import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
import org.junit.After;
import org.junit.Before;
@@ -69,6 +73,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
import java.util.Collections;
@RunWith(AndroidJUnit4.class)
@@ -96,7 +101,8 @@
DownloadableSubscription.forActivationCode("abcde");
static {
SUBSCRIPTION_WITH_METADATA.setCarrierName("test name");
- SUBSCRIPTION_WITH_METADATA.setAccessRules(new UiccAccessRule[] { ACCESS_RULE });
+ SUBSCRIPTION_WITH_METADATA.setAccessRules(
+ Arrays.asList(new UiccAccessRule[] { ACCESS_RULE }));
}
private static final String OS_VERSION = "1.0";
@@ -122,14 +128,18 @@
// Whether refreshSubscriptionsAndSendResult was called.
private boolean mCalledRefreshSubscriptionsAndSendResult;
+ // Number of OTA status changed.
+ private int mNumOtaStatusChanged;
+
TestEuiccController(Context context, EuiccConnector connector) {
super(context, connector);
+ mNumOtaStatusChanged = 0;
}
@Override
public void addResolutionIntent(
Intent extrasIntent, String resolutionAction, String callingPackage,
- EuiccOperation op) {
+ boolean retried, EuiccOperation op) {
mResolutionAction = resolutionAction;
mOp = op;
}
@@ -148,6 +158,11 @@
mCalledRefreshSubscriptionsAndSendResult = true;
sendResult(callbackIntent, resultCode, extrasIntent);
}
+
+ @Override
+ public void sendOtaStatusChangedBroadcast() {
+ ++mNumOtaStatusChanged;
+ }
}
@Before
@@ -203,9 +218,55 @@
assertNull(callGetEid(true /* success */, null /* eid */));
}
+ @Test(expected = SecurityException.class)
+ public void testGetOtaStatus_noPrivileges() {
+ setHasWriteEmbeddedPermission(false /* hasPermission */);
+ callGetOtaStatus(true /* success */, 1 /* status */);
+ }
+
+ @Test
+ public void testGetOtaStatus_withWriteEmbeddedPermission() {
+ setHasWriteEmbeddedPermission(true /* hasPermission */);
+ assertEquals(1, callGetOtaStatus(true /* success */, 1 /* status */));
+ }
+
+ @Test
+ public void testGetOtaStatus_failure() {
+ setHasWriteEmbeddedPermission(true /* hasPermission */);
+ assertEquals(
+ EUICC_OTA_STATUS_UNAVAILABLE,
+ callGetOtaStatus(false /* success */, 1 /* status */));
+ }
+
+ @Test
+ public void testStartOtaUpdatingIfNecessary_serviceNotAvailable() {
+ setHasWriteEmbeddedPermission(true /* hasPermission */);
+ callStartOtaUpdatingIfNecessary(
+ false /* serviceAvailable */, EuiccManager.EUICC_OTA_IN_PROGRESS);
+ assertEquals(mController.mNumOtaStatusChanged, 0);
+ }
+
+ @Test
+ public void testStartOtaUpdatingIfNecessary_otaStatusChanged() {
+ setHasWriteEmbeddedPermission(true /* hasPermission */);
+ callStartOtaUpdatingIfNecessary(
+ true /* serviceAvailable */, EuiccManager.EUICC_OTA_IN_PROGRESS);
+ callStartOtaUpdatingIfNecessary(
+ true /* serviceAvailable */, EuiccManager.EUICC_OTA_FAILED);
+ callStartOtaUpdatingIfNecessary(
+ true /* serviceAvailable */, EuiccManager.EUICC_OTA_SUCCEEDED);
+ callStartOtaUpdatingIfNecessary(
+ true /* serviceAvailable */, EuiccManager.EUICC_OTA_NOT_NEEDED);
+ callStartOtaUpdatingIfNecessary(
+ true /* serviceAvailable */, EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE);
+
+ assertEquals(mController.mNumOtaStatusChanged, 5);
+ }
+
+
@Test
public void testGetEuiccInfo_success() {
- assertEquals(OS_VERSION, callGetEuiccInfo(true /* success */, EUICC_INFO).osVersion);
+ assertEquals(OS_VERSION, callGetEuiccInfo(true /* success */, EUICC_INFO).getOsVersion());
}
@Test
@@ -350,6 +411,17 @@
}
@Test
+ public void testDownloadSubscription_needConfirmationCode() throws Exception {
+ setHasWriteEmbeddedPermission(true);
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_NEED_CONFIRMATION_CODE, "whatever" /* callingPackage */);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,
+ 0 /* detailedCode */);
+ verifyResolutionIntent(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE,
+ EuiccOperation.ACTION_DOWNLOAD_CONFIRMATION_CODE);
+ }
+
+ @Test
public void testDownloadSubscription_success() throws Exception {
setHasWriteEmbeddedPermission(true);
callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
@@ -765,16 +837,20 @@
private void setHasCarrierPrivilegesOnActiveSubscription(boolean hasPrivileges)
throws Exception {
SubscriptionInfo subInfo = new SubscriptionInfo(
- 0, "", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null);
+ when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
+ hasPrivileges);
when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(
Collections.singletonList(subInfo));
}
private void prepareOperationSubscription(boolean hasPrivileges) throws Exception {
SubscriptionInfo subInfo = new SubscriptionInfo(
- SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, 0, 0, "",
+ SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, "0", "0", "",
true /* isEmbedded */, hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null);
+ when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
+ hasPrivileges);
when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
Collections.singletonList(subInfo));
}
@@ -795,6 +871,40 @@
return mController.getEid();
}
+ private int callGetOtaStatus(final boolean success, final int status) {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Exception {
+ GetOtaStatusCommandCallback cb = invocation.getArgument(0);
+ if (success) {
+ cb.onGetOtaStatusComplete(status);
+ } else {
+ cb.onEuiccServiceUnavailable();
+ }
+ return null;
+ }
+ }).when(mMockConnector).getOtaStatus(Mockito.<GetOtaStatusCommandCallback>any());
+ return mController.getOtaStatus();
+ }
+
+ private void callStartOtaUpdatingIfNecessary(
+ final boolean serviceAvailable, int status) {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Exception {
+ OtaStatusChangedCallback cb = invocation.getArgument(0);
+ if (!serviceAvailable) {
+ cb.onEuiccServiceUnavailable();
+ } else {
+ cb.onOtaStatusChanged(status);
+ }
+ return null;
+ }
+ }).when(mMockConnector).startOtaIfNecessary(Mockito.<OtaStatusChangedCallback>any());
+
+ mController.startOtaUpdatingIfNecessary();
+ }
+
private EuiccInfo callGetEuiccInfo(final boolean success, final @Nullable EuiccInfo euiccInfo) {
doAnswer(new Answer<Void>() {
@Override
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 22070a2..4e0035c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -300,6 +300,8 @@
verifyDataSmsIntentBroadcasts(1);
}
+ @FlakyTest
+ @Ignore
@Test
@MediumTest
public void testInjectSms() {
@@ -486,6 +488,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMultiPartSms() {
@@ -755,6 +758,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testBroadcastUndeliveredMultiPart() throws Exception {
@@ -777,28 +781,4 @@
verifySmsIntentBroadcasts(0);
}
-
- @FlakyTest
- @Ignore
- @Test
- @MediumTest
- public void testWaitingStateTimeout() throws Exception {
- transitionFromStartupToIdle();
-
- // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
- mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
- new AsyncResult(null, mSmsMessage, null));
- waitForMs(100);
-
- ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, times(1)).sendBroadcast(
- intentArgumentCaptor.capture());
- assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION,
- intentArgumentCaptor.getAllValues().get(0).getAction());
- assertEquals("WaitingState", getCurrentState().getName());
-
- waitForMs(InboundSmsHandler.STATE_TIMEOUT + 300);
-
- assertEquals("IdleState", getCurrentState().getName());
- }
}
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 335634d..57f8df9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -21,10 +21,12 @@
import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -50,14 +52,15 @@
import com.android.internal.telephony.ContextFixture;
import com.android.internal.telephony.ISub;
-import com.android.internal.telephony.ImsSMSDispatcher;
import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.TelephonyTestUtils;
import com.android.internal.telephony.TestApplication;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -65,12 +68,15 @@
import java.util.HashMap;
public class GsmSmsDispatcherTest extends TelephonyTest {
+
+ private static final long TIMEOUT_MS = 500;
+
@Mock
private android.telephony.SmsMessage mSmsMessage;
@Mock
private SmsMessage mGsmSmsMessage;
@Mock
- private ImsSMSDispatcher mImsSmsDispatcher;
+ private SmsDispatchersController mSmsDispatchersController;
@Mock
private GsmInboundSmsHandler mGsmInboundSmsHandler;
@Mock
@@ -103,8 +109,8 @@
@Override
public void onLooperPrepared() {
- mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsUsageMonitor,
- mImsSmsDispatcher, mGsmInboundSmsHandler);
+ mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsDispatchersController,
+ mGsmInboundSmsHandler);
setReady(true);
}
}
@@ -118,6 +124,7 @@
// in the cache, a real instance is used.
mServiceManagerMockedServices.put("isub", mISubStub);
+ doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
mGsmSmsDispatcherTestHandler = new GsmSmsDispatcherTestHandler(getClass().getSimpleName());
mGsmSmsDispatcherTestHandler.start();
waitUntilReady();
@@ -146,8 +153,8 @@
when(mCountryDetector.detectCountry())
.thenReturn(new Country("US", Country.COUNTRY_SOURCE_SIM));
- mGsmSmsDispatcher.sendText(
- "6501002000", "121" /*scAddr*/, "test sms", null, null, null, null, false);
+ mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
// Blocked number provider is notified about the emergency contact asynchronously.
@@ -156,6 +163,7 @@
}
@FlakyTest
+ @Ignore
@Test @MediumTest
public void testSendSmsToEmergencyNumber_notifiesBlockedNumberProvider() throws Exception {
setupMockPackagePermissionChecks();
@@ -166,7 +174,7 @@
mGsmSmsDispatcher.sendText(
getEmergencyNumberFromSystemPropertiesOrDefault(), "121" /*scAddr*/, "test sms",
- null, null, null, null, false);
+ null, null, null, null, false, -1, false, -1);
verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
// Blocked number provider is notified about the emergency contact asynchronously.
@@ -174,6 +182,16 @@
assertEquals(1, mFakeBlockedNumberContentProvider.mNumEmergencyContactNotifications);
}
+ @Test @SmallTest
+ public void testSmsMessageValidityPeriod() throws Exception {
+ int vp;
+ vp = SmsMessage.getRelativeValidityPeriod(-5);
+ assertEquals(-1, vp);
+
+ vp = SmsMessage.getRelativeValidityPeriod(100);
+ assertEquals(100 / 5 - 1, vp);
+ }
+
private String getEmergencyNumberFromSystemPropertiesOrDefault() {
String systemEmergencyNumbers = SystemProperties.get("ril.ecclist");
if (systemEmergencyNumbers == null) {
@@ -183,7 +201,10 @@
}
}
- @Test @SmallTest
+ @Test
+ @SmallTest
+ @FlakyTest
+ @Ignore
public void testSendTextWithInvalidDestAddr() throws Exception {
// unmock ActivityManager to be able to register receiver, create real PendingIntent and
// receive TEST_INTENT
@@ -195,7 +216,7 @@
new Intent(TEST_INTENT), 0);
// send invalid dest address: +
mGsmSmsDispatcher.sendText("+", "222" /*scAddr*/, TAG,
- pendingIntent, null, null, null, false);
+ pendingIntent, null, null, null, false, -1, false, -1);
waitForMs(500);
verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
any(Message.class));
@@ -227,6 +248,7 @@
Settings.Global.DEVICE_PROVISIONED, 1);
mGsmSmsDispatcher.sendRawPdu(mSmsTracker);
+ waitForHandlerAction(mGsmSmsDispatcher, TIMEOUT_MS);
verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
similarity index 84%
rename from tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
index bc8d4e1..2ea1f28 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,12 +11,12 @@
* 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.
+ * limitations under the License
*/
// Note: Package name is intentionally wrong for this test; the internal junk class is used to test
// that parcelables of types other than android.* are stripped out.
-package com.android.telephony.ims;
+package com.android.internal.telephony.ims;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -28,13 +28,15 @@
import android.telecom.DisconnectCause;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Tests for the {@link com.android.ims.ImsCallProfile} class.
+ * Tests for the {@link ImsCallProfile} class.
+ *
+ * Test must NOT be in the "android." namespace.
*/
@RunWith(AndroidJUnit4.class)
public class ImsCallProfileTest {
@@ -49,15 +51,15 @@
mTest = in.readInt();
}
- public static final Creator<JunkParcelable> CREATOR = new Creator<JunkParcelable>() {
+ public static final Creator<JunkParcelable> CREATOR = new Creator<ImsCallProfileTest.JunkParcelable>() {
@Override
- public JunkParcelable createFromParcel(Parcel in) {
- return new JunkParcelable(in);
+ public ImsCallProfileTest.JunkParcelable createFromParcel(Parcel in) {
+ return new ImsCallProfileTest.JunkParcelable(in);
}
@Override
- public JunkParcelable[] newArray(int size) {
- return new JunkParcelable[size];
+ public ImsCallProfileTest.JunkParcelable[] newArray(int size) {
+ return new ImsCallProfileTest.JunkParcelable[size];
}
};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
new file mode 100644
index 0000000..fd19f80
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 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.ims;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.ims.MmTelFeatureConnection;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Hashtable;
+
+public class ImsManagerTest extends TelephonyTest {
+ private static final String UNSET_PROVISIONED_STRING = "unset";
+ private static final boolean ENHANCED_4G_MODE_DEFAULT_VAL = true;
+ private static final boolean ENHANCED_4G_MODE_EDITABLE = true;
+ private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
+ private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
+ private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
+ private static final boolean WFC_IMS_EDITABLE_VAL = true;
+ private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
+ private static final int WFC_IMS_MODE_DEFAULT_VAL =
+ ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
+ private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
+ ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
+
+ PersistableBundle mBundle;
+
+ @Mock
+ IBinder mBinder;
+ @Mock
+ ImsConfigImplBase mImsConfigImplBaseMock;
+ Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
+ Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
+ ImsConfigImplBase.ImsConfigStub mImsConfigStub;
+ @Mock MmTelFeatureConnection mMmTelFeatureConnection;
+
+ private final int[] mSubId = {0};
+ private int mPhoneId;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp("SubscriptionControllerTest");
+ mPhoneId = mPhone.getPhoneId();
+ mBundle = mContextFixture.getCarrierConfigBundle();
+
+ doReturn(mSubId).when(mSubscriptionController).getSubId(mPhoneId);
+
+ doReturn(mSubscriptionController).when(mBinder).queryLocalInterface(anyString());
+ mServiceManagerMockedServices.put("isub", mBinder);
+
+ doReturn(true).when(mMmTelFeatureConnection).isBinderAlive();
+
+ mImsManagerInstances.remove(mPhoneId);
+
+ setDefaultValues();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private void setDefaultValues() {
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
+ ENHANCED_4G_MODE_EDITABLE);
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+ WFC_IMS_EDITABLE_VAL);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
+ WFC_IMS_ENABLE_DEFAULT_VAL);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
+ WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL);
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT,
+ WFC_IMS_MODE_DEFAULT_VAL);
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT,
+ WFC_IMS_ROAMING_MODE_DEFAULT_VAL);
+ mBundle.putBoolean(CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL,
+ ENHANCED_4G_MODE_DEFAULT_VAL);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, true);
+ }
+
+ @Test @SmallTest
+ public void testGetDefaultValues() {
+ doReturn("-1").when(mSubscriptionController)
+ .getSubscriptionProperty(anyInt(), anyString(), anyString());
+
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+
+ assertEquals(WFC_IMS_ENABLE_DEFAULT_VAL, imsManager.isWfcEnabledByUser());
+ verify(mSubscriptionController, times(1)).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ENABLED),
+ anyString());
+
+ assertEquals(ENHANCED_4G_MODE_DEFAULT_VAL,
+ imsManager.isEnhanced4gLteModeSettingEnabledByUser());
+ verify(mSubscriptionController, times(1)).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
+ anyString());
+
+ assertEquals(WFC_IMS_MODE_DEFAULT_VAL, imsManager.getWfcMode(false));
+ verify(mSubscriptionController, times(1)).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ anyString());
+
+ assertEquals(WFC_IMS_ROAMING_MODE_DEFAULT_VAL, imsManager.getWfcMode(true));
+ verify(mSubscriptionController, times(1)).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ anyString());
+
+ assertEquals(VT_IMS_ENABLE_DEFAULT_VAL, imsManager.isVtEnabledByUser());
+ verify(mSubscriptionController, times(1)).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.VT_IMS_ENABLED),
+ anyString());
+ }
+
+ @Test @SmallTest
+ public void testSetValues() {
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+
+ imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
+ verify(mSubscriptionController, times(1)).setSubscriptionProperty(
+ eq(mSubId[0]),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ eq("1"));
+
+ imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED, true);
+ verify(mSubscriptionController, times(1)).setSubscriptionProperty(
+ eq(mSubId[0]),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ eq("1"));
+
+ imsManager.setVtSetting(false);
+ verify(mSubscriptionController, times(1)).setSubscriptionProperty(
+ eq(mSubId[0]),
+ eq(SubscriptionManager.VT_IMS_ENABLED),
+ eq("0"));
+
+ // enhanced 4g mode must be editable to use setEnhanced4gLteModeSetting
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
+ ENHANCED_4G_MODE_EDITABLE);
+ imsManager.setEnhanced4gLteModeSetting(true);
+ verify(mSubscriptionController, times(1)).setSubscriptionProperty(
+ eq(mSubId[0]),
+ eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
+ eq("1"));
+
+ imsManager.setWfcSetting(true);
+ verify(mSubscriptionController, times(1)).setSubscriptionProperty(
+ eq(mSubId[0]),
+ eq(SubscriptionManager.WFC_IMS_ENABLED),
+ eq("1"));
+ }
+
+ @Test
+ public void testGetProvisionedValues() throws Exception {
+ ImsManager imsManager = initializeProvisionedValues();
+
+ assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+ assertEquals(true, imsManager.isVtProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
+
+ assertEquals(true, imsManager.isVolteProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
+
+ // If we call get again, times should still be one because the value should be fetched
+ // from cache.
+ assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+ assertEquals(true, imsManager.isVtProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
+
+ assertEquals(true, imsManager.isVolteProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
+ }
+
+ @Test
+ public void testSetProvisionedValues() throws Exception {
+ ImsManager imsManager = initializeProvisionedValues();
+
+ assertEquals(true, imsManager.isWfcProvisionedOnDevice());
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+ imsManager.getConfigInterface().setProvisionedValue(
+ ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED,
+ ImsConfig.FeatureValueConstants.OFF);
+
+ assertEquals(0, (int) mProvisionedIntVals.get(
+ ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+ assertEquals(false, imsManager.isWfcProvisionedOnDevice());
+
+ verify(mImsConfigImplBaseMock, times(1)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED),
+ eq(0));
+ verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
+
+ }
+
+ /**
+ * Tests that when a WFC mode is set for home/roaming, that setting is sent to the ImsService
+ * correctly.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
+ // First, Set WFC home/roaming mode that is not the Carrier Config default.
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ anyString());
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ anyString());
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Roaming mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Home mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
+ }
+
+ /**
+ * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
+ * editable.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_wfcNotEditable() throws Exception {
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+ WFC_IMS_NOT_EDITABLE_VAL);
+ // Set some values that are different than the defaults for WFC mode.
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ anyString());
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ anyString());
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
+ // not WFC mode is editable. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_MODE_DEFAULT_VAL));
+ }
+
+ /**
+ * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
+ * Manager.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+ * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
+ * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_noUserSettingSet() throws Exception {
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+
+ // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+
+ // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_MODE_DEFAULT_VAL));
+ }
+
+ private ImsManager initializeProvisionedValues() throws Exception {
+ when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
+ .thenAnswer(invocation -> {
+ return getProvisionedInt((Integer) (invocation.getArguments()[0]));
+ });
+
+ when(mImsConfigImplBaseMock.setConfig(anyInt(), anyInt()))
+ .thenAnswer(invocation -> {
+ mProvisionedIntVals.put((Integer) (invocation.getArguments()[0]),
+ (Integer) (invocation.getArguments()[1]));
+ return ImsConfig.OperationStatusConstants.SUCCESS;
+ });
+
+
+ // Configure ImsConfigStub
+ mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
+ doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
+
+ // Configure ImsManager
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ try {
+ replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
+ mMmTelFeatureConnection);
+ } catch (Exception ex) {
+ fail("failed with " + ex);
+ }
+
+ return imsManager;
+ }
+
+ // If the value is ever set, return the set value. If not, return a constant value 1000.
+ private int getProvisionedInt(int item) {
+ if (mProvisionedIntVals.containsKey(item)) {
+ return mProvisionedIntVals.get(item);
+ } else {
+ return ImsConfig.FeatureValueConstants.ON;
+ }
+ }
+
+ // If the value is ever set, return the set value. If not, return a constant value "unset".
+ private String getProvisionedString(int item) {
+ if (mProvisionedStringVals.containsKey(item)) {
+ return mProvisionedStringVals.get(item);
+ } else {
+ return UNSET_PROVISIONED_STRING;
+ }
+ }
+}
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 5576227..3587b8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -16,6 +16,22 @@
package com.android.internal.telephony.ims;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertFalse;
+
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+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.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -28,14 +44,12 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ims.feature.ImsFeature;
import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
-
-import com.android.ims.internal.IImsServiceController;
-import com.android.internal.telephony.PhoneConstants;
import org.junit.After;
import org.junit.Before;
@@ -43,6 +57,7 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.HashSet;
@@ -51,18 +66,6 @@
import java.util.Set;
import java.util.stream.Collectors;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
/**
* Unit tests for ImsResolver
*/
@@ -77,13 +80,22 @@
private static final ComponentName TEST_CARRIER_2_DEFAULT_NAME = new ComponentName(
"TestCarrier2Pkg", "Carrier2ImsService");
- @Mock Context mMockContext;
- @Mock PackageManager mMockPM;
- @Mock ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
- @Mock CarrierConfigManager mMockCarrierConfigManager;
+ @Mock
+ Context mMockContext;
+ @Mock
+ PackageManager mMockPM;
+ @Mock
+ ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
+ @Mock
+ CarrierConfigManager mMockCarrierConfigManager;
+ @Mock
+ ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
+ @Mock
+ ImsServiceFeatureQueryManager mMockQueryManager;
private ImsResolver mTestImsResolver;
private BroadcastReceiver mTestPackageBroadcastReceiver;
private BroadcastReceiver mTestCarrierConfigReceiver;
+ private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
private PersistableBundle[] mCarrierConfigs;
@Before
@@ -101,24 +113,22 @@
/**
* Add a package to the package manager and make sure it is added to the cache of available
- * ImsServices in the ImsResolver
+ * ImsServices in the ImsResolver.
*/
@Test
@SmallTest
- public void testAddPackageToCache() {
+ public void testAddDevicePackageToCache() {
setupResolver(1/*numSlots*/);
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
+ HashSet<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
- mTestImsResolver.populateCacheAndStartBind();
+ // Complete package manager lookup and cache.
+ startBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
ImsResolver.ImsServiceInfo testCachedService =
mTestImsResolver.getImsServiceInfoFromCache(
TEST_DEVICE_DEFAULT_NAME.getPackageName());
@@ -127,6 +137,35 @@
}
/**
+ * Add a carrier ImsService to the package manager and make sure the features declared here are
+ * ignored. We should only allow dynamic query for these services.
+ */
+ @Test
+ @SmallTest
+ public void testAddCarrierPackageToCache() {
+ setupResolver(1/*numSlots*/);
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, features, true);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBind();
+
+ ImsResolver.ImsServiceInfo testCachedService =
+ mTestImsResolver.getImsServiceInfoFromCache(
+ TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ assertNotNull(testCachedService);
+ // none of the manifest features we added above should be reported for carrier package.
+ assertTrue(testCachedService.getSupportedFeatures().isEmpty());
+ // we should report that the service does not use metadata to report features.
+ assertFalse(testCachedService.featureFromMetadata);
+ }
+
+ /**
* Set the carrier config override value and ensure that ImsResolver calls .bind on that
* package name with the correct ImsFeatures.
*/
@@ -134,56 +173,101 @@
@SmallTest
public void testCarrierPackageBind() throws RemoteException {
setupResolver(1/*numSlots*/);
- // Set CarrierConfig default package name and make it available to the package manager
+ // Setup the carrier features
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ // Set CarrierConfig default package name and make it available as the CarrierConfig.
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- });
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
+ // Start bind to carrier service
+ startBind();
+ // setup features response
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- verify(controller).bind(convertToHashSet(features, 0));
+ verify(controller).bind(features);
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
}
/**
- * Ensure that no ImsService is bound if there is no carrier or device package explictly set.
+ * Creates a carrier ImsService that defines FEATURE_EMERGENCY_MMTEL and ensure that the
+ * controller sets this capability.
+ */
+ @Test
+ @SmallTest
+ public void testCarrierPackageBindWithEmergencyCalling() throws RemoteException {
+ setupResolver(1/*numSlots*/);
+ // Set CarrierConfig default package name and make it available to the package manager
+ setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
+
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
+
+ verify(controller).bind(features);
+ verify(controller, never()).unbind();
+ assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
+ }
+
+ /**
+ * Creates a carrier ImsService that does not report FEATURE_EMERGENCY_MMTEL and then update the
+ * ImsService to define it. Ensure that the controller sets this capability once enabled.
+ */
+ @Test
+ @SmallTest
+ public void testCarrierPackageChangeEmergencyCalling() throws RemoteException {
+ setupResolver(1/*numSlots*/);
+ // Set CarrierConfig default package name and make it available to the package manager
+ setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
+
+ // Bind without emergency calling
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
+ verify(controller).bind(features);
+ verify(controller, never()).unbind();
+ assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
+
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = new HashSet<>();
+ newFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ newFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL));
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, newFeatures, 2);
+
+ //Verify new feature is added to the carrier override.
+ // add all features for slot 0
+ verify(controller, atLeastOnce()).changeImsServiceFeatures(newFeatures);
+ }
+
+ /**
+ * Ensure that no ImsService is bound if there is no carrier or device package explicitly set.
*/
@Test
@SmallTest
public void testDontBindWhenNullCarrierPackage() throws RemoteException {
setupResolver(1/*numSlots*/);
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- });
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
// Set the CarrierConfig string to null so that ImsResolver will not bind to the available
// Services
setConfigCarrierString(0, null);
- mTestImsResolver.populateCacheAndStartBind();
+ startBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ verify(mMockQueryManager, never()).startQuery(any(), any());
verify(controller, never()).bind(any());
verify(controller, never()).unbind();
}
@@ -198,28 +282,25 @@
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- });
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
+ ImsServiceController controller = setupController();
- mTestImsResolver.populateCacheAndStartBind();
-
+ startBind();
+ // Wait to make sure that there are no dynamic queries that are being completed.
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
- HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
verify(controller).bind(featureSet);
verify(controller, never()).unbind();
+ verify(mMockQueryManager, never()).startQuery(any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
}
@@ -234,37 +315,33 @@
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ // Only return info if not using the compat argument
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
- deviceFeatureSet.removeAll(carrierFeatureSet);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 0);
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -279,36 +356,24 @@
public void testGetDeviceCarrierFeatures() throws RemoteException {
setupResolver(2/*numSlots*/);
ImsServiceController deviceController = mock(ImsServiceController.class);
- IImsServiceController iDeviceController = mock(IImsServiceController.class);
- when(deviceController.getImsServiceController()).thenReturn(iDeviceController);
ImsServiceController carrierController = mock(ImsServiceController.class);
- IImsServiceController iCarrierController = mock(IImsServiceController.class);
- when(carrierController.getImsServiceController()).thenReturn(iCarrierController);
- mTestImsResolver.populateCacheAndStartBind();
// Callback from mock ImsServiceControllers
// All features on slot 1 should be the device default
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.MMTEL, deviceController);
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.RCS, deviceController);
- // The carrier override does not support emergency voice
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
- // The carrier override contains these features
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.MMTEL, carrierController);
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.RCS, carrierController);
+ mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_MMTEL, deviceController);
+ mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_RCS, deviceController);
+ mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController);
+ // The carrier override contains this feature
+ mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController);
// Get the IImsServiceControllers for each feature on each slot and verify they are correct.
- assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1/*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
- assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1 /*Slot id*/, ImsFeature.MMTEL, null));
- assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1 /*Slot id*/, ImsFeature.RCS, null));
- assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1 /*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
- assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
- 0 /*Slot id*/, ImsFeature.MMTEL, null));
- assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
- 0 /*Slot id*/, ImsFeature.RCS, null));
+ assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
+ 1 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
+ assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
+ 1 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
+ assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
+ 0 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
+ assertEquals(carrierController, mTestImsResolver.getImsServiceControllerAndListen(
+ 0 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
}
/**
@@ -321,21 +386,15 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Doesn't include RCS feature by default
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- });
-
+ setupPackageQuery(info);
+ ImsServiceController controller = setupController();
// Bind using default features
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+ startBind();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
+ convertToHashSet(features, 0);
featureSet.addAll(convertToHashSet(features, 1));
verify(controller).bind(featureSet);
@@ -345,16 +404,11 @@
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newFeatures, true));
- // Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_DEVICE_DEFAULT_NAME.getPackageName());
//Verify new feature is added to the device default.
- HashSet<Pair<Integer, Integer>> newFeatureSet = convertToHashSet(newFeatures, 0);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatureSet =
+ convertToHashSet(newFeatures, 0);
newFeatureSet.addAll(convertToHashSet(newFeatures, 1));
verify(controller).changeImsServiceFeatures(newFeatureSet);
}
@@ -369,63 +423,59 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // we will first have bound to device and then the features will change once the dynamic
+ // returns. So, instead of checking the bind parameters, we will check the change parameters
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// add RCS to features list
Set<String> newDeviceFeatures = new HashSet<>();
- newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newDeviceFeatures, true));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_DEVICE_DEFAULT_NAME.getPackageName());
//Verify new feature is added to the device default.
// add all features for slot 1
- HashSet<Pair<Integer, Integer>> newDeviceFeatureSet =
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newDeviceFeatureSet =
convertToHashSet(newDeviceFeatures, 1);
- // remove carrier overrides for slot 0
- newDeviceFeatures.removeAll(carrierFeatures);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+ // remove carrier overrides for slot 0
+ newDeviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
verify(carrierController, never()).changeImsServiceFeatures(any());
}
@@ -440,61 +490,51 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // device ImsService will bind with all of its defined features first and then when the
+ // carrier query comes back, it will change. So, checking change instead of bind here.
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// add RCS to carrier features list
- Set<String> newCarrierFeatures = new HashSet<>();
- newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- newCarrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.clear();
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
//Verify new feature is added to the carrier override.
// add all features for slot 0
- HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
- convertToHashSet(newCarrierFeatures, 0);
- verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
- deviceFeatureSet.removeAll(newCarrierFeatureSet);
+ verify(carrierController).changeImsServiceFeatures(carrierFeatures);
+ deviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -509,68 +549,56 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // we will first have bound to device and then the features will change once the dynamic
+ // returns. So, instead of checking the bind parameters, we will check the change parameters
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
- // remove RCS from carrier features list
- Set<String> newCarrierFeatures = new HashSet<>();
- newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- info.clear();
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
-
+ // change supported feature to MMTEL only
+ carrierFeatures.clear();
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
//Verify new feature is added to the carrier override.
- // add all features for slot 0
- HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
- convertToHashSet(newCarrierFeatures, 0);
- verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
+ verify(carrierController).changeImsServiceFeatures(carrierFeatures);
Set<String> newDeviceFeatures = new HashSet<>();
- newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- HashSet<Pair<Integer, Integer>> newDeviceFeatureSet = convertToHashSet(newDeviceFeatures,
- 1);
- newDeviceFeatures.removeAll(newCarrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newDeviceFeatureSet =
+ convertToHashSet(newDeviceFeatures, 1);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+ newDeviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
}
@@ -583,44 +611,35 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
- Set<String> carrierFeatures = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Tell the package manager that a new carrier app is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
// device features change
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- deviceFeatureSet.removeAll(carrierFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -634,43 +653,36 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Tell the package manager that carrier app is uninstalled
- Intent removePackageIntent = new Intent();
- removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
- removePackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageRemoved(TEST_CARRIER_DEFAULT_NAME.getPackageName());
// Verify that the carrier controller is unbound
verify(carrierController).unbind();
assertNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change to include all supported functionality
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -685,29 +697,27 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
setConfigCarrierString(0, null);
Intent carrierConfigIntent = new Intent();
- carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+ carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
@@ -716,7 +726,8 @@
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -731,50 +742,53 @@
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
- deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures1 = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures1.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures1.add(ImsResolver.METADATA_RCS_FEATURE);
- Set<String> carrierFeatures2 = new HashSet<>();
- // Carrier service doesn't support the emergency voice feature.
- carrierFeatures2.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, true));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures1 = new HashSet<>();
+ // Carrier service 1
+ carrierFeatures1.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures1.add(
+ new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures2 = new HashSet<>();
+ // Carrier service 2 doesn't support the voice feature.
+ carrierFeatures2.add(
+ new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, new HashSet<>(), true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController1 = mock(ImsServiceController.class);
ImsServiceController carrierController2 = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController1, carrierController2);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, 1);
setConfigCarrierString(0, TEST_CARRIER_2_DEFAULT_NAME.getPackageName());
Intent carrierConfigIntent = new Intent();
- carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+ carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, 1);
// Verify that carrier 1 is unbound
verify(carrierController1).unbind();
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// Verify that carrier 2 is bound
- HashSet<Pair<Integer, Integer>> carrier2FeatureSet = convertToHashSet(carrierFeatures2, 0);
- verify(carrierController2).bind(carrier2FeatureSet);
+ verify(carrierController2).bind(carrierFeatures2);
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
- TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+ TEST_CARRIER_2_DEFAULT_NAME.getPackageName()));
// device features change to accommodate for the features carrier 2 lacks
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures2);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+ deviceFeatureSet.removeAll(carrierFeatures2);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -792,7 +806,7 @@
}
mTestImsResolver = new ImsResolver(mMockContext, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
- numSlots);
+ numSlots, true);
ArgumentCaptor<BroadcastReceiver> packageBroadcastCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -804,40 +818,142 @@
mTestCarrierConfigReceiver = carrierConfigCaptor.getValue();
mTestPackageBroadcastReceiver = packageBroadcastCaptor.getValue();
mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
+ when(mMockQueryManagerFactory.create(any(Context.class),
+ any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
+ mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory);
+ }
+
+ private void setupPackageQuery(List<ResolveInfo> infos) {
+ // Only return info if not using the compat argument
+ when(mMockPM.queryIntentServicesAsUser(
+ argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+ anyInt(), anyInt())).thenReturn(infos);
+ }
+
+ private void setupPackageQuery(ComponentName name, Set<String> features,
+ boolean isPermissionGranted) {
+ List<ResolveInfo> info = new ArrayList<>();
+ info.add(getResolveInfo(name, features, isPermissionGranted));
+ // Only return info if not using the compat argument
+ when(mMockPM.queryIntentServicesAsUser(
+ argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+ anyInt(), anyInt())).thenReturn(info);
+ }
+
+ private ImsServiceController setupController() {
+ ImsServiceController controller = mock(ImsServiceController.class);
+ mTestImsResolver.setImsServiceControllerFactory(
+ new ImsResolver.ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ when(controller.getComponentName()).thenReturn(componentName);
+ return controller;
+ }
+ });
+ return controller;
+ }
+
+ private void startBind() {
+ mTestImsResolver.initPopulateCacheAndStartBind();
+ ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
+ ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
+ verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
+ mDynamicQueryListener = queryManagerCaptor.getValue();
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ private void setupDynamicQueryFeatures(ComponentName name,
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
+ // wait for schedule to happen
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ // ensure that startQuery was called
+ when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
+ .thenReturn(true);
+ verify(mMockQueryManager, Mockito.times(times)).startQuery(eq(name), any(String.class));
+ mDynamicQueryListener.onComplete(name, features);
+ // wait for handling of onComplete
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ public void packageChanged(String packageName) {
+ // Tell the package manager that a new device feature is installed
+ Intent addPackageIntent = new Intent();
+ addPackageIntent.setAction(Intent.ACTION_PACKAGE_CHANGED);
+ addPackageIntent.setData(new Uri.Builder().scheme("package").opaquePart(packageName)
+ .build());
+ mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ public void packageRemoved(String packageName) {
+ Intent removePackageIntent = new Intent();
+ removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
+ removePackageIntent.setData(new Uri.Builder().scheme("package")
+ .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+ mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
}
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController) {
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
- when(deviceController.getComponentName()).thenReturn(componentName);
- return deviceController;
- } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
- componentName.getPackageName())) {
- when(carrierController.getComponentName()).thenReturn(componentName);
- return carrierController;
- }
- return null;
- });
+ mTestImsResolver.setImsServiceControllerFactory(
+ new ImsResolver.ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
+ componentName.getPackageName())) {
+ when(deviceController.getComponentName()).thenReturn(componentName);
+ return deviceController;
+ } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+ componentName.getPackageName())) {
+ when(carrierController.getComponentName()).thenReturn(componentName);
+ return carrierController;
+ }
+ return null;
+ }
+ });
}
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController1, ImsServiceController carrierController2) {
- mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
- if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
- when(deviceController.getComponentName()).thenReturn(componentName);
- return deviceController;
- } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
- componentName.getPackageName())) {
- when(carrierController1.getComponentName()).thenReturn(componentName);
- return carrierController1;
- } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
- componentName.getPackageName())) {
- when(carrierController2.getComponentName()).thenReturn(componentName);
- return carrierController2;
- }
- return null;
- });
+ mTestImsResolver.setImsServiceControllerFactory(
+ new ImsResolver.ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
+ componentName.getPackageName())) {
+ when(deviceController.getComponentName()).thenReturn(componentName);
+ return deviceController;
+ } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+ componentName.getPackageName())) {
+ when(carrierController1.getComponentName()).thenReturn(componentName);
+ return carrierController1;
+ } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
+ componentName.getPackageName())) {
+ when(carrierController2.getComponentName()).thenReturn(componentName);
+ return carrierController2;
+ }
+ return null;
+ }
+ });
}
@@ -846,25 +962,28 @@
CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, packageName);
}
- private HashSet<Pair<Integer, Integer>> convertToHashSet(Set<String> features, int subId) {
- HashSet<Pair<Integer, Integer>> featureSet = features.stream()
- .map(f -> new Pair<>(subId, metadataStringToFeature(f)))
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> convertToHashSet(
+ Set<String> features, int slotId) {
+ return features.stream()
+ // We do not count this as a valid feature set member.
+ .filter(f -> !ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE.equals(f))
+ .map(f -> new ImsFeatureConfiguration.FeatureSlotPair(slotId,
+ metadataStringToFeature(f)))
.collect(Collectors.toCollection(HashSet::new));
- return featureSet;
}
private int metadataStringToFeature(String f) {
switch (f) {
- case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
- return ImsFeature.EMERGENCY_MMTEL;
case ImsResolver.METADATA_MMTEL_FEATURE:
- return ImsFeature.MMTEL;
+ return ImsFeature.FEATURE_MMTEL;
case ImsResolver.METADATA_RCS_FEATURE:
- return ImsFeature.RCS;
+ return ImsFeature.FEATURE_RCS;
}
return -1;
}
+ // Make sure the metadata provided in the service definition creates the associated features in
+ // the ImsServiceInfo. Note: this only tests for one slot.
private boolean isImsServiceInfoEqual(ComponentName name, Set<String> features,
ImsResolver.ImsServiceInfo sInfo) {
if (!Objects.equals(sInfo.name, name)) {
@@ -873,17 +992,23 @@
for (String f : features) {
switch (f) {
case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.EMERGENCY_MMTEL)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL))) {
return false;
}
break;
case ImsResolver.METADATA_MMTEL_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.MMTEL)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL))) {
return false;
}
break;
case ImsResolver.METADATA_RCS_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.RCS)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS))) {
return false;
}
break;
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 7b831a0..af2f81b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -38,9 +38,10 @@
import android.os.RemoteException;
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.Pair;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
-import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import org.junit.After;
import org.junit.Before;
@@ -60,25 +61,35 @@
@Ignore
public class ImsServiceControllerTest extends ImsTestBase {
- private static final int RETRY_TIMEOUT = 50; // ms
+ private static final ImsServiceController.RebindRetry REBIND_RETRY =
+ new ImsServiceController.RebindRetry() {
+ @Override
+ public long getStartDelay() {
+ return 50;
+ }
+
+ @Override
+ public long getMaximumDelay() {
+ return 1000;
+ }
+ };
@Spy TestImsServiceControllerAdapter mMockServiceControllerBinder;
- @Mock IBinder mMockBinder;
@Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
- @Mock IImsServiceFeatureListener mMockProxyCallbacks;
+ @Mock IImsServiceFeatureCallback mMockProxyCallbacks;
@Mock Context mMockContext;
private final ComponentName mTestComponentName = new ComponentName("TestPkg",
"ImsServiceControllerTest");
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private ImsServiceController mTestImsServiceController;
- private final Handler mTestHandler = new Handler(Looper.getMainLooper());
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
- mMockCallbacks, mTestHandler);
- mTestImsServiceController.addImsServiceFeatureListener(mMockProxyCallbacks);
+ mMockCallbacks, mHandler, REBIND_RETRY);
+ mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
}
@@ -86,7 +97,6 @@
@After
@Override
public void tearDown() throws Exception {
- mTestHandler.removeCallbacksAndMessages(null);
mTestImsServiceController = null;
super.tearDown();
}
@@ -97,9 +107,11 @@
@FlakyTest
@Test
public void testBindService() {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ArgumentCaptor<Intent> intentCaptor =
ArgumentCaptor.forClass(Intent.class);
@@ -109,7 +121,7 @@
| Context.BIND_IMPORTANT;
verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
Intent testIntent = intentCaptor.getValue();
- assertEquals(ImsResolver.SERVICE_INTERFACE, testIntent.getAction());
+ assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction());
assertEquals(mTestComponentName, testIntent.getComponent());
}
@@ -119,8 +131,9 @@
@FlakyTest
@Test
public void testBindFailureWhenBound() {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
// already bound, should return false
@@ -136,16 +149,18 @@
@FlakyTest
@Test
public void testBindServiceAndConnected() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
verify(binder).linkToDeath(any(), anyInt());
- verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
- verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(2));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
+ verify(mMockServiceControllerBinder).createRcsFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(2),
@@ -157,15 +172,73 @@
}
/**
+ * Tests Emergency MMTEL ImsServiceController callbacks are properly called when an ImsService
+ * is bound and connected.
+ */
+ @FlakyTest
+ @Test
+ public void testBindEmergencyMmTel() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, Emergency MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
+ // Slot 1, MmTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+
+ bindAndConnectService(testFeatures);
+
+ IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+ verify(binder).linkToDeath(any(), anyInt());
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
+ // We do not want this callback to happen for emergency MMTEL
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(0),
+ eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+ eq(mTestImsServiceController));
+ // Make sure this callback happens, which will notify the framework of emergency calling
+ // availability.
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+ assertEquals(mMockServiceControllerBinder.getBinder(),
+ mTestImsServiceController.getImsServiceControllerBinder());
+ }
+
+ /**
+ * Tests that if a callback is added after the ImsServiceController is already bound, we get a
+ * imsFeatureCreated callback.
+ */
+ @FlakyTest
+ @Test
+ public void testCallbacksHappenWhenAddedAfterBind() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, Emergency MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
+ // Slot 1, MmTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ mTestImsServiceController.removeImsServiceFeatureCallbacks();
+
+ bindAndConnectService(testFeatures);
+ // add the callback after bind
+ mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
+
+ // Make sure this callback happens for Emergency MMTEL and MMTEL
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+ assertEquals(mMockServiceControllerBinder.getBinder(),
+ mTestImsServiceController.getImsServiceControllerBinder());
+ }
+
+ /**
* Tests ImsServiceController callbacks are properly called when an ImsService is bound and
* connected.
*/
@FlakyTest
@Test
public void testBindServiceAndConnectedDisconnected() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ServiceConnection conn = bindAndConnectService(testFeatures);
conn.onServiceDisconnected(mTestComponentName);
@@ -189,9 +262,11 @@
@FlakyTest
@Test
public void testBindServiceBindUnbind() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ServiceConnection conn = bindAndConnectService(testFeatures);
mTestImsServiceController.unbind();
@@ -215,9 +290,11 @@
@FlakyTest
@Test
public void testBindServiceAndBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
@@ -240,20 +317,22 @@
@FlakyTest
@Test
public void testBindServiceAndAddFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
// Create a new list with an additional item
- HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
- testFeaturesWithAddition.add(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+ testFeatures);
+ testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
- verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
@@ -265,21 +344,24 @@
@FlakyTest
@Test
public void testBindServiceAndRemoveFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 2, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
- verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
// Create a new list with one less item
- HashSet<Pair<Integer, Integer>> testFeaturesWithSubtraction = new HashSet<>(testFeatures);
- testFeaturesWithSubtraction.remove(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithSubtraction =
+ new HashSet<>(testFeatures);
+ testFeaturesWithSubtraction.remove(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
@@ -295,15 +377,17 @@
@FlakyTest
@Test
public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // slot 2, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
- verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(2));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
@@ -327,17 +411,20 @@
@FlakyTest
@Test
public void testBindUnbindServiceAndAddFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
mTestImsServiceController.unbind();
// Create a new list with an additional item
- HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
- testFeaturesWithAddition.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+ testFeatures);
+ // Try to create an RCS feature
+ testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
- verify(mMockServiceControllerBinder, never()).createImsFeature(eq(1), eq(2));
+ verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(1));
verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(2),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(1), eq(2));
@@ -350,17 +437,18 @@
@FlakyTest
@Test
public void testAutoBindAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
- mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
getDeathRecipient().binderDied();
- waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
- 2 * RETRY_TIMEOUT);
- // The service should autobind after RETRY_TIMEOUT occurs
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
+ // The service should autobind after rebind event occurs
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
}
@@ -370,11 +458,12 @@
@FlakyTest
@Test
public void testNoAutoBindBeforeTimeout() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
- mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
getDeathRecipient().binderDied();
@@ -388,17 +477,19 @@
@FlakyTest
@Test
public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
- mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
getDeathRecipient().binderDied();
mTestImsServiceController.unbind();
- waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
- 2 * RETRY_TIMEOUT);
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
+
// Unbind should stop the autobind from occurring.
verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
}
@@ -410,21 +501,23 @@
@FlakyTest
@Test
public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
- testFeatures.add(new Pair<>(1, 1));
- testFeatures.add(new Pair<>(1, 2));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ // Slot 1, RCS
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
- mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
getDeathRecipient().binderDied();
mTestImsServiceController.bind(testFeatures);
- waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
- 2 * RETRY_TIMEOUT);
+ 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());
}
- private ServiceConnection bindAndConnectService(HashSet<Pair<Integer, Integer>> testFeatures) {
+ private ServiceConnection bindAndConnectService(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
ArgumentCaptor<ServiceConnection> serviceCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
assertTrue(mTestImsServiceController.bind(testFeatures));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
index 0e22be1..38b3203 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
@@ -16,21 +16,18 @@
package com.android.internal.telephony.ims;
-import android.app.PendingIntent;
-import android.os.Message;
import android.os.RemoteException;
-import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsUt;
import static org.mockito.Mockito.spy;
@@ -45,115 +42,53 @@
public class ImsServiceControllerBinder extends IImsServiceController.Stub {
@Override
- public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+ public void setListener(IImsServiceControllerListener l) {
+ }
+
+ @Override
+ public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return TestImsServiceControllerAdapter.this.createMMTelFeature(slotId);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ return TestImsServiceControllerAdapter.this.createRcsFeature(slotId);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
throws RemoteException {
- TestImsServiceControllerAdapter.this.createImsFeature(slotId, feature);
- mStatusCallback = c;
+ TestImsServiceControllerAdapter.this.removeImsFeature(slotId, featureType);
}
@Override
- public void removeImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
- throws RemoteException {
- TestImsServiceControllerAdapter.this.removeImsFeature(slotId, feature);
- }
-
- @Override
- public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
- IImsRegistrationListener listener) throws RemoteException {
- return 0;
- }
-
- @Override
- public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
-
- }
-
- @Override
- public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
- throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isOpened(int slotId, int featureType) throws RemoteException {
- return false;
- }
-
- @Override
- public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
- return ImsFeature.STATE_NOT_AVAILABLE;
- }
-
- @Override
- public void addRegistrationListener(int slotId, int featureType,
- IImsRegistrationListener listener) throws RemoteException {
-
- }
-
- @Override
- public void removeRegistrationListener(int slotId, int featureType,
- IImsRegistrationListener listener) throws RemoteException {
-
- }
-
- @Override
- public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
- int callSessionType, int callType) throws RemoteException {
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
return null;
}
@Override
- public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
- ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
- return null;
+ public void notifyImsServiceReadyForFeatureCreation() {
}
@Override
- public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
- String callId) throws RemoteException {
- return null;
+ public IImsConfig getConfig(int slotId) throws RemoteException {
+ return new ImsConfigImplBase().getIImsConfig();
}
@Override
- public IImsUt getUtInterface(int slotId, int featureType)
- throws RemoteException {
- return null;
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ return new ImsRegistrationImplBase().getBinder();
}
@Override
- public IImsConfig getConfigInterface(int slotId, int featureType)
- throws RemoteException {
- return null;
+ public void enableIms(int slotId) {
}
@Override
- public void turnOnIms(int slotId, int featureType)
- throws RemoteException {
+ public void disableIms(int slotId) {
}
- @Override
- public void turnOffIms(int slotId, int featureType) throws RemoteException {
-
- }
-
- @Override
- public IImsEcbm getEcbmInterface(int slotId, int featureType)
- throws RemoteException {
- return null;
- }
-
- @Override
- public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
- throws RemoteException {
-
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
- throws RemoteException {
- return null;
- }
}
private ImsServiceControllerBinder mBinder;
@@ -167,7 +102,13 @@
}
// Used by Mockito for verification that this method is being called in spy
- public void createImsFeature(int subId, int feature) throws RemoteException {
+ public IImsMmTelFeature createMMTelFeature(int slotId) {
+ return null;
+ }
+
+ // Used by Mockito for verification that this method is being called in spy
+ public IImsRcsFeature createRcsFeature(int slotId) {
+ return null;
}
// Used by Mockito for verification that this method is being called in spy
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
index 120874b..2a2e38f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
@@ -17,14 +17,12 @@
package com.android.internal.telephony.imsphone;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.telephony.ServiceState;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
-import com.android.internal.telephony.Call;
+import android.telephony.ims.ImsCallProfile;
+
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
index b01ae4a..80be96c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
@@ -16,40 +16,32 @@
package com.android.internal.telephony.imsphone;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsExternalCallState;
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.Connection;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.verify;
import android.net.Uri;
import android.support.test.filters.FlakyTest;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsExternalCallState;
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Unit tests for the {@link ImsExternalCallTracker}.
*/
+@Ignore
public class ImsExternalCallTrackerTest {
private static final Uri TEST_ADDRESS = Uri.parse("tel:6505551212");
private static final int CALL_ID = 1;
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 4fc9411..c602108 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -16,18 +16,6 @@
package com.android.internal.telephony.imsphone;
-import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.ims.ImsStreamMediaProfile;
-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.mockito.Mock;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
@@ -36,12 +24,25 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.support.test.filters.FlakyTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.telephony.ims.ImsStreamMediaProfile;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+
public class ImsPhoneCallTest extends TelephonyTest {
@Mock
ImsPhoneConnection mConnection1;
@Mock
ImsPhoneConnection mConnection2;
- @Mock
+
ImsStreamMediaProfile mMediaProfile;
private ImsPhoneCall mImsCallUT;
@@ -52,6 +53,7 @@
replaceInstance(ImsPhoneCallTracker.class, "mPhone", mImsCT, mImsPhone);
mImsCallUT = new ImsPhoneCall(mImsCT, ImsPhoneCall.CONTEXT_FOREGROUND);
+ mMediaProfile = new ImsStreamMediaProfile();
}
@After
@@ -61,6 +63,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testAttachDetach() {
@@ -84,6 +87,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testConnectionDisconnected() {
@@ -102,6 +106,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testHangup() {
@@ -114,6 +119,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testUpdateRingBackTone() {
@@ -146,6 +152,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testMultiParty() {
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 be8d6f6..8914aee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -25,39 +25,41 @@
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.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isNull;
+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.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.test.filters.FlakyTest;
-import android.telephony.DisconnectCause;
import android.telecom.VideoProfile;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
import com.android.ims.ImsConfig;
-import com.android.ims.ImsConnectionStateListener;
import com.android.ims.ImsException;
-import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsServiceClass;
-import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsCallSession;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
@@ -78,7 +80,9 @@
public class ImsPhoneCallTrackerTest extends TelephonyTest {
private ImsPhoneCallTracker mCTUT;
private ImsCTHandlerThread mImsCTHandlerThread;
- private ImsConnectionStateListener mImsConnectionStateListener;
+ private MmTelFeature.Listener mMmTelListener;
+ private ImsRegistrationImplBase.Callback mRegistrationCallback;
+ private ImsFeature.CapabilityCallback mCapabilityCallback;
private ImsCall.Listener mImsCallListener;
private ImsCall mImsCall;
private ImsCall mSecondImsCall;
@@ -88,6 +92,10 @@
private ImsCallSession mImsCallSession;
@Mock
private SharedPreferences mSharedPreferences;
+ @Mock
+ private ImsPhoneConnection.Listener mImsPhoneConnectionListener;
+ @Mock
+ private ImsConfig mImsConfig;
private Handler mCTHander;
private class ImsCTHandlerThread extends HandlerThread {
@@ -169,20 +177,14 @@
mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
imsCallMocking(mImsCall);
imsCallMocking(mSecondImsCall);
- doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus();
- doReturn(mImsCallProfile).when(mImsManager).createCallProfile(eq(mServiceId),
- anyInt(), anyInt());
+ doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState();
+ doReturn(mImsCallProfile).when(mImsManager).createCallProfile(anyInt(), anyInt());
- //cache the listener
- doAnswer(new Answer<Integer>() {
- @Override
- public Integer answer(InvocationOnMock invocation) throws Throwable {
- mImsConnectionStateListener =
- (ImsConnectionStateListener) invocation.getArguments()[2];
- return mServiceId;
- }
- }).when(mImsManager).open(anyInt(), (PendingIntent) any(),
- (ImsConnectionStateListener) any());
+ doAnswer(invocation -> {
+ mMmTelListener = (MmTelFeature.Listener) invocation.getArguments()[0];
+ return null;
+ }).when(mImsManager).open(any(MmTelFeature.Listener.class));
+
doAnswer(new Answer<ImsCall>() {
@Override
@@ -191,18 +193,34 @@
(ImsCall.Listener) invocation.getArguments()[2];
return mImsCall;
}
- }).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any());
+ }).when(mImsManager).takeCall(any(), any(), any());
doAnswer(new Answer<ImsCall>() {
@Override
public ImsCall answer(InvocationOnMock invocation) throws Throwable {
- mImsCallListener =
- (ImsCall.Listener) invocation.getArguments()[3];
+ mImsCallListener = (ImsCall.Listener) invocation.getArguments()[2];
+ mSecondImsCall.setListener(mImsCallListener);
+
return mSecondImsCall;
}
- }).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(),
+ }).when(mImsManager).makeCall(eq(mImsCallProfile), (String []) any(),
(ImsCall.Listener) any());
+ doAnswer(invocation -> {
+ mRegistrationCallback = invocation.getArgument(0);
+ return mRegistrationCallback;
+ }).when(mImsManager).addRegistrationCallback(any(ImsRegistrationImplBase.Callback.class));
+
+ doAnswer(invocation -> {
+ mCapabilityCallback = (ImsFeature.CapabilityCallback) invocation.getArguments()[0];
+ return mCapabilityCallback;
+
+ }).when(mImsManager).addCapabilitiesCallback(any(ImsFeature.CapabilityCallback.class));
+
+ doReturn(mImsConfig).when(mImsManager).getConfigInterface();
+
+ doNothing().when(mImsManager).addNotifyStatusChangedCallbackIfAvailable(any());
+
mImsCTHandlerThread = new ImsCTHandlerThread(this.getClass().getSimpleName());
mImsCTHandlerThread.start();
@@ -221,29 +239,95 @@
@Test
@SmallTest
+ public void testImsRegistered() {
+ // when IMS is registered
+ mRegistrationCallback.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ // then service state should be IN_SERVICE and ImsPhone state set to registered
+ verify(mImsPhone).setServiceState(eq(ServiceState.STATE_IN_SERVICE));
+ verify(mImsPhone).setImsRegistered(eq(true));
+ }
+
+ @Test
+ @SmallTest
+ public void testImsRegistering() {
+ // when IMS is registering
+ mRegistrationCallback.onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ // then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered
+ verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE));
+ verify(mImsPhone).setImsRegistered(eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testImsDeregistered() {
+ // when IMS is deregistered
+ mRegistrationCallback.onDeregistered(new ImsReasonInfo());
+ // then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered
+ verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE));
+ verify(mImsPhone).setImsRegistered(eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testVowifiDisabledOnLte() {
+ // LTE is registered.
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(
+ mImsManager).getRegistrationTech();
+ assertFalse(mCTUT.isVowifiEnabled());
+
+ // enable Voice over LTE
+ ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ mCapabilityCallback.onCapabilitiesStatusChanged(caps);
+ waitForHandlerAction(mCTHander, 1000);
+
+ // Voice over IWLAN is still disabled
+ assertFalse(mCTUT.isVowifiEnabled());
+ }
+
+ @Test
+ @SmallTest
+ public void testVowifiDisabledOnIwlan() {
+ // LTE is registered.
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when(
+ mImsManager).getRegistrationTech();
+ assertFalse(mCTUT.isVowifiEnabled());
+
+ // enable Voice over IWLAN
+ ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ mCapabilityCallback.onCapabilitiesStatusChanged(caps);
+ waitForHandlerAction(mCTHander, 1000);
+
+ // Voice over IWLAN is enabled
+ assertTrue(mCTUT.isVowifiEnabled());
+ }
+
+ @Test
+ @SmallTest
public void testImsFeatureCapabilityChange() {
- int[] featureEnableArray = {-1, -1, -1, -1, -1, -1},
- featureDisableArray = {-1, -1, -1, -1, -1, -1};
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(
+ mImsManager).getRegistrationTech();
assertFalse(mCTUT.isVolteEnabled());
assertFalse(mCTUT.isVideoCallEnabled());
- //enable VoLTE feature
- featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] =
- ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
- mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
- featureEnableArray,
- featureDisableArray);
+
+ // enable only Voice
+ ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+ caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ mCapabilityCallback.onCapabilitiesStatusChanged(caps);
waitForHandlerAction(mCTHander, 1000);
+
assertTrue(mCTUT.isVolteEnabled());
assertFalse(mCTUT.isVideoCallEnabled());
// video call not enabled
verify(mImsPhone, times(0)).notifyForVideoCapabilityChanged(anyBoolean());
verify(mImsPhone, times(1)).onFeatureCapabilityChanged();
+
// enable video call
- featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] =
- ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE;
- mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
- featureEnableArray,
- featureDisableArray);
+ ImsFeature.Capabilities capsVideo = new ImsFeature.Capabilities();
+ capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ mCapabilityCallback.onCapabilitiesStatusChanged(capsVideo);
waitForHandlerAction(mCTHander, 1000);
assertTrue(mCTUT.isVideoCallEnabled());
verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true));
@@ -255,8 +339,7 @@
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
assertFalse(mCTUT.mRingingCall.isRinging());
// mock a MT call
- Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
- mContext.sendBroadcast(mIntent);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(1)).notifyIncomingRing();
assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -325,14 +408,13 @@
assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
// mock a new MT
try {
- doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(0), (Intent) any(),
- (ImsCall.Listener) any());
+ doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
+ any(Bundle.class), any(ImsCall.Listener.class));
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
}
- Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
- mContext.sendBroadcast(mIntent);
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -380,7 +462,47 @@
assertEquals("clir_key0", mStringCaptor.getValue());
}
+ /**
+ * Ensures for an emergency call that the dial method will default the CLIR to
+ * {@link CommandsInterface#CLIR_SUPPRESSION}, ensuring the caller's ID is shown.
+ */
+ @Test
+ @SmallTest
+ public void testEmergencyDialSuppressClir() {
+ mCTUT.setSharedPreferenceProxy((Context context) -> {
+ return mSharedPreferences;
+ });
+ // Mock implementation of phone number utils treats everything as an emergency.
+ mCTUT.setPhoneNumberUtilsProxy((String string) -> {
+ return true;
+ });
+ // Set preference to hide caller ID.
+ ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
+ doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt(
+ stringCaptor.capture(), anyInt());
+
+ try {
+ mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null);
+
+ ArgumentCaptor<ImsCallProfile> profileCaptor = ArgumentCaptor.forClass(
+ ImsCallProfile.class);
+ verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
+ eq(new String[]{"+17005554141"}), any());
+
+ // Because this is an emergency call, we expect caller id to be visible now.
+ assertEquals(mImsCallProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR),
+ CommandsInterface.CLIR_SUPPRESSION);
+ } catch (CallStateException cse) {
+ cse.printStackTrace();
+ Assert.fail("unexpected exception thrown" + cse.getMessage());
+ } catch (ImsException ie) {
+ ie.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ie.getMessage());
+ }
+ }
+
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testImsMOCallDial() {
@@ -388,7 +510,7 @@
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
try {
mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
- verify(mImsManager, times(1)).makeCall(eq(0), eq(mImsCallProfile),
+ verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
} catch (Exception ex) {
ex.printStackTrace();
@@ -415,7 +537,7 @@
assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState());
try {
mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
- verify(mImsManager, times(1)).makeCall(eq(mServiceId), eq(mImsCallProfile),
+ verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
} catch (Exception ex) {
ex.printStackTrace();
@@ -451,10 +573,9 @@
verify(mImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.PAUSE), (Message) isNull());
// mock a new MT
try {
- doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(),
- (ImsCall.Listener) any());
- Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
- mContext.sendBroadcast(mIntent);
+ doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
+ any(Bundle.class), any(ImsCall.Listener.class));
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
} catch (Exception ex) {
ex.printStackTrace();
@@ -488,8 +609,8 @@
@SmallTest
public void testDialImsServiceUnavailable() throws ImsException {
doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
- mImsManager).createCallProfile(anyInt(), anyInt(), anyInt());
- mCTUT.mRetryTimeout = () -> 0; //ms
+ mImsManager).createCallProfile(anyInt(), anyInt());
+ mCTUT.setRetryTimeout(() -> 0);
assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -501,12 +622,12 @@
// wait for handler to process ImsService connection retry
waitForHandlerAction(mCTHander, 1000); // 1 second timeout
- verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class),
+ verify(mImsManager, never()).makeCall(nullable(ImsCallProfile.class),
eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class));
// Make sure that open is called in ImsPhoneCallTracker when it was first connected and
// again after retry.
- verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
- nullable(ImsConnectionStateListener.class));
+ verify(mImsManager, times(2)).open(
+ nullable(MmTelFeature.Listener.class));
}
@FlakyTest
@@ -518,7 +639,7 @@
mImsManager).setUiTTYMode(nullable(Context.class), anyInt(),
nullable(Message.class));
// Remove retry timeout delay
- mCTUT.mRetryTimeout = () -> 0; //ms
+ mCTUT.setRetryTimeout(() -> 0); //ms
mCTUT.setUiTTYMode(0, new Message());
@@ -526,8 +647,8 @@
waitForHandlerAction(mCTHander, 100);
// Make sure that open is called in ImsPhoneCallTracker to re-establish connection to
// ImsService
- verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
- nullable(ImsConnectionStateListener.class));
+ verify(mImsManager, times(2)).open(
+ nullable(MmTelFeature.Listener.class));
}
@Test
@@ -541,10 +662,96 @@
@Test
@SmallTest
+ public void testImsAlternateEmergencyDisconnect() {
+ assertEquals(DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL,
+ mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0),
+ Call.State.ACTIVE));
+ }
+
+ @Test
+ @SmallTest
public void testLowBatteryDisconnectDialing() {
assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING));
assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.DIALING));
}
+
+ /**
+ * Tests that no hold tone is played if the call is remotely held and the media direction is
+ * send/receive (i.e. there is an audio stream present).
+ */
+ @Test
+ @SmallTest
+ public void testNoRemoteHoldtone() {
+ //establish a MT call
+ testImsMTCallAccept();
+ ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection();
+ ImsCall call = connection.getImsCall();
+
+ // Set the media direction to send/receive.
+ ImsCallProfile callProfile = new ImsCallProfile();
+ callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ call.setCallProfile(callProfile);
+
+ try {
+ mCTUT.onCallHoldReceived(call);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+ verify(mImsPhone, never()).startOnHoldTone(nullable(Connection.class));
+ }
+
+ /**
+ * 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).
+ */
+ @Test
+ @SmallTest
+ public void testRemoteToneInactive() {
+ //establish a MT call
+ testImsMTCallAccept();
+ ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection();
+ ImsCall call = connection.getImsCall();
+
+ // Set the media direction to inactive to trigger a hold tone.
+ ImsCallProfile callProfile = new ImsCallProfile();
+ callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE;
+ call.setCallProfile(callProfile);
+
+ try {
+ mCTUT.onCallHoldReceived(call);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+ verify(mImsPhone, times(1)).startOnHoldTone(nullable(Connection.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoteHoldtone() {
+ // Set carrier config to always play remote hold tone.
+ mCTUT.setAlwaysPlayRemoteHoldTone(true);
+ //establish a MT call
+ testImsMTCallAccept();
+ ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection();
+ ImsCall call = connection.getImsCall();
+
+ // Set the media direction to send/receive; normally we don't play a hold tone but the
+ // carrier config option is set to ensure we will do it in this case.
+ ImsCallProfile callProfile = new ImsCallProfile();
+ callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ call.setCallProfile(callProfile);
+
+ try {
+ mCTUT.onCallHoldReceived(call);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+ verify(mImsPhone, times(1)).startOnHoldTone(nullable(Connection.class));
+ }
}
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 c089706..87d4467 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -23,10 +23,10 @@
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.ims.ImsCallProfile;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.GsmCdmaCall;
@@ -159,10 +159,10 @@
mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
assertEquals(PhoneConstants.PRESENTATION_UNKNOWN, mConnectionUT.getNumberPresentation());
assertEquals(PhoneConstants.PRESENTATION_UNKNOWN, mConnectionUT.getCnapNamePresentation());
- doReturn(ImsCallProfile.OIR_PRESENTATION_PAYPHONE).when(mImsCallProfile)
- .getCallExtraInt(eq(ImsCallProfile.EXTRA_CNAP));
- doReturn(ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED).when(mImsCallProfile)
- .getCallExtraInt(eq(ImsCallProfile.EXTRA_OIR));
+ mImsCallProfile.setCallExtraInt(ImsCallProfile.EXTRA_CNAP,
+ ImsCallProfile.OIR_PRESENTATION_PAYPHONE);
+ mImsCallProfile.setCallExtraInt(ImsCallProfile.EXTRA_OIR,
+ ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED);
mConnectionUT.updateAddressDisplay(mImsCall);
assertEquals(ImsCallProfile.OIRToPresentation(ImsCallProfile.OIR_PRESENTATION_PAYPHONE),
@@ -287,8 +287,7 @@
mConnectionUT = new ImsPhoneConnection(mImsPhone, testAddress[0], mImsCT,
mForeGroundCall, false);
mConnectionUT.setIsIncoming(true);
- doReturn(testAddress[1]).when(mImsCallProfile)
- .getCallExtra(eq(ImsCallProfile.EXTRA_OI));
+ mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, testAddress[1]);
mConnectionUT.updateAddressDisplay(mImsCall);
assertEquals(testAddress[2], mConnectionUT.getAddress());
}
@@ -306,8 +305,7 @@
mConnectionUT = new ImsPhoneConnection(mImsPhone, inputAddress, mImsCT, mForeGroundCall,
false);
mConnectionUT.setIsIncoming(false);
- doReturn(updateAddress).when(mImsCallProfile)
- .getCallExtra(eq(ImsCallProfile.EXTRA_OI));
+ mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, updateAddress);
mConnectionUT.updateAddressDisplay(mImsCall);
assertEquals(inputAddress, mConnectionUT.getAddress());
}
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 f2e69cc..aae876d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -52,10 +52,10 @@
import android.telephony.ServiceState;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.ImsEcbmStateListener;
import com.android.ims.ImsManager;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
import com.android.ims.ImsUtInterface;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CommandsInterface;
@@ -389,8 +389,9 @@
String dialString = "1234567890";
int videoState = 0;
- mImsPhoneUT.dial(dialString, videoState);
- verify(mImsCT).dial(dialString, videoState, null);
+ mImsPhoneUT.dial(dialString,
+ new ImsPhone.ImsDialArgs.Builder().setVideoState(videoState).build());
+ verify(mImsCT).dial(eq(dialString), any(ImsPhone.ImsDialArgs.class));
}
@Test
@@ -509,23 +510,26 @@
@SmallTest
public void testCellBarring() throws Exception {
Message msg = mTestHandler.obtainMessage();
- mImsPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAOC, msg);
+ mImsPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAOC, msg,
+ CommandsInterface.SERVICE_CLASS_NONE);
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mImsUtInterface).queryCallBarring(eq(ImsUtInterface.CB_BAOC),
- messageArgumentCaptor.capture());
+ messageArgumentCaptor.capture(), eq(CommandsInterface.SERVICE_CLASS_NONE));
assertEquals(msg, messageArgumentCaptor.getValue().obj);
- mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOIC, true, "abc", msg);
+ mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOIC, true, "abc", msg,
+ CommandsInterface.SERVICE_CLASS_NONE);
verify(mImsUtInterface).updateCallBarring(eq(ImsUtInterface.CB_BOIC),
eq(CommandsInterface.CF_ACTION_ENABLE), messageArgumentCaptor.capture(),
- (String[]) eq(null));
+ (String[]) eq(null), eq(CommandsInterface.SERVICE_CLASS_NONE));
assertEquals(msg, messageArgumentCaptor.getValue().obj);
- mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOICxH, false, "abc", msg);
+ mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOICxH, false, "abc", msg,
+ CommandsInterface.SERVICE_CLASS_NONE);
verify(mImsUtInterface).updateCallBarring(eq(ImsUtInterface.CB_BOIC_EXHC),
eq(CommandsInterface.CF_ACTION_DISABLE), messageArgumentCaptor.capture(),
- (String[])eq(null));
+ (String[])eq(null), eq(CommandsInterface.SERVICE_CLASS_NONE));
assertEquals(msg, messageArgumentCaptor.getValue().obj);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
index 7ace8cb..22d9f78 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
@@ -18,6 +18,7 @@
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
+import android.support.test.filters.FlakyTest;
import android.telecom.Connection;
import com.android.internal.telephony.TelephonyTest;
@@ -25,11 +26,14 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.ComparisonFailure;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class ImsRttTextHandlerTest extends TelephonyTest {
private static final int TEST_TIMEOUT = 1000;
@@ -119,31 +123,44 @@
* Test that the text handler sends after enough characters have been sent from in-call
* @throws Exception
*/
+ @FlakyTest
@Test
public void testSendAfterEnoughChars() throws Exception {
+ // Register a read notifier
+ CountDownLatch readNotifier = new CountDownLatch(1);
+ mRttTextHandler.setReadNotifier(readNotifier);
// Send four characters
mPipeToHandler.write("abcd");
mPipeToHandler.flush();
+ // Wait for the stream to consume the characters
+ readNotifier.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+ waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
// make sure at it hasn't been sent.
Assert.assertEquals("", mNetworkWriter.getContents());
+
+ // Send the second part
Thread.sleep(10);
+ // Register a read notifier
+ readNotifier = new CountDownLatch(1);
+ mRttTextHandler.setReadNotifier(readNotifier);
// Send four more characters
mPipeToHandler.write("efgh");
mPipeToHandler.flush();
// Wait for the stream to consume the characters
- int count = 0;
- while (mHandlerSideOfPipeToHandler.ready()) {
- Thread.sleep(10);
- count += 1;
- if (count >= 5) {
- break;
- }
- }
+ boolean res = readNotifier.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+ // Wait for the handler to write to the mock network writer
waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
// make sure that all characters were sent.
- Assert.assertEquals("abcdefgh", mNetworkWriter.getContents());
+ try {
+ Assert.assertEquals("abcdefgh", mNetworkWriter.getContents());
+ } catch (ComparisonFailure e) {
+ throw new ComparisonFailure(e.getMessage()
+ + ", network buffer=" + mRttTextHandler.getNetworkBufferText()
+ + ", res=" + res,
+ e.getExpected(), e.getActual());
+ }
}
/**
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 bd5785d..71d0794 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -35,21 +35,24 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
-import android.support.test.filters.FlakyTest;
+import android.hardware.radio.V1_0.SetupDataCallResult;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsCallSession;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.GsmCdmaConnection;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.dataconnection.DataCallResponse;
import com.android.internal.telephony.nano.TelephonyProto;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import com.android.internal.telephony.nano.TelephonyProto.RadioAccessTechnology;
@@ -76,9 +79,6 @@
private ImsCallSession mImsCallSession;
@Mock
- private ImsReasonInfo mImsReasonInfo;
-
- @Mock
private ServiceState mServiceState;
@Mock
@@ -88,15 +88,18 @@
private UUSInfo mUusInfo;
+ private ImsReasonInfo mImsReasonInfo;
+
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mMetrics = new TelephonyMetrics();
mUusInfo = new UUSInfo(1, 2, new byte[]{1, 2});
doReturn("123").when(mImsCallSession).getCallId();
- doReturn("extramessage").when(mImsReasonInfo).getExtraMessage();
- doReturn(123).when(mImsReasonInfo).getCode();
- doReturn(456).when(mImsReasonInfo).getExtraCode();
+ mImsReasonInfo = new ImsReasonInfo();
+ mImsReasonInfo.mExtraMessage = "extramessage";
+ mImsReasonInfo.mCode = 123;
+ mImsReasonInfo.mExtraCode = 456;
doReturn(ROAMING_TYPE_DOMESTIC).when(mServiceState).getVoiceRoamingType();
doReturn(ROAMING_TYPE_DOMESTIC).when(mServiceState).getDataRoamingType();
@@ -179,6 +182,46 @@
assertEquals("Test", log.events[0].modemRestart.reason);
}
+ // Test write Carrier Identification matching event
+ @Test
+ @SmallTest
+ public void testWriteCarrierIdMatchingEventWithInvalidMatchingScore() throws Exception {
+
+ mMetrics.writeCarrierIdMatchingEvent(mPhone.getPhoneId(), 1,
+ TelephonyManager.UNKNOWN_CARRIER_ID, "mccmncTest", "gid1Test");
+ TelephonyLog log = buildProto();
+
+ assertEquals(1, log.events.length);
+ assertEquals(0, log.callSessions.length);
+ assertEquals(0, log.smsSessions.length);
+
+ assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+ assertEquals(1, log.events[0].carrierIdMatching.cidTableVersion);
+ assertEquals(TelephonyEvent.Type.CARRIER_ID_MATCHING, log.events[0].type);
+ assertEquals("mccmncTest", log.events[0].carrierIdMatching.result.mccmnc);
+ assertTrue(log.events[0].carrierIdMatching.result.gid1.isEmpty());
+ }
+
+ // Test write Carrier Identification matching event
+ @Test
+ @SmallTest
+ public void testWriteCarrierIdMatchingEvent() throws Exception {
+
+ mMetrics.writeCarrierIdMatchingEvent(mPhone.getPhoneId(), 1, 1, "mccmncTest", "gid1Test");
+ TelephonyLog log = buildProto();
+
+ assertEquals(1, log.events.length);
+ assertEquals(0, log.callSessions.length);
+ assertEquals(0, log.smsSessions.length);
+
+ assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+ assertEquals(TelephonyEvent.Type.CARRIER_ID_MATCHING, log.events[0].type);
+ assertEquals(1, log.events[0].carrierIdMatching.cidTableVersion);
+ assertEquals(1, log.events[0].carrierIdMatching.result.carrierId);
+ assertEquals("mccmncTest", log.events[0].carrierIdMatching.result.mccmnc);
+ assertEquals("gid1Test", log.events[0].carrierIdMatching.result.gid1);
+ }
+
// Test write on IMS call start
@Test
@SmallTest
@@ -248,9 +291,11 @@
public void testWriteImsSetFeatureValue() throws Exception {
mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
- ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 1);
mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
- ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 1);
mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
TelephonyLog log = buildProto();
@@ -347,11 +392,21 @@
@Test
@SmallTest
public void testWriteOnSetupDataCallResponse() throws Exception {
- DataCallResponse response = new DataCallResponse(5, 6, 7, 8, "IPV4V6", FAKE_IFNAME,
- FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+ SetupDataCallResult result = new SetupDataCallResult();
+ result.status = 5;
+ result.suggestedRetryTime = 6;
+ result.cid = 7;
+ result.active = 8;
+ result.type = "IPV4V6";
+ result.ifname = FAKE_IFNAME;
+ result.addresses = FAKE_ADDRESS;
+ result.dnses = FAKE_DNS;
+ result.gateways = FAKE_GATEWAY;
+ result.pcscf = FAKE_PCSCF_ADDRESS;
+ result.mtu = 1440;
mMetrics.writeOnRilSolicitedResponse(mPhone.getPhoneId(), 1, 2,
- RIL_REQUEST_SETUP_DATA_CALL, response);
+ RIL_REQUEST_SETUP_DATA_CALL, result);
TelephonyLog log = buildProto();
assertEquals(1, log.events.length);
@@ -488,8 +543,8 @@
@Test
@SmallTest
public void testWriteRilSetupDataCall() throws Exception {
- mMetrics.writeRilSetupDataCall(
- mPhone.getPhoneId(), 1, 14, 3, "apn", 0, "IPV4V6");
+ mMetrics.writeSetupDataCall(
+ mPhone.getPhoneId(), 14, 3, "apn", "IPV4V6");
TelephonyLog log = buildProto();
@@ -611,13 +666,19 @@
@Test
@SmallTest
public void testWriteOnImsCapabilities() throws Exception {
- boolean[] caps1 = new boolean[]{true, false, true, false, true, false};
- mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps1);
- boolean[] caps2 = new boolean[]{true, false, true, false, true, false};
+ MmTelFeature.MmTelCapabilities caps1 = new MmTelFeature.MmTelCapabilities();
+ caps1.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+ caps1.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, caps1);
// The duplicate one should be filtered out.
- mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps2);
- boolean[] caps3 = new boolean[]{false, true, false, true, false, true};
- mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps3);
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, caps1);
+ MmTelFeature.MmTelCapabilities caps2 = new MmTelFeature.MmTelCapabilities();
+ caps2.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+ caps2.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(),
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, caps2);
TelephonyLog log = buildProto();
assertEquals(2, log.events.length);
@@ -627,21 +688,27 @@
TelephonyEvent event = log.events[0];
assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
- assertEquals(caps1[0], event.imsCapabilities.voiceOverLte);
- assertEquals(caps1[1], event.imsCapabilities.videoOverLte);
- assertEquals(caps1[2], event.imsCapabilities.voiceOverWifi);
- assertEquals(caps1[3], event.imsCapabilities.videoOverWifi);
- assertEquals(caps1[4], event.imsCapabilities.utOverLte);
- assertEquals(caps1[5], event.imsCapabilities.utOverWifi);
+ assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
+ event.imsCapabilities.voiceOverLte);
+ assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO),
+ event.imsCapabilities.videoOverLte);
+ assertEquals(caps1.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT),
+ event.imsCapabilities.utOverLte);
+ assertEquals(false, event.imsCapabilities.voiceOverWifi);
+ assertEquals(false, event.imsCapabilities.videoOverWifi);
+ assertEquals(false, event.imsCapabilities.utOverWifi);
event = log.events[1];
assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
- assertEquals(caps3[0], event.imsCapabilities.voiceOverLte);
- assertEquals(caps3[1], event.imsCapabilities.videoOverLte);
- assertEquals(caps3[2], event.imsCapabilities.voiceOverWifi);
- assertEquals(caps3[3], event.imsCapabilities.videoOverWifi);
- assertEquals(caps3[4], event.imsCapabilities.utOverLte);
- assertEquals(caps3[5], event.imsCapabilities.utOverWifi);
+ assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE),
+ event.imsCapabilities.voiceOverWifi);
+ assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO),
+ event.imsCapabilities.videoOverWifi);
+ assertEquals(caps2.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT),
+ event.imsCapabilities.utOverWifi);
+ assertEquals(false, event.imsCapabilities.voiceOverLte);
+ assertEquals(false, event.imsCapabilities.videoOverLte);
+ assertEquals(false, event.imsCapabilities.utOverLte);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
index 504b4e1..c02f68b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -41,7 +41,7 @@
throw new RuntimeException("Not Implemented");
}
@Override
- public void setDataEnabled(boolean enable) {
+ public void setUserDataEnabled(boolean enable) {
throw new RuntimeException("Not Implemented");
}
@Override
@@ -53,14 +53,6 @@
throw new RuntimeException("Not Implemented");
}
@Override
- public boolean isApnSupported(String name) {
- throw new RuntimeException("Not Implemented");
- }
- @Override
- public int getApnPriority(String name) {
- throw new RuntimeException("Not Implemented");
- }
- @Override
public LinkProperties getLinkProperties(String apnType) {
throw new RuntimeException("Not Implemented");
}
@@ -93,7 +85,11 @@
throw new RuntimeException("Not Implemented");
}
@Override
- public boolean getDataEnabled() {
+ public boolean isUserDataEnabled() {
+ throw new RuntimeException("Not Implemented");
+ }
+ @Override
+ public boolean isDataEnabled() {
throw new RuntimeException("Not Implemented");
}
@Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index 0d19f47..1b20833 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -1146,12 +1146,8 @@
throw new RuntimeException("not implemented");
}
- public Connection dial(String dialString, int videoState) throws CallStateException {
- throw new RuntimeException("not implemented");
- }
-
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
- throws CallStateException {
+ @Override
+ public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException {
throw new RuntimeException("not implemented");
}
@@ -1216,6 +1212,14 @@
throw new RuntimeException("not implemented");
}
+ public void getCallBarring(String facility, String password, Message onComplete,
+ int serviceClass) {
+ }
+
+ public void setCallBarring(String facility, boolean lockState, String password,
+ Message onComplete, int serviceClass) {
+ }
+
public void getOutgoingCallerIdDisplay(Message onComplete) {
throw new RuntimeException("not implemented");
}
@@ -1284,11 +1288,15 @@
throw new RuntimeException("not implemented");
}
- public boolean getDataEnabled() {
+ public boolean isUserDataEnabled() {
throw new RuntimeException("not implemented");
}
- public void setDataEnabled(boolean enable) {
+ public boolean isDataEnabled() {
+ throw new RuntimeException("not implemented");
+ }
+
+ public void setUserDataEnabled(boolean enable) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
index 60995a1..b14fc10 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -16,19 +16,17 @@
package com.android.internal.telephony.mocks;
+import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
import android.content.Context;
import android.content.Intent;
-import android.os.UserHandle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.ISub;
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -37,12 +35,13 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
// must extend SubscriptionController as some people use it directly within-process
public class SubscriptionControllerMock extends SubscriptionController {
final AtomicInteger mDefaultDataSubId = new AtomicInteger(INVALID_SUBSCRIPTION_ID);
+ final AtomicInteger mDefaultVoiceSubId = new AtomicInteger(INVALID_SUBSCRIPTION_ID);
final ITelephonyRegistry.Stub mTelephonyRegistry;
final int[][] mSlotIndexToSubId;
@@ -232,22 +231,25 @@
}
@Override
public void setDefaultVoiceSubId(int subId) {
- throw new RuntimeException("not implemented");
+ if (subId == DEFAULT_SUBSCRIPTION_ID) {
+ throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
+ }
+ mDefaultVoiceSubId.set(subId);
+ broadcastDefaultVoiceSubIdChanged(subId);
}
@Override
public int getDefaultVoiceSubId() {
- throw new RuntimeException("not implemented");
+ if (mDefaultVoiceSubId != null) {
+ return mDefaultVoiceSubId.get();
+ } else {
+ return INVALID_SUBSCRIPTION_ID;
+ }
}
@Override
public void clearDefaultsForInactiveSubIds() {
throw new RuntimeException("not implemented");
}
@Override
- public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotId, boolean needCheck,
- String callingPackage) {
- throw new RuntimeException("not implemented");
- }
- @Override
public void updatePhonesAvailability(Phone[] phones) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
index b0b604c..2c73efa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
@@ -23,12 +23,14 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.CellInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.VoLteServiceState;
-import com.android.internal.telephony.IPhoneStateListener;
+
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
import java.util.ArrayList;
@@ -276,6 +278,17 @@
}
@Override
+ public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
+ public void notifyPhysicalChannelConfigurationForSubscriber(int subId,
+ List<PhysicalChannelConfig> configs) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
public void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
int backgroundCallState) {
throw new RuntimeException("Not implemented");
@@ -317,4 +330,9 @@
int activationType, int state) {
throw new RuntimeException("Not implemented");
}
+
+ @Override
+ public void notifyUserMobileDataStateChangedForPhoneId(int phoneId, int subId, boolean state) {
+ throw new RuntimeException("Not implemented");
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/AnswerToResetTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/AnswerToResetTest.java
new file mode 100644
index 0000000..e136e96
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/AnswerToResetTest.java
@@ -0,0 +1,384 @@
+/*
+ * 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.uicc;
+
+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 android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+public class AnswerToResetTest {
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestNullString() {
+ AnswerToReset atr = AnswerToReset.parseAtr(null);
+ assertNull(atr);
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestOddLength() {
+ String str = "3B02145";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNull(atr);
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestTooShortLength() {
+ String str = "3B";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNull(atr);
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestNoInterfaceByteNoHistoricalByte() {
+ String str = "3B00";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3B);
+ assertEquals(atr.getFormatByte(), (byte) 0x00);
+ assertTrue(atr.getInterfaceBytes().isEmpty());
+ assertNull(atr.getHistoricalBytes());
+ assertNull(atr.getCheckByte());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestNoHistoricalByte() {
+ String str = "3F909580B1FE001F4297";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x90);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB1);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x1F);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect, atr.getInterfaceBytes());
+
+ assertNull(atr.getHistoricalBytes());
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestNoInterfaceByte() {
+ String str = "3F078031A073BE211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x07);
+ assertTrue(atr.getInterfaceBytes().isEmpty());
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect));
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestSuccess() {
+ String str = "3F979580B1FE001F428031A073BE211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB1);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x1F);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestSuccessWithoutCheckByte() {
+ String str = "3F979580B0FE0010428031A073BE2117";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xB0);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x00, null, (byte) 0x10);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+
+ assertEquals(atr.getCheckByte(), null);
+ assertFalse(atr.isEuiccSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestFailWithoutCheckByte() {
+ String str = "3F979581B0FE0010428031A073BE2117";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNull(atr);
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestFailWithExtraByte() {
+ String str = "3F979580B1FE001F428031A073BE21179718";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNull(atr);
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestEuiccSupported() {
+ String str = "3F979580BFFE8210428031A073BE211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+ assertTrue(atr.isEuiccSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestEuiccSupportedWithLowerCaseString() {
+ String str = "3f979580bffe8210428031a073be211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+ assertTrue(atr.isEuiccSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestEuiccNotSupportedDueToIncorrectT() {
+ String str = "3F979580BEFE8210428031A073BE211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBE);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x82, null, (byte) 0x10);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+ assertFalse(atr.isEuiccSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToRestEuiccNotSupportedDueToIncorrectTB() {
+ String str = "3F979580BFFE8110428031A073BE211797";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNotNull(atr);
+ assertEquals(atr.getConventionByte(), (byte) 0x3F);
+ assertEquals(atr.getFormatByte(), (byte) 0x97);
+
+ assertEquals(atr.getInterfaceBytes().size(), 4);
+ AnswerToReset.InterfaceByte expect_t1 =
+ new AnswerToReset.InterfaceByte((byte) 0x95, null, null, (byte) 0x80);
+ AnswerToReset.InterfaceByte expect_t2 =
+ new AnswerToReset.InterfaceByte(null, null, null, (byte) 0xBF);
+ AnswerToReset.InterfaceByte expect_t3 =
+ new AnswerToReset.InterfaceByte((byte) 0xFE, (byte) 0x81, null, (byte) 0x10);
+ AnswerToReset.InterfaceByte expect_t4 =
+ new AnswerToReset.InterfaceByte((byte) 0x42, null, null, null);
+ ArrayList<AnswerToReset.InterfaceByte> expect_ib = new ArrayList<>(
+ Arrays.asList(expect_t1, expect_t2, expect_t3, expect_t4)
+ );
+ assertEquals(expect_ib, atr.getInterfaceBytes());
+
+ assertEquals(atr.getHistoricalBytes().getRawData().length, 7);
+ byte[] expect_hb = new byte[]{
+ (byte) 0x80, (byte) 0x31, (byte) 0xA0, (byte) 0x73,
+ (byte) 0xBE, (byte) 0x21, (byte) 0x17};
+ assertTrue(Arrays.equals(atr.getHistoricalBytes().getRawData(), expect_hb));
+
+ assertEquals(atr.getCheckByte(), Byte.valueOf((byte) 0x97));
+
+ assertFalse(atr.isEuiccSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToResetExtendedApduSupported() {
+ String str = "3B9F96803FC7828031E073F62158574A4D020C6030005F";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertTrue(atr.isExtendedApduSupported());
+
+ str = "3B9F96803FC7828031E073F62182574A4D020C6030005F";
+ atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+
+ str = "3F9F96803FC7828031E073F62158574A4D020C6030005F";
+ atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+
+ str = "3F9F96803FC7828031E073F62182574A4D020C6030005F";
+ atr = AnswerToReset.parseAtr(str);
+ assertTrue(atr.isExtendedApduSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToResetExtendedApduNotSupportedDueToNoTag() {
+ String str = "3F6D000080318065B00501025E83009000";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+
+ str = "3B6D000080318065B00501025E83009000";
+ atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToResetExtendedApduNotSupportedDueToLessLength() {
+ String str = "3B9E96803FC7828031E072F621574A4D020C6030005F";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+
+ str = "3F9D96803FC7828031E071F6574A4D020C6030005F";
+ atr = AnswerToReset.parseAtr(str);
+ assertFalse(atr.isExtendedApduSupported());
+ }
+
+ @Test
+ @SmallTest
+ public void testAnswerToResetParseLtvNodeWithIncorrectLength() {
+ String str = "3B9E96803FC7828031E073F621574A4D020C6030005F";
+ AnswerToReset atr = AnswerToReset.parseAtr(str);
+ assertNull(atr.getHistoricalBytes());
+
+ str = "3B9E96803FC7828031E071F621574A4D020C6030005F";
+ atr = AnswerToReset.parseAtr(str);
+ assertNull(atr.getHistoricalBytes());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
deleted file mode 100644
index e4795fa..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
+++ /dev/null
@@ -1,145 +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.uicc;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
-import com.android.internal.telephony.uicc.IccCardStatus.CardState;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.Mock;
-
-public class IccCardProxyTest extends TelephonyTest {
- private IccCardProxy mIccCardProxyUT;
- // private UiccCard mUiccCard;
- private IccCardProxyHandlerThread mIccCardProxyHandlerThread;
- private static final int PHONE_ID = 0;
- private static final int PHONE_COUNT = 1;
-
- private static final int SCARY_SLEEP_MS = 200;
- // Must match IccCardProxy.EVENT_ICC_CHANGED
- private static final int EVENT_ICC_CHANGED = 3;
-
- @Mock private Handler mMockedHandler;
- @Mock private IccCardStatus mIccCardStatus;
- @Mock private UiccCard mUiccCard;
- @Mock private UiccCardApplication mUiccCardApplication;
-
- private class IccCardProxyHandlerThread extends HandlerThread {
-
- private IccCardProxyHandlerThread(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- /* create a new UICC Controller associated with the simulated Commands */
- mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID);
- setReady(true);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp(this.getClass().getSimpleName());
- doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
- doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
- mSimulatedCommands.setIccCardStatus(mIccCardStatus);
- mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG);
- mIccCardProxyHandlerThread.start();
- waitUntilReady();
- }
-
- @After
- public void tearDown() throws Exception {
- mIccCardProxyHandlerThread.quitSafely();
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testInitialCardState() {
- assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN);
- }
-
- @Test
- @SmallTest
- public void testPowerOn() {
- mSimulatedCommands.setRadioPower(true, null);
- mSimulatedCommands.notifyRadioOn();
- when(mUiccController.getUiccCard(anyInt())).thenReturn(mUiccCard);
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState());
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- logd("IccCardProxy state = " + mIccCardProxyUT.getState());
- }
-
- @Test
- @SmallTest
- public void testCardLoaded() {
- testPowerOn();
- when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- }
-
- @Test
- @SmallTest
- public void testAppNotLoaded() {
- testPowerOn();
- when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_UNKNOWN);
- when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- }
-
- @Test
- @Ignore
- @FlakyTest
- @SmallTest
- public void testAppReady() {
- testPowerOn();
- when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_READY);
- when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.READY);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
new file mode 100644
index 0000000..ac68f6c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
@@ -0,0 +1,83 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+package com.android.internal.telephony.uicc;
+
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.MockitoAnnotations;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import com.android.internal.telephony.TelephonyTest;
+
+import android.content.Context;
+import android.os.HandlerThread;
+
+public class IccRecordsTest extends TelephonyTest {
+
+ private IccRecords mIccRecords;
+
+ private class IccRecordsTestHandler extends HandlerThread {
+ private IccRecordsTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mIccRecords = new SIMRecords(mUiccCardApplication3gpp, mContext, mSimulatedCommands);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ new IccRecordsTestHandler(TAG).start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testDisposeCallsUnregisterForIccRefresh() {
+ // verify called below when IccRecords object is created
+ verify(mSimulatedCommandsVerifier).registerForIccRefresh(eq(mIccRecords),
+ eq(IccRecords.EVENT_REFRESH), isNull());
+ mIccRecords.dispose();
+ // verify called within dispose
+ verify(mSimulatedCommandsVerifier).unregisterForIccRefresh(eq(mIccRecords));
+
+ }
+
+
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
new file mode 100644
index 0000000..7b4b9f3
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
@@ -0,0 +1,90 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+package com.android.internal.telephony.uicc;
+
+import org.mockito.Mock;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import com.android.internal.telephony.TelephonyTest;
+import org.mockito.ArgumentCaptor;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.os.Message;
+
+public class IsimUiccRecordsTest extends TelephonyTest {
+
+ private IsimUiccRecords mIsimUiccRecords;
+
+ private class IsimUiccRecordsTestHandler extends HandlerThread {
+ private IsimUiccRecordsTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext, mSimulatedCommands);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ new IsimUiccRecordsTestHandler(TAG).start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mIsimUiccRecords.dispose();
+
+ }
+
+ @Test
+ public void testBroadcastRefresh() {
+ Message msg = new Message();
+ msg.what = IccRecords.EVENT_REFRESH;
+ msg.obj = new AsyncResult(null, null, null);
+ mIsimUiccRecords.handleMessage(msg);
+ ArgumentCaptor<Intent> intentCapture = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(intentCapture.capture());
+
+ assertEquals(
+ ((Intent) intentCapture.getValue()).getAction(), IsimUiccRecords.INTENT_ISIM_REFRESH);
+ }
+
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
index 60f5f6f..62b1fc9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
@@ -14,8 +14,21 @@
* limitations under the License.
*/
package com.android.internal.telephony.uicc;
+
+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.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
import android.os.AsyncResult;
+import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.TelephonyTest;
@@ -23,26 +36,15 @@
import org.junit.After;
import org.junit.Before;
-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.junit.Assert.assertEquals;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
import org.junit.Test;
import org.mockito.Mock;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.os.Handler;
-import android.os.Message;
public class UiccCardApplicationTest extends TelephonyTest {
private UiccCardApplication mUiccCardApplication;
@Mock
private IccCardApplicationStatus mUiccCardAppStatus;
@Mock
- private UiccCard mUiccCard;
+ private UiccProfile mUiccProfile;
private Handler mHandler;
private UiccCardAppTestHandlerThread mTestHandlerThread;
private int mAttemptsRemaining = -1;
@@ -59,7 +61,7 @@
}
@Override
public void onLooperPrepared() {
- mUiccCardApplication = new UiccCardApplication(mUiccCard, mUiccCardAppStatus,
+ mUiccCardApplication = new UiccCardApplication(mUiccProfile, mUiccCardAppStatus,
mContext, mSimulatedCommands);
mHandler = new Handler(mTestHandlerThread.getLooper()) {
@Override
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 a7c3c48..27b8531 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -20,14 +20,6 @@
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.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.isA;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.HandlerThread;
@@ -35,12 +27,10 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.cat.CatService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
public class UiccCardTest extends TelephonyTest {
@@ -57,11 +47,8 @@
private static final int UICCCARD_UPDATE_CARD_STATE_EVENT = 1;
private static final int UICCCARD_UPDATE_CARD_APPLICATION_EVENT = 2;
private static final int UICCCARD_CARRIER_PRIVILEDGE_LOADED_EVENT = 3;
- private static final int UICCCARD_ABSENT = 4;
@Mock
- private CatService mCAT;
- @Mock
private IccCardStatus mIccCardStatus;
@Mock
private Handler mMockedHandler;
@@ -76,7 +63,8 @@
@Override
public void onLooperPrepared() {
mUicccard = new UiccCard(mContextFixture.getTestDouble(),
- mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
+ mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
+ new Object());
/* create a custom handler for the Handler Thread */
mHandler = new Handler(mTestHandlerThread.getLooper()) {
@Override
@@ -128,15 +116,15 @@
mIccCardStatus.mCdmaSubscriptionAppIndex =
mIccCardStatus.mImsSubscriptionAppIndex =
mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
mIccIoResult = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes("FF40"));
mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
/* starting the Handler Thread */
- mTestHandlerThread = new UiccCardHandlerThread(TAG);
+ mTestHandlerThread = new UiccCardHandlerThread(getClass().getSimpleName());
mTestHandlerThread.start();
waitUntilReady();
- replaceInstance(UiccCard.class, "mCatService", mUicccard, mCAT);
}
@After
@@ -150,11 +138,11 @@
public void tesUiccCartdInfoSanity() {
/* before update sanity test */
assertEquals(0, mUicccard.getNumApplications());
- assertNull(mUicccard.getCardState());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUicccard.getCardState());
assertNull(mUicccard.getUniversalPinState());
assertNull(mUicccard.getOperatorBrandOverride());
- /* CarrierPrivilegeRule equals null, return true */
- assertTrue(mUicccard.areCarrierPriviligeRulesLoaded());
+ /* UiccProfile mock should return false */
+ assertFalse(mUicccard.areCarrierPriviligeRulesLoaded());
for (IccCardApplicationStatus.AppType mAppType :
IccCardApplicationStatus.AppType.values()) {
assertFalse(mUicccard.isApplicationOnIcc(mAppType));
@@ -162,34 +150,6 @@
}
@Test @SmallTest
- public void testUpdateUiccCardApplication() {
- /* update app status and index */
- IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
- IccCardApplicationStatus.AppType.APPTYPE_CSIM,
- IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA0");
- IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
- IccCardApplicationStatus.AppType.APPTYPE_ISIM,
- IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA1");
- IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
- IccCardApplicationStatus.AppType.APPTYPE_USIM,
- IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
- mIccCardStatus.mApplications = new IccCardApplicationStatus[]{cdmaApp, imsApp, umtsApp};
- mIccCardStatus.mCdmaSubscriptionAppIndex = 0;
- mIccCardStatus.mImsSubscriptionAppIndex = 1;
- mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
- Message mCardUpdate = mHandler.obtainMessage(UICCCARD_UPDATE_CARD_APPLICATION_EVENT);
- setReady(false);
- mCardUpdate.sendToTarget();
-
- waitUntilReady();
-
- assertEquals(3, mUicccard.getNumApplications());
- assertTrue(mUicccard.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_CSIM));
- assertTrue(mUicccard.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_ISIM));
- assertTrue(mUicccard.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_USIM));
- }
-
- @Test @SmallTest
public void testUpdateUiccCardState() {
int mChannelId = 1;
/* set card as present */
@@ -207,6 +167,7 @@
waitForMs(50);
+ /* todo: This part should move to UiccProfileTest
assertTrue(mUicccard.areCarrierPriviligeRulesLoaded());
verify(mSimulatedCommandsVerifier, times(2)).iccOpenLogicalChannel(isA(String.class),
anyInt(), isA(Message.class));
@@ -214,51 +175,6 @@
eq(mChannelId), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyString(),
isA(Message.class)
);
- }
-
- @Test @SmallTest
- public void testUpdateUiccCardPinState() {
- mIccCardStatus.mUniversalPinState = IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
- mUicccard.update(mContextFixture.getTestDouble(), mSimulatedCommands, mIccCardStatus);
- assertEquals(IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED,
- mUicccard.getUniversalPinState());
- }
-
- @Test @SmallTest
- public void testCarrierPriviledgeLoadedListener() {
- mUicccard.registerForCarrierPrivilegeRulesLoaded(mMockedHandler,
- UICCCARD_CARRIER_PRIVILEDGE_LOADED_EVENT, null);
- ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
- ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
- testUpdateUiccCardState();
- verify(mMockedHandler, atLeast(1)).sendMessageDelayed(mCaptorMessage.capture(),
- mCaptorLong.capture());
- assertEquals(UICCCARD_CARRIER_PRIVILEDGE_LOADED_EVENT, mCaptorMessage.getValue().what);
- }
-
- @Test @SmallTest
- public void testCardAbsentListener() {
- mUicccard.registerForAbsent(mMockedHandler, UICCCARD_ABSENT, null);
- /* assume hotswap capable, avoid bootup on card removal */
- mContextFixture.putBooleanResource(com.android.internal.R.bool.config_hotswapCapable, true);
- mSimulatedCommands.setRadioPower(true, null);
-
- /* Mock Card State transition from card_present to card_absent */
- logd("UICC Card Present update");
- mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
- Message mCardUpdate = mHandler.obtainMessage(UICCCARD_UPDATE_CARD_STATE_EVENT);
- mCardUpdate.sendToTarget();
- waitForMs(50);
-
- logd("UICC Card absent update");
- mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
- mUicccard.update(mContextFixture.getTestDouble(), mSimulatedCommands, mIccCardStatus);
- waitForMs(50);
-
- ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
- ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
- verify(mMockedHandler, atLeast(1)).sendMessageDelayed(mCaptorMessage.capture(),
- mCaptorLong.capture());
- assertEquals(UICCCARD_ABSENT, mCaptorMessage.getValue().what);
+ */
}
}
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 8bb5d8c..5edb2d8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -51,7 +51,7 @@
private static final int EVENT_TEST_DONE = 2;
@Mock
- private UiccCard mUiccCard;
+ private UiccProfile mUiccProfile;
private class UiccCarrierPrivilegeRulesHandlerThread extends HandlerThread {
@@ -70,7 +70,7 @@
/* Upon handling this event, new CarrierPrivilegeRule
will be created with the looper of HandlerThread */
mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(
- mUiccCard, mHandler.obtainMessage(EVENT_TEST_DONE));
+ mUiccProfile, mHandler.obtainMessage(EVENT_TEST_DONE));
break;
case EVENT_TEST_DONE:
setReady(true);
@@ -110,7 +110,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -122,7 +122,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
anyInt(), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -132,7 +132,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
setReady(false);
@@ -326,7 +326,7 @@
}
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -338,7 +338,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
anyInt(), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -348,7 +348,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
@@ -389,7 +389,7 @@
}
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -401,7 +401,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
anyInt(), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -411,7 +411,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
@@ -449,7 +449,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -461,7 +461,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
anyInt(), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -471,7 +471,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
@@ -516,7 +516,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -525,7 +525,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
setReady(false);
@@ -564,7 +564,7 @@
}
return null;
}
- }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
doAnswer(new Answer<Void>() {
@Override
@@ -577,7 +577,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
eq(P2), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -591,7 +591,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
eq(P2_EXTENDED_DATA), anyInt(), anyString(), any(Message.class));
doAnswer(new Answer<Void>() {
@@ -601,7 +601,7 @@
message.sendToTarget();
return null;
}
- }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
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 e62700a..756015d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -14,26 +14,31 @@
* limitations under the License.
*/
package com.android.internal.telephony.uicc;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import android.support.test.filters.SmallTest;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.TelephonyTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
public class UiccControllerTest extends TelephonyTest {
private UiccController mUiccControllerUT;
@@ -77,6 +82,8 @@
doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
+ // set number of slots to 1
+ mContextFixture.putIntResource(com.android.internal.R.integer.config_num_physical_slots, 1);
replaceInstance(UiccController.class, "mInstance", null, null);
@@ -86,6 +93,8 @@
mIccCardStatus.mImsSubscriptionAppIndex =
mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
mSimulatedCommands.setIccCardStatus(mIccCardStatus);
+ // slotIndex should be invalid when testing with older versions (before 1.2) of hal
+ mIccCardStatus.physicalSlotIndex = UiccController.INVALID_SLOT_ID;
mUiccControllerHandlerThread = new UiccControllerHandlerThread(TAG);
mUiccControllerHandlerThread.start();
waitUntilReady();
@@ -102,11 +111,25 @@
@Test @SmallTest
public void testSanity() {
- assertEquals(PHONE_COUNT, mUiccControllerUT.getUiccCards().length);
+ // radio power is expected to be on which should trigger icc card and slot status requests
+ verify(mSimulatedCommandsVerifier, times(1)).getIccCardStatus(any(Message.class));
+ verify(mMockRadioConfig, times(1)).getSimSlotsStatus(any(Message.class));
+
+ // response to getIccCardStatus should create mUiccSlots[0] and UiccCard for it, and update
+ // phoneId to slotId mapping
+ UiccSlot uiccSlot = mUiccControllerUT.getUiccSlot(0);
+ UiccCard uiccCard = mUiccControllerUT.getUiccCardForSlot(0);
+ assertNotNull(uiccSlot);
+ // this assert verifies that phoneId 0 maps to slotId 0, since UiccCard object for both are
+ // same
+ assertEquals(uiccCard, mUiccControllerUT.getUiccCardForPhone(0));
+
assertNotNull(mUiccControllerUT.getUiccCard(0));
- assertNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_3GPP));
- assertNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_3GPP2));
- assertNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_IMS));
+ assertEquals(mSimRecords, mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_3GPP));
+ assertEquals(mRuimRecords, mUiccControllerUT.getIccRecords(0,
+ UiccController.APP_FAM_3GPP2));
+ assertEquals(mIsimUiccRecords, mUiccControllerUT.getIccRecords(0,
+ UiccController.APP_FAM_IMS));
assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP));
assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP2));
assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_IMS));
@@ -161,9 +184,10 @@
assertNotNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_3GPP));
assertNotNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_3GPP2));
assertNotNull(mUiccControllerUT.getIccRecords(0, UiccController.APP_FAM_IMS));
- assertNotNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP));
- assertNotNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP2));
- assertNotNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_IMS));
+ // null because getIccFileHandler() has not been mocked for mocked applications
+ assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP));
+ assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_3GPP2));
+ assertNull(mUiccControllerUT.getIccFileHandler(0, UiccController.APP_FAM_IMS));
}
@Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
new file mode 100644
index 0000000..404754c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -0,0 +1,605 @@
+/*
+ * Copyright 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.uicc;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.telephony.CarrierConfigManager;
+
+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.uicc.IccCardApplicationStatus.AppState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class UiccProfileTest extends TelephonyTest {
+ private UiccProfile mUiccProfile;
+
+ public UiccProfileTest() {
+ super();
+ }
+
+ private IccIoResult mIccIoResult;
+ private static final int SCARY_SLEEP_MS = 200;
+
+ private UiccProfileHandlerThread mTestHandlerThread;
+ private Handler mHandler;
+ private static final int UICCPROFILE_UPDATE_PROFILE_EVENT = 1;
+ private static final int UICCPROFILE_UPDATE_APPLICATION_EVENT = 2;
+ private static final int UICCPROFILE_CARRIER_PRIVILEDGE_LOADED_EVENT = 3;
+
+ @Mock
+ private CatService mCAT;
+ @Mock
+ private IccCardStatus mIccCardStatus;
+ @Mock
+ private Handler mMockedHandler;
+ @Mock
+ private UiccCard mUiccCard;
+
+ private class UiccProfileHandlerThread extends HandlerThread {
+
+ private UiccProfileHandlerThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mUiccProfile = new UiccProfile(mContextFixture.getTestDouble(),
+ mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
+ mUiccCard, new Object());
+ /* create a custom handler for the Handler Thread */
+ mHandler = new Handler(mTestHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UICCPROFILE_UPDATE_PROFILE_EVENT:
+ /* Upon handling this event, new CarrierPrivilegeRule
+ will be created with the looper of HandlerThread */
+ logd("Update UICC Profile");
+ mUiccProfile.update(mContextFixture.getTestDouble(),
+ mSimulatedCommands, mIccCardStatus);
+ setReady(true);
+ break;
+ case UICCPROFILE_UPDATE_APPLICATION_EVENT:
+ logd("Update UICC Profile Applications");
+ mUiccProfile.update(mContextFixture.getTestDouble(),
+ mSimulatedCommands, mIccCardStatus);
+ setReady(true);
+ break;
+ default:
+ logd("Unknown Event " + msg.what);
+ }
+ }
+ };
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ setReady(true);
+ logd("Create UiccProfile");
+ }
+ }
+
+ private IccCardApplicationStatus composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType appType,
+ IccCardApplicationStatus.AppState appState, String aid) {
+ IccCardApplicationStatus mIccCardAppStatus = new IccCardApplicationStatus();
+ 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());
+ /* initially there are no application available, but the array should not be empty. */
+ 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.setIccIoResultForApduLogicalChannel(mIccIoResult);
+ /* starting the Handler Thread */
+ mTestHandlerThread = new UiccProfileHandlerThread(TAG);
+ mTestHandlerThread.start();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+
+ replaceInstance(UiccProfile.class, "mCatService", mUiccProfile, mCAT);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void tesUiccProfileInfoSanity() {
+ assertEquals(1, mUiccProfile.getNumApplications());
+ assertNull(mUiccProfile.getUniversalPinState());
+ assertNull(mUiccProfile.getOperatorBrandOverride());
+ for (IccCardApplicationStatus.AppType mAppType :
+ IccCardApplicationStatus.AppType.values()) {
+ if (mAppType == IccCardApplicationStatus.AppType.APPTYPE_USIM) {
+ assertTrue(mUiccProfile.isApplicationOnIcc(mAppType));
+ } else {
+ assertFalse(mUiccProfile.isApplicationOnIcc(mAppType));
+ }
+
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplication() {
+ /* update app status and index */
+ IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_CSIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA0");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA1");
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{cdmaApp, imsApp, umtsApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = 0;
+ mIccCardStatus.mImsSubscriptionAppIndex = 1;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+
+ assertEquals(3, mUiccProfile.getNumApplications());
+ assertTrue(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_CSIM));
+ assertTrue(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_ISIM));
+ assertTrue(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_USIM));
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfile() {
+ int mChannelId = 1;
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ mSimulatedCommands.setOpenChannelId(mChannelId);
+ Message mCardUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_PROFILE_EVENT);
+ setReady(false);
+ mCardUpdate.sendToTarget();
+ /* try to create a new CarrierPrivilege, loading state -> loaded state */
+ /* wait till the async result and message delay */
+ waitUntilReady();
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+
+ assertTrue(mUiccProfile.areCarrierPriviligeRulesLoaded());
+ verify(mSimulatedCommandsVerifier, times(2)).iccOpenLogicalChannel(isA(String.class),
+ anyInt(), isA(Message.class));
+ verify(mSimulatedCommandsVerifier, times(2)).iccTransmitApduLogicalChannel(
+ anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyString(),
+ isA(Message.class)
+ );
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfilePinState() {
+ mIccCardStatus.mUniversalPinState = IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+ mUiccProfile.update(mContextFixture.getTestDouble(), mSimulatedCommands, mIccCardStatus);
+ assertEquals(IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED,
+ mUiccProfile.getUniversalPinState());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierPriviledgeLoadedListener() {
+ mUiccProfile.registerForCarrierPrivilegeRulesLoaded(mMockedHandler,
+ UICCPROFILE_CARRIER_PRIVILEDGE_LOADED_EVENT, null);
+ ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
+ ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
+ testUpdateUiccProfile();
+ verify(mMockedHandler, atLeast(1)).sendMessageDelayed(mCaptorMessage.capture(),
+ mCaptorLong.capture());
+ assertEquals(UICCPROFILE_CARRIER_PRIVILEDGE_LOADED_EVENT, mCaptorMessage.getValue().what);
+ }
+
+ @Test
+ @SmallTest
+ public void testInitialCardState() {
+ // after updateExternalState() is called, the state will not be UNKNOWN
+ assertEquals(mUiccProfile.getState(), State.NOT_READY);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationNotReady() {
+ /* update app status and index */
+ IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_CSIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA0");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{cdmaApp, imsApp, umtsApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = 0;
+ mIccCardStatus.mImsSubscriptionAppIndex = 1;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(3, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ assertEquals(mUiccProfile.getState(), State.NOT_READY);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationAllReady() {
+ /* update app status and index */
+ IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_CSIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA0");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{cdmaApp, imsApp, umtsApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = 0;
+ mIccCardStatus.mImsSubscriptionAppIndex = 1;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(3, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationAllSupportedAppsReady() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp, unknownApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(3, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationWithDuplicateApps() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ IccCardApplicationStatus umtsAppDup = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp, unknownApp,
+ umtsAppDup};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(4, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationWithDuplicateAppsInDifferentOrder() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ IccCardApplicationStatus umtsAppDup = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{umtsAppDup, imsApp, umtsApp,
+ unknownApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(4, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationNoApplication() {
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = -1;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(0, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded since there is no applications.
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationNoSupportApplication() {
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{unknownApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = -1;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(1, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded since there is no applications.
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+ }
+
+ private void testWithCsimApp() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_CSIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp, cdmaApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = 2;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(3, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationCdmaSupported() {
+ // CDMA supported
+ doReturn(true).when(mUiccController).isCdmaSupported();
+
+ testWithCsimApp();
+
+ // CDMA is supported and CSIM app is not ready, so state should be NOT_READY
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationCdmaNotSupported() {
+ // CDMA supported
+ doReturn(false).when(mUiccController).isCdmaSupported();
+
+ testWithCsimApp();
+
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ // CSIM is not ready but that should not matter since CDMA is not supported.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateExternalState() {
+ // IO_ERROR
+ doReturn(IccCardStatus.CardState.CARDSTATE_ERROR).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.CARD_IO_ERROR, mUiccProfile.getState());
+
+ // RESTRICTED
+ doReturn(IccCardStatus.CardState.CARDSTATE_RESTRICTED).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.CARD_RESTRICTED, mUiccProfile.getState());
+
+ // CARD PRESENT; no mUiccApplication - state should be NOT_READY
+ doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+
+ // set mUiccApplication
+ testUpdateUiccProfileApplicationAllReady();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierConfigHandling() {
+ testUpdateUiccProfileApplication();
+
+ // Fake carrier name
+ String fakeCarrierName = "fakeCarrierName";
+ PersistableBundle carrierConfigBundle = mContextFixture.getCarrierConfigBundle();
+ carrierConfigBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
+ carrierConfigBundle.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING,
+ fakeCarrierName);
+
+ // broadcast CARRIER_CONFIG_CHANGED
+ mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ waitForMs(200);
+
+ // verify that setSimOperatorNameForPhone() is called with fakeCarrierName
+ ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mTelephonyManager, atLeast(1)).setSimOperatorNameForPhone(anyInt(),
+ stringArgumentCaptor.capture());
+ boolean carrierFound = false;
+ for (String carrierName : stringArgumentCaptor.getAllValues()) {
+ if (fakeCarrierName.equals(carrierName)) {
+ carrierFound = true;
+ break;
+ }
+ }
+ assertTrue(carrierFound);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
new file mode 100644
index 0000000..457f021
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.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.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class UiccSlotTest extends TelephonyTest {
+ private UiccSlot mUiccSlot;
+ private UiccSlotTestHandlerThread mTestHandlerThread;
+ private Handler mTestHandler;
+
+ @Mock
+ private Handler mMockedHandler;
+ @Mock
+ private IccCardStatus mIccCardStatus;
+
+ private static final int UICCCARD_ABSENT = 1;
+ private static final int UICCCARD_UPDATE_CARD_STATE_EVENT = 2;
+
+ private class UiccSlotTestHandlerThread extends HandlerThread {
+ private UiccSlotTestHandlerThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mUiccSlot = new UiccSlot(mContext, true /* isActive */);
+ mTestHandler = new Handler(mTestHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UICCCARD_UPDATE_CARD_STATE_EVENT:
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
+ setReady(true);
+ break;
+ default:
+ logd("Unknown Event " + msg.what);
+ break;
+ }
+ }
+ };
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mContextFixture.putBooleanResource(com.android.internal.R.bool.config_hotswapCapable, true);
+ /* initially there are no application available */
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
+ mIccCardStatus.mCdmaSubscriptionAppIndex =
+ mIccCardStatus.mImsSubscriptionAppIndex =
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+
+ /* starting the Handler Thread */
+ mTestHandlerThread = new UiccSlotTestHandlerThread(getClass().getSimpleName());
+ mTestHandlerThread.start();
+
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateInactiveSlotStatus() {
+ IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ iss.iccid = "fake-iccid";
+
+ // initial state
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getIccId());
+
+ // update slot to inactive
+ mUiccSlot.update(null, iss);
+
+ // assert on updated values
+ assertFalse(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
+ assertEquals(iss.iccid, mUiccSlot.getIccId());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateActiveSlotStatus() {
+ // initial state
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getIccId());
+
+ mSimulatedCommands.setRadioPower(true, null);
+ int phoneId = 0;
+ IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = phoneId;
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_ACTIVE;
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ iss.iccid = "fake-iccid";
+
+ // update slot to inactive
+ mUiccSlot.update(mSimulatedCommands, iss);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertEquals(iss.iccid, mUiccSlot.getIccId());
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+
+ // update slot to active
+ mUiccSlot.update(mSimulatedCommands, iss);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isActive());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateSlotStatusEuiccIsSupported() {
+ IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ iss.iccid = "fake-iccid";
+ iss.atr = "3F979580BFFE8210428031A073BE211797";
+
+ // initial state
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getIccId());
+
+ // update slot to inactive
+ mUiccSlot.update(null, iss);
+
+ // assert on updated values
+ assertFalse(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
+ assertEquals(iss.iccid, mUiccSlot.getIccId());
+
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_ACTIVE;
+
+ // update slot to active
+ mUiccSlot.update(mSimulatedCommands, iss);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isActive());
+ assertTrue(mUiccSlot.isEuicc());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateSlotStatusEuiccIsNotSupported() {
+ IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ iss.iccid = "fake-iccid";
+ iss.atr = "3F979580BFFE8110428031A073BE211797";
+
+ // initial state
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getIccId());
+
+ // update slot to inactive
+ mUiccSlot.update(null, iss);
+
+ // assert on updated values
+ assertFalse(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
+ assertEquals(iss.iccid, mUiccSlot.getIccId());
+
+ iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_ACTIVE;
+
+ // update slot to active
+ mUiccSlot.update(mSimulatedCommands, iss);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isActive());
+ assertFalse(mUiccSlot.isEuicc());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateAbsentState() {
+ int phoneId = 0;
+ // Make sure when received CARDSTATE_ABSENT state in the first time,
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getUiccCard());
+ }
+
+ @Test
+ @SmallTest
+ public void testUiccSlotCreateAndDispose() {
+ int phoneId = 0;
+ // Simulate when SIM is added, UiccCard and UiccProfile should be created.
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mTelephonyComponentFactory).makeUiccProfile(
+ anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
+ anyObject());
+ 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.
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccProfile).dispose();
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getUiccCard());
+ }
+}
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 2f44b0a..f933596 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -100,10 +100,9 @@
msg.what = integerArgumentCaptor.getValue();
// The first broadcast should be sent after initialization.
- UiccCard[] cards = new UiccCard[CARD_COUNT];
- cards[0] = new UiccCard(mContext, mSimulatedCommands,
- makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
- when(UiccController.getInstance().getUiccCards()).thenReturn(cards);
+ UiccCard card = new UiccCard(mContext, mSimulatedCommands,
+ makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object());
+ when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
uiccLauncher.handleMessage(msg);
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -116,9 +115,8 @@
intentArgumentCaptor.getValue().getAction());
// Card state's changed to restricted. Broadcast should be sent.
- cards[0].update(mContext, mSimulatedCommands,
+ card.update(mContext, mSimulatedCommands,
makeCardStatus(CardState.CARDSTATE_RESTRICTED));
- when(UiccController.getInstance().getUiccCards()).thenReturn(cards);
uiccLauncher.handleMessage(msg);
broadcast_count++;
@@ -132,9 +130,8 @@
verify(mContext, times(broadcast_count)).sendBroadcast(any(Intent.class));
// Card state's changed from restricted. Broadcast should be sent.
- cards[0].update(mContext, mSimulatedCommands,
+ card.update(mContext, mSimulatedCommands,
makeCardStatus(CardState.CARDSTATE_PRESENT));
- when(UiccController.getInstance().getUiccCards()).thenReturn(cards);
uiccLauncher.handleMessage(msg);
broadcast_count++;
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
new file mode 100644
index 0000000..dc621a5
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -0,0 +1,1000 @@
+/*
+ * 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.uicc.euicc;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.telephony.UiccAccessRule;
+import android.telephony.euicc.EuiccCardManager;
+import android.telephony.euicc.EuiccNotification;
+import android.telephony.euicc.EuiccRulesAuthTable;
+import android.util.ExceptionUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
+import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
+import com.android.internal.telephony.uicc.euicc.apdu.LogicalChannelMocker;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class EuiccCardTest extends TelephonyTest {
+ private static final long WAIT_TIMEOUT_MLLIS = 5000;
+
+ private static class ResultCaptor<T> extends AsyncResultCallback<T> {
+ public T result;
+ public Throwable exception;
+
+ private CountDownLatch mLatch;
+
+ private ResultCaptor() {
+ mLatch = new CountDownLatch(1);
+ }
+
+ public void await() {
+ try {
+ mLatch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("Execution is interrupted: " + e);
+ }
+ }
+
+ @Override
+ public void onResult(T r) {
+ result = r;
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ exception = e;
+ mLatch.countDown();
+ }
+ }
+
+ private class UiccCardHandlerThread extends HandlerThread {
+ private UiccCardHandlerThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mEuiccCard =
+ new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus,
+ 0 /* phoneId */, new Object()) {
+ @Override
+ protected byte[] getDeviceId() {
+ return IccUtils.bcdToBytes("987654321012345");
+ }
+
+ @Override
+ protected void loadEidAndNotifyRegistrants() {}
+
+ @Override
+ protected Resources getResources() {
+ return mMockResources;
+ }
+
+ };
+ mHandler = new Handler(mTestHandlerThread.getLooper());
+ setReady(true);
+ }
+ }
+
+ @Mock
+ private CommandsInterface mMockCi;
+ @Mock
+ private IccCardStatus mMockIccCardStatus;
+
+ private UiccCardHandlerThread mTestHandlerThread;
+ private Handler mHandler;
+
+ private EuiccCard mEuiccCard;
+
+ @Mock
+ private Resources mMockResources;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mMockIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
+ mMockIccCardStatus.mCdmaSubscriptionAppIndex =
+ mMockIccCardStatus.mImsSubscriptionAppIndex =
+ mMockIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+ mMockIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+
+ mTestHandlerThread = new UiccCardHandlerThread(getClass().getSimpleName());
+ mTestHandlerThread.start();
+
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestHandlerThread.quit();
+ super.tearDown();
+ }
+
+ private void assertUnexpectedException(Throwable e) {
+ if (e != null) {
+ fail("Unexpected exception: " + ExceptionUtils.getCompleteMessage(e) + "\n-----\n"
+ + Log.getStackTraceString(e.getCause()) + "-----");
+ }
+ }
+
+ @Test
+ public void testLoadEidAndNotifyRegistrants() throws InterruptedException {
+ int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");
+
+ {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
+ mMockIccCardStatus, 0 /* phoneId */, new Object());
+ latch.countDown();
+ });
+ assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));
+ }
+
+ final int eventEidReady = 0;
+ final CountDownLatch latch = new CountDownLatch(1);
+ Handler handler = new Handler(mTestHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == eventEidReady) {
+ assertEquals("1A2B3C4D", mEuiccCard.getEid());
+ latch.countDown();
+ }
+ }
+ };
+
+ mEuiccCard.registerForEidReady(handler, eventEidReady, null /* obj */);
+ assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));
+
+ verifyStoreData(channel, "BF3E035C015A");
+ }
+
+ @Test
+ public void testGetAllProfiles() {
+ int channel = mockLogicalChannelResponses(
+ "BF2D14A012E3105A0A896700000000004523019F7001019000");
+
+ ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getAllProfiles(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ EuiccProfileInfo[] profiles = resultCaptor.result;
+ assertEquals(1, profiles.length);
+ assertEquals("98760000000000543210", profiles[0].getIccid());
+ assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState());
+ verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76");
+ }
+
+ @Test
+ public void testFSuffix() {
+ // iccID is 987600000000005432FF.
+ int channel = mockLogicalChannelResponses(
+ "BF2D14A012E3105A0A896700000000004523FF9F7001019000");
+
+ ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getAllProfiles(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ EuiccProfileInfo[] profiles = resultCaptor.result;
+ assertEquals(1, profiles.length);
+ assertEquals("987600000000005432", profiles[0].getIccid());
+ assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState());
+ verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76");
+ }
+
+ @Test
+ public void testGetProfile() {
+ int channel = mockLogicalChannelResponses("BF2D8184A08181E37F"
+ + "5A0A89670000000000452301" // ICCID
+ + "90046E69636B" // Nickname
+ + "9103746D6F" // Service provider name
+ + "92027031" // Profile name
+ + "B70F800312F34581030102038203040506" // Operator id
+ + "9F700101" // Profile state
+ + "950101" // Profile class
+ + "990206C0" // Policy rules
+ + "BF7645E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4"
+ + "CA1D636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070"
+ + "E30ADB080000000000000001" // Carrier privilege rules
+ + "9000");
+
+ ResultCaptor<EuiccProfileInfo> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getProfile("98760000000000543210", resultCaptor, mHandler);
+ resultCaptor.await();
+
+ EuiccProfileInfo profile = resultCaptor.result;
+ assertEquals("98760000000000543210", profile.getIccid());
+ assertEquals("nick", profile.getNickname());
+ assertEquals("tmo", profile.getServiceProviderName());
+ assertEquals("p1", profile.getProfileName());
+ assertEquals("213", profile.getCarrierIdentifier().getMcc());
+ assertEquals("54", profile.getCarrierIdentifier().getMnc());
+ assertEquals("010203", profile.getCarrierIdentifier().getGid1());
+ assertEquals("040506", profile.getCarrierIdentifier().getGid2());
+ assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profile.getState());
+ assertEquals(EuiccProfileInfo.PROFILE_CLASS_PROVISIONING, profile.getProfileClass());
+ assertEquals(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ profile.getPolicyRules());
+ assertArrayEquals(
+ new UiccAccessRule[] {
+ new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4"),
+ "com.google.android.apps.myapp", 1)
+ },
+ profile.getUiccAccessRules().toArray());
+ verifyStoreData(channel, "BF2D195A0A896700000000004523015C0B5A909192B79F709599BF76");
+ }
+
+ @Test
+ public void testDisableProfile() {
+ int channel = mockLogicalChannelResponses("BF32038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.disableProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3211A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testDisableProfile_SimRefresh() {
+ int channel = mockLogicalChannelResponses("6106", "6f00");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.disableProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3211A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testDisableProfile_Error() {
+ int channel = mockLogicalChannelResponses("BF32038001039000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.disableProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF3211A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testSwitchToProfile() {
+ int channel = mockLogicalChannelResponses("BF31038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.switchToProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3111A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testSwitchToProfile_SimRefresh() {
+ int channel = mockLogicalChannelResponses("6106", "6f00");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.switchToProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3111A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testSwitchToProfile_Error() {
+ int channel = mockLogicalChannelResponses("BF31038001039000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.switchToProfile("98760000000000543210", true, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF3111A00C5A0A896700000000004523018101FF");
+ }
+
+ @Test
+ public void testGetEid() {
+ int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");
+
+ ResultCaptor<String> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getEid(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("1A2B3C4D", resultCaptor.result);
+ verifyStoreData(channel, "BF3E035C015A");
+ }
+
+ @Test
+ public void testSetNickname() {
+ int channel = mockLogicalChannelResponses("BF29038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.setNickname("98760000000000543210", "new nickname", resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF291A5A0A89670000000000452301900C6E6577206E69636B6E616D65");
+ }
+
+ @Test
+ public void testDeleteProfile() {
+ int channel = mockLogicalChannelResponses("BF33038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.deleteProfile("98760000000000543210", resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF330C5A0A89670000000000452301");
+ }
+
+ @Test
+ public void testDeleteProfile_Error() {
+ int channel = mockLogicalChannelResponses("BF33038001039000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.deleteProfile("98760000000000543210", resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF330C5A0A89670000000000452301");
+ }
+
+ @Test
+ public void testGetDefaultSmdpAddress() {
+ int channel = mockLogicalChannelResponses(
+ "BF3C148008534D44502E434F4D8108736D64732E636F6D9000");
+
+ ResultCaptor<String> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getDefaultSmdpAddress(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("SMDP.COM", resultCaptor.result);
+ verifyStoreData(channel, "BF3C00");
+ }
+
+ @Test
+ public void testGetSmdsAddress() {
+ int channel = mockLogicalChannelResponses(
+ "BF3C148008534D44502E434F4D8108736D64732E636F6D9000");
+
+ ResultCaptor<String> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getSmdsAddress(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("smds.com", resultCaptor.result);
+ verifyStoreData(channel, "BF3C00");
+ }
+
+ @Test
+ public void testSetDefaultSmdpAddress() {
+ int channel = mockLogicalChannelResponses("BF3F038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.setDefaultSmdpAddress("smdp.gsma.com", resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3F0F800D736D64702E67736D612E636F6D");
+ }
+
+ @Test
+ public void testGetRulesAuthTable() {
+ int channel = mockLogicalChannelResponses("BF4347"
+ + "A021" // Rule #1
+ + "800206C0" // Policy rules: DO_NOT_DELETE | DO_NOT_DISABLE
+ + "A118" // Operator IDs
+ + "B70A800312F3458103010203" // ID #1: 213, 54, [1,2,3], null
+ + "B70A800312F3458203040506" // ID #2: 213, 54, null, [4,5,6]
+ + "820108" // Flag (no user consent)
+ + "A022" // Rule #2
+ + "80020780" // Policy rules: DO_NOT_DISABLE
+ + "A118" // Operator IDs
+ + "B70A800312E3458103010203" // ID #1: 213, 54E, [1,2,3], null
+ + "B70A8003EEEE458203040506" // ID #2: EEE, 54E, null, [4,5,6]
+ + "82020780" // Flag (user consent)
+ + "9000");
+
+ ResultCaptor<EuiccRulesAuthTable> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getRulesAuthTable(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ EuiccRulesAuthTable rat = resultCaptor.result;
+ assertEquals(-1,
+ rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ new CarrierIdentifier(new byte[] {0x12, (byte) 0xF3, 0x45}, null, null)));
+ assertEquals(1,
+ rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ new CarrierIdentifier(new byte[] {0x23, 0x67, 0x45}, null, "040506")));
+ assertFalse(rat.hasPolicyRuleFlag(0,
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED));
+ assertTrue(rat.hasPolicyRuleFlag(1,
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED));
+ verifyStoreData(channel, "BF4300");
+ }
+
+ @Test
+ public void testResetMemory() {
+ int channel = mockLogicalChannelResponses("BF34038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.resetMemory(EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF340482020640");
+ }
+
+ @Test
+ public void testResetMemory_SimRefresh() {
+ int channel = mockLogicalChannelResponses("6106", "6f00");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.resetMemory(EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF340482020640");
+ }
+
+ @Test
+ public void testGetEuiccChallenge() {
+ int channel = mockLogicalChannelResponses("BF2E0580030102039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getEuiccChallenge(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertArrayEquals(new byte[] {1, 2, 3}, resultCaptor.result);
+ verifyStoreData(channel, "BF2E00");
+ }
+
+ @Test
+ public void testGetEuiccInfo1() {
+ int channel = mockLogicalChannelResponses("BF20030102039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getEuiccInfo1(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("BF2003010203", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel, "BF2000");
+ }
+
+ @Test
+ public void testGetEuiccInfo2() {
+ int channel = mockLogicalChannelResponses("BF22030102039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.getEuiccInfo2(resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("BF2203010203", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel, "BF2200");
+ }
+
+ @Test
+ public void testAuthenticateServer() {
+ when(mMockResources.getStringArray(
+ com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities))
+ .thenReturn(new String[] {});
+
+ int channel = mockLogicalChannelResponses("BF3802A0009000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.authenticateServer("A1B2C3-X4Y5Z6", // Matching id
+ Asn1Node.newBuilder(0xA0).build().toBytes(),
+ Asn1Node.newBuilder(0xA1).build().toBytes(),
+ Asn1Node.newBuilder(0xA2).build().toBytes(),
+ Asn1Node.newBuilder(0xA3).build().toBytes(), resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ assertEquals("BF3802A000", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel,
+ "BF382D" + "A000" + "A100" + "A200" + "A300" + "A023"
+ + "800D4131423243332D583459355A36" // Matching id
+ + "A112800489674523" // TAC
+ + "A100" // Device capabilities
+ + "82088967452301214305"); // IMEI
+ }
+
+ @Test
+ public void testAuthenticateServer_Error() {
+ when(mMockResources.getStringArray(
+ com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities))
+ .thenReturn(new String[] {});
+
+ int channel = mockLogicalChannelResponses("BF38038101039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.authenticateServer("A1B2C3-X4Y5Z6", // Matching id
+ Asn1Node.newBuilder(0xA0).build().toBytes(),
+ Asn1Node.newBuilder(0xA1).build().toBytes(),
+ Asn1Node.newBuilder(0xA2).build().toBytes(),
+ Asn1Node.newBuilder(0xA3).build().toBytes(), resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel,
+ "BF382D" + "A000" + "A100" + "A200" + "A300" + "A023"
+ + "800D4131423243332D583459355A36" // Matching id
+ + "A112800489674523" // TAC
+ + "A100" // Device capabilities
+ + "82088967452301214305"); // IMEI
+ }
+
+ @Test
+ public void testAuthenticateService_devCap() {
+ when(mMockResources.getStringArray(
+ com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities))
+ .thenReturn(new String[] {
+ "gsm,11",
+ "utran,11",
+ "cdma1x,1",
+ "hrpd,3",
+ "ehrpd,12",
+ "eutran,11"});
+
+ int channel = mockLogicalChannelResponses("BF3802A0009000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.authenticateServer("A1B2C3-X4Y5Z6", // Matching id
+ Asn1Node.newBuilder(0xA0).build().toBytes(),
+ Asn1Node.newBuilder(0xA1).build().toBytes(),
+ Asn1Node.newBuilder(0xA2).build().toBytes(),
+ Asn1Node.newBuilder(0xA3).build().toBytes(), resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ assertEquals("BF3802A000", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel,
+ "BF3846" + "A000" + "A100" + "A200" + "A300" + "A03C"
+ + "800D4131423243332D583459355A36" // Matching id
+ + "A12B800489674523" // TAC
+ // Device capabilities
+ + "A11980030B000081030B0000830303000084030C000085030B0000"
+ + "82088967452301214305"); // IMEI
+ }
+
+ @Test
+ public void testPrepareDownload() {
+ int channel = mockLogicalChannelResponses("BF2102A0009000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.prepareDownload(
+ IccUtils.hexStringToBytes("4131423243332D583459355A36"), // hashCc
+ Asn1Node.newBuilder(0xA0).build().toBytes(),
+ Asn1Node.newBuilder(0xA1).build().toBytes(),
+ Asn1Node.newBuilder(0xA2).build().toBytes(), resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("BF2102A000", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel,
+ "BF2115" + "A000" + "A100"
+ + "040D4131423243332D583459355A36" // hashCc
+ + "A200");
+ }
+
+ @Test
+ public void testPrepareDownload_Error() {
+ int channel = mockLogicalChannelResponses("BF2105A1030201039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.prepareDownload(
+ IccUtils.hexStringToBytes("4131423243332D583459355A36"), // hashCc
+ Asn1Node.newBuilder(0xA0).build().toBytes(),
+ Asn1Node.newBuilder(0xA1).build().toBytes(),
+ Asn1Node.newBuilder(0xA2).build().toBytes(), resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel,
+ "BF2115" + "A000" + "A100"
+ + "040D4131423243332D583459355A36" // hashCc
+ + "A200");
+ }
+
+ @Test
+ public void testLoadBoundProfilePackage() {
+ int channel = mockLogicalChannelResponses(
+ // For boundProfilePackage head + initialiseSecureChannelRequest
+ // (ES8+.InitialiseSecureChannel)
+ "9000",
+ // For firstSequenceOf87 (ES8+.ConfigureISDP)
+ "9000",
+ // For head of sequenceOf88 (ES8+.StoreMetadata)
+ "9000",
+ // For body (element 1) of sequenceOf88 (ES8+.StoreMetadata)
+ "9000",
+ "9000",
+ // For head of sequenceOf86 (ES8+.LoadProfileElements)
+ "9000",
+ // For body (element 1) of sequenceOf86 (ES8+.LoadProfileElements)
+ "9000",
+ // Profile installation result (element 2 of sequenceOf86)
+ "BF37009000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.loadBoundProfilePackage(
+ Asn1Node.newBuilder(0xBF36)
+ .addChild(Asn1Node.newBuilder(0xBF23))
+ .addChild(Asn1Node.newBuilder(0xA0)
+ .addChildAsBytes(0x87, new byte[] {1, 2, 3}))
+ .addChild(Asn1Node.newBuilder(0xA1)
+ .addChildAsBytes(0x88, new byte[] {4, 5, 6}))
+ .addChild(Asn1Node.newBuilder(0xA2))
+ .addChild(Asn1Node.newBuilder(0xA3)
+ .addChildAsBytes(0x86, new byte[] {7, 8, 9})
+ .addChildAsBytes(0x86, new byte[] {0xA, 0xB, 0xC}))
+ .build().toBytes(),
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("BF3700", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel, "BF361FBF2300"); // ES8+.InitialiseSecureChannel
+ verifyStoreData(channel, "A0058703010203"); // ES8+.ConfigureISDP
+ verifyStoreData(channel, "A105"); // ES8+.StoreMetadata
+ verifyStoreData(channel, "8803040506"); // ES8+.StoreMetadata
+ verifyStoreData(channel, "A200");
+ verifyStoreData(channel, "A30A"); // ES8+.LoadProfileElements
+ verifyStoreData(channel, "8603070809"); // ES8+.LoadProfileElements
+ verifyStoreData(channel, "86030A0B0C"); // ES8+.LoadProfileElements
+ }
+
+ @Test
+ public void testLoadBoundProfilePackage_Error() {
+ int channel = mockLogicalChannelResponses(
+ // For boundProfilePackage head + initialiseSecureChannelRequest
+ // (ES8+.InitialiseSecureChannel)
+ "9000",
+ // For firstSequenceOf87 (ES8+.ConfigureISDP)
+ "9000",
+ // For head of sequenceOf88 (ES8+.StoreMetadata)
+ "9000",
+ // For body (element 1) of sequenceOf88 (ES8+.StoreMetadata)
+ "9000",
+ "9000",
+ // For head of sequenceOf86 (ES8+.LoadProfileElements)
+ "9000",
+ // For body (element 1) of sequenceOf86 (ES8+.LoadProfileElements)
+ "9000",
+ // Profile installation result (element 2 of sequenceOf86)
+ "BF370ABF2707A205A1038101039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.loadBoundProfilePackage(
+ Asn1Node.newBuilder(0xBF36)
+ .addChild(Asn1Node.newBuilder(0xBF23))
+ .addChild(Asn1Node.newBuilder(0xA0)
+ .addChildAsBytes(0x87, new byte[] {1, 2, 3}))
+ .addChild(Asn1Node.newBuilder(0xA1)
+ .addChildAsBytes(0x88, new byte[] {4, 5, 6}))
+ .addChild(Asn1Node.newBuilder(0xA2))
+ .addChild(Asn1Node.newBuilder(0xA3)
+ .addChildAsBytes(0x86, new byte[] {7, 8, 9})
+ .addChildAsBytes(0x86, new byte[] {0xA, 0xB, 0xC}))
+ .build().toBytes(),
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF361FBF2300"); // ES8+.InitialiseSecureChannel
+ verifyStoreData(channel, "A0058703010203"); // ES8+.ConfigureISDP
+ verifyStoreData(channel, "A105"); // ES8+.StoreMetadata
+ verifyStoreData(channel, "8803040506"); // ES8+.StoreMetadata
+ verifyStoreData(channel, "A200");
+ verifyStoreData(channel, "A30A"); // ES8+.LoadProfileElements
+ verifyStoreData(channel, "8603070809"); // ES8+.LoadProfileElements
+ verifyStoreData(channel, "86030A0B0C"); // ES8+.LoadProfileElements
+ }
+
+ @Test
+ public void testCancelSession() {
+ int channel = mockLogicalChannelResponses("BF41009000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.cancelSession(IccUtils.hexStringToBytes("A1B2C3"),
+ EuiccCardManager.CANCEL_REASON_POSTPONED, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals("BF4100", IccUtils.bytesToHexString(resultCaptor.result));
+ verifyStoreData(channel, "BF41088003A1B2C3810101");
+ }
+
+ @Test
+ public void testCancelSession_Error() {
+ int channel = mockLogicalChannelResponses("BF41038101039000");
+
+ ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.cancelSession(IccUtils.hexStringToBytes("A1B2C3"),
+ EuiccCardManager.CANCEL_REASON_POSTPONED, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF41088003A1B2C3810101");
+ }
+
+ @Test
+ public void testListNotifications() {
+ int channel = mockLogicalChannelResponses("BF282BA029"
+ + "BF2F118001010C08736D64702E636F6D81020410" // Notification #1
+ + "BF2F128001020C09736D6470322E636F6D81020420" // Notification #2
+ + "9000");
+
+ ResultCaptor<EuiccNotification[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.listNotifications(
+ EuiccNotification.EVENT_DELETE | EuiccNotification.EVENT_DISABLE,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertArrayEquals(
+ new EuiccNotification[] {
+ new EuiccNotification(1, "smdp.com", EuiccNotification.EVENT_DELETE, null),
+ new EuiccNotification(2, "smdp2.com", EuiccNotification.EVENT_DISABLE, null)
+ },
+ resultCaptor.result);
+ verifyStoreData(channel, "BF280481020430");
+ }
+
+ @Test
+ public void testListNotifications_Error() {
+ int channel = mockLogicalChannelResponses("BF28038101039000");
+
+ ResultCaptor<EuiccNotification[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.listNotifications(
+ EuiccNotification.EVENT_DELETE | EuiccNotification.EVENT_DISABLE,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF280481020430");
+ }
+
+ @Test
+ public void testRetrieveNotificationList() {
+ int channel = mockLogicalChannelResponses("BF2B2FA02D"
+ // Notification #1
+ + "3014BF2F118001010C08736D64702E636F6D81020410"
+ // Notification #2
+ + "3015BF2F128001020C09736D6470322E636F6D81020420"
+ + "9000");
+
+ ResultCaptor<EuiccNotification[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.retrieveNotificationList(
+ EuiccNotification.EVENT_DELETE | EuiccNotification.EVENT_DISABLE,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertArrayEquals(
+ new EuiccNotification[] {
+ new EuiccNotification(1, "smdp.com", EuiccNotification.EVENT_DELETE,
+ IccUtils.hexStringToBytes(
+ "3014BF2F118001010C08736D64702E636F6D81020410")),
+ new EuiccNotification(2, "smdp2.com", EuiccNotification.EVENT_DISABLE,
+ IccUtils.hexStringToBytes(
+ "3015BF2F128001020C09736D6470322E636F6D81020420"))
+ },
+ resultCaptor.result);
+ verifyStoreData(channel, "BF2B06A00481020430");
+ }
+
+ @Test
+ public void testRetrieveNotificationList_Empty() {
+ int channel = mockLogicalChannelResponses("BF2B038101019000");
+
+ ResultCaptor<EuiccNotification[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.retrieveNotificationList(
+ EuiccNotification.EVENT_DELETE | EuiccNotification.EVENT_DISABLE,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertArrayEquals(new EuiccNotification[0], resultCaptor.result);
+ verifyStoreData(channel, "BF2B06A00481020430");
+ }
+
+ @Test
+ public void testRetrieveNotificationList_Error() {
+ int channel = mockLogicalChannelResponses("BF2B038101039000");
+
+ ResultCaptor<EuiccNotification[]> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.retrieveNotificationList(
+ EuiccNotification.EVENT_DELETE | EuiccNotification.EVENT_DISABLE,
+ resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(3, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF2B06A00481020430");
+ }
+
+ @Test
+ public void testRetrieveNotification() {
+ int channel = mockLogicalChannelResponses("BF2B18A016"
+ + "3014BF2F118001010C08736D64702E636F6D81020410" // Notification
+ + "9000");
+
+ ResultCaptor<EuiccNotification> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.retrieveNotification(5, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(
+ new EuiccNotification(1, "smdp.com", EuiccNotification.EVENT_DELETE,
+ IccUtils.hexStringToBytes("3014BF2F118001010C08736D64702E636F6D81020410")),
+ resultCaptor.result);
+ verifyStoreData(channel, "BF2B05A003800105");
+ }
+
+ @Test
+ public void testRetrieveNotification_Error() {
+ int channel = mockLogicalChannelResponses("BF2B038101019000");
+
+ ResultCaptor<EuiccNotification> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.retrieveNotification(5, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertEquals(1, ((EuiccCardErrorException) resultCaptor.exception).getErrorCode());
+ verifyStoreData(channel, "BF2B05A003800105");
+ }
+
+ @Test
+ public void testRemoveNotificationFromList() {
+ int channel = mockLogicalChannelResponses("BF30038001009000");
+
+ ResultCaptor<Void> resultCaptor = new ResultCaptor<>();
+ mEuiccCard.removeNotificationFromList(5, resultCaptor, mHandler);
+ resultCaptor.await();
+
+ assertUnexpectedException(resultCaptor.exception);
+ verifyStoreData(channel, "BF3003800105");
+ }
+
+ @Test
+ public void testAddDeviceCapability() throws InvalidAsn1DataException, TagNotFoundException {
+ Asn1Node.Builder devCapsBuilder = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1);
+
+ String devCapItem = "gsm,11";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ Asn1Node node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_0));
+ Asn1Node child = node.getChild(Tags.TAG_CTX_0);
+ assertTrue(Arrays.equals(new byte[] {11, 0 , 0}, child.asBytes()));
+
+ devCapItem = "utran,11";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_1));
+ child = node.getChild(Tags.TAG_CTX_1);
+ assertTrue(Arrays.equals(new byte[] {11, 0 , 0}, child.asBytes()));
+
+ devCapItem = "cdma_1x,1";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_2));
+ child = node.getChild(Tags.TAG_CTX_2);
+ assertTrue(Arrays.equals(new byte[] {1, 0 , 0}, child.asBytes()));
+
+ devCapItem = "hrpd,1";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_3));
+ child = node.getChild(Tags.TAG_CTX_3);
+ assertTrue(Arrays.equals(new byte[] {1, 0 , 0}, child.asBytes()));
+
+ devCapItem = "ehrpd,12";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_4));
+ child = node.getChild(Tags.TAG_CTX_4);
+ assertTrue(Arrays.equals(new byte[] {12, 0 , 0}, child.asBytes()));
+
+ devCapItem = "eutran,11";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_5));
+ child = node.getChild(Tags.TAG_CTX_5);
+ assertTrue(Arrays.equals(new byte[] {11, 0 , 0}, child.asBytes()));
+
+ devCapItem = "nfc,0";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_6));
+ child = node.getChild(Tags.TAG_CTX_6);
+ assertTrue(Arrays.equals(new byte[] {0, 0 , 0}, child.asBytes()));
+
+ devCapItem = "crl,0";
+ mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
+ node = devCapsBuilder.build();
+
+ assertTrue(node.hasChild(Tags.TAG_CTX_7));
+ child = node.getChild(Tags.TAG_CTX_7);
+ assertTrue(Arrays.equals(new byte[] {0, 0 , 0}, child.asBytes()));
+
+ // Array length should not be 3.
+ Asn1Node.Builder devCapsBuilder2 = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1);
+ devCapItem = "gsm,1,1";
+ mEuiccCard.addDeviceCapability(devCapsBuilder2, devCapItem);
+ node = devCapsBuilder2.build();
+
+ assertFalse(node.hasChild(Tags.TAG_CTX_0));
+ }
+
+ 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());
+ }
+
+ private int mockLogicalChannelResponses(Object... responses) {
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
+ "E00582030200009000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses);
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+ return channel;
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccSpecVersionTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccSpecVersionTest.java
new file mode 100644
index 0000000..1046ef7
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccSpecVersionTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.uicc.euicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.junit.Test;
+
+public class EuiccSpecVersionTest {
+ @Test
+ public void testCreate() {
+ EuiccSpecVersion ver = new EuiccSpecVersion(1, 2, 3);
+ assertEquals(1, ver.getMajor());
+ assertEquals(2, ver.getMinor());
+ assertEquals(3, ver.getRevision());
+ }
+
+ @Test
+ public void testCreateFromBytes() {
+ EuiccSpecVersion ver = new EuiccSpecVersion(new byte[] {1, 2, 3});
+ assertEquals(1, ver.getMajor());
+ assertEquals(2, ver.getMinor());
+ assertEquals(3, ver.getRevision());
+ }
+
+ @Test
+ public void testCompareTo() {
+ EuiccSpecVersion ver1 = new EuiccSpecVersion(1, 2, 3);
+ EuiccSpecVersion ver2 = new EuiccSpecVersion(1, 2, 3);
+ assertEquals(0, ver1.compareTo(ver2));
+ assertEquals(0, ver2.compareTo(ver1));
+ assertTrue(ver1.equals(ver2));
+ assertFalse(ver1.equals(null));
+ assertFalse(ver1.equals(new Object()));
+ assertEquals(ver1.hashCode(), ver2.hashCode());
+
+ ver2 = new EuiccSpecVersion(2, 2, 3);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+ assertFalse(ver2.equals(ver1));
+ assertNotEquals(ver1.hashCode(), ver2.hashCode());
+
+ ver2 = new EuiccSpecVersion(1, 3, 3);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+ assertFalse(ver2.equals(ver1));
+ assertNotEquals(ver1.hashCode(), ver2.hashCode());
+
+ ver2 = new EuiccSpecVersion(1, 2, 4);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+ assertFalse(ver2.equals(ver1));
+ assertNotEquals(ver1.hashCode(), ver2.hashCode());
+
+ ver2 = new EuiccSpecVersion(2, 2, 2);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+
+ ver2 = new EuiccSpecVersion(2, 1, 3);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+
+ ver2 = new EuiccSpecVersion(1, 3, 2);
+ assertEquals(-1, ver1.compareTo(ver2));
+ assertEquals(1, ver2.compareTo(ver1));
+ }
+
+ @Test
+ public void testFromOpenChannelResponse() {
+ assertEquals(new EuiccSpecVersion(2, 1, 3),
+ EuiccSpecVersion.fromOpenChannelResponse(
+ IccUtils.hexStringToBytes("E00582030201039000")));
+ }
+
+ @Test
+ public void testFromOpenChannelResponseError() {
+ // Invalid data
+ assertNull(EuiccSpecVersion.fromOpenChannelResponse(
+ IccUtils.hexStringToBytes("E00F05820202039000")));
+ // Wrong length
+ assertNull(EuiccSpecVersion.fromOpenChannelResponse(
+ IccUtils.hexStringToBytes("E005820202039000")));
+ // Wrong tag
+ assertNull(EuiccSpecVersion.fromOpenChannelResponse(
+ IccUtils.hexStringToBytes("E10482030201039000")));
+ }
+}
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
new file mode 100644
index 0000000..55626a2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+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.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ApduSenderTest {
+ private static final long WAIT_TIMEOUT_MLLIS = 5000;
+
+ private static class ResponseCaptor extends AsyncResultCallback<byte[]> {
+ public byte[] response;
+ public Throwable exception;
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ public void await() {
+ try {
+ if (!mLatch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS)) {
+ fail("Execution timed out.");
+ }
+ } catch (InterruptedException e) {
+ fail("Execution interrupted: " + e);
+ }
+ }
+
+ @Override
+ public void onResult(byte[] bytes) {
+ response = bytes;
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onException(Throwable e) {
+ exception = e;
+ mLatch.countDown();
+ }
+ }
+
+ @Mock
+ private CommandsInterface mMockCi;
+
+ private HandlerThread mThread;
+ private Handler mHandler;
+ private ResponseCaptor mResponseCaptor;
+ private byte[] mSelectResponse;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mThread = new HandlerThread("ApduSenderTest");
+ mThread.start();
+ mHandler = mThread.getThreadHandler();
+
+ mResponseCaptor = new ResponseCaptor();
+ mSelectResponse = null;
+ }
+
+ @After
+ public void tearDown() {
+ mThread.quit();
+ }
+
+ @Test
+ public void testSendEmptyCommands() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "A1A1A19000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ sender.send((selectResponse, requestBuilder) -> mSelectResponse = selectResponse,
+ mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ assertEquals("A1A1A19000", IccUtils.bytesToHexString(mSelectResponse));
+ assertNull(mResponseCaptor.response);
+ assertNull(mResponseCaptor.exception);
+ verify(mMockCi).iccOpenLogicalChannel(eq(aid), anyInt(), any());
+ verify(mMockCi).iccCloseLogicalChannel(eq(channel), any());
+ }
+
+ @Test
+ public void testOpenChannelErrorStatus() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
+ new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
+
+ sender.send((selectResponse, requestBuilder) -> mSelectResponse = new byte[0],
+ mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ assertNull("Request provider should not be called when failed to open channel.",
+ mSelectResponse);
+ assertTrue(mResponseCaptor.exception instanceof ApduException);
+ verify(mMockCi).iccOpenLogicalChannel(eq(aid), anyInt(), any());
+ }
+
+ @Test
+ public void testSend() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ sender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(10, 1, 2, 3, 0, "a"),
+ mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ 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());
+ }
+
+ @Test
+ public void testSendMultiApdus() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "A29000",
+ "A39000", "A49000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ sender.send((selectResponse, requestBuilder) -> {
+ requestBuilder.addApdu(10, 1, 2, 3, 0, "a");
+ requestBuilder.addApdu(10, 1, 2, 3, "ab");
+ requestBuilder.addApdu(10, 1, 2, 3);
+ requestBuilder.addStoreData("abcd");
+ }, mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ 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());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(1), eq("ab"), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(0), eq(""), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
+ eq(0), eq(2), eq("abcd"), any());
+ }
+
+ @Test
+ public void testSendLongResponse() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A16104",
+ "B2B2B2B26102", "C3C39000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ sender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(10, 1, 2, 3, 0, "a"),
+ mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ 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());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0),
+ eq(0), eq(4), eq(""), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel), eq(0xC0), eq(0),
+ eq(0), eq(2), eq(""), any());
+ }
+
+ @Test
+ public void testSendStoreDataLongDataLongResponse() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000", "9000",
+ "B22B6103", "B2222B9000", "C39000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ // Each segment has 0xFF (the limit of a single command) bytes.
+ String s1 = new String(new char[0xFF]).replace("\0", "AA");
+ String s2 = new String(new char[0xFF]).replace("\0", "BB");
+ String s3 = new String(new char[16]).replace("\0", "CC");
+ String longData = s1 + s2 + s3;
+ sender.send((selectResponse, requestBuilder) -> {
+ requestBuilder.addApdu(10, 1, 2, 3, 0, "a");
+ requestBuilder.addApdu(10, 1, 2, 3, 0, "b");
+ requestBuilder.addStoreData(longData);
+ }, mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ 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());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(0), eq("b"), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
+ eq(0), eq(0xFF), eq(s1), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
+ eq(1), eq(0xFF), eq(s2), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
+ eq(2), eq(16), eq(s3), any());
+ }
+
+ @Test
+ public void testSendErrorResponseInMiddle() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000",
+ "B22B6103", "6985");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ // Each segment has 0xFF (the limit of a single command) bytes.
+ String s1 = new String(new char[0xFF]).replace("\0", "AA");
+ String s2 = new String(new char[0xFF]).replace("\0", "BB");
+ String s3 = new String(new char[16]).replace("\0", "CC");
+ String longData = s1 + s2 + s3;
+ sender.send((selectResponse, requestBuilder) -> {
+ requestBuilder.addApdu(10, 1, 2, 3, 0, "a");
+ requestBuilder.addStoreData(longData);
+ }, mResponseCaptor, mHandler);
+ mResponseCaptor.await();
+
+ 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());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
+ eq(0), eq(0xFF), eq(s1), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
+ eq(1), eq(0xFF), eq(s2), any());
+ verify(mMockCi, never()).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2),
+ eq(0x91), eq(2), eq(16), eq(s3), any());
+ }
+
+ @Test
+ public void testChannelAlreadyOpened() throws InterruptedException {
+ String aid = "B2C3D4";
+ ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+ int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+ LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+ ResponseCaptor outerResponseCaptor = new ResponseCaptor();
+ sender.send(
+ (selectResponse, requestBuilder) -> sender.send(
+ (selectResponseOther, requestBuilderOther) ->
+ mSelectResponse = selectResponseOther,
+ mResponseCaptor, mHandler),
+ outerResponseCaptor, mHandler);
+ mResponseCaptor.await();
+ outerResponseCaptor.await();
+
+ assertNull("Should not open channel when another one is already opened.", mSelectResponse);
+ assertTrue(mResponseCaptor.exception instanceof ApduException);
+ verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(aid), anyInt(), any());
+ }
+}
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
new file mode 100644
index 0000000..e9796a1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
@@ -0,0 +1,117 @@
+/*
+ * 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.uicc.euicc.apdu;
+
+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 android.annotation.Nullable;
+import android.os.AsyncResult;
+import android.os.Message;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/** Utility to set up mocks for communication with UICC through logical channel. */
+public final class LogicalChannelMocker {
+ private static final int LOGICAL_CHANNEL = 1;
+
+ /**
+ * @param responseObject Can be either a string or an exception.
+ * @return The mock channel number.
+ */
+ public static int mockOpenLogicalChannelResponse(CommandsInterface mockCi,
+ @Nullable Object responseObject) {
+ boolean isException = responseObject instanceof Throwable;
+ int[] responseInts = isException ? null : getSelectResponse(responseObject.toString());
+ Throwable exception = isException ? (Throwable) responseObject : null;
+
+ ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
+ doAnswer((Answer<Void>) invocation -> {
+ Message msg = response.getValue();
+ AsyncResult.forMessage(msg, responseInts, exception);
+ msg.sendToTarget();
+ return null;
+ }).when(mockCi).iccOpenLogicalChannel(anyString(), anyInt(), response.capture());
+ return LOGICAL_CHANNEL;
+ }
+
+ /**
+ * @param responseObjects Can be either a string or an exception. For string, the last 4
+ * digits are the status (sw1, sw1).
+ */
+ 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();
+ return null;
+ }
+ }).when(mockCi).iccTransmitApduLogicalChannel(eq(channel), anyInt(), anyInt(), anyInt(),
+ anyInt(), anyInt(), anyString(), response.capture());
+ }
+
+ public static void mockCloseLogicalChannel(CommandsInterface mockCi, int channel) {
+ ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
+ doAnswer((Answer<Void>) invocation -> {
+ Message msg = response.getValue();
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ return null;
+ }).when(mockCi).iccCloseLogicalChannel(eq(channel), response.capture());
+ }
+
+ private static int[] getSelectResponse(String responseHex) {
+ byte[] responseBytes = IccUtils.hexStringToBytes("00" + responseHex);
+ int[] responseInts = new int[responseBytes.length];
+ responseInts[0] = LOGICAL_CHANNEL;
+ for (int i = 1; i < responseInts.length; ++i) {
+ responseInts[i] = responseBytes[i];
+ }
+ return responseInts;
+ }
+
+ private LogicalChannelMocker() {
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1DecoderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1DecoderTest.java
new file mode 100644
index 0000000..cfd8b99
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1DecoderTest.java
@@ -0,0 +1,280 @@
+/*
+ * 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.euicc.asn1;
+
+import static org.junit.Assert.assertArrayEquals;
+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 android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
+import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
+
+import org.junit.Test;
+
+public class Asn1DecoderTest {
+
+ @SmallTest
+ @Test
+ public void testNormalOperation() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "e30c5a0a98102100000000000000";
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+ assertEquals(0, decoder.getPosition());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node root = decoder.nextNode();
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ assertTrue(root.isConstructed());
+ assertEquals(0xE3, root.getTag());
+ assertEquals(0x0E, root.getEncodedLength());
+ assertFalse(root.hasValue());
+
+ Asn1Node node = root.getChild(0x5A);
+ assertEquals(1, root.getChildren().size());
+ assertEquals(1, root.getChildren(0x5A).size());
+ assertEquals(node, root.getChildren().get(0));
+ assertEquals(node, root.getChildren(0x5A).get(0));
+ assertFalse(node.isConstructed());
+ assertEquals(0x5A, node.getTag());
+ assertEquals(0x0C, node.getEncodedLength());
+ assertTrue(node.hasValue());
+ assertArrayEquals(
+ new byte[] {(byte) 0x98, 0x10, 0x21, 0, 0, 0, 0, 0, 0, 0}, node.asBytes());
+
+ assertFalse(decoder.hasNextNode());
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ }
+
+ @SmallTest
+ @Test
+ public void testHighTagNumberForm() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "bf370c5a0a98102100000000000000";
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node root = decoder.nextNode();
+ assertTrue(root.isConstructed());
+ assertEquals(0xBF37, root.getTag());
+ assertEquals(0x0F, root.getEncodedLength());
+ assertTrue(root.isConstructed());
+
+ Asn1Node node = root.getChild(0x5A);
+ assertEquals(0x0C, node.getEncodedLength());
+ }
+
+ @SmallTest
+ @Test
+ public void testNodeList() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "e30c5a0a98102100000000000000e30c5a0a98102100000000000001";
+
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+ assertEquals(0, decoder.getPosition());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node firstRoot = decoder.nextNode();
+ assertEquals(hex.length() / 4, decoder.getPosition());
+ assertTrue(firstRoot.isConstructed());
+ assertEquals(0xE3, firstRoot.getTag());
+ Asn1Node childOfFirst = firstRoot.getChild(0x5A);
+ assertNotNull(childOfFirst);
+ assertFalse(childOfFirst.isConstructed());
+ assertEquals(0x5A, childOfFirst.getTag());
+ assertTrue(childOfFirst.hasValue());
+ assertArrayEquals(
+ new byte[] {(byte) 0x98, 0x10, 0x21, 0, 0, 0, 0, 0, 0, 0}, childOfFirst.asBytes());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node secondRoot = decoder.nextNode();
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ assertTrue(secondRoot.isConstructed());
+ assertEquals(0xE3, secondRoot.getTag());
+ Asn1Node childOfSecond = secondRoot.getChild(0x5A);
+ assertNotNull(childOfSecond);
+ assertFalse(childOfSecond.isConstructed());
+ assertEquals(0x5A, childOfSecond.getTag());
+ assertTrue(childOfSecond.hasValue());
+ assertArrayEquals(
+ new byte[] {(byte) 0x98, 0x10, 0x21, 0, 0, 0, 0, 0, 0, 1}, childOfSecond.asBytes());
+
+ assertFalse(decoder.hasNextNode());
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ }
+
+ @SmallTest
+ @Test
+ public void testMissingData() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "e30c5a0a98102100000000000000e30c5a0a98102100000000";
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+ assertEquals(0, decoder.getPosition());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node firstRoot = decoder.nextNode();
+ assertEquals(0x0E, decoder.getPosition());
+ assertTrue(firstRoot.isConstructed());
+ assertEquals(0xE3, firstRoot.getTag());
+ Asn1Node childOfFirst = firstRoot.getChild(0x5A);
+ assertNotNull(childOfFirst);
+ assertFalse(childOfFirst.isConstructed());
+ assertEquals(0x5A, childOfFirst.getTag());
+ assertTrue(childOfFirst.hasValue());
+ assertArrayEquals(
+ new byte[] {(byte) 0x98, 0x10, 0x21, 0, 0, 0, 0, 0, 0, 0}, childOfFirst.asBytes());
+
+ assertTrue(decoder.hasNextNode());
+ try {
+ decoder.nextNode();
+ fail("Bytes should not be parsed.");
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0xE3, e.getTag());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void testEmptyData() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "e3025a00e3025a00";
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+ assertEquals(0, decoder.getPosition());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node firstRoot = decoder.nextNode();
+ assertEquals(hex.length() / 4, decoder.getPosition());
+ assertTrue(firstRoot.isConstructed());
+ assertEquals(0xE3, firstRoot.getTag());
+ Asn1Node childOfFirst = firstRoot.getChild(0x5A);
+ assertNotNull(childOfFirst);
+ assertFalse(childOfFirst.isConstructed());
+ assertEquals(0x5A, childOfFirst.getTag());
+ assertTrue(childOfFirst.hasValue());
+ assertArrayEquals(new byte[] {}, childOfFirst.asBytes());
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node secondRoot = decoder.nextNode();
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ assertTrue(secondRoot.isConstructed());
+ assertEquals(0xE3, secondRoot.getTag());
+ Asn1Node childOfSecond = secondRoot.getChild(0x5A);
+ assertNotNull(childOfSecond);
+ assertFalse(childOfSecond.isConstructed());
+ assertEquals(0x5A, childOfSecond.getTag());
+ assertTrue(childOfSecond.hasValue());
+ assertArrayEquals(new byte[] {}, childOfSecond.asBytes());
+
+ assertFalse(decoder.hasNextNode());
+ assertEquals(hex.length() / 2, decoder.getPosition());
+ }
+
+ @SmallTest
+ @Test
+ public void testLongFormLength() throws TagNotFoundException, InvalidAsn1DataException {
+ String hex = "bf37075a820003010203";
+ Asn1Decoder decoder = new Asn1Decoder(hex);
+
+ assertTrue(decoder.hasNextNode());
+ Asn1Node root = decoder.nextNode();
+ assertTrue(root.isConstructed());
+ assertEquals(0xBF37, root.getTag());
+ assertEquals(10, root.getEncodedLength());
+ assertTrue(root.isConstructed());
+
+ Asn1Node node = root.getChild(0x5A);
+ assertEquals(5, node.getEncodedLength());
+ assertArrayEquals(new byte[] {1, 2, 3}, node.asBytes());
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testDecodeEmptyByteArray() throws TagNotFoundException, InvalidAsn1DataException {
+ new Asn1Decoder("").nextNode();
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testDecodeIndexOutOfBounds() throws TagNotFoundException, InvalidAsn1DataException {
+ new Asn1Decoder(new byte[] {1, 2}, 0, 3);
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testIncompleteLength() throws TagNotFoundException, InvalidAsn1DataException {
+ try {
+ new Asn1Decoder("BF37").nextNode();
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0, e.getTag());
+ assertEquals("Invalid length at position: 2 (tag=0)", e.getMessage());
+ throw e;
+ }
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testInvalidTag() throws TagNotFoundException, InvalidAsn1DataException {
+ try {
+ new Asn1Decoder("BF818283840100").nextNode();
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0, e.getTag());
+ assertEquals("Cannot parse tag at position: 0 (tag=0)", e.getMessage());
+ throw e;
+ }
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testIncompleteData() throws TagNotFoundException, InvalidAsn1DataException {
+ try {
+ new Asn1Decoder("BF81010200").nextNode();
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0xBF8101, e.getTag());
+ assertEquals(
+ "Incomplete data at position: 4, expected bytes: 2, "
+ + "actual bytes: 1 (tag=12550401)",
+ e.getMessage());
+ throw e;
+ }
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testIncompleteLongFormLength()
+ throws TagNotFoundException, InvalidAsn1DataException {
+ try {
+ new Asn1Decoder("BF81018200").nextNode();
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0xBF8101, e.getTag());
+ assertEquals("Cannot parse length at position: 4 (tag=12550401)", e.getMessage());
+ throw e;
+ }
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testInvalidLongFormLength() throws TagNotFoundException, InvalidAsn1DataException {
+ try {
+ new Asn1Decoder("BF8101851234567890").nextNode();
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0xBF8101, e.getTag());
+ assertEquals("Cannot parse length at position: 4 (tag=12550401)", e.getMessage());
+ throw e;
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1NodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1NodeTest.java
new file mode 100644
index 0000000..e987a1f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/Asn1NodeTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.euicc.asn1;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
+import com.android.internal.telephony.uicc.asn1.Asn1Node;
+import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
+import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
+
+import org.junit.Test;
+
+public class Asn1NodeTest {
+
+ @SmallTest
+ @Test
+ public void testCreateConstructedNodeWithShortTag() {
+ Asn1Node node = Asn1Node.newBuilder(0x65).build();
+ assertEquals(0x65, node.getTag());
+ assertTrue(node.isConstructed());
+ assertEquals(2, node.getEncodedLength());
+ assertEquals(0, node.getDataLength());
+ assertArrayEquals(new byte[] {0x65, 0x00}, node.toBytes());
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateConstructedNodeWithLongTag() {
+ Asn1Node node = Asn1Node.newBuilder(0x3F34).build();
+ assertEquals(0x3F34, node.getTag());
+ assertTrue(node.isConstructed());
+ assertEquals(3, node.getEncodedLength());
+ assertEquals(0, node.getDataLength());
+ assertArrayEquals(new byte[] {0x3F, 0x34, 0x00}, node.toBytes());
+ }
+
+ @SmallTest
+ @Test
+ public void testChildren() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node =
+ Asn1Node.newBuilder(0x3F34)
+ .addChild(Asn1Node.newBuilder(0x35).addChild(Asn1Node.newBuilder(0x36)))
+ .addChild(Asn1Node.newBuilder(0x35))
+ .addChild(Asn1Node.newBuilder(0x37))
+ .build();
+
+ assertEquals(0x3F34, node.getTag());
+ assertEquals(3, node.getChildren().size());
+ assertEquals(0x35, node.getChildren().get(0).getTag());
+ assertEquals(0x35, node.getChildren().get(1).getTag());
+ assertEquals(0x37, node.getChildren().get(2).getTag());
+ assertEquals(2, node.getChildren(0x35).size());
+ assertEquals(0x35, node.getChildren(0x35).get(0).getTag());
+ assertEquals(0x35, node.getChildren(0x35).get(1).getTag());
+ assertEquals(1, node.getChild(0x35).getChildren().size());
+ assertEquals(0, node.getChildren().get(1).getChildren().size());
+ assertEquals(0x36, node.getChild(0x35).getChildren().get(0).getTag());
+
+ assertTrue(node.hasChild(0x35, 0x36));
+ assertFalse(node.hasChild(0x38, 0x39));
+ assertTrue(node.getChild(0x35).hasChild(0x36));
+ assertFalse(node.getChild(0x35).hasChild(0x39));
+ }
+
+ @SmallTest
+ @Test
+ public void testNonExitingChildren() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node =
+ Asn1Node.newBuilder(0x3F34)
+ .addChild(Asn1Node.newBuilder(0x34))
+ .addChild(Asn1Node.newBuilder(0x35).addChild(Asn1Node.newBuilder(0x36)))
+ .addChildAsInteger(0x11, 123)
+ .build();
+
+ assertEquals(0, node.getChild(0x34).getChildren(0x37).size());
+ assertEquals(0, node.getChildren(0x37).size());
+ assertEquals(0, node.getChild(0x11).getChildren(0x37).size());
+ assertEquals(0, node.getChild(0x11).getChildren().size());
+ try {
+ node.getChild(0x35).getChild(0x37);
+ fail("0x37 should not exist.");
+ } catch (TagNotFoundException e) {
+ assertEquals(0x37, e.getTag());
+ }
+ try {
+ node.getChild(0x35, 0x37);
+ fail("0x37 should not exist.");
+ } catch (TagNotFoundException e) {
+ assertEquals(0x37, e.getTag());
+ }
+ try {
+ node.getChild(0x11).getChild(0x37);
+ fail("0x37 should not exist.");
+ } catch (TagNotFoundException e) {
+ assertEquals(0x37, e.getTag());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void testBuilderAndGetters() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node =
+ Asn1Node.newBuilder(0x30)
+ .addChildAsInteger(0x11, 1234567)
+ .addChildAsString(0x12, "This is a test.")
+ .addChild(
+ Asn1Node.newBuilder(0x31)
+ .addChildAsBits(0x13, 0xF2344)
+ .addChildAsBytes(0x14, new byte[] {-1, 0, -1}))
+ .addChildAsBoolean(0x15, true)
+ .addChildAsBoolean(0x16, false)
+ .addChildren(IccUtils.hexStringToBytes("13040422C4F01403FF00FF"))
+ .build();
+
+ assertEquals(54, node.getEncodedLength());
+ assertEquals(52, node.getDataLength());
+
+ final String nodeHex =
+ "3034110312D687120F54686973206973206120746573742E310B13040422C4F0"
+ + "1403FF00FF1501FF16010013040422C4F01403FF00FF";
+ assertEquals(nodeHex, node.toHex());
+ assertEquals("3034", node.getHeadAsHex());
+ assertEquals(1234567, node.getChild(0x11).asInteger());
+ assertEquals("This is a test.", node.getChild(0x12).asString());
+ assertEquals(0xF2344, node.getChild(0x31).getChild(0x13).asBits());
+ assertArrayEquals(new byte[] {-1, 0, -1}, node.getChild(0x31).getChild(0x14).asBytes());
+ assertTrue(node.getChild(0x15).asBoolean());
+ assertFalse(node.getChild(0x16).asBoolean());
+ assertEquals(0xF2344, node.getChild(0x13).asBits());
+ assertArrayEquals(new byte[] {-1, 0, -1}, node.getChild(0x14).asBytes());
+ }
+
+ @SmallTest
+ @Test
+ public void testGettersInvalidData() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0x30).addChildAsString(0x12, "string").build();
+ try {
+ node.getChild(0x12).asInteger();
+ fail("Should not be parsed.");
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0x12, e.getTag());
+ }
+ try {
+ node.getChild(0x12).asBits();
+ fail("Should not be parsed.");
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0x12, e.getTag());
+ }
+ try {
+ node.getChild(0x12).asBoolean();
+ fail("Should not be parsed.");
+ } catch (InvalidAsn1DataException e) {
+ assertEquals(0x12, e.getTag());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void testWriteToBytes() throws InvalidAsn1DataException {
+ final String nodeHex =
+ "3031110312D687120F54686973206973206120746573742E310B13040422C4F0"
+ + "1403FF00FF1501FF13040422C4F01403FF00FF";
+
+ Asn1Node node = new Asn1Decoder(nodeHex).nextNode();
+ // Writes the bytes before children accessed.
+ assertEquals(nodeHex, node.toHex());
+ // Writes the bytes after children accessed.
+ node.getChildren();
+ assertEquals(nodeHex, node.toHex());
+ }
+
+ @SmallTest
+ @Test
+ public void testLongFormLength() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).addChildAsBytes(0x5A, new byte[257]).build();
+ assertEquals(266, node.getEncodedLength());
+ assertEquals(261, node.getDataLength());
+ assertEquals(257, node.getChild(0x5A).getDataLength());
+ assertEquals("BF37820105", node.getHeadAsHex());
+
+ byte[] expected = new byte[266];
+ System.arraycopy(IccUtils.hexStringToBytes("BF378201055A820101"), 0, expected, 0, 9);
+ byte[] actual = new byte[266];
+ node.writeToBytes(actual, 0);
+ assertArrayEquals(expected, actual);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuilder_NonConstructedTag() {
+ Asn1Node.newBuilder(0x87);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderAddChildAsInteger_ConstructedTag() {
+ Asn1Node.newBuilder(0xBF37).addChildAsInteger(0xBF37, 1);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderAddChildAsString_ConstructedTag() {
+ Asn1Node.newBuilder(0xBF37).addChildAsString(0xBF37, "");
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderAddChildAsBytes_ConstructedTag() {
+ Asn1Node.newBuilder(0xBF37).addChildAsBytesFromHex(0xBF37, "00");
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderAddChildAsBits_ConstructedTag() {
+ Asn1Node.newBuilder(0xBF37).addChildAsBits(0xBF37, 1);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderAddChildAsBoolean_ConstructedTag() {
+ Asn1Node.newBuilder(0xBF37).addChildAsBoolean(0xBF37, true);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsInteger_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asInteger();
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsLong_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asRawLong();
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsString_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asString();
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsBytes_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asBytes();
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsBits_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asBits();
+ }
+
+ @SmallTest
+ @Test(expected = IllegalStateException.class)
+ public void testAsBoolean_ConstructedTag() throws InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).build();
+ node.asBoolean();
+ }
+
+ @SmallTest
+ @Test(expected = InvalidAsn1DataException.class)
+ public void testAsBoolean_InvalidData() throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).addChildAsString(1, "1").build();
+ node.getChild(1).asBoolean();
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testWriteToBytes_IndexOutOfBounds()
+ throws TagNotFoundException, InvalidAsn1DataException {
+ Asn1Node node = Asn1Node.newBuilder(0xBF37).addChildAsString(1, "1").build();
+ byte[] bytes = new byte[1];
+ node.writeToBytes(bytes, 0);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/IccUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/IccUtilsTest.java
new file mode 100644
index 0000000..9c1dd20
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/IccUtilsTest.java
@@ -0,0 +1,255 @@
+/*
+ * 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.uicc.euicc.asn1;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.junit.Test;
+
+public class IccUtilsTest {
+
+ @SmallTest
+ @Test
+ public void testBytesToInt() {
+ assertEquals(0, IccUtils.bytesToInt(new byte[] {}, 0, 0));
+ assertEquals(0, IccUtils.bytesToInt(new byte[] {0}, 0, 0));
+ assertEquals(0x1234, IccUtils.bytesToInt(new byte[] {0x12, 0x34}, 0, 2));
+ assertEquals(0x34, IccUtils.bytesToInt(new byte[] {0x12, 0x34}, 1, 1));
+ assertEquals(0xFF, IccUtils.bytesToInt(new byte[] {-2, 0, -1, -3, -4}, 1, 2));
+ assertEquals(0x7FFFFFFF, IccUtils.bytesToInt(new byte[] {0x7F, -1, -1, -1}, 0, 4));
+ assertEquals(0x80, IccUtils.bytesToInt(new byte[] {0x00, -128}, 0, 2));
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testBytesToInt_IllegalLength() {
+ IccUtils.bytesToInt(new byte[] {1, 2, 3, 4, 4}, 0, 5);
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testBytesToInt_IndexOutOfBounds() {
+ IccUtils.bytesToInt(new byte[] {1, 2}, 0, 3);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testBytesToInt_NegativeResult() {
+ IccUtils.bytesToInt(new byte[] {-1, 1, 2, 3}, 0, 4);
+ }
+
+ @SmallTest
+ @Test
+ public void testBytesToLong() {
+ assertEquals(0, IccUtils.bytesToRawLong(new byte[] {}, 0, 0));
+ assertEquals(0, IccUtils.bytesToRawLong(new byte[] {0}, 0, 0));
+ assertEquals(
+ 0x12345678,
+ IccUtils.bytesToRawLong(new byte[] {0x12, 0x34, 0x56, 0x78}, 0, 4));
+ assertEquals(0x34, IccUtils.bytesToRawLong(new byte[] {0x12, 0x34, 0x56, 0x78}, 1, 1));
+ assertEquals(0xFF, IccUtils.bytesToRawLong(new byte[] {-2, 0, -1, -3, -4}, 1, 2));
+ assertEquals(
+ -1,
+ IccUtils.bytesToRawLong(new byte[] {-1, -1, -1, -1, -1, -1, -1, -1}, 0, 8));
+ assertEquals(0x80, IccUtils.bytesToRawLong(new byte[] {0x00, -128}, 0, 2));
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testBytesToLong_IllegalLength() {
+ IccUtils.bytesToRawLong(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9}, 0, 9);
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testBytesToLong_IndexOutOfBounds() {
+ IccUtils.bytesToRawLong(new byte[] {1, 2}, 0, 3);
+ }
+
+ @SmallTest
+ @Test
+ public void testUnsignedToBytes() {
+ assertArrayEquals(new byte[] {0}, IccUtils.unsignedIntToBytes(0));
+ assertArrayEquals(new byte[] {0x12, 0x34}, IccUtils.unsignedIntToBytes(0x1234));
+ assertArrayEquals(
+ new byte[] {0x7F, 0x12, -2, 0x34}, IccUtils.unsignedIntToBytes(0x7F12FE34));
+ assertArrayEquals(new byte[] {-128}, IccUtils.unsignedIntToBytes(0x80));
+ assertArrayEquals(new byte[] {-128, 0x05}, IccUtils.unsignedIntToBytes(0x8005));
+
+ byte[] bs = new byte[] {0, 0, 0, 0, 0, 0};
+ assertEquals(4, IccUtils.unsignedIntToBytes(0x7F12FE34, bs, 2));
+ assertArrayEquals(new byte[] {0, 0, 0x7F, 0x12, -2, 0x34}, bs);
+ }
+
+ @SmallTest
+ @Test
+ public void testSignedToBytes() {
+ assertArrayEquals(new byte[] {0}, IccUtils.signedIntToBytes(0));
+ assertArrayEquals(new byte[] {0x12, 0x34}, IccUtils.signedIntToBytes(0x1234));
+ assertArrayEquals(
+ new byte[] {0x7F, 0x12, -2, 0x34}, IccUtils.signedIntToBytes(0x7F12FE34));
+ assertArrayEquals(new byte[] {0, -128}, IccUtils.signedIntToBytes(0x80));
+ assertArrayEquals(new byte[] {0, -128, 0x05}, IccUtils.signedIntToBytes(0x8005));
+
+ byte[] bs = new byte[] {0, 0, 0, 0, 0, 0};
+ assertEquals(4, IccUtils.signedIntToBytes(0x801234, bs, 2));
+ assertArrayEquals(new byte[] {0, 0, 0, -128, 0x12, 0x34}, bs);
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testUnsignedIntToBytes_NoEnoughSpace() {
+ byte[] bs = new byte[] {0, 0, 0, 0, 0, 0};
+ IccUtils.unsignedIntToBytes(0x7F12FE34, bs, 3);
+ }
+
+ @SmallTest
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testSignedIntToBytes_NoEnoughSpace() {
+ byte[] bs = new byte[] {0, 0, 0, 0, 0, 0};
+ IccUtils.signedIntToBytes(0x801234, bs, 3);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testUnsignedIntToBytes_NegativeNumber() {
+ IccUtils.unsignedIntToBytes(-1);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testSignedIntToBytes_NegativeNumber() {
+ IccUtils.signedIntToBytes(-1);
+ }
+
+ @SmallTest
+ @Test
+ public void testByteNumForUnsignedInt() {
+ assertEquals(1, IccUtils.byteNumForUnsignedInt(0));
+ assertEquals(1, IccUtils.byteNumForUnsignedInt(0x12));
+ assertEquals(1, IccUtils.byteNumForUnsignedInt(0x7F));
+ assertEquals(1, IccUtils.byteNumForUnsignedInt(0x80));
+ assertEquals(2, IccUtils.byteNumForUnsignedInt(0x7FFF));
+ assertEquals(2, IccUtils.byteNumForUnsignedInt(0x8000));
+ assertEquals(3, IccUtils.byteNumForUnsignedInt(0x7FFFFF));
+ assertEquals(3, IccUtils.byteNumForUnsignedInt(0x800000));
+ assertEquals(4, IccUtils.byteNumForUnsignedInt(0x12345678));
+ assertEquals(4, IccUtils.byteNumForUnsignedInt(0x7FFFFFFF));
+ }
+
+ @SmallTest
+ @Test
+ public void testByteNumForSignedInt() {
+ assertEquals(1, IccUtils.byteNumForSignedInt(0));
+ assertEquals(1, IccUtils.byteNumForSignedInt(0x12));
+ assertEquals(1, IccUtils.byteNumForSignedInt(0x7F));
+ assertEquals(2, IccUtils.byteNumForSignedInt(0x80));
+ assertEquals(2, IccUtils.byteNumForSignedInt(0x7FFF));
+ assertEquals(3, IccUtils.byteNumForSignedInt(0x8000));
+ assertEquals(3, IccUtils.byteNumForSignedInt(0x7FFFFF));
+ assertEquals(4, IccUtils.byteNumForSignedInt(0x800000));
+ assertEquals(4, IccUtils.byteNumForSignedInt(0x12345678));
+ assertEquals(4, IccUtils.byteNumForSignedInt(0x7FFFFFFF));
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testByteNumForUnsignedInt_NegativeNumber() {
+ IccUtils.byteNumForUnsignedInt(0xFFFFFFFF);
+ }
+
+ @SmallTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testByteNumForSignedInt_NegativeNumber() {
+ IccUtils.byteNumForSignedInt(0xFFFFFFFF);
+ }
+
+ @SmallTest
+ @Test
+ public void testCountTrailingZeros() {
+ assertEquals(8, IccUtils.countTrailingZeros((byte) 0));
+ assertEquals(7, IccUtils.countTrailingZeros((byte) 0x80));
+ assertEquals(5, IccUtils.countTrailingZeros((byte) 0xA0));
+ assertEquals(0, IccUtils.countTrailingZeros((byte) 1));
+ }
+
+ @SmallTest
+ @Test
+ public void testBytesToHexString() {
+ assertEquals("", IccUtils.bytesToHexString(new byte[] {}));
+ assertEquals("00", IccUtils.bytesToHexString(new byte[] {0}));
+ assertEquals(
+ "FF12FE34FD56FC78",
+ IccUtils.bytesToHexString(new byte[] {-1, 0x12, -2, 0x34, -3, 0x56, -4, 0x78}));
+ }
+
+ @SmallTest
+ @Test
+ public void testHexStringToBytes() {
+ assertArrayEquals(new byte[] {}, IccUtils.hexStringToBytes(""));
+ assertArrayEquals(new byte[] {0}, IccUtils.hexStringToBytes("00"));
+ assertArrayEquals(
+ new byte[] {-1, 0x12, -2, 0x34, -3, 0x56, -4, 0x78},
+ IccUtils.hexStringToBytes("FF12FE34FD56FC78"));
+ }
+
+ @SmallTest
+ @Test
+ public void testBcdToString() {
+ assertEquals("", IccUtils.bcdToString(new byte[] {}));
+ assertEquals("00", IccUtils.bcdToString(new byte[] {0}));
+ assertEquals("214365", IccUtils.bcdToString(new byte[] {0x12, 0x34, 0x56}));
+ }
+
+ @SmallTest
+ @Test
+ public void testBcdToBytes() {
+ assertArrayEquals(new byte[] {}, IccUtils.bcdToBytes(""));
+ assertArrayEquals(new byte[] {0}, IccUtils.bcdToBytes("00"));
+ assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, IccUtils.bcdToBytes("123456"));
+ assertArrayEquals(new byte[] {0x21, 0x43, 0x05}, IccUtils.bcdToBytes("12345"));
+
+ byte[] output;
+ output = new byte[3];
+ IccUtils.bcdToBytes("123456", output);
+ assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, output);
+
+ output = new byte[2];
+ IccUtils.bcdToBytes("123456", output);
+ assertArrayEquals(new byte[] {0x21, 0x43}, output);
+
+ output = new byte[4];
+ IccUtils.bcdToBytes("12345", output);
+ assertArrayEquals(new byte[] {0x21, 0x43, 0x05, 0}, output);
+ }
+
+ @SmallTest
+ @Test
+ public void testStripTrailingFs() {
+ assertNull(IccUtils.stripTrailingFs(null));
+ assertEquals("", IccUtils.stripTrailingFs(""));
+ assertEquals("1234", IccUtils.stripTrailingFs("1234"));
+ assertEquals("1234", IccUtils.stripTrailingFs("1234ff"));
+ assertEquals("1234", IccUtils.stripTrailingFs("1234FF"));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccNotificationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccNotificationTest.java
new file mode 100644
index 0000000..1845407
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccNotificationTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.uicc.euicc.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 android.telephony.euicc.EuiccNotification;
+
+import org.junit.Test;
+
+public class EuiccNotificationTest {
+ @Test
+ public void testEqualsHashCode() {
+ EuiccNotification n =
+ new EuiccNotification(1, "g.co", EuiccNotification.EVENT_DELETE, new byte[]{1});
+ assertTrue(n.equals(n));
+ assertFalse(n.equals(new Object()));
+
+ EuiccNotification t = null;
+ assertFalse(n.equals(t));
+
+ t = new EuiccNotification(1, "g.co", EuiccNotification.EVENT_DELETE, new byte[]{1});
+ assertTrue(n.equals(t));
+ assertEquals(n.hashCode(), t.hashCode());
+
+ t = new EuiccNotification(2, "g.co", EuiccNotification.EVENT_DELETE, new byte[]{1});
+ assertFalse(n.equals(t));
+ assertNotEquals(n.hashCode(), t.hashCode());
+
+ t = new EuiccNotification(1, "x.co", EuiccNotification.EVENT_DELETE, new byte[]{1});
+ assertFalse(n.equals(t));
+ assertNotEquals(n.hashCode(), t.hashCode());
+
+ t = new EuiccNotification(1, "g.co", 0, new byte[]{1});
+ assertFalse(n.equals(t));
+ assertNotEquals(n.hashCode(), t.hashCode());
+
+ t = new EuiccNotification(1, "g.co", EuiccNotification.EVENT_DELETE, null);
+ assertFalse(n.equals(t));
+ assertNotEquals(n.hashCode(), t.hashCode());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccRulesAuthTableTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccRulesAuthTableTest.java
new file mode 100644
index 0000000..65849cf
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/data/EuiccRulesAuthTableTest.java
@@ -0,0 +1,554 @@
+/*
+ * 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.uicc.euicc.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.telephony.euicc.EuiccRulesAuthTable;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class EuiccRulesAuthTableTest {
+ @Test
+ public void testFindIndex() {
+ CarrierIdentifier opA = new CarrierIdentifier(new byte[] {0x21, 0x63, 0x54}, null, "4");
+ CarrierIdentifier opB = new CarrierIdentifier(new byte[] {0x21, 0x69, 0x54}, "4", null);
+ EuiccRulesAuthTable rat =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ // Matches none
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ // Matches none
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ // Matches opA
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ // Matches none
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ // Matches opA and opB
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ // Matches none
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ // Matches opB
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ // Matches opA
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+
+ assertEquals(1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opA));
+ assertEquals(3, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opB));
+ assertEquals(2, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opA));
+ assertEquals(2, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opB));
+ assertTrue(rat.hasPolicyRuleFlag(1, EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED));
+ assertFalse(rat.hasPolicyRuleFlag(
+ 2, EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED));
+ assertTrue(rat.hasPolicyRuleFlag(3, EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED));
+ }
+
+ @Test
+ public void testFindIndex_AllowAllWithUserConsent() {
+ CarrierIdentifier opA = new CarrierIdentifier(new byte[] {0x21, 0x63, 0x54}, null, "4");
+ CarrierIdentifier opB = new CarrierIdentifier(
+ new byte[] {0x78, (byte) 0xF4, 0x25}, "4", null);
+ EuiccRulesAuthTable rat =
+ new EuiccRulesAuthTable.Builder(1)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ // Matches none
+ new CarrierIdentifier(
+ new byte[] {(byte) 0xEE, (byte) 0xEE, (byte) 0xEE},
+ null,
+ null)
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertEquals(0, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opA));
+ assertEquals(0, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opB));
+ assertEquals(0, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opA));
+ assertEquals(0, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opB));
+ }
+
+ @Test
+ public void testFindIndex_DisallowAll() {
+ CarrierIdentifier opA = new CarrierIdentifier(new byte[] {0x21, 0x63, 0x54}, null, "4");
+ CarrierIdentifier opB = new CarrierIdentifier(
+ new byte[] {0x78, (byte) 0xF4, 0x25}, "4", null);
+ EuiccRulesAuthTable rat = new EuiccRulesAuthTable.Builder(1)
+ .add(0, Arrays.asList(new CarrierIdentifier[] {}), 0).build();
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opA));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opB));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opA));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opB));
+ }
+
+ @Test
+ public void testFindIndex_DisallowAllWithEmptyRules() {
+ CarrierIdentifier opA = new CarrierIdentifier(new byte[] {0x21, 0x63, 0x54}, null, "4");
+ CarrierIdentifier opB = new CarrierIdentifier(new byte[] {0x78, 0x34, 0x25}, "4", null);
+ EuiccRulesAuthTable rat = new EuiccRulesAuthTable.Builder(0).build();
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opA));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, opB));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opA));
+ assertEquals(-1, rat.findIndex(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE, opB));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuild_NotEnoughRules() {
+ new EuiccRulesAuthTable.Builder(1).build();
+ }
+
+ @Test(expected = ArrayIndexOutOfBoundsException.class)
+ public void testBuild_TooManyRules() {
+ new EuiccRulesAuthTable.Builder(0)
+ .add(0, Arrays.asList(new CarrierIdentifier[] {}), 0).build();
+ }
+
+ @Test(expected = ArrayIndexOutOfBoundsException.class)
+ public void testHasPolicyRuleFlag_OutOfBounds() {
+ EuiccRulesAuthTable rat = new EuiccRulesAuthTable.Builder(1)
+ .add(0, Arrays.asList(new CarrierIdentifier[] {}), 0).build();
+ rat.hasPolicyRuleFlag(1, EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED);
+ }
+
+ @Test
+ public void testMatch() {
+ assertTrue(EuiccRulesAuthTable.match("12", "12"));
+ assertTrue(EuiccRulesAuthTable.match("1E", "12"));
+ assertTrue(EuiccRulesAuthTable.match("12E", "12"));
+ assertTrue(EuiccRulesAuthTable.match("EEE", "12"));
+ assertTrue(EuiccRulesAuthTable.match("120", "120"));
+ assertTrue(EuiccRulesAuthTable.match("12E", "120"));
+ assertTrue(EuiccRulesAuthTable.match("EEE", "120"));
+
+ assertFalse(EuiccRulesAuthTable.match("13", "12"));
+ assertFalse(EuiccRulesAuthTable.match("2E", "12"));
+ assertFalse(EuiccRulesAuthTable.match("123", "120"));
+ assertFalse(EuiccRulesAuthTable.match("1E", "120"));
+ assertFalse(EuiccRulesAuthTable.match("EE", "120"));
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ EuiccRulesAuthTable rat =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ assertTrue(parcel != null);
+ rat.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ EuiccRulesAuthTable fromParcel = EuiccRulesAuthTable.CREATOR.createFromParcel(parcel);
+
+ assertEquals(rat, fromParcel);
+
+ // Empty rules.
+ rat = new EuiccRulesAuthTable.Builder(0).build();
+ parcel = Parcel.obtain();
+ rat.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ fromParcel = EuiccRulesAuthTable.CREATOR.createFromParcel(parcel);
+
+ assertEquals(rat, fromParcel);
+
+ // Null carrier identifier.
+ rat =
+ new EuiccRulesAuthTable.Builder(1)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ null,
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ parcel = Parcel.obtain();
+ rat.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ fromParcel = EuiccRulesAuthTable.CREATOR.createFromParcel(parcel);
+
+ assertEquals(rat, fromParcel);
+ }
+
+ @Test
+ public void testEquals() {
+ EuiccRulesAuthTable rat =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+
+ // Same object.
+ EuiccRulesAuthTable that = rat;
+ assertTrue(rat.equals(that));
+
+ // Same values with rat.
+ that =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertTrue(rat.equals(that));
+
+ // Null object.
+ that = null;
+ assertFalse(rat.equals(that));
+
+ that =
+ new EuiccRulesAuthTable.Builder(3)
+ // One less RAT.
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .build();
+ assertFalse(rat.equals(that));
+
+ that =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ // Only one item
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null)
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertFalse(rat.equals(that));
+
+ that =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ // Different value from rat
+ new CarrierIdentifier(
+ new byte[] {0x22, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertFalse(rat.equals(that));
+
+ that =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ // Null here.
+ null,
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertTrue(rat.equals(that));
+
+ that =
+ new EuiccRulesAuthTable.Builder(4)
+ .add(
+ // Different policy rules
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(new CarrierIdentifier[] {}),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, (byte) 0xF3, 0x54},
+ "4",
+ null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x63, 0x54}, null, "5"),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, null)
+ ),
+ 0)
+ .add(
+ EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE
+ | EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE,
+ Arrays.asList(
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x69, 0x54}, "5", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, "4", null),
+ new CarrierIdentifier(
+ new byte[] {0x21, 0x6E, 0x54}, null, "4")
+ ),
+ EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED)
+ .build();
+ assertFalse(rat.equals(that));
+ }
+}