Merge "Migrate GSM SignalStrength to WCDMA on HAL 1.0"
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 4239dd4..44ada6d 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -642,6 +642,38 @@
}
}
+message EmergencyNumberInfo {
+ // Dialing address
+ optional string address = 1 /* [
+ (datapol.semantic_type) = ST_PHONE_NUMBER,
+ (datapol.qualifier) = {is_public: true}
+ ] */;
+
+ // Country code string (lowercase character) in ISO 3166 format
+ optional string country_iso = 2 /* [(datapol.semantic_type) = ST_LOCATION] */;
+
+ // Mobile Network Code
+ optional string mnc = 3 /* [(datapol.semantic_type) = ST_LOCATION] */;
+
+ // Bitmask of emergency service categories
+ optional int32 service_categories_bitmask = 4;
+
+ // Emergency Uniform Resources Names (URN)
+ // Reference: https://tools.ietf.org/html/rfc5031
+ repeated string urns = 5;
+
+ // Bitmask of the sources
+ optional int32 number_sources_bitmask = 6;
+
+ // Emergency call routing information.
+ // Emergency call routing is a flag to tell how modem handles the calling with
+ // emergency numbers. For example, 110 in India, modem needs to handle/route
+ // it like a normal call. There are only two possible options for emergency
+ // call routing: emergency call routing vs normal call routing. It is usually
+ // a country or carrier requirement.
+ optional int32 routing = 7;
+}
+
message TelephonyEvent {
enum Type {
@@ -710,6 +742,9 @@
// Enabled modem change event.
ENABLED_MODEM_CHANGED = 20;
+
+ // Emergency Number update event (Device HAL >= 1.4).
+ EMERGENCY_NUMBER_REPORT = 21;
}
enum ApnType {
@@ -1649,6 +1684,9 @@
// The modem state represent by a bitmap, the i-th bit(LSB) indicates the i-th modem
// state (0 - disabled, 1 - enabled).
optional int32 enabled_modem_bitmap = 24;
+
+ // Updated Emergency Call info.
+ optional EmergencyNumberInfo updated_emergency_number = 25;
}
message ActiveSubscriptionInfo {
@@ -1952,6 +1990,12 @@
// Detailed cause code for CS Call failures
// frameworks/base/telephony/java/android/telephony/PreciseDisconnectCause.java
optional int32 precise_disconnect_cause = 6;
+
+ // Indicate if the call is an emergency call
+ optional bool is_emergency_call = 7;
+
+ // Indicate the emergency call information dialed from the CS call
+ optional EmergencyNumberInfo emergency_number_info = 8;
}
// Single Radio Voice Call Continuity(SRVCC) progress state
@@ -2173,6 +2217,11 @@
// Uplink call quality summary at the end of a call
optional CallQualitySummary call_quality_summary_ul = 25;
+ // Indicate if it is IMS emergency call
+ optional bool is_ims_emergency_call = 26;
+
+ // Emergency call info
+ optional EmergencyNumberInfo ims_emergency_number_info = 27;
}
// Time when call has started, in minutes since epoch,
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index b48f21c..002e082 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -101,6 +101,8 @@
protected abstract void handlePollCalls(AsyncResult ar);
+ protected abstract Phone getPhone();
+
protected Connection getHoConnection(DriverCall dc) {
for (Connection hoConn : mHandoverConnections) {
log("getHoConnection - compare number: hoConn= " + hoConn.toString());
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index b5b3796..d4d606d 100755
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -506,10 +506,9 @@
*
* @hide
*/
- public void setEmergencyCallInfo() {
- Call call = getCall();
- if (call != null) {
- Phone phone = call.getPhone();
+ public void setEmergencyCallInfo(CallTracker ct) {
+ if (ct != null) {
+ Phone phone = ct.getPhone();
if (phone != null) {
EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
if (tracker != null) {
@@ -517,9 +516,17 @@
if (num != null) {
mIsEmergencyCall = true;
mEmergencyNumberInfo = num;
+ } else {
+ Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null");
}
+ } else {
+ Rlog.e(TAG, "setEmergencyCallInfo: emergency number tracker is null");
}
+ } else {
+ Rlog.e(TAG, "setEmergencyCallInfo: phone is null");
}
+ } else {
+ Rlog.e(TAG, "setEmergencyCallInfo: call tracker is null");
}
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 2b003a9..0a7acee 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -1091,8 +1091,10 @@
newUnknownConnectionCdma = null;
}
}
+
if (locallyDisconnectedConnections.size() > 0) {
- mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);
+ mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections,
+ getNetworkCountryIso());
}
/* Disconnect any pending Handover connections */
@@ -1163,7 +1165,7 @@
for (GsmCdmaConnection conn : connections) {
if (conn != null) activeConnections.add(conn);
}
- mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections);
+ mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections, getNetworkCountryIso());
}
private void handleRadioNotAvailable() {
@@ -1236,7 +1238,8 @@
return;
} else {
try {
- mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex());
+ mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex(),
+ getNetworkCountryIso());
mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());
} catch (CallStateException ex) {
// Ignore "connection not found"
@@ -1331,7 +1334,7 @@
} catch (CallStateException ex) {
call_index = -1;
}
- mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, call_index);
+ mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, call_index, getNetworkCountryIso());
}
if (VDBG) Rlog.v(LOG_TAG, "logHangupEvent logged " + count + " Connections ");
}
@@ -1353,7 +1356,8 @@
for (int i = 0; i < count; i++) {
GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
if (!cn.mDisconnected && cn.getGsmCdmaIndex() == index) {
- mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
+ mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex(),
+ getNetworkCountryIso());
mCi.hangupConnection(index, obtainCompleteMessage());
return;
}
@@ -1368,7 +1372,8 @@
for (int i = 0; i < count; i++) {
GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
if (!cn.mDisconnected) {
- mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
+ mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex(),
+ getNetworkCountryIso());
mCi.hangupConnection(cn.getGsmCdmaIndex(), obtainCompleteMessage());
}
}
@@ -1551,7 +1556,8 @@
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
- mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll);
+ mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll,
+ getNetworkCountryIso());
mDroppedDuringPoll.clear();
break;
@@ -1742,6 +1748,7 @@
}
@UnsupportedAppUsage
+ @Override
public GsmCdmaPhone getPhone() {
return mPhone;
}
@@ -1797,6 +1804,20 @@
MAX_CONNECTIONS_PER_CALL_CDMA;
}
+ private String getNetworkCountryIso() {
+ String countryIso = "";
+ if (mPhone != null) {
+ ServiceStateTracker sst = mPhone.getServiceStateTracker();
+ if (sst != null) {
+ LocaleTracker lt = sst.getLocaleTracker();
+ if (lt != null) {
+ countryIso = lt.getCurrentCountry();
+ }
+ }
+ }
+ return countryIso;
+ }
+
/**
* Called to force the call tracker to cleanup any stale calls. Does this by triggering
* {@code GET_CURRENT_CALLS} on the RIL.
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index f5f2519..b3dea8e 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -136,7 +136,7 @@
mHandler = new MyHandler(mOwner.getLooper());
mAddress = dc.number;
- setEmergencyCallInfo();
+ setEmergencyCallInfo(mOwner);
mIsIncoming = dc.isMT;
mCreateTime = System.currentTimeMillis();
@@ -179,7 +179,7 @@
mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
if (isEmergencyCall) {
- setEmergencyCallInfo();
+ setEmergencyCallInfo(mOwner);
}
mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 83d5432..00cea0d 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -236,12 +236,22 @@
}
private void bindService() {
+ Intent intent = null;
String packageName = getPackageName();
+ String className = getClassName();
if (TextUtils.isEmpty(packageName)) {
loge("Can't find the binding package");
return;
}
+ if (TextUtils.isEmpty(className)) {
+ intent = new Intent(NetworkService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ } else {
+ ComponentName cm = new ComponentName(packageName, className);
+ intent = new Intent(NetworkService.SERVICE_INTERFACE).setComponent(cm);
+ }
+
if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
logd("Service " + packageName + " already bound or being bound.");
return;
@@ -258,9 +268,6 @@
mPhone.getContext().unbindService(mServiceConnection);
}
- Intent intent = new Intent(NetworkService.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.
@@ -312,6 +319,39 @@
return packageName;
}
+ private String getClassName() {
+ String className;
+ int resourceId;
+ String carrierConfig;
+
+ switch (mTransportType) {
+ case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+ resourceId = com.android.internal.R.string.config_wwan_network_service_class;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
+ break;
+ case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
+ resourceId = com.android.internal.R.string.config_wlan_network_service_class;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
+ break;
+ default:
+ throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+ + mTransportType);
+ }
+
+ // Read class name from resource overlay
+ className = mPhone.getContext().getResources().getString(resourceId);
+
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ // If carrier config overrides it, use the one from carrier config
+ className = b.getString(carrierConfig, className);
+ }
+
+ return className;
+ }
private void logd(String msg) {
Rlog.d(mTag, msg);
}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 67a2244..387858d 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -4004,6 +4004,29 @@
}
/**
+ * Check if the device can only make the emergency call. The device is emergency call only if
+ * none of the phone is in service, and one of them has the capability to make the emergency
+ * call.
+ *
+ * @return {@code True} if the device is emergency call only, otherwise return {@code False}.
+ */
+ public static boolean isEmergencyCallOnly() {
+ boolean isEmergencyCallOnly = false;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone != null) {
+ ServiceState ss = phone.getServiceStateTracker().getServiceState();
+ // One of the phone is in service, hence the device is not emergency call only.
+ if (ss.getState() == ServiceState.STATE_IN_SERVICE
+ || ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+ return false;
+ }
+ isEmergencyCallOnly |= ss.isEmergencyOnly();
+ }
+ }
+ return isEmergencyCallOnly;
+ }
+
+ /**
* Get data connection tracker based on the transport type
*
* @param transportType Transport type defined in AccessNetworkConstants.TransportType
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index bb425a2..8422d50 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -156,8 +156,7 @@
"carrierActionDisableMeteredApn";
static final String REASON_CSS_INDICATOR_CHANGED = "cssIndicatorChanged";
static final String REASON_RELEASED_BY_CONNECTIVITY_SERVICE = "releasedByConnectivityService";
- static final String REASON_APN_ADDED_TO_WHITELIST = "apnAddedToWhiteList";
- static final String REASON_APN_REMOVED_FROM_WHITELIST = "apnRemovedFromWhiteList";
+ static final String REASON_DATA_ENABLED_OVERRIDE = "dataEnabledOverride";
// Used for band mode selection methods
static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index f889578..f8f3232 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -54,6 +54,7 @@
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
@@ -818,7 +819,8 @@
// requests.
private void updatePreferredDataPhoneId() {
Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall);
- if (voicePhone != null && voicePhone.isUserDataEnabled()) {
+ if (voicePhone != null && voicePhone.getDataEnabledSettings().isDataEnabled(
+ ApnSetting.TYPE_DEFAULT)) {
// If a phone is in call and user enabled its mobile data, we
// should switch internet connection to it. Because the other modem
// will lose data connection anyway.
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index b6fdbd0..79f2356 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -956,7 +956,6 @@
uusInfo, result);
return;
}
-
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
@@ -1015,7 +1014,9 @@
try {
radioProxy14.emergencyDial(rr.mSerial, dialInfo,
emergencyNumberInfo.getEmergencyServiceCategoryBitmaskInternalDial(),
- (ArrayList) emergencyNumberInfo.getEmergencyUrns(),
+ emergencyNumberInfo.getEmergencyUrns() != null
+ ? new ArrayList(emergencyNumberInfo.getEmergencyUrns())
+ : new ArrayList<>(),
emergencyNumberInfo.getEmergencyCallRouting(),
hasKnownUserIntentEmergency,
emergencyNumberInfo.getEmergencyNumberSourceBitmask()
@@ -5237,6 +5238,8 @@
return "GET_CURRENT_CALLS";
case RIL_REQUEST_DIAL:
return "DIAL";
+ case RIL_REQUEST_EMERGENCY_DIAL:
+ return "EMERGENCY_DIAL";
case RIL_REQUEST_GET_IMSI:
return "GET_IMSI";
case RIL_REQUEST_HANGUP:
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 4c414d4..8d06ec1 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -124,10 +124,13 @@
private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
/** Send the user confirmed SMS */
- static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class
+ static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class
/** Don't send SMS (user did not confirm). */
- static final int EVENT_STOP_SENDING = 7; // accessed from inner class
+ static final int EVENT_STOP_SENDING = 6; // accessed from inner class
+
+ /** Don't send SMS for this app (User had already denied eariler.) */
+ static final int EVENT_SENDING_NOT_ALLOWED = 7;
/** Confirmation required for third-party apps sending to an SMS short code. */
private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
@@ -317,6 +320,16 @@
break;
}
+ case EVENT_SENDING_NOT_ALLOWED:
+ {
+ SmsTracker tracker = (SmsTracker) msg.obj;
+ Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
+ + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+ tracker.onFailed(
+ mContext, RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, 0 /*errorCode*/);
+ break;
+ }
+
case EVENT_STOP_SENDING:
{
SmsTracker tracker = (SmsTracker) msg.obj;
@@ -1270,9 +1283,7 @@
case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
Rlog.w(TAG, "User denied this app from sending to premium SMS");
- Message msg = obtainMessage(EVENT_STOP_SENDING, tracker);
- msg.arg1 = ConfirmDialogListener.SHORT_CODE_MSG;
- msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
+ Message msg = obtainMessage(EVENT_SENDING_NOT_ALLOWED, tracker);
sendMessage(msg);
return false; // reject this message
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 958f905..3ec24d5 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -2628,7 +2628,7 @@
final boolean forceDisplayNoService = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_display_no_service_when_sim_unready)
&& !mIsSimReady;
- if (mEmergencyOnly && !forceDisplayNoService) {
+ if (!forceDisplayNoService && Phone.isEmergencyCallOnly()) {
// No service but emergency call allowed
plmn = Resources.getSystem().
getText(com.android.internal.R.string.emergency_calls_only).toString();
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index e6aad7b..e94346a 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -18,9 +18,9 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
-import static android.telephony.data.ApnSetting.TYPE_MMS;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.AppOpsManager;
@@ -41,7 +41,6 @@
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
@@ -50,7 +49,6 @@
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.UiccSlotInfo;
-import android.telephony.data.ApnSetting;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -58,6 +56,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCard;
@@ -1715,11 +1714,11 @@
public void syncGroupedSetting(int refSubId) {
// Currently it only syncs allow MMS. Sync other settings as well if needed.
- int apnWhiteList = Integer.valueOf(getSubscriptionProperty(
- refSubId, SubscriptionManager.WHITE_LISTED_APN_DATA, mContext.getOpPackageName()));
+ String dataEnabledOverrideRules = getSubscriptionProperty(
+ refSubId, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES);
ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.WHITE_LISTED_APN_DATA, apnWhiteList);
+ value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, dataEnabledOverrideRules);
databaseUpdateHelper(value, refSubId, true);
}
@@ -2540,11 +2539,11 @@
}
/**
- * Store properties associated with SubscriptionInfo in database
+ * Get properties associated with SubscriptionInfo from database
+ *
* @param subId Subscription Id of Subscription
* @param propKey Column name in SubscriptionInfo database
* @return Value associated with subId and propKey column in database
- * @hide
*/
@Override
public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
@@ -2552,14 +2551,29 @@
mContext, subId, callingPackage, "getSubscriptionProperty")) {
return null;
}
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getSubscriptionProperty(subId, propKey);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get properties associated with SubscriptionInfo from database. Note this is the version
+ * without permission check for telephony internal use only.
+ *
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @return Value associated with subId and propKey column in database
+ */
+ public String getSubscriptionProperty(int subId, String propKey) {
String resultValue = null;
- ContentResolver resolver = mContext.getContentResolver();
- Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
+ try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
new String[]{propKey},
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
- new String[]{subId + ""}, null);
-
- try {
+ new String[]{subId + ""}, null)) {
if (cursor != null) {
if (cursor.moveToFirst()) {
switch (propKey) {
@@ -2586,6 +2600,9 @@
case SubscriptionManager.WHITE_LISTED_APN_DATA:
resultValue = cursor.getInt(0) + "";
break;
+ case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
+ resultValue = cursor.getString(0);
+ break;
default:
if(DBG) logd("Invalid column name");
break;
@@ -2596,11 +2613,8 @@
} else {
if(DBG) logd("Query failed");
}
- } finally {
- if (cursor != null) {
- cursor.close();
- }
}
+
if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
return resultValue;
}
@@ -3419,9 +3433,9 @@
+ logicalSlotIndex);
}
- // Getting physicalSlotIndex
+ // Getting and validating the physicalSlotIndex.
int physicalSlotIndex = getPhysicalSlotIndexFromLogicalSlotIndex(logicalSlotIndex);
- if (!SubscriptionManager.isValidSlotIndex(physicalSlotIndex)) {
+ if (physicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
@@ -3608,6 +3622,8 @@
}
}
+ // TODO: This method should belong to Telephony manager like other data enabled settings and
+ // override APIs. Remove this once TelephonyManager API is added.
@Override
public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
if (DBG) logd("[setAlwaysAllowMmsData]+ alwaysAllow:" + alwaysAllow + " subId:" + subId);
@@ -3618,51 +3634,49 @@
final long identity = Binder.clearCallingIdentity();
try {
validateSubId(subId);
-
- ContentValues value = new ContentValues(1);
- int apnWhiteList = Integer.valueOf(getSubscriptionProperty(subId,
- SubscriptionManager.WHITE_LISTED_APN_DATA, mContext.getOpPackageName()));
- apnWhiteList = alwaysAllow ? (apnWhiteList | TYPE_MMS) : (apnWhiteList & ~TYPE_MMS);
- value.put(SubscriptionManager.WHITE_LISTED_APN_DATA, apnWhiteList);
- if (DBG) logd("[setAlwaysAllowMmsData]- alwaysAllow:" + alwaysAllow + " set");
-
- boolean result = databaseUpdateHelper(value, subId, true) > 0;
-
- if (result) {
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
- Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
- if (phone != null) {
- phone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .notifyApnWhiteListChange(TYPE_MMS, alwaysAllow);
- }
- }
-
- return result;
+ Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
+ if (phone == null) return false;
+ return phone.getDataEnabledSettings().setAlwaysAllowMmsData(alwaysAllow);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
- * whether apnType is whitelisted. Being white listed means data connection is allowed
- * even if user data is turned off.
+ * Set allowing mobile data during voice call.
+ *
+ * @param subId Subscription index
+ * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride}
+ * for details.
+ * @return {@code true} if settings changed, otherwise {@code false}.
*/
- public boolean isApnWhiteListed(int subId, String callingPackage, int apnType) {
- return (getWhiteListedApnDataTypes(subId, callingPackage) & apnType) == apnType;
- }
+ public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) {
+ if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId);
- private @ApnSetting.ApnType int getWhiteListedApnDataTypes(int subId, String callingPackage) {
- String whiteListedApnData = getSubscriptionProperty(subId,
- SubscriptionManager.WHITE_LISTED_APN_DATA, callingPackage);
+ validateSubId(subId);
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules);
- try {
- return Integer.valueOf(whiteListedApnData);
- } catch (NumberFormatException e) {
- loge("[getWhiteListedApnDataTypes] couldn't parse apn data:" + whiteListedApnData);
+ boolean result = databaseUpdateHelper(value, subId, true) > 0;
+
+ if (result) {
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
}
- return ApnSetting.TYPE_NONE;
+ return result;
+ }
+
+ /**
+ * Get data enabled override rules.
+ *
+ * @param subId Subscription index
+ * @return Data enabled override rules in string
+ */
+ @NonNull
+ public String getDataEnabledOverrideRules(int subId) {
+ return TextUtils.emptyIfNull(getSubscriptionProperty(subId,
+ SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
}
}
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 63986ad..6b85792 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -39,6 +39,7 @@
import android.util.SparseArray;
import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.cdnr.EfData.EFSource;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccRecords;
@@ -380,8 +381,8 @@
com.android.internal.R.bool.config_display_no_service_when_sim_unready)
&& !isSimReady;
ServiceState ss = getServiceState();
- if (ss.getVoiceRegState() == ServiceState.STATE_POWER_OFF || !ss.isEmergencyOnly()
- || forceDisplayNoService) {
+ if (ss.getVoiceRegState() == ServiceState.STATE_POWER_OFF
+ || forceDisplayNoService || !Phone.isEmergencyCallOnly()) {
plmn = mContext.getResources().getString(
com.android.internal.R.string.lockscreen_carrier_default);
} else {
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 421feb9..8b99bfb 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -238,7 +238,9 @@
* configuration from carrier config if it exists. If not, read it from resources.
*/
private void bindQualifiedNetworksService() {
+ Intent intent = null;
String packageName = getQualifiedNetworksServicePackageName();
+ String className = getQualifiedNetworksServiceClassName();
if (DBG) log("Qualified network service package = " + packageName);
if (TextUtils.isEmpty(packageName)) {
@@ -246,6 +248,15 @@
return;
}
+ if (TextUtils.isEmpty(className)) {
+ intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ } else {
+ ComponentName cm = new ComponentName(packageName, className);
+ intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE)
+ .setComponent(cm);
+ }
+
if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
if (DBG) log("Service " + packageName + " already bound or being bound.");
return;
@@ -266,11 +277,8 @@
try {
mServiceConnection = new QualifiedNetworksServiceConnection();
log("bind to " + packageName);
- if (!mPhone.getContext().bindService(
- new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE)
- .setPackage(packageName),
- mServiceConnection,
- Context.BIND_AUTO_CREATE)) {
+ if (!mPhone.getContext().bindService(intent, mServiceConnection,
+ Context.BIND_AUTO_CREATE)) {
loge("Cannot bind to the qualified networks service.");
return;
}
@@ -306,6 +314,30 @@
return packageName;
}
+ /**
+ * Get the qualified network service class name.
+ *
+ * @return class name of the qualified networks service package.
+ */
+ private String getQualifiedNetworksServiceClassName() {
+ // Read package name from the resource
+ String className = mPhone.getContext().getResources().getString(
+ com.android.internal.R.string.config_qualified_networks_service_class);
+
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ if (b != null) {
+ // If carrier config overrides it, use the one from carrier config
+ String carrierConfigClassName = b.getString(CarrierConfigManager
+ .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
+ if (!TextUtils.isEmpty(carrierConfigClassName)) {
+ if (DBG) log("Found carrier config override " + carrierConfigClassName);
+ className = carrierConfigClassName;
+ }
+ }
+
+ return className;
+ }
private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() {
List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
new file mode 100644
index 0000000..2ed9bfe
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.dataconnection.DataEnabledOverride.OverrideConditions.Condition;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class represents the rules for overriding data enabled settings in different conditions.
+ * When data is disabled by the user, data can still be turned on temporarily when conditions
+ * satisfy any rule here.
+ */
+public class DataEnabledOverride {
+
+ private final Set<OverrideRule> mRules = new HashSet<>();
+
+ /**
+ * The rule for allowing data during voice call.
+ */
+ private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL =
+ new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL
+ | OverrideConditions.CONDITION_NON_DEFAULT);
+
+ /**
+ * The rule for always allowing mms. Without adding any condition to the rule, any condition can
+ * satisfy this rule for mms.
+ */
+ private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS =
+ new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY);
+
+ /**
+ * Data enabled override rule
+ */
+ private static class OverrideRule {
+ /**
+ * APN type of the rule. The rule is APN type specific. The override is applicable to the
+ * specified APN type as well. For now we only support one APN type per rule. Can be
+ * expanded to multiple APN types in the future.
+ */
+ private final @ApnType int mApnType;
+
+ /** The required conditions for overriding */
+ private final OverrideConditions mRequiredConditions;
+
+ /**
+ * Constructor
+ *
+ * @param rule The override rule string. For example, {@code mms=nonDefault} or
+ * {@code default=voiceCall & nonDefault}
+ */
+ OverrideRule(@NonNull String rule) {
+ String[] tokens = rule.trim().split("\\s*=\\s*");
+ if (tokens.length != 2) {
+ throw new IllegalArgumentException("Invalid data enabled override rule format: "
+ + rule);
+ }
+
+ if (TextUtils.isEmpty(tokens[0])) {
+ throw new IllegalArgumentException("APN type can't be empty");
+ }
+
+ mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]);
+ if (mApnType == ApnSetting.TYPE_NONE) {
+ throw new IllegalArgumentException("Invalid APN type. Rule=" + rule);
+ }
+
+ mRequiredConditions = new OverrideConditions(tokens[1]);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param apnType APN type of the rule
+ * @param requiredConditions The required conditions for the rule
+ */
+ private OverrideRule(int apnType, int requiredConditions) {
+ mApnType = apnType;
+ mRequiredConditions = new OverrideConditions(requiredConditions);
+ }
+
+ /**
+ * Check if this rule can be satisfied by the given APN type and provided conditions.
+ *
+ * @param apnType APN type to check
+ * @param providedConditions The provided conditions to check
+ * @return {@code true} if satisfied
+ */
+ boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) {
+ return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL)
+ && mRequiredConditions.allMet(providedConditions);
+ }
+
+ @Override
+ public String toString() {
+ return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OverrideRule that = (OverrideRule) o;
+ return mApnType == that.mApnType
+ && Objects.equals(mRequiredConditions, that.mRequiredConditions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mApnType, mRequiredConditions);
+ }
+ }
+
+ /**
+ * Represent the conditions for overriding data enabled settings
+ */
+ static class OverrideConditions {
+ // Possible values for data enabled override condition. Note these flags are bitmasks.
+ /** Unconditionally override enabled settings */
+ static final int CONDITION_UNCONDITIONALLY = 0;
+
+ /** Enable data only on subscription that is not user selected default data subscription */
+ static final int CONDITION_NON_DEFAULT = 1 << 0;
+
+ /** Enable data only when device has ongoing voice call */
+ static final int CONDITION_IN_VOICE_CALL = 1 << 1;
+
+ /** Enable data unconditionally in string format */
+ static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally";
+
+ /** Enable data only on subscription that is not default in string format */
+ static final String CONDITION_NON_DEFAULT_STRING = "nonDefault";
+
+ /** Enable data only when device has ongoing voice call in string format */
+ static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall";
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = {
+ CONDITION_NON_DEFAULT,
+ CONDITION_IN_VOICE_CALL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Condition {}
+
+ private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>();
+ private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>();
+
+ static {
+ OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT,
+ CONDITION_NON_DEFAULT_STRING);
+ OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL,
+ CONDITION_VOICE_CALL_STRING);
+
+ OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING,
+ CONDITION_UNCONDITIONALLY);
+ OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING,
+ CONDITION_NON_DEFAULT);
+ OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING,
+ CONDITION_IN_VOICE_CALL);
+ }
+
+ private final @Condition int mConditions;
+
+ /**
+ * Conditions for overriding data enabled setting
+ *
+ * @param conditions Conditions in string format
+ */
+ OverrideConditions(@NonNull String conditions) {
+ mConditions = getBitmaskFromString(conditions);
+ }
+
+ /**
+ * Conditions for overriding data enabled setting
+ *
+ * @param conditions Conditions in bitmask
+ */
+ OverrideConditions(@Condition int conditions) {
+ mConditions = conditions;
+ }
+
+ private static String getStringFromBitmask(@Condition int conditions) {
+ if (conditions == CONDITION_UNCONDITIONALLY) {
+ return CONDITION_UNCONDITIONALLY_STRING;
+ }
+ List<String> conditionsStrings = new ArrayList<>();
+ for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) {
+ if ((conditions & condition) == condition) {
+ conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition));
+ }
+ }
+ return TextUtils.join("&", conditionsStrings);
+ }
+
+ private static @Condition int getBitmaskFromString(@NonNull String str) {
+ if (TextUtils.isEmpty(str)) {
+ throw new IllegalArgumentException("Empty rule string");
+ }
+
+ String[] conditionStrings = str.trim().split("\\s*&\\s*");
+ int bitmask = 0;
+
+ for (String conditionStr : conditionStrings) {
+ if (!TextUtils.isEmpty(conditionStr)) {
+ if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) {
+ throw new IllegalArgumentException("Invalid conditions: " + str);
+ }
+ bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr);
+ }
+ }
+
+ return bitmask;
+ }
+
+ /**
+ * Check if provided conditions can meet all conditions in the rule.
+ *
+ * @param providedConditions The provided conditions
+ * @return {@code true} if all conditions are met.
+ */
+ boolean allMet(@Condition int providedConditions) {
+ return (providedConditions & mConditions) == mConditions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OverrideConditions that = (OverrideConditions) o;
+ return mConditions == that.mConditions;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConditions);
+ }
+
+ @Override
+ public String toString() {
+ return getStringFromBitmask(mConditions);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param rules Data enabled override rules
+ */
+ public DataEnabledOverride(@NonNull String rules) {
+ updateRules(rules);
+ }
+
+ /**
+ * Update the data enabled override rules.
+ *
+ * @param newRules New override rules
+ */
+ @VisibleForTesting
+ public void updateRules(@NonNull String newRules) {
+ mRules.clear();
+ String[] rulesString = newRules.trim().split("\\s*,\\s*");
+ for (String rule : rulesString) {
+ if (!TextUtils.isEmpty(rule)) {
+ mRules.add(new OverrideRule(rule));
+ }
+ }
+ }
+
+ /**
+ * Set always allowing MMS
+ *
+ * @param allow {@code true} if always allowing, otherwise {@code false}.
+ */
+ public void setAlwaysAllowMms(boolean allow) {
+ if (allow) {
+ mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
+ } else {
+ mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
+ }
+ }
+
+ /**
+ * Set allowing mobile data during voice call.
+ *
+ * @param allow {@code true} if allowing using data during voice call, {@code false} if
+ * disallowed.
+ */
+ public void setDataAllowedInVoiceCall(boolean allow) {
+ if (allow) {
+ mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
+ } else {
+ mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
+ }
+ }
+
+ /**
+ * Check if data is allowed during voice call.
+ *
+ * @return {@code true} if data is allowed during voice call.
+ */
+ public boolean isDataAllowedInVoiceCall() {
+ return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
+ }
+
+ private boolean canSatisfyAnyRule(@ApnType int apnType,
+ @Condition int providedConditions) {
+ for (OverrideRule rule : mRules) {
+ if (rule.isSatisfiedByConditions(apnType, providedConditions)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private @Condition int getCurrentConditions(Phone phone) {
+ int conditions = 0;
+
+ if (phone != null) {
+ // Check if the device is on voice call
+ if (phone.getCallTracker().getState() != PhoneConstants.State.IDLE) {
+ conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL;
+ }
+
+ if (phone.getSubId() != SubscriptionController.getInstance().getDefaultDataSubId()) {
+ conditions |= OverrideConditions.CONDITION_NON_DEFAULT;
+ }
+ }
+
+ return conditions;
+ }
+
+ /**
+ * Check for given APN type if we should enable data.
+ *
+ * @param phone Phone object
+ * @param apnType APN type
+ * @return {@code true} if data should be enabled for the current condition.
+ */
+ public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) {
+ return canSatisfyAnyRule(apnType, getCurrentConditions(phone));
+ }
+
+ /**
+ * Get data enabled override rules.
+ *
+ * @return Get data enabled override rules in string format
+ */
+ @NonNull
+ public String getRules() {
+ List<String> ruleStrings = new ArrayList<>();
+ for (OverrideRule rule : mRules) {
+ ruleStrings.add(rule.toString());
+ }
+ return TextUtils.join(",", ruleStrings);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DataEnabledOverride that = (DataEnabledOverride) o;
+ return mRules.equals(that.mRules);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRules);
+ }
+
+ @Override
+ public String toString() {
+ return "DataEnabledOverride: [rules=\"" + getRules() + "\"]";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index 1f5dba9..b0be081 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -27,6 +27,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.data.ApnSetting;
import android.util.LocalLog;
import android.util.Pair;
@@ -97,30 +99,70 @@
private final Phone mPhone;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
private ContentResolver mResolver = null;
private final RegistrantList mOverallDataEnabledChangedRegistrants = new RegistrantList();
+ // TODO: Merge this with mOverallDataEnabledChangedRegistrants. In the future, notifying data
+ // enabled changed with APN types bitmask
+ private final RegistrantList mOverallDataEnabledOverrideChangedRegistrants =
+ new RegistrantList();
+
private final LocalLog mSettingChangeLocalLog = new LocalLog(50);
+ private DataEnabledOverride mDataEnabledOverride;
+
+ // for msim, user data enabled setting depends on subId.
+ private final SubscriptionManager.OnSubscriptionsChangedListener
+ mOnSubscriptionsChangeListener =
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ synchronized (this) {
+ if (mSubId != mPhone.getSubId()) {
+ log("onSubscriptionsChanged subId: " + mSubId + " to: "
+ + mPhone.getSubId());
+ mSubId = mPhone.getSubId();
+ mDataEnabledOverride = getDataEnabledOverride();
+ updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
+ mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
+ }
+ }
+ }
+ };
+
@Override
public String toString() {
return "[mInternalDataEnabled=" + mInternalDataEnabled
+ ", isUserDataEnabled=" + isUserDataEnabled()
+ ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
+ ", mPolicyDataEnabled=" + mPolicyDataEnabled
- + ", mCarrierDataEnabled=" + mCarrierDataEnabled + "]";
+ + ", mCarrierDataEnabled=" + mCarrierDataEnabled
+ + ", mIsDataEnabled=" + mIsDataEnabled
+ + ", " + mDataEnabledOverride
+ + "]";
}
public DataEnabledSettings(Phone phone) {
mPhone = phone;
mResolver = mPhone.getContext().getContentResolver();
+ SubscriptionManager subscriptionManager = (SubscriptionManager) mPhone.getContext()
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+ mDataEnabledOverride = getDataEnabledOverride();
updateDataEnabled();
}
+ private DataEnabledOverride getDataEnabledOverride() {
+ return new DataEnabledOverride(SubscriptionController.getInstance()
+ .getDataEnabledOverrideRules(mPhone.getSubId()));
+ }
+
public synchronized void setInternalDataEnabled(boolean enabled) {
- localLog("InternalDataEnabled", enabled);
if (mInternalDataEnabled != enabled) {
+ localLog("InternalDataEnabled", enabled);
mInternalDataEnabled = enabled;
updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED);
}
@@ -133,10 +175,10 @@
// Can't disable data for stand alone opportunistic subscription.
if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return;
- localLog("UserDataEnabled", enabled);
boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(),
Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0));
if (changed) {
+ localLog("UserDataEnabled", enabled);
mPhone.notifyUserMobileDataStateChanged(enabled);
updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
MultiSimSettingController.getInstance().notifyUserDataEnabled(mPhone.getSubId(),
@@ -155,9 +197,59 @@
Settings.Global.MOBILE_DATA, mPhone.getSubId(), defaultVal);
}
+ /**
+ * Set whether always allowing MMS data connection.
+ *
+ * @param alwaysAllow {@code true} if MMS data is always allowed.
+ *
+ * @return {@code false} if the setting is changed.
+ */
+ public synchronized boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
+ localLog("setAlwaysAllowMmsData", alwaysAllow);
+ mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow);
+ boolean changed = SubscriptionController.getInstance()
+ .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
+ if (changed) {
+ updateDataEnabled();
+ notifyDataEnabledOverrideChanged();
+ }
+
+ return changed;
+ }
+
+ /**
+ * Set allowing mobile data during voice call.
+ *
+ * @param allow {@code true} if allowing using data during voice call, {@code false} if
+ * disallowed
+ *
+ * @return {@code false} if the setting is changed.
+ */
+ public synchronized boolean setAllowDataDuringVoiceCall(boolean allow) {
+ localLog("setAllowDataDuringVoiceCall", allow);
+ mDataEnabledOverride.setDataAllowedInVoiceCall(allow);
+ boolean changed = SubscriptionController.getInstance()
+ .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
+ if (changed) {
+ updateDataEnabled();
+ notifyDataEnabledOverrideChanged();
+ }
+
+ return changed;
+ }
+
+ /**
+ * Check if data is allowed during voice call.
+ *
+ * @return {@code true} if data is allowed during voice call.
+ */
+ public synchronized boolean isDataAllowedInVoiceCall() {
+ return mDataEnabledOverride.isDataAllowedInVoiceCall();
+ }
+
public synchronized void setPolicyDataEnabled(boolean enabled) {
- localLog("PolicyDataEnabled", enabled);
if (mPolicyDataEnabled != enabled) {
+ localLog("PolicyDataEnabled", enabled);
mPolicyDataEnabled = enabled;
updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED);
}
@@ -168,8 +260,8 @@
}
public synchronized void setCarrierDataEnabled(boolean enabled) {
- localLog("CarrierDataEnabled", enabled);
if (mCarrierDataEnabled != enabled) {
+ localLog("CarrierDataEnabled", enabled);
mCarrierDataEnabled = enabled;
updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER);
}
@@ -205,7 +297,8 @@
if (isProvisioning()) {
mIsDataEnabled = isProvisioningDataEnabled();
} else {
- mIsDataEnabled = mInternalDataEnabled && isUserDataEnabled()
+ mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride
+ .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL))
&& mPolicyDataEnabled && mCarrierDataEnabled;
}
}
@@ -235,13 +328,12 @@
}
public synchronized void setDataRoamingEnabled(boolean enabled) {
- localLog("setDataRoamingEnabled", enabled);
-
// will trigger handleDataOnRoamingChange() through observer
boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(),
Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled);
if (changed) {
+ localLog("setDataRoamingEnabled", enabled);
MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(),
enabled);
}
@@ -284,6 +376,30 @@
mOverallDataEnabledChangedRegistrants.remove(h);
}
+ private void notifyDataEnabledOverrideChanged() {
+ mOverallDataEnabledOverrideChangedRegistrants.notifyRegistrants();
+ }
+
+ /**
+ * Register for data enabled override changed event.
+ *
+ * @param h The handler
+ * @param what The event
+ */
+ public void registerForDataEnabledOverrideChanged(Handler h, int what) {
+ mOverallDataEnabledOverrideChangedRegistrants.addUnique(h, what, null);
+ notifyDataEnabledOverrideChanged();
+ }
+
+ /**
+ * Unregistered for data enabled override changed event.
+ *
+ * @param h The handler
+ */
+ public void unregisterForDataEnabledOverrideChanged(Handler h) {
+ mOverallDataEnabledOverrideChangedRegistrants.remove(h);
+ }
+
private static boolean isStandAloneOpportunistic(int subId, Context context) {
SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo(
subId, context.getOpPackageName());
@@ -292,11 +408,12 @@
public synchronized boolean isDataEnabled(int apnType) {
boolean userDataEnabled = isUserDataEnabled();
- boolean isApnWhiteListed = SubscriptionController.getInstance().isApnWhiteListed(
- mPhone.getSubId(), mPhone.getContext().getOpPackageName(), apnType);
+ // Check if we should temporarily enable data in certain conditions.
+ boolean isDataEnabledOverridden = mDataEnabledOverride
+ .shouldOverrideDataEnabledSettings(mPhone, apnType);
return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled
- && (userDataEnabled || isApnWhiteListed));
+ && (userDataEnabled || isDataEnabledOverridden));
}
private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 716331c..90b4600 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -285,12 +285,22 @@
}
private void bindDataService() {
+ Intent intent = null;
String packageName = getDataServicePackageName();
+ String className = getDataServiceClassName();
if (TextUtils.isEmpty(packageName)) {
loge("Can't find the binding package");
return;
}
+ if (TextUtils.isEmpty(className)) {
+ intent = new Intent(DataService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ } else {
+ ComponentName cm = new ComponentName(packageName, className);
+ intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
+ }
+
if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
if (DBG) log("Service " + packageName + " already bound or being bound.");
return;
@@ -316,9 +326,7 @@
try {
mServiceConnection = new CellularDataServiceConnection();
if (!mPhone.getContext().bindService(
- new Intent(DataService.SERVICE_INTERFACE).setPackage(packageName),
- mServiceConnection,
- Context.BIND_AUTO_CREATE)) {
+ intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
loge("Cannot bind to the data service.");
return;
}
@@ -399,6 +407,55 @@
return packageName;
}
+ /**
+ * Get the data service class name for our current transport type.
+ *
+ * @return class name of the data service package for the the current transportType.
+ */
+ private String getDataServiceClassName() {
+ return getDataServiceClassName(mTransportType);
+ }
+
+
+ /**
+ * Get the data service class by transport type.
+ *
+ * @param transportType either WWAN or WLAN
+ * @return class name of the data service package for the specified transportType.
+ */
+ private String getDataServiceClassName(int transportType) {
+ String className;
+ int resourceId;
+ String carrierConfig;
+ switch (transportType) {
+ case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+ resourceId = com.android.internal.R.string.config_wwan_data_service_class;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
+ break;
+ case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
+ resourceId = com.android.internal.R.string.config_wlan_data_service_class;
+ carrierConfig = CarrierConfigManager
+ .KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
+ break;
+ default:
+ throw new IllegalStateException("Transport type not WWAN or WLAN. type="
+ + transportType);
+ }
+
+ // Read package name from resource overlay
+ className = mPhone.getContext().getResources().getString(resourceId);
+
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+ // If carrier config overrides it, use the one from carrier config
+ className = b.getString(carrierConfig, className);
+ }
+
+ return className;
+ }
+
private void sendCompleteMessage(Message msg, int code) {
if (msg != null) {
msg.arg1 = code;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 686dc9a..9bf8c8f 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -69,6 +69,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.DataFailCause;
+import android.telephony.DataFailCause.FailCause;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PcoData;
import android.telephony.Rlog;
@@ -161,15 +162,15 @@
public @interface RequestNetworkType {}
/**
- * Normal request for {@link #requestNetwork(NetworkRequest, int, LocalLog)}. For request
+ * Normal request for {@link #requestNetwork(NetworkRequest, int, Message)}. For request
* network, this adds the request to the {@link ApnContext}. If there were no network request
* attached to the {@link ApnContext} earlier, this request setups a data connection.
*/
public static final int REQUEST_TYPE_NORMAL = 1;
/**
- * Handover request for {@link #requestNetwork(NetworkRequest, int, LocalLog)} or
- * {@link #releaseNetwork(NetworkRequest, int, LocalLog)}. For request network, this
+ * Handover request for {@link #requestNetwork(NetworkRequest, int, Message)} or
+ * {@link #releaseNetwork(NetworkRequest, int)}. For request network, this
* initiates the handover data setup process. The existing data connection will be seamlessly
* handover to the new network. For release network, this performs a data connection softly
* clean up at the underlying layer (versus normal data release).
@@ -192,7 +193,7 @@
public static final int RELEASE_TYPE_NORMAL = 1;
/**
- * Detach request for {@link #releaseNetwork(NetworkRequest, int, LocalLog)} only. This
+ * Detach request for {@link #releaseNetwork(NetworkRequest, int)} only. This
* forces the APN context detach from the data connection. If this {@link ApnContext} is the
* last one attached to the data connection, the data connection will be torn down, otherwise
* the data connection remains active.
@@ -200,7 +201,7 @@
public static final int RELEASE_TYPE_DETACH = 2;
/**
- * Handover request for {@link #releaseNetwork(NetworkRequest, int, LocalLog)}. For release
+ * Handover request for {@link #releaseNetwork(NetworkRequest, int)}. For release
* network, this performs a data connection softly clean up at the underlying layer (versus
* normal data release).
*/
@@ -211,6 +212,12 @@
static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type";
static final String DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE = "extra_request_type";
static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success";
+ /**
+ * The flag indicates whether after handover failure, the data connection should remain on the
+ * original transport.
+ */
+ static final String DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK =
+ "extra_handover_failure_fallback";
private final String mLogTag;
@@ -708,6 +715,8 @@
mDataEnabledSettings.registerForDataEnabledChanged(this,
DctConstants.EVENT_DATA_ENABLED_CHANGED, null);
+ mDataEnabledSettings.registerForDataEnabledOverrideChanged(this,
+ DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED);
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
@@ -863,6 +872,9 @@
mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
unregisterServiceStateTrackerEvents();
mDataServiceManager.unregisterForServiceBindingChanged(this);
+
+ mDataEnabledSettings.unregisterForDataEnabledChanged(this);
+ mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this);
}
/**
@@ -1829,7 +1841,7 @@
}
}
- boolean isPermanentFailure(@DataFailCause.FailCause int dcFailCause) {
+ boolean isPermanentFailure(@FailCause int dcFailCause) {
return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause,
mPhone.getSubId())
&& (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST));
@@ -2183,7 +2195,7 @@
SystemClock.elapsedRealtime() + delay, alarmIntent);
}
- private void notifyNoData(@DataFailCause.FailCause int lastFailCauseCode,
+ private void notifyNoData(@FailCause int lastFailCauseCode,
ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
if (isPermanentFailure(lastFailCauseCode)
@@ -2313,13 +2325,19 @@
private void sendRequestNetworkCompleteMsg(Message message, boolean success,
@TransportType int transport,
- @RequestNetworkType int requestType) {
+ @RequestNetworkType int requestType,
+ @FailCause int cause) {
if (message == null) return;
Bundle b = message.getData();
b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
b.putInt(DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE, requestType);
b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
+ // TODO: For now this is the only fail cause that we know modem keeps data connection on
+ // original transport. Might add more complicated logic or mapping in the future.
+ b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK,
+ (requestType == REQUEST_TYPE_HANDOVER
+ && cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED));
message.sendToTarget();
}
@@ -2334,7 +2352,8 @@
ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext == null) {
loge("onEnableApn(" + apnType + "): NO ApnContext");
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType);
+ sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
+ DataFailCause.NONE);
return;
}
@@ -2349,7 +2368,8 @@
str = "onEnableApn: dependency is not met.";
if (DBG) log(str);
apnContext.requestLog(str);
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType);
+ sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
+ DataFailCause.NONE);
return;
}
@@ -2363,16 +2383,15 @@
return;
case CONNECTED:
if (DBG) log("onEnableApn: 'CONNECTED' so return");
- apnContext.requestLog("onEnableApn state=CONNECTED, so return");
-
+ // Don't add to local log since this is so common
sendRequestNetworkCompleteMsg(onCompleteMsg, true, mTransportType,
- requestType);
+ requestType, DataFailCause.NONE);
return;
case DISCONNECTING:
if (DBG) log("onEnableApn: 'DISCONNECTING' so return");
apnContext.requestLog("onEnableApn state=DISCONNECTING, so return");
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType);
+ requestType, DataFailCause.NONE);
return;
case IDLE:
// fall through: this is unexpected but if it happens cleanup and try setup
@@ -2399,7 +2418,7 @@
addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
} else {
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType);
+ requestType, DataFailCause.NONE);
}
}
@@ -2689,7 +2708,7 @@
List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
if (messageList != null) {
for (Message msg : messageList) {
- sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType);
+ sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType, cause);
}
messageList.clear();
}
@@ -3774,10 +3793,8 @@
onDataEnabledChanged(enabled, reason);
}
break;
- case DctConstants.EVENT_APN_WHITE_LIST_CHANGE:
- int apnType = msg.arg1;
- boolean enable = msg.arg2 == 1;
- onApnWhiteListChange(apnType, enable);
+ case DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED:
+ onDataEnabledOverrideRulesChanged();
break;
default:
Rlog.e("DcTracker", "Unhandled event=" + msg);
@@ -4265,30 +4282,22 @@
setActivity(activity);
}
- public void notifyApnWhiteListChange(int apnType, boolean enable) {
- Message msg = obtainMessage(DctConstants.EVENT_APN_WHITE_LIST_CHANGE);
- msg.arg1 = apnType;
- msg.arg2 = enable ? 1 : 0;
- sendMessage(msg);
- }
-
- private void onApnWhiteListChange(int apnType, boolean enable) {
+ private void onDataEnabledOverrideRulesChanged() {
if (DBG) {
- log("onApnWhiteListChange: enable=" + enable + ", apnType=" + apnType);
+ log("onDataEnabledOverrideRulesChanged");
}
- final ApnContext apnContext = mApnContextsByType.get(apnType);
- if (apnContext == null) return;
-
- if (isDataAllowed(apnContext, REQUEST_TYPE_NORMAL, null)) {
- if (apnContext.getDataConnection() != null) {
- apnContext.getDataConnection().reevaluateRestrictedState();
+ for (ApnContext apnContext : mPrioritySortedApnContexts) {
+ if (isDataAllowed(apnContext, REQUEST_TYPE_NORMAL, null)) {
+ if (apnContext.getDataConnection() != null) {
+ apnContext.getDataConnection().reevaluateRestrictedState();
+ }
+ setupDataOnConnectableApn(apnContext, Phone.REASON_DATA_ENABLED_OVERRIDE,
+ RetryFailures.ALWAYS);
+ } else if (shouldCleanUpConnection(apnContext, true)) {
+ apnContext.setReason(Phone.REASON_DATA_ENABLED_OVERRIDE);
+ cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
}
- setupDataOnConnectableApn(apnContext, Phone.REASON_APN_ADDED_TO_WHITELIST,
- RetryFailures.ALWAYS);
- } else if (shouldCleanUpConnection(apnContext, true)) {
- apnContext.setReason(Phone.REASON_APN_REMOVED_FROM_WHITELIST);
- cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index d86e676..28aa3c7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -181,9 +181,12 @@
DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
int transport = bundle.getInt(
DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
+ boolean fallback = bundle.getBoolean(
+ DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
HandoverParams handoverParams = mPendingHandovers.remove(msg);
if (handoverParams != null) {
- onDataHandoverSetupCompleted(nr, success, transport, handoverParams);
+ onDataHandoverSetupCompleted(nr, success, transport, fallback,
+ handoverParams);
} else {
logl("Handover completed but cannot find handover entry!");
}
@@ -375,34 +378,37 @@
if (!handoverPending) {
log("No handover request pending. Handover process is now completed");
- handoverParams.callback.onCompleted(true);
+ handoverParams.callback.onCompleted(true, false);
}
}
private void onDataHandoverSetupCompleted(NetworkRequest networkRequest, boolean success,
- int targetTransport, HandoverParams handoverParams) {
+ int targetTransport, boolean fallback,
+ HandoverParams handoverParams) {
log("onDataHandoverSetupCompleted: " + networkRequest + ", success=" + success
+ ", targetTransport="
- + AccessNetworkConstants.transportTypeToString(targetTransport));
+ + AccessNetworkConstants.transportTypeToString(targetTransport)
+ + ", fallback=" + fallback);
- // At this point, handover setup has been completed on the target transport. No matter
- // succeeded or not, remove the request from the source transport because even the setup
- // failed on target transport, we can retry again there.
+ // At this point, handover setup has been completed on the target transport.
+ // If it succeeded, or it failed without falling back to the original transport,
+ // we should release the request from the original transport.
+ if (!fallback) {
+ int originTransport = (targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+ : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+ int releaseType = success
+ ? DcTracker.RELEASE_TYPE_HANDOVER
+ // If handover fails, we need to tear down the existing connection, so the
+ // new data connection can be re-established on the new transport. If we leave
+ // the existing data connection in current transport, then DCT and qualified
+ // network service will be out of sync.
+ : DcTracker.RELEASE_TYPE_NORMAL;
+ releaseNetworkInternal(networkRequest, releaseType, originTransport);
+ mNetworkRequests.put(networkRequest, targetTransport);
+ }
- int originTransport = (targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
- : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
- int releaseType = success
- ? DcTracker.RELEASE_TYPE_HANDOVER
- // If handover fails, we need to tear down the existing connection, so the
- // new data connection can be re-established on the new transport. If we leave
- // the existing data connection in current transport, then DCT and qualified
- // network service will be out of sync.
- : DcTracker.RELEASE_TYPE_NORMAL;
- releaseNetworkInternal(networkRequest, releaseType, originTransport);
- mNetworkRequests.put(networkRequest, targetTransport);
-
- handoverParams.callback.onCompleted(success);
+ handoverParams.callback.onCompleted(success, fallback);
}
protected void log(String s) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
index fd3b31d..ea11223 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
@@ -199,8 +199,10 @@
* Called when handover is completed.
*
* @param success {@true} if handover succeeded, otherwise failed.
+ * @param fallback {@true} if handover failed, the data connection fallback to the
+ * original transport
*/
- void onCompleted(boolean success);
+ void onCompleted(boolean success, boolean fallback);
}
public final @ApnType int apnType;
@@ -347,28 +349,33 @@
+ AccessNetworkConstants.transportTypeToString(targetTransport));
mPendingHandoverApns.put(networks.apnType, targetTransport);
mHandoverNeededEventRegistrants.notifyResult(
- new HandoverParams(networks.apnType, targetTransport, success -> {
- // The callback for handover completed.
- if (success) {
- logl("Handover succeeded.");
- } else {
- logl("APN type "
- + ApnSetting.getApnTypeString(networks.apnType)
- + " handover to "
- + AccessNetworkConstants.transportTypeToString(
- targetTransport) + " failed.");
- }
- // No matter succeeded or not, we need to set the current transport
- // to the new one. If failed, there will be retry afterwards anyway.
- setCurrentTransport(networks.apnType, targetTransport);
- mPendingHandoverApns.delete(networks.apnType);
+ new HandoverParams(networks.apnType, targetTransport,
+ (success, fallback) -> {
+ // The callback for handover completed.
+ if (success) {
+ logl("Handover succeeded.");
+ } else {
+ logl("APN type "
+ + ApnSetting.getApnTypeString(networks.apnType)
+ + " handover to "
+ + AccessNetworkConstants.transportTypeToString(
+ targetTransport) + " failed."
+ + ", fallback=" + fallback);
+ }
+ if (success || !fallback) {
+ // If handover succeeds or failed without falling back
+ // to the original transport, we should move to the new
+ // transport (even if it is failed).
+ setCurrentTransport(networks.apnType, targetTransport);
+ }
+ mPendingHandoverApns.delete(networks.apnType);
- // If there are still pending available network changes, we need to
- // process the rest.
- if (mAvailableNetworksList.size() > 0) {
- sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
- }
- }));
+ // If there are still pending available network changes, we
+ // need to process the rest.
+ if (mAvailableNetworksList.size() > 0) {
+ sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
+ }
+ }));
}
mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
} else {
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 6e622aa..2c822ba 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -43,10 +43,13 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.ecc.nano.ProtobufEccData;
import com.android.phone.ecc.nano.ProtobufEccData.EccInfo;
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -58,8 +61,6 @@
import java.util.List;
import java.util.zip.GZIPInputStream;
-import libcore.io.IoUtils;
-
/**
* Emergency Number Tracker that handles update of emergency number list from RIL and emergency
* number database. This is multi-sim based and each Phone has a EmergencyNumberTracker.
@@ -358,6 +359,7 @@
if (!emergencyNumberListRadio.equals(mEmergencyNumberListFromRadio)) {
try {
EmergencyNumber.mergeSameNumbersInEmergencyNumberList(emergencyNumberListRadio);
+ writeUpdatedEmergencyNumberListMetrics(emergencyNumberListRadio);
mEmergencyNumberListFromRadio = emergencyNumberListRadio;
if (!DBG) {
mEmergencyNumberListRadioLocalLog.log("updateRadioEmergencyNumberList:"
@@ -382,6 +384,7 @@
mCountryIso = countryIso.toLowerCase();
cacheEmergencyDatabaseByCountry(countryIso);
+ writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase);
if (!DBG) {
mEmergencyNumberListDatabaseLocalLog.log(
"updateEmergencyNumberListDatabaseAndNotify:"
@@ -835,6 +838,17 @@
Rlog.e(TAG, str);
}
+ private void writeUpdatedEmergencyNumberListMetrics(
+ List<EmergencyNumber> updatedEmergencyNumberList) {
+ if (updatedEmergencyNumberList == null) {
+ return;
+ }
+ for (EmergencyNumber num : updatedEmergencyNumberList) {
+ TelephonyMetrics.getInstance().writeEmergencyNumberUpdateEvent(
+ mPhone.getPhoneId(), num);
+ }
+ }
+
/**
* Dump Emergency Number List info in the tracking
*
diff --git a/src/java/com/android/internal/telephony/ims/RcsMessageStoreController.java b/src/java/com/android/internal/telephony/ims/RcsMessageStoreController.java
index e7d3f48..4669141 100644
--- a/src/java/com/android/internal/telephony/ims/RcsMessageStoreController.java
+++ b/src/java/com/android/internal/telephony/ims/RcsMessageStoreController.java
@@ -74,16 +74,16 @@
import static com.android.internal.telephony.ims.RcsMessageStoreUtil.getMessageTableUri;
import static com.android.internal.telephony.ims.RcsParticipantQueryHelper.getUriForParticipant;
-import static com.android.internal.telephony.ims.RcsThreadQueryHelper.get1To1ThreadUri;
-import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getAllParticipantsInThreadUri;
-import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getGroupThreadUri;
-import static com.android.internal.telephony.ims.RcsThreadQueryHelper.getParticipantInThreadUri;
+import static com.android.internal.telephony.ims.RcsThreadHelper.get1To1ThreadUri;
+import static com.android.internal.telephony.ims.RcsThreadHelper.getGroupThreadUri;
+import static com.android.internal.telephony.ims.RcsThreadHelper.getParticipantInThreadUri;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -106,6 +106,8 @@
import android.telephony.ims.aidl.IRcs;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
/**
* Backing implementation of {@link RcsMessageStore}.
@@ -122,7 +124,7 @@
private final RcsParticipantQueryHelper mParticipantQueryHelper;
private final RcsMessageQueryHelper mMessageQueryHelper;
private final RcsEventQueryHelper mEventQueryHelper;
- private final RcsThreadQueryHelper mThreadQueryHelper;
+ private final RcsThreadHelper mThreadHelper;
private final RcsMessageStoreUtil mMessageStoreUtil;
/**
@@ -142,42 +144,36 @@
return sInstance;
}
- interface ThrowingSupplier<T> {
- T get() throws RemoteException;
- }
-
- interface ThrowingRunnable {
- void run() throws RemoteException;
- }
-
+ /**
+ * This call cannot be nested with either {@link #performWriteOperation} or {@link
+ * #performReadOperation} as the permission check will then fail since we have cleared the
+ * calling identity.
+ */
private void performWriteOperation(String callingPackage, ThrowingRunnable fn) {
- RcsPermissions.checkWritePermissions(mContext, callingPackage);
-
- try {
+ performWriteOperation(callingPackage, () -> {
fn.run();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ return null;
+ });
}
- private <T> T performCreateOperation(String callingPackage, ThrowingSupplier<T> fn) {
+ /**
+ * This call cannot be nested with either {@link #performWriteOperation} or {@link
+ * #performReadOperation} as the permission check will then fail since we have cleared the
+ * calling identity.
+ */
+ private <T> T performWriteOperation(String callingPackage, ThrowingSupplier<T> fn) {
RcsPermissions.checkWritePermissions(mContext, callingPackage);
-
- try {
- return fn.get();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ return Binder.withCleanCallingIdentity(fn);
}
+ /**
+ * This call cannot be nested with either {@link #performWriteOperation} or {@link
+ * #performReadOperation} as the permission check will then fail since we have cleared the
+ * calling identity.
+ */
private <T> T performReadOperation(String callingPackage, ThrowingSupplier<T> fn) {
RcsPermissions.checkReadPermissions(mContext, callingPackage);
-
- try {
- return fn.get();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ return Binder.withCleanCallingIdentity(fn);
}
@VisibleForTesting
@@ -186,14 +182,14 @@
mContentResolver = context.getContentResolver();
mParticipantQueryHelper = new RcsParticipantQueryHelper(mContentResolver);
mMessageQueryHelper = new RcsMessageQueryHelper(mContentResolver);
- mThreadQueryHelper = new RcsThreadQueryHelper(mContentResolver, mParticipantQueryHelper);
+ mThreadHelper = new RcsThreadHelper(mContentResolver, mParticipantQueryHelper);
mEventQueryHelper = new RcsEventQueryHelper(mContentResolver);
mMessageStoreUtil = new RcsMessageStoreUtil(mContentResolver);
}
@Override
public boolean deleteThread(int threadId, int threadType, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
int deletionCount = mContentResolver.delete(
threadType == THREAD_TYPE_GROUP ? RCS_GROUP_THREAD_URI : RCS_1_TO_1_THREAD_URI,
RCS_THREAD_ID_COLUMN + "=?",
@@ -217,7 +213,7 @@
return performReadOperation(callingPackage, () -> {
Bundle bundle = new Bundle();
bundle.putParcelable(THREAD_QUERY_PARAMETERS_KEY, queryParameters);
- return mThreadQueryHelper.performThreadQuery(bundle);
+ return mThreadHelper.performThreadQuery(bundle);
});
}
@@ -227,7 +223,7 @@
return performReadOperation(callingPackage, () -> {
Bundle bundle = new Bundle();
bundle.putParcelable(QUERY_CONTINUATION_TOKEN, continuationToken);
- return mThreadQueryHelper.performThreadQuery(bundle);
+ return mThreadHelper.performThreadQuery(bundle);
});
}
@@ -293,15 +289,15 @@
@Override
public int createRcs1To1Thread(int recipientId, String callingPackage) {
- return performCreateOperation(callingPackage,
- () -> mThreadQueryHelper.create1To1Thread(recipientId));
+ return performWriteOperation(callingPackage,
+ () -> mThreadHelper.create1To1Thread(recipientId));
}
@Override
public int createGroupThread(int[] participantIds, String groupName, Uri groupIcon,
String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
- int groupThreadId = mThreadQueryHelper.createGroupThread(groupName, groupIcon);
+ return performWriteOperation(callingPackage, () -> {
+ int groupThreadId = mThreadHelper.createGroupThread(groupName, groupIcon);
if (groupThreadId <= 0) {
throw new RemoteException("Could not create RcsGroupThread.");
}
@@ -311,7 +307,7 @@
// under one transaction
if (participantIds != null) {
for (int participantId : participantIds) {
- addParticipantToGroupThread(groupThreadId, participantId, callingPackage);
+ mThreadHelper.addParticipantToGroupThread(groupThreadId, participantId);
}
}
@@ -326,7 +322,7 @@
*/
@Override
public int createRcsParticipant(String canonicalAddress, String alias, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues contentValues = new ContentValues();
long canonicalAddressId = Telephony.RcsColumns.RcsCanonicalAddressHelper
@@ -494,11 +490,7 @@
public void addParticipantToGroupThread(int rcsThreadId, int participantId,
String callingPackage) {
performWriteOperation(callingPackage, () -> {
- ContentValues contentValues = new ContentValues(2);
- contentValues.put(RCS_THREAD_ID_COLUMN, rcsThreadId);
- contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
-
- mContentResolver.insert(getAllParticipantsInThreadUri(rcsThreadId), contentValues);
+ mThreadHelper.addParticipantToGroupThread(rcsThreadId, participantId);
});
}
@@ -515,7 +507,7 @@
public int addIncomingMessage(int rcsThreadId,
RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams,
String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues contentValues = new ContentValues();
contentValues.put(ARRIVAL_TIMESTAMP_COLUMN,
@@ -536,7 +528,7 @@
public int addOutgoingMessage(int rcsThreadId,
RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParameters,
String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues contentValues = new ContentValues();
mMessageQueryHelper.createContentValuesForGenericMessage(contentValues, rcsThreadId,
@@ -795,7 +787,7 @@
@Override
public int storeFileTransfer(int messageId, boolean isIncoming,
RcsFileTransferCreationParams fileTransferCreationParameters, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues contentValues = mMessageQueryHelper.getContentValuesForFileTransfer(
fileTransferCreationParameters);
Uri uri = mContentResolver.insert(
@@ -979,7 +971,7 @@
@Override
public int createGroupThreadNameChangedEvent(long timestamp, int threadId,
int originationParticipantId, String newName, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues eventSpecificValues = new ContentValues();
eventSpecificValues.put(NEW_NAME_COLUMN, newName);
@@ -991,7 +983,7 @@
@Override
public int createGroupThreadIconChangedEvent(long timestamp, int threadId,
int originationParticipantId, Uri newIcon, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues eventSpecificValues = new ContentValues();
eventSpecificValues.put(NEW_ICON_URI_COLUMN,
newIcon == null ? null : newIcon.toString());
@@ -1004,7 +996,7 @@
@Override
public int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId,
int originationParticipantId, int participantId, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues eventSpecificValues = new ContentValues();
eventSpecificValues.put(DESTINATION_PARTICIPANT_ID_COLUMN, participantId);
@@ -1017,7 +1009,7 @@
@Override
public int createGroupThreadParticipantLeftEvent(long timestamp, int threadId,
int originationParticipantId, int participantId, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues eventSpecificValues = new ContentValues();
eventSpecificValues.put(DESTINATION_PARTICIPANT_ID_COLUMN, participantId);
@@ -1029,7 +1021,7 @@
@Override
public int createParticipantAliasChangedEvent(long timestamp, int participantId,
String newAlias, String callingPackage) {
- return performCreateOperation(callingPackage, () -> {
+ return performWriteOperation(callingPackage, () -> {
ContentValues contentValues = new ContentValues(4);
contentValues.put(TIMESTAMP_COLUMN, timestamp);
contentValues.put(SOURCE_PARTICIPANT_ID_COLUMN, participantId);
diff --git a/src/java/com/android/internal/telephony/ims/RcsThreadQueryHelper.java b/src/java/com/android/internal/telephony/ims/RcsThreadHelper.java
similarity index 93%
rename from src/java/com/android/internal/telephony/ims/RcsThreadQueryHelper.java
rename to src/java/com/android/internal/telephony/ims/RcsThreadHelper.java
index d4bc299..6d95430 100644
--- a/src/java/com/android/internal/telephony/ims/RcsThreadQueryHelper.java
+++ b/src/java/com/android/internal/telephony/ims/RcsThreadHelper.java
@@ -48,13 +48,13 @@
* A helper class focused on querying RCS threads from the
* {@link com.android.providers.telephony.RcsProvider}
*/
-class RcsThreadQueryHelper {
+class RcsThreadHelper {
private static final int THREAD_ID_INDEX_IN_INSERTION_URI = 1;
private final ContentResolver mContentResolver;
private final RcsParticipantQueryHelper mParticipantQueryHelper;
- RcsThreadQueryHelper(ContentResolver contentResolver,
+ RcsThreadHelper(ContentResolver contentResolver,
RcsParticipantQueryHelper participantQueryHelper) {
mContentResolver = contentResolver;
mParticipantQueryHelper = participantQueryHelper;
@@ -130,6 +130,14 @@
return threadId;
}
+ void addParticipantToGroupThread(int rcsThreadId, int participantId) {
+ ContentValues contentValues = new ContentValues(2);
+ contentValues.put(RCS_THREAD_ID_COLUMN, rcsThreadId);
+ contentValues.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
+
+ mContentResolver.insert(getAllParticipantsInThreadUri(rcsThreadId), contentValues);
+ }
+
static Uri get1To1ThreadUri(int rcsThreadId) {
return Uri.withAppendedPath(RCS_1_TO_1_THREAD_URI, Integer.toString(rcsThreadId));
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 286dd13..8c5be19 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -98,6 +98,7 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.uicc.IccRecords;
@@ -233,6 +234,16 @@
return mCurrentSubscriberUris;
}
+ @Override
+ public EmergencyNumberTracker getEmergencyNumberTracker() {
+ return mDefaultPhone.getEmergencyNumberTracker();
+ }
+
+ @Override
+ public ServiceStateTracker getServiceStateTracker() {
+ return mDefaultPhone.getServiceStateTracker();
+ }
+
// Create Cf (Call forward) so that dialling number &
// mIsCfu (true if reason is call forward unconditional)
// mOnComplete (Message object passed by client) can be packed &
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index b8eb808..2f53e91 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -92,9 +92,11 @@
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneInternalInterface;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
@@ -2344,7 +2346,8 @@
String callId = imsCall.getSession().getCallId();
mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
- reasonInfo, mCallQualityMetrics.get(callId));
+ reasonInfo, mCallQualityMetrics.get(callId), conn.getEmergencyNumberInfo(),
+ getNetworkCountryIso());
pruneCallQualityMetricsHistory();
mPhone.notifyImsReason(reasonInfo);
@@ -4196,6 +4199,21 @@
mAlwaysPlayRemoteHoldTone = shouldPlayRemoteHoldTone;
}
+ private String getNetworkCountryIso() {
+ String countryIso = "";
+ if (mPhone != null) {
+ ServiceStateTracker sst = mPhone.getServiceStateTracker();
+ if (sst != null) {
+ LocaleTracker lt = sst.getLocaleTracker();
+ if (lt != null) {
+ countryIso = lt.getCurrentCountry();
+ }
+ }
+ }
+ return countryIso;
+ }
+
+ @Override
public ImsPhone getPhone() {
return mPhone;
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 085952b..e33bd6d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -248,7 +248,7 @@
mIsEmergency = isEmergency;
if (isEmergency) {
- setEmergencyCallInfo();
+ setEmergencyCallInfo(mOwner);
}
fetchDtmfToneDelay(phone);
diff --git a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
index 9a0b7e0..d4bacc6 100644
--- a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.metrics;
+import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo;
import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import com.android.internal.telephony.nano.TelephonyProto.ImsReasonInfo;
@@ -156,4 +157,17 @@
mEvent.callQualitySummaryUl = callQualitySummary;
return this;
}
+
+ /** Set if the Ims call is emergency. */
+ public CallSessionEventBuilder setIsImsEmergencyCall(boolean isImsEmergencyCall) {
+ mEvent.isImsEmergencyCall = isImsEmergencyCall;
+ return this;
+ }
+
+ /** Set the Ims emergency call information. */
+ public CallSessionEventBuilder setImsEmergencyNumberInfo(
+ EmergencyNumberInfo imsEmergencyNumberInfo) {
+ mEvent.imsEmergencyNumberInfo = imsEmergencyNumberInfo;
+ 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 8a97579..73439c3 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.metrics;
+import static com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo;
import static com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
import static com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import static com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
@@ -143,6 +144,16 @@
return this;
}
+ /**
+ * Set and build EMERGENCY_NUMBER_REPORT event
+ */
+ public TelephonyEventBuilder setUpdatedEmergencyNumber(
+ EmergencyNumberInfo emergencyNumberInfo) {
+ mEvent.type = TelephonyEvent.Type.EMERGENCY_NUMBER_REPORT;
+ mEvent.updatedEmergencyNumber = emergencyNumberInfo;
+ return this;
+ }
+
public TelephonyEventBuilder setCarrierKeyChange(CarrierKeyChange carrierKeyChange) {
mEvent.type = TelephonyEvent.Type.CARRIER_KEY_CHANGED;
mEvent.carrierKeyChange = carrierKeyChange;
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index cacb894..6d56d1d 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -42,6 +42,7 @@
import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.CallQuality;
+import android.telephony.DisconnectCause;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
@@ -52,6 +53,7 @@
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataService;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
@@ -73,6 +75,7 @@
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.nano.TelephonyProto;
import com.android.internal.telephony.nano.TelephonyProto.ActiveSubscriptionInfo;
+import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo;
import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
@@ -110,6 +113,7 @@
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
/**
* Telephony metrics holds all metrics events and convert it into telephony proto buf.
@@ -296,6 +300,8 @@
return "CARRIER_ID_MATCHING";
case TelephonyEvent.Type.NITZ_TIME:
return "NITZ_TIME";
+ case TelephonyEvent.Type.EMERGENCY_NUMBER_REPORT:
+ return "EMERGENCY_NUMBER_REPORT";
default:
return Integer.toString(event);
}
@@ -1407,13 +1413,14 @@
* @param phoneId Phone id
* @param connections Array of GsmCdmaConnection objects
*/
- public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections) {
+ public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections,
+ String countryIso) {
logv("Logging CallList Changed Connections Size = " + connections.size());
InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
if (callSession == null) {
Rlog.e(TAG, "writeRilCallList: Call session is missing");
} else {
- RilCall[] calls = convertConnectionsToRilCalls(connections);
+ RilCall[] calls = convertConnectionsToRilCalls(connections, countryIso);
callSession.addEvent(
new CallSessionEventBuilder(
TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED)
@@ -1433,17 +1440,31 @@
return true;
}
- private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections) {
+ private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections,
+ String countryIso) {
RilCall[] calls = new RilCall[mConnections.size()];
for (int i = 0; i < mConnections.size(); i++) {
calls[i] = new RilCall();
calls[i].index = i;
- convertConnectionToRilCall(mConnections.get(i), calls[i]);
+ convertConnectionToRilCall(mConnections.get(i), calls[i], countryIso);
}
return calls;
}
- private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call) {
+ private EmergencyNumberInfo convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num) {
+ EmergencyNumberInfo emergencyNumberInfo = new EmergencyNumberInfo();
+ emergencyNumberInfo.address = num.getNumber();
+ emergencyNumberInfo.countryIso = num.getCountryIso();
+ emergencyNumberInfo.mnc = num.getMnc();
+ emergencyNumberInfo.serviceCategoriesBitmask = num.getEmergencyServiceCategoryBitmask();
+ emergencyNumberInfo.urns = num.getEmergencyUrns().stream().toArray(String[]::new);
+ emergencyNumberInfo.numberSourcesBitmask = num.getEmergencyNumberSourceBitmask();
+ emergencyNumberInfo.routing = num.getEmergencyCallRouting();
+ return emergencyNumberInfo;
+ }
+
+ private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call,
+ String countryIso) {
if (conn.isIncoming()) {
call.type = Type.MT;
} else {
@@ -1484,6 +1505,18 @@
call.callEndReason = conn.getDisconnectCause();
call.isMultiparty = conn.isMultiparty();
call.preciseDisconnectCause = conn.getPreciseDisconnectCause();
+
+ // Emergency call metrics when call ends
+ if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED
+ && conn.isEmergencyCall() && conn.getEmergencyNumberInfo() != null) {
+ /** Only collect this emergency number information per sample percentage */
+ if (ThreadLocalRandom.current().nextDouble(0, 100)
+ < getSamplePercentageForEmergencyCall(countryIso)) {
+ call.isEmergencyCall = conn.isEmergencyCall();
+ call.emergencyNumberInfo = convertEmergencyNumberToEmergencyNumberInfo(
+ conn.getEmergencyNumberInfo());
+ }
+ }
}
/**
@@ -1504,7 +1537,7 @@
RilCall[] calls = new RilCall[1];
calls[0] = new RilCall();
calls[0].index = -1;
- convertConnectionToRilCall(conn, calls[0]);
+ convertConnectionToRilCall(conn, calls[0], "");
callSession.addEvent(callSession.startElapsedTimeMs,
new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
.setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL)
@@ -1533,7 +1566,8 @@
* @param conn Connection object associated with the call that is being hung-up
* @param callId Call id
*/
- public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId) {
+ public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId,
+ String countryIso) {
InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
if (callSession == null) {
Rlog.e(TAG, "writeRilHangup: Call session is missing");
@@ -1541,7 +1575,7 @@
RilCall[] calls = new RilCall[1];
calls[0] = new RilCall();
calls[0].index = callId;
- convertConnectionToRilCall(conn, calls[0]);
+ convertConnectionToRilCall(conn, calls[0], countryIso);
callSession.addEvent(
new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
.setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP)
@@ -2034,26 +2068,32 @@
* @param reasonInfo Call end reason
*/
public void writeOnImsCallTerminated(int phoneId, ImsCallSession session,
- ImsReasonInfo reasonInfo, CallQualityMetrics cqm) {
+ ImsReasonInfo reasonInfo, CallQualityMetrics cqm,
+ EmergencyNumber emergencyNumber, String countryIso) {
InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
if (callSession == null) {
Rlog.e(TAG, "Call session is missing");
} else {
+ CallSessionEventBuilder callSessionEvent = new CallSessionEventBuilder(
+ TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED);
+ callSessionEvent.setCallIndex(getCallId(session));
+ callSessionEvent.setImsReasonInfo(toImsReasonInfoProto(reasonInfo));
+
if (cqm != null) {
- callSession.addEvent(
- new CallSessionEventBuilder(
- TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
- .setCallIndex(getCallId(session))
- .setImsReasonInfo(toImsReasonInfoProto(reasonInfo))
- .setCallQualitySummaryDl(cqm.getCallQualitySummaryDl())
- .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl()));
- } else {
- callSession.addEvent(
- new CallSessionEventBuilder(
- TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
- .setCallIndex(getCallId(session))
- .setImsReasonInfo(toImsReasonInfoProto(reasonInfo)));
+ callSessionEvent.setCallQualitySummaryDl(cqm.getCallQualitySummaryDl())
+ .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl());
}
+
+ if (emergencyNumber != null) {
+ /** Only collect this emergency number information per sample percentage */
+ if (ThreadLocalRandom.current().nextDouble(0, 100)
+ < getSamplePercentageForEmergencyCall(countryIso)) {
+ callSessionEvent.setIsImsEmergencyCall(true);
+ callSessionEvent.setImsEmergencyNumberInfo(
+ convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber));
+ }
+ }
+ callSession.addEvent(callSessionEvent);
}
}
@@ -2420,6 +2460,23 @@
}
/**
+ * Write emergency number update event
+ *
+ * @param emergencyNumber Updated emergency number
+ */
+ public void writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber) {
+ if (emergencyNumber == null) {
+ return;
+ }
+ final EmergencyNumberInfo emergencyNumberInfo =
+ convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber);
+
+ TelephonyEvent event = new TelephonyEventBuilder(phoneId).setUpdatedEmergencyNumber(
+ emergencyNumberInfo).build();
+ addTelephonyEvent(event);
+ }
+
+ /**
* Convert SMS format
*/
private int convertSmsFormat(String format) {
@@ -2582,6 +2639,38 @@
ImsReasonInfo reasonInfo) {}
public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {}
+ /**
+ * Get the sample percentage of collecting metrics based on countries' population.
+ *
+ * The larger population the country has, the lower percentage we use to collect this
+ * metrics. Since the exact population changes frequently, buckets of the population are used
+ * instead of its exact number. Seven different levels of sampling percentage are assigned
+ * based on the scale of population for countries.
+ */
+ private double getSamplePercentageForEmergencyCall(String countryIso) {
+ String countriesFor1Percentage = "cn,in";
+ String countriesFor5Percentage = "us,id,br,pk,ng,bd,ru,mx,jp,et,ph,eg,vn,cd,tr,ir,de";
+ String countriesFor15Percentage = "th,gb,fr,tz,it,za,mm,ke,kr,co,es,ug,ar,ua,dz,sd,iq";
+ String countriesFor25Percentage = "pl,ca,af,ma,sa,pe,uz,ve,my,ao,mz,gh,np,ye,mg,kp,cm";
+ String countriesFor35Percentage = "au,tw,ne,lk,bf,mw,ml,ro,kz,sy,cl,zm,gt,zw,nl,ec,sn";
+ String countriesFor45Percentage = "kh,td,so,gn,ss,rw,bj,tn,bi,be,cu,bo,ht,gr,do,cz,pt";
+ if (countriesFor1Percentage.contains(countryIso)) {
+ return 1;
+ } else if (countriesFor5Percentage.contains(countryIso)) {
+ return 5;
+ } else if (countriesFor15Percentage.contains(countryIso)) {
+ return 15;
+ } else if (countriesFor25Percentage.contains(countryIso)) {
+ return 25;
+ } else if (countriesFor35Percentage.contains(countryIso)) {
+ return 35;
+ } else if (countriesFor45Percentage.contains(countryIso)) {
+ return 45;
+ } else {
+ return 50;
+ }
+ }
+
private static int mapSimStateToProto(int simState) {
switch (simState) {
case TelephonyManager.SIM_STATE_ABSENT:
diff --git a/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java b/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java
index 6c89448..7891c7c 100644
--- a/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java
+++ b/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java
@@ -38,7 +38,7 @@
public void onReceive(Context context, Intent intent) {
String pkgName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
- if (!UiccProfile.isPackageInstalled(context, pkgName)) {
+ if (!UiccProfile.isPackageBundled(context, pkgName)) {
InstallCarrierAppUtils.showNotification(context, pkgName);
InstallCarrierAppUtils.registerPackageInstallReceiver(context);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 116591a..2358c43 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -34,6 +34,7 @@
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.os.UserManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -140,15 +141,28 @@
new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
- mContext.getContentResolver().unregisterContentObserver(this);
- for (String pkgName : getUninstalledCarrierPackages()) {
- InstallCarrierAppUtils.showNotification(mContext, pkgName);
- InstallCarrierAppUtils.registerPackageInstallReceiver(mContext);
+ synchronized (mLock) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ mProvisionCompleteContentObserverRegistered = false;
+ showCarrierAppNotificationsIfPossible();
}
}
};
+ private boolean mProvisionCompleteContentObserverRegistered;
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ mContext.unregisterReceiver(this);
+ mUserUnlockReceiverRegistered = false;
+ showCarrierAppNotificationsIfPossible();
+ }
+ }
+ };
+ private boolean mUserUnlockReceiverRegistered;
+
+ private final BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
@@ -253,7 +267,7 @@
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- c.registerReceiver(mReceiver, intentfilter);
+ c.registerReceiver(mCarrierConfigChangedReceiver, intentfilter);
}
/**
@@ -271,11 +285,22 @@
unregisterAllAppEvents();
unregisterCurrAppEvents();
+ if (mProvisionCompleteContentObserverRegistered) {
+ mContext.getContentResolver()
+ .unregisterContentObserver(mProvisionCompleteContentObserver);
+ mProvisionCompleteContentObserverRegistered = false;
+ }
+
+ if (mUserUnlockReceiverRegistered) {
+ mContext.unregisterReceiver(mUserUnlockReceiver);
+ mUserUnlockReceiverRegistered = false;
+ }
+
InstallCarrierAppUtils.hideAllNotifications(mContext);
InstallCarrierAppUtils.unregisterPackageInstallReceiver(mContext);
mCi.unregisterForOffOrNotAvailable(mHandler);
- mContext.unregisterReceiver(mReceiver);
+ mContext.unregisterReceiver(mCarrierConfigChangedReceiver);
if (mCatService != null) mCatService.dispose();
for (UiccCardApplication app : mUiccApplications) {
@@ -1158,10 +1183,13 @@
}
}
- static boolean isPackageInstalled(Context context, String pkgName) {
+ static boolean isPackageBundled(Context context, String pkgName) {
PackageManager pm = context.getPackageManager();
try {
- pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);
+ // We also match hidden-until-installed apps. The assumption here is that some other
+ // mechanism (like CarrierAppUtils) would automatically enable such an app, so we
+ // shouldn't prompt the user about it.
+ pm.getApplicationInfo(pkgName, PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS);
if (DBG) log(pkgName + " is installed.");
return true;
} catch (PackageManager.NameNotFoundException e) {
@@ -1188,21 +1216,47 @@
synchronized (mLock) {
mCarrierPrivilegeRegistrants.notifyRegistrants();
- boolean isProvisioned = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 1) == 1;
- // Only show dialog if the phone is through with Setup Wizard. Otherwise, wait for
- // completion and show a notification instead
- if (isProvisioned) {
+ boolean isProvisioned = isProvisioned();
+ boolean isUnlocked = isUserUnlocked();
+ // Only show dialog if the phone is through with Setup Wizard and is unlocked.
+ // Otherwise, wait for completion and unlock and show a notification instead.
+ if (isProvisioned && isUnlocked) {
for (String pkgName : getUninstalledCarrierPackages()) {
promptInstallCarrierApp(pkgName);
}
} else {
- final Uri uri = Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED);
- mContext.getContentResolver().registerContentObserver(
- uri,
- false,
- mProvisionCompleteContentObserver);
+ if (!isProvisioned) {
+ final Uri uri = Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED);
+ mContext.getContentResolver().registerContentObserver(
+ uri,
+ false,
+ mProvisionCompleteContentObserver);
+ mProvisionCompleteContentObserverRegistered = true;
+ }
+ if (!isUnlocked) {
+ mContext.registerReceiver(
+ mUserUnlockReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+ mUserUnlockReceiverRegistered = true;
+ }
+ }
+ }
+ }
+
+ private boolean isProvisioned() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1) == 1;
+ }
+
+ private boolean isUserUnlocked() {
+ return mContext.getSystemService(UserManager.class).isUserUnlocked();
+ }
+
+ private void showCarrierAppNotificationsIfPossible() {
+ if (isProvisioned() && isUserUnlocked()) {
+ for (String pkgName : getUninstalledCarrierPackages()) {
+ InstallCarrierAppUtils.showNotification(mContext, pkgName);
+ InstallCarrierAppUtils.registerPackageInstallReceiver(mContext);
}
}
}
@@ -1226,7 +1280,7 @@
for (UiccAccessRule accessRule : accessRules) {
String certHexString = accessRule.getCertificateHexString().toUpperCase();
String pkgName = certPackageMap.get(certHexString);
- if (!TextUtils.isEmpty(pkgName) && !isPackageInstalled(mContext, pkgName)) {
+ if (!TextUtils.isEmpty(pkgName) && !isPackageBundled(mContext, pkgName)) {
uninstalledCarrierPackages.add(pkgName);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
index e7e694c..5e2affb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
@@ -127,7 +127,7 @@
assertNull(connection1.getEmergencyNumberInfo());
assertFalse(connection1.hasKnownUserIntentEmergency());
- connection2.setEmergencyCallInfo();
+ connection2.setEmergencyCallInfo(mPhone.getCallTracker());
connection2.setHasKnownUserIntentEmergency(true);
connection1.migrateFrom(connection2);
@@ -141,7 +141,7 @@
@Test
public void testEmergencyCallParameters() {
Connection connection = new TestConnection(TEST_PHONE_TYPE);
- connection.setEmergencyCallInfo();
+ connection.setEmergencyCallInfo(mPhone.getCallTracker());
assertTrue(connection.isEmergencyCall());
assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
connection.setHasKnownUserIntentEmergency(true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 5d1ece3..e6be985 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -265,6 +265,8 @@
return Context.APP_OPS_SERVICE;
} else if (serviceClass == TelecomManager.class) {
return Context.TELECOM_SERVICE;
+ } else if (serviceClass == UserManager.class) {
+ return Context.USER_SERVICE;
}
return super.getSystemServiceName(serviceClass);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 49ca3c6..9753722 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -108,7 +108,8 @@
+ " INTEGER DEFAULT " + SubscriptionManager.PROFILE_CLASS_DEFAULT + ","
+ SubscriptionManager.SUBSCRIPTION_TYPE + " INTEGER DEFAULT 0,"
+ SubscriptionManager.WHITE_LISTED_APN_DATA + " INTEGER DEFAULT 0,"
- + SubscriptionManager.GROUP_OWNER + " TEXT"
+ + SubscriptionManager.GROUP_OWNER + " TEXT,"
+ + SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES + " TEXT"
+ ");";
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 9a5b481..f9176af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -646,6 +646,7 @@
doReturn(0).when(mPhone).getPhoneId();
doReturn(1).when(mPhone2).getPhoneId();
doReturn(true).when(mPhone2).isUserDataEnabled();
+ doReturn(mDataEnabledSettings).when(mPhone2).getDataEnabledSettings();
for (int i = 0; i < numPhones; i++) {
mSlotIndexToSubId[i] = new int[1];
mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index eaeea94..0d1551c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -24,12 +24,14 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
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.Manifest;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.os.Bundle;
@@ -38,11 +40,15 @@
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.UiccSlotInfo;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.filters.FlakyTest;
+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;
@@ -63,6 +69,8 @@
private MockContentResolver mMockContentResolver;
private FakeTelephonyProvider mFakeTelephonyProvider;
@Mock
+ private UiccSlot mUiccSlot;
+ @Mock
private ITelephonyRegistry.Stub mTelephonyRegisteryMock;
@Mock
private MultiSimSettingController mMultiSimSettingControllerMock;
@@ -928,4 +936,70 @@
// Make sure the return sub ids are sorted by slot index
assertTrue("active sub ids = " + subIds, Arrays.equals(subIds, new int[]{2, 1}));
}
+
+ @Test
+ public void testGetEnabledSubscriptionIdSingleSIM() {
+ // A single SIM device may have logical slot 0 mapped to physical slot 1
+ // (i.e. logical slot -1 mapped to physical slot 0)
+ UiccSlotInfo slot0 = getFakeUiccSlotInfo(false, -1);
+ UiccSlotInfo slot1 = getFakeUiccSlotInfo(true, 0);
+ UiccSlotInfo [] uiccSlotInfos = {slot0, slot1};
+ UiccSlot [] uiccSlots = {mUiccSlot, mUiccSlot};
+
+ doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
+ doReturn(uiccSlots).when(mUiccController).getUiccSlots();
+ assertEquals(2, UiccController.getInstance().getUiccSlots().length);
+
+ ContentResolver resolver = mContext.getContentResolver();
+ // logical 0 should find physical 1, has settings enabled subscription 0
+ Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 1, 0);
+
+ int enabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(0);
+ assertEquals(0, enabledSubscription);
+ }
+
+ @Test
+ public void testGetEnabledSubscriptionIdDualSIM() {
+ doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
+ doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
+ // A dual SIM device may have logical slot 0 mapped to physical slot 0
+ // (i.e. logical slot 1 mapped to physical slot 1)
+ UiccSlotInfo slot0 = getFakeUiccSlotInfo(true, 0);
+ UiccSlotInfo slot1 = getFakeUiccSlotInfo(true, 1);
+ UiccSlotInfo [] uiccSlotInfos = {slot0, slot1};
+ UiccSlot [] uiccSlots = {mUiccSlot, mUiccSlot};
+
+ doReturn(2).when(mTelephonyManager).getPhoneCount();
+ doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
+ doReturn(uiccSlots).when(mUiccController).getUiccSlots();
+ assertEquals(2, UiccController.getInstance().getUiccSlots().length);
+
+ ContentResolver resolver = mContext.getContentResolver();
+ // logical 0 should find physical 0, has settings enabled subscription 0
+ Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 0, 0);
+ Settings.Global.putInt(resolver, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + 1, 1);
+
+ int enabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(0);
+ int secondEabledSubscription = mSubscriptionControllerUT.getEnabledSubscriptionId(1);
+ assertEquals(0, enabledSubscription);
+ assertEquals(1, secondEabledSubscription);
+ }
+
+
+ private UiccSlotInfo getFakeUiccSlotInfo(boolean active, int logicalSlotIndex) {
+ return new UiccSlotInfo(active, false, "fake card Id",
+ UiccSlotInfo.CARD_STATE_INFO_PRESENT, logicalSlotIndex, true, true);
+ }
+
+ // TODO: Move this test once SubscriptionManager.setAlwaysAllowMmsData is moved to telephony
+ // manager.
+ @Test
+ @SmallTest
+ public void testSetAlwaysAllowMmsData() throws Exception {
+ mSubscriptionControllerUT.setAlwaysAllowMmsData(0, true);
+ verify(mDataEnabledSettings).setAlwaysAllowMmsData(eq(true));
+ clearInvocations(mDataEnabledSettings);
+ mSubscriptionControllerUT.setAlwaysAllowMmsData(0, false);
+ verify(mDataEnabledSettings).setAlwaysAllowMmsData(eq(false));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index fc424d5..b0f0948 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -63,6 +63,7 @@
import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
+import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.dataconnection.TransportManager;
@@ -229,6 +230,8 @@
@Mock
protected DataEnabledSettings mDataEnabledSettings;
@Mock
+ protected DataEnabledOverride mDataEnabledOverride;
+ @Mock
protected PhoneConfigurationManager mPhoneConfigurationManager;
@Mock
protected CellularNetworkValidator mCellularNetworkValidator;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledOverrideTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledOverrideTest.java
new file mode 100644
index 0000000..7791147
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledOverrideTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+
+import android.telephony.data.ApnSetting;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DataEnabledOverrideTest extends TelephonyTest {
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testCreateByRules() throws Exception {
+ DataEnabledOverride deo1 = new DataEnabledOverride(
+ "mms=nonDefault, default=inVoiceCall&nonDefault");
+ DataEnabledOverride deo2 = new DataEnabledOverride(
+ "mms=nonDefault, default=inVoiceCall");
+ DataEnabledOverride deo3 = new DataEnabledOverride(
+ "default=inVoiceCall&nonDefault, mms=nonDefault");
+ assertEquals(deo1, deo3);
+ assertNotEquals(deo1, deo2);
+ }
+
+ @Test
+ @SmallTest
+ public void testOverrideEnabled() throws Exception {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "mms=nonDefault, default=inVoiceCall&nonDefault");
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_MMS));
+
+ doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+
+ doReturn(PhoneConstants.State.OFFHOOK).when(mCT).getState();
+
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+ }
+
+ @Test
+ @SmallTest
+ public void testGetRules() throws Exception {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "mms=nonDefault, default=inVoiceCall&nonDefault");
+ String rules = dataEnabledOverride.getRules();
+ assertEquals(dataEnabledOverride, new DataEnabledOverride(rules));
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateRules() throws Exception {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "mms=nonDefault, default=inVoiceCall&nonDefault");
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_MMS));
+
+ doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+
+ doReturn(PhoneConstants.State.OFFHOOK).when(mCT).getState();
+
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+
+ dataEnabledOverride.updateRules("");
+
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_MMS));
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+ }
+
+ @Test
+ @SmallTest
+ public void testAlwaysEnabled() throws Exception {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "mms =unconditionally, default= unconditionally , ");
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_MMS));
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+
+ dataEnabledOverride.updateRules("");
+
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_MMS));
+ assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_DEFAULT));
+ }
+
+ @Test
+ @SmallTest
+ public void testAllApnTypesInRule() throws Exception {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride("*=inVoiceCall");
+ doReturn(PhoneConstants.State.OFFHOOK).when(mCT).getState();
+
+ assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
+ mPhone, ApnSetting.TYPE_FOTA));
+ }
+
+ @Test
+ @SmallTest
+ public void testInvalidRules() throws Exception {
+ try {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "default=xyz");
+ fail("Invalid conditions but not threw IllegalArgumentException.");
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ try {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "mms=");
+ fail("Invalid conditions but not threw IllegalArgumentException.");
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ try {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "abc=nonDefault");
+ fail("Invalid APN type but not threw IllegalArgumentException.");
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ try {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ " =nonDefault");
+ fail("Invalid APN type but not threw IllegalArgumentException.");
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ try {
+ DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
+ "Invalid rule");
+ fail("Invalid rule but not threw IllegalArgumentException.");
+ } catch (IllegalArgumentException ex) {
+
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testSetAlwaysAllowMms() throws Exception {
+ DataEnabledOverride deo = new DataEnabledOverride("");
+ deo.setAlwaysAllowMms(true);
+ assertTrue(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
+ deo.setAlwaysAllowMms(false);
+ assertFalse(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSetDataAllowedInVoiceCall() throws Exception {
+ DataEnabledOverride deo = new DataEnabledOverride("");
+ deo.setDataAllowedInVoiceCall(true);
+ assertFalse(deo.getRules(), deo.shouldOverrideDataEnabledSettings(mPhone,
+ ApnSetting.TYPE_DEFAULT));
+ assertTrue(deo.isDataAllowedInVoiceCall());
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
+ doReturn(PhoneConstants.State.OFFHOOK).when(mCT).getState();
+ assertTrue(deo.getRules(), deo.shouldOverrideDataEnabledSettings(mPhone,
+ ApnSetting.TYPE_DEFAULT));
+ deo.setDataAllowedInVoiceCall(false);
+ assertFalse(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_DEFAULT));
+ assertFalse(deo.isDataAllowedInVoiceCall());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
new file mode 100644
index 0000000..3bc6f47
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.HandlerThread;
+import android.telephony.data.ApnSetting;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Objects;
+
+public class DataEnabledSettingsTest extends TelephonyTest {
+
+ private DataEnabledSettings mDataEnabledSettingsUT;
+
+ private DataEnabledSettingsTestHandler mDataEnabledSettingsTestHandler;
+
+ private String mRules = "";
+
+ private class DataEnabledSettingsTestHandler extends HandlerThread {
+
+ private DataEnabledSettingsTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mDataEnabledSettingsUT = new DataEnabledSettings(mPhone);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ doReturn(mRules).when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
+
+ doAnswer(invocation -> {
+ String rules = (String) invocation.getArguments()[1];
+ boolean changed = !Objects.equals(mRules, rules);
+ mRules = rules;
+ return changed;
+ }).when(mSubscriptionController).setDataEnabledOverrideRules(anyInt(), anyString());
+
+ mDataEnabledSettingsTestHandler = new DataEnabledSettingsTestHandler(
+ getClass().getSimpleName());
+ mDataEnabledSettingsTestHandler.start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testSetDataAllowedInVoiceCall() throws Exception {
+ mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(true);
+ ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
+ stringCaptor.capture());
+ assertEquals("*=nonDefault&inVoiceCall", stringCaptor.getValue());
+
+ clearInvocations(mSubscriptionController);
+
+ mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(false);
+ verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
+ stringCaptor.capture());
+ assertEquals("", stringCaptor.getValue());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetAlwaysAllowMmsData() throws Exception {
+ mDataEnabledSettingsUT.setUserDataEnabled(false);
+ assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(true));
+ ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
+ stringCaptor.capture());
+ assertEquals("mms=unconditionally", stringCaptor.getValue());
+ assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
+
+ clearInvocations(mSubscriptionController);
+
+ assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(false));
+ verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
+ stringCaptor.capture());
+ assertEquals("", stringCaptor.getValue());
+ assertFalse(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
+
+ mDataEnabledSettingsUT.setUserDataEnabled(true);
+ assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
+ }
+}
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 6b5e193..336186d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -115,6 +115,7 @@
public static final String FAKE_APN3 = "FAKE APN 3";
public static final String FAKE_APN4 = "FAKE APN 4";
public static final String FAKE_APN5 = "FAKE APN 5";
+ public static final String FAKE_APN6 = "FAKE APN 6";
public static final String FAKE_IFNAME = "FAKE IFNAME";
public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
public static final String FAKE_GATEWAY = "11.22.33.44";
@@ -398,6 +399,39 @@
-1 // skip_464xlat
});
+ mc.addRow(new Object[]{
+ 2168, // id
+ FAKE_PLMN, // numeric
+ "sp-mode", // name
+ FAKE_APN6, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "mms", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ });
+
return mc;
}
} else if (uri.isPathPrefixMatch(
@@ -735,11 +769,7 @@
@Test
@MediumTest
- @Ignore
- @FlakyTest
- public void testApnWhiteList() 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
+ public void testTrySetupDataMmsAllowedDataDisabled() throws Exception {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -751,44 +781,31 @@
logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
- waitForMs(200);
+ waitForMs(500);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
any(Message.class));
- verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
+ verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending DATA_DISABLED_CMD for default data");
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
- doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
- AsyncResult ar = new AsyncResult(null,
- new Pair<>(false, DataEnabledSettings.REASON_USER_DATA_ENABLED), null);
- mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
+ mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
waitForMs(200);
// expected tear down all metered DataConnections
- 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_MMS));
-
- clearInvocations(mSimulatedCommandsVerifier);
- mDct.notifyApnWhiteListChange(ApnSetting.TYPE_MMS, false);
- waitForMs(200);
-
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+ verify(mSimulatedCommandsVerifier, times(2)).deactivateDataCall(
eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_MMS));
clearInvocations(mSimulatedCommandsVerifier);
- mDct.notifyApnWhiteListChange(ApnSetting.TYPE_MMS, true);
+ doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
+ mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
waitForMs(200);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
index 540eb78..fc80e76 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
@@ -129,7 +129,7 @@
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, params.targetTransport);
// Notify handover succeeded.
- params.callback.onCompleted(true);
+ params.callback.onCompleted(true, false);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
@@ -158,7 +158,7 @@
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
// Notify handover succeeded.
- params.callback.onCompleted(true);
+ params.callback.onCompleted(true, false);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
}
@@ -256,7 +256,7 @@
assertEquals(1, listQueue.size());
// Notify handover succeeded.
- params.callback.onCompleted(true);
+ params.callback.onCompleted(true, false);
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
waitForMs(100);
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 a0c15c9..5bdc61c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -766,6 +766,12 @@
verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
}
+ @Test
+ public void testNonNullTrackersInImsPhone() throws Exception {
+ assertNotNull(mImsPhoneUT.getEmergencyNumberTracker());
+ assertNotNull(mImsPhoneUT.getServiceStateTracker());
+ }
+
private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
ServiceState ss = new ServiceState();
ss.setStateOutOfService();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index ff976d2..a6db17e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -41,6 +41,7 @@
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.feature.MmTelFeature;
@@ -73,6 +74,7 @@
import org.mockito.Mock;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
public class TelephonyMetricsTest extends TelephonyTest {
@@ -224,6 +226,39 @@
assertEquals("gid1Test", log.events[0].carrierIdMatching.result.gid1);
}
+ // Test write Emergency Number update event
+ @Test
+ @SmallTest
+ public void testWriteEmergencyNumberUpdateEvent() throws Exception {
+ EmergencyNumber number = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ mMetrics.writeEmergencyNumberUpdateEvent(mPhone.getPhoneId(), number);
+ 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.EMERGENCY_NUMBER_REPORT, log.events[0].type);
+ assertEquals("911", log.events[0].updatedEmergencyNumber.address);
+ assertEquals("30", log.events[0].updatedEmergencyNumber.mnc);
+ assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ log.events[0].updatedEmergencyNumber.serviceCategoriesBitmask);
+ assertEquals(0, log.events[0].updatedEmergencyNumber.urns.length);
+ assertEquals(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ log.events[0].updatedEmergencyNumber.numberSourcesBitmask);
+ assertEquals(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL,
+ log.events[0].updatedEmergencyNumber.routing);
+ }
+
// Test write on IMS call start
@Test
@SmallTest
@@ -516,7 +551,7 @@
doReturn(Call.State.DIALING).when(mConnection).getState();
mMetrics.writeRilDial(mPhone.getPhoneId(), mConnection, 2, mUusInfo);
doReturn(Call.State.DISCONNECTED).when(mConnection).getState();
- mMetrics.writeRilHangup(mPhone.getPhoneId(), mConnection, 3);
+ mMetrics.writeRilHangup(mPhone.getPhoneId(), mConnection, 3, "");
mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
TelephonyLog log = buildProto();