Check permissions on getDeviceId. am: 79eecef63f am: a924220685
am: 9c3a86f07d
* commit '9c3a86f07d00c32f29869b8a24626ef67038acd2':
Check permissions on getDeviceId.
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
index b84e94d..f460cdc 100644
--- a/src/java/android/provider/Telephony.java
+++ b/src/java/android/provider/Telephony.java
@@ -988,6 +988,15 @@
"android.provider.Telephony.SMS_CB_RECEIVED";
/**
+ * Action: A SMS based carrier provision intent. Used to identify default
+ * carrier provisioning app on the device.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SMS_CARRIER_PROVISION_ACTION =
+ "android.provider.Telephony.SMS_CARRIER_PROVISION";
+
+ /**
* Broadcast Action: A new Emergency Broadcast message has been received
* by the device. The intent will have the following extra
* values:</p>
@@ -1786,7 +1795,6 @@
* It's convenient for use with SMS messages.
* @param context the context object to use.
* @param recipient the recipient to send to.
- * @hide
*/
public static long getOrCreateThreadId(Context context, String recipient) {
Set<String> recipients = new HashSet<String>();
@@ -1804,7 +1812,6 @@
* <p>Find the thread ID of the same set of recipients (in any order,
* without any additions). If one is found, return it. Otherwise,
* return a unique thread ID.</p>
- * @hide
*/
public static long getOrCreateThreadId(
Context context, Set<String> recipients) {
@@ -2589,6 +2596,17 @@
public static final String BEARER = "bearer";
/**
+ * Radio Access Technology bitmask.
+ * To check what values can be contained, refer to {@link android.telephony.ServiceState}.
+ * 0 indicates all techs otherwise first bit refers to RAT/bearer 1, second bit refers to
+ * RAT/bearer 2 and so on.
+ * Bitmask for a radio tech R is (1 << (R - 1))
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String BEARER_BITMASK = "bearer_bitmask";
+
+ /**
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
@@ -2654,6 +2672,48 @@
* @hide
*/
public static final String MTU = "mtu";
+
+ /**
+ * Is this APN added/edited/deleted by a user or carrier?
+ * <p>Type: INTEGER </p>
+ * @hide
+ */
+ public static final String EDITED = "edited";
+
+ /**
+ * Following are possible values for the EDITED field
+ * @hide
+ */
+ public static final int UNEDITED = 0;
+ /**
+ * @hide
+ */
+ public static final int USER_EDITED = 1;
+ /**
+ * @hide
+ */
+ public static final int USER_DELETED = 2;
+ /**
+ * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
+ * by the user is still present in the new APN database and therefore must remain tagged
+ * as user deleted rather than completely removed from the database
+ * @hide
+ */
+ public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+ /**
+ * @hide
+ */
+ public static final int CARRIER_EDITED = 4;
+ /**
+ * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+ * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+ * @hide
+ */
+ public static final int CARRIER_DELETED = 5;
+ /**
+ * @hide
+ */
+ public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
}
/**
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
index b6bd4bb..80270ea 100644
--- a/src/java/android/telephony/CellBroadcastMessage.java
+++ b/src/java/android/telephony/CellBroadcastMessage.java
@@ -90,6 +90,7 @@
mSmsCbMessage = new SmsCbMessage(in);
mDeliveryTime = in.readLong();
mIsRead = (in.readInt() != 0);
+ mSubId = in.readInt();
}
/** Parcelable: no special flags. */
@@ -103,6 +104,7 @@
mSmsCbMessage.writeToParcel(out, flags);
out.writeLong(mDeliveryTime);
out.writeInt(mIsRead ? 1 : 0);
+ out.writeInt(mSubId);
}
public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
index a07d141..189dc4b 100644
--- a/src/java/android/telephony/SmsManager.java
+++ b/src/java/android/telephony/SmsManager.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.BaseBundle;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -87,131 +88,163 @@
* Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI
* when constructing the download URL of a new MMS (boolean type)
*/
- public static final String MMS_CONFIG_APPEND_TRANSACTION_ID = "enabledTransID";
+ public static final String MMS_CONFIG_APPEND_TRANSACTION_ID =
+ CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL;
/**
* Whether MMS is enabled for the current carrier (boolean type)
*/
- public static final String MMS_CONFIG_MMS_ENABLED = "enabledMMS";
+ public static final String
+ MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
/**
* Whether group MMS is enabled for the current carrier (boolean type)
*/
- public static final String MMS_CONFIG_GROUP_MMS_ENABLED = "enableGroupMms";
+ public static final String
+ MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL;
/**
- * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location
- * instead of the default MMSC (boolean type)
+ * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead
+ * of the default MMSC (boolean type)
*/
- public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED = "enabledNotifyWapMMSC";
+ public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED =
+ CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL;
/**
* Whether alias is enabled (boolean type)
*/
- public static final String MMS_CONFIG_ALIAS_ENABLED = "aliasEnabled";
+ public static final String
+ MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL;
/**
* Whether audio is allowed to be attached for MMS messages (boolean type)
*/
- public static final String MMS_CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio";
+ public static final String
+ MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL;
/**
* Whether multipart SMS is enabled (boolean type)
*/
- public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED = "enableMultipartSMS";
+ public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED =
+ CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL;
/**
* Whether SMS delivery report is enabled (boolean type)
*/
- public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED = "enableSMSDeliveryReports";
+ public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL;
/**
* Whether content-disposition field should be expected in an MMS PDU (boolean type)
*/
public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION =
- "supportMmsContentDisposition";
+ CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL;
/**
* Whether multipart SMS should be sent as separate messages
*/
public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES =
- "sendMultipartSmsAsSeparateMessages";
+ CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL;
/**
* Whether MMS read report is enabled (boolean type)
*/
- public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED = "enableMMSReadReports";
+ public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL;
/**
* Whether MMS delivery report is enabled (boolean type)
*/
- public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED = "enableMMSDeliveryReports";
+ public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL;
/**
* Max MMS message size in bytes (int type)
*/
- public static final String MMS_CONFIG_MAX_MESSAGE_SIZE = "maxMessageSize";
+ public static final String
+ MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT;
/**
* Max MMS image width (int type)
*/
- public static final String MMS_CONFIG_MAX_IMAGE_WIDTH = "maxImageWidth";
+ public static final String
+ MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT;
/**
* Max MMS image height (int type)
*/
- public static final String MMS_CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight";
+ public static final String
+ MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT;
/**
* Limit of recipients of MMS messages (int type)
*/
- public static final String MMS_CONFIG_RECIPIENT_LIMIT = "recipientLimit";
+ public static final String
+ MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT;
/**
* Min alias character count (int type)
*/
- public static final String MMS_CONFIG_ALIAS_MIN_CHARS = "aliasMinChars";
+ public static final String
+ MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT;
/**
* Max alias character count (int type)
*/
- public static final String MMS_CONFIG_ALIAS_MAX_CHARS = "aliasMaxChars";
+ public static final String
+ MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT;
/**
- * When the number of parts of a multipart SMS reaches this threshold, it should be
- * converted into an MMS (int type)
+ * When the number of parts of a multipart SMS reaches this threshold, it should be converted
+ * into an MMS (int type)
*/
- public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = "smsToMmsTextThreshold";
+ public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD =
+ CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT;
/**
* Some carriers require SMS to be converted into MMS when text length reaches this threshold
* (int type)
*/
public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD =
- "smsToMmsTextLengthThreshold";
+ CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT;
/**
* Max message text size (int type)
*/
- public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE = "maxMessageTextSize";
+ public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE =
+ CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT;
/**
* Max message subject length (int type)
*/
- public static final String MMS_CONFIG_SUBJECT_MAX_LENGTH = "maxSubjectLength";
+ public static final String
+ MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT;
/**
* MMS HTTP socket timeout in milliseconds (int type)
*/
- public static final String MMS_CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout";
+ public static final String
+ MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT;
/**
* The name of the UA Prof URL HTTP header for MMS HTTP request (String type)
*/
- public static final String MMS_CONFIG_UA_PROF_TAG_NAME = "uaProfTagName";
+ public static final String
+ MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING;
/**
* The User-Agent header value for MMS HTTP request (String type)
*/
- public static final String MMS_CONFIG_USER_AGENT = "userAgent";
+ public static final String
+ MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING;
/**
* The UA Profile URL header value for MMS HTTP request (String type)
*/
- public static final String MMS_CONFIG_UA_PROF_URL = "uaProfUrl";
+ public static final String
+ MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING;
/**
* A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type)
*/
- public static final String MMS_CONFIG_HTTP_PARAMS = "httpParams";
+ public static final String
+ MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING;
/**
* Email gateway number (String type)
*/
- public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber";
+ public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER =
+ CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING;
/**
* The suffix to append to the NAI header value for MMS HTTP request (String type)
*/
- public static final String MMS_CONFIG_NAI_SUFFIX = "naiSuffix";
+ public static final String
+ MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING;
/**
- * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers
- * don't want this shown. (Boolean type)
+ * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want
+ * this shown. (Boolean type)
*/
public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS =
- "config_cellBroadcastAppLinks";
+ CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL;
+ /**
+ * Whether the carrier MMSC supports charset field in Content-Type header. If this is false,
+ * then we don't add "charset" to "Content-Type"
+ */
+ public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER =
+ CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL;
/*
* Forwarded constants from SimDialogActivity.
*/
@@ -257,6 +290,13 @@
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextMessageInternal(destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
+ }
+
+ private void sendTextMessageInternal(String destinationAddress, String scAddress,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForCarrierApp) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
@@ -269,6 +309,50 @@
ISms iccISms = getISmsServiceOrThrow();
iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
destinationAddress,
+ scAddress, text, sentIntent, deliveryIntent,
+ persistMessageForCarrierApp);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Only the carrier app can call this method.</p>
+ *
+ * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ * @hide
+ */
+ public void sendTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextMessageInternal(destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent, false /* persistMessageForCarrierApp*/);
+ }
+
+ /**
+ * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
+ * for internal use only.
+ *
+ * @hide
+ */
+ public void sendTextMessageWithSelfPermissions(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ destinationAddress,
scAddress, text, sentIntent, deliveryIntent);
} catch (RemoteException ex) {
// ignore it
@@ -279,7 +363,7 @@
* Inject an SMS PDU into the android application framework.
*
* The caller should have carrier privileges.
- * @see android.telephony.TelephonyManager.hasCarrierPrivileges
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
*
* @param pdu is the byte array of pdu to be injected into android application framework
* @param format is the format of SMS pdu (3gpp or 3gpp2)
@@ -301,7 +385,8 @@
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
- iccISms.injectSmsPdu(pdu, format, receivedIntent);
+ iccISms.injectSmsPduForSubscriber(
+ getSubscriptionId(), pdu, format, receivedIntent);
}
} catch (RemoteException ex) {
// ignore it
@@ -369,6 +454,14 @@
public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents, true /* persistMessageForCarrierApp*/);
+ }
+
+ private void sendMultipartTextMessageInternal(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+ boolean persistMessageForCarrierApp) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
@@ -382,7 +475,7 @@
iccISms.sendMultipartTextForSubscriber(getSubscriptionId(),
ActivityThread.currentPackageName(),
destinationAddress, scAddress, parts,
- sentIntents, deliveryIntents);
+ sentIntents, deliveryIntents, persistMessageForCarrierApp);
} catch (RemoteException ex) {
// ignore it
}
@@ -401,6 +494,21 @@
}
/**
+ * Send a multi-part text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Only the carrier app can call this method.</p>
+ *
+ * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+ * @hide
+ **/
+ public void sendMultipartTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents, false /* persistMessageForCarrierApp*/);
+ }
+
+ /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -452,6 +560,35 @@
}
/**
+ * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
+ * for internal use only.
+ *
+ * @hide
+ */
+ public void sendDataMessageWithSelfPermissions(
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (data == null || data.length == 0) {
+ throw new IllegalArgumentException("Invalid message data");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress,
+ destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+
+
+ /**
* Get the SmsManager associated with the default subscription id. The instance will always be
* associated with the default subscription id, even if the default subscription id is changed.
*
@@ -1443,4 +1580,70 @@
return null;
}
+ /**
+ * Filters a bundle to only contain MMS config variables.
+ *
+ * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
+ * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
+ * supplied bundle.
+ *
+ * @param config a Bundle that contains MMS config variables and possibly more.
+ * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
+ * @hide
+ */
+ public static Bundle getMmsConfig(BaseBundle config) {
+ Bundle filtered = new Bundle();
+ filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID,
+ config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID));
+ filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED,
+ config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
+ config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO,
+ config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO));
+ filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED,
+ config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
+ config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
+ filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
+ config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
+ filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
+ filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE));
+ filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH));
+ filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT));
+ filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT));
+ filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS));
+ filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS));
+ filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
+ config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
+ filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
+ config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
+ filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
+ config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
+ filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH,
+ config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH));
+ filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
+ config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
+ filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME,
+ config.getString(MMS_CONFIG_UA_PROF_TAG_NAME));
+ filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT));
+ filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL));
+ filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS));
+ filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
+ config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
+ filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX));
+ filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
+ config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
+ filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
+ config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
+ return filtered;
+ }
+
}
diff --git a/src/java/android/telephony/SmsMessage.java b/src/java/android/telephony/SmsMessage.java
index 1dba484..3323078 100644
--- a/src/java/android/telephony/SmsMessage.java
+++ b/src/java/android/telephony/SmsMessage.java
@@ -149,17 +149,18 @@
}
/**
- * Create an SmsMessage from a raw PDU.
- *
- * <p><b>This method will soon be deprecated</b> and all applications which handle
+ * Create an SmsMessage from a raw PDU. Guess format based on Voice
+ * technology first, if it fails use other format.
+ * All applications which handle
* incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
* intent <b>must</b> now pass the new {@code format} String extra from the intent
* into the new method {@code createFromPdu(byte[], String)} which takes an
* extra format parameter. This is required in order to correctly decode the PDU on
* devices that require support for both 3GPP and 3GPP2 formats at the same time,
- * such as dual-mode GSM/CDMA and CDMA/LTE phones. Guess format based on Voice
- * technology first, if it fails use other format.
+ * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+ * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
*/
+ @Deprecated
public static SmsMessage createFromPdu(byte[] pdu) {
SmsMessage message = null;
@@ -181,13 +182,15 @@
/**
* Create an SmsMessage from a raw PDU with the specified message format. The
- * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format}
+ * message format is passed in the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format}
* String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
* or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
*
- * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
- * @param format the format extra from the SMS_RECEIVED_ACTION intent
- * @hide pending API council approval
+ * @param pdu the message PDU from the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+ * @param format the format extra from the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
*/
public static SmsMessage createFromPdu(byte[] pdu, String format) {
SmsMessageBase wrappedMessage;
@@ -388,7 +391,7 @@
ted.languageTable, ted.languageShiftTable);
}
} else { // Assume unicode.
- nextPos = pos + Math.min(limit / 2, textLen - pos);
+ nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
}
if ((nextPos <= pos) || (nextPos > textLen)) {
Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 45f720c..472004a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -93,6 +93,7 @@
protected Registrant mGsmBroadcastSmsRegistrant;
protected Registrant mCatCcAlphaRegistrant;
protected Registrant mSsRegistrant;
+ protected Registrant mLceInfoRegistrant;
// Preferred network type received from PhoneFactory.
// This is used when establishing a connection to the
@@ -104,8 +105,6 @@
protected int mPhoneType;
// RIL Version
protected int mRilVersion = -1;
- // Supported Radio Access Family
- protected int mSupportedRaf = RadioAccessFamily.RAF_UNKNOWN;
public BaseCommands(Context context) {
mContext = context; // May be null (if so we won't log statistics)
@@ -748,14 +747,14 @@
mRilConnectedRegistrants.remove(h);
}
- public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mSubscriptionStatusRegistrants.add(r);
- }
+ public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mSubscriptionStatusRegistrants.add(r);
+ }
- public void unregisterForSubscriptionStatusChanged(Handler h) {
- mSubscriptionStatusRegistrants.remove(h);
- }
+ public void unregisterForSubscriptionStatusChanged(Handler h) {
+ mSubscriptionStatusRegistrants.remove(h);
+ }
//***** Protected Methods
/**
@@ -847,11 +846,6 @@
return mRilVersion;
}
- @Override
- public int getSupportedRadioAccessFamily() {
- return mSupportedRaf;
- }
-
public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
Message response) {
}
@@ -881,4 +875,29 @@
public void unregisterForRadioCapabilityChanged(Handler h) {
mPhoneRadioCapabilityChangedRegistrants.remove(h);
}
+
+ @Override
+ public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+ }
+
+ @Override
+ public void stopLceService(Message result) {
+ }
+
+ @Override
+ public void pullLceData(Message result) {
+ }
+
+ @Override
+ public void registerForLceInfo(Handler h, int what, Object obj) {
+ mLceInfoRegistrant = new Registrant(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForLceInfo(Handler h) {
+ if (mLceInfoRegistrant != null && mLceInfoRegistrant.getHandler() == h) {
+ mLceInfoRegistrant.clear();
+ mLceInfoRegistrant = null;
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
index 64c57f2..719b9fd 100644
--- a/src/java/com/android/internal/telephony/Call.java
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import android.telecom.ConferenceParticipant;
+
import java.util.ArrayList;
import java.util.List;
@@ -121,6 +123,14 @@
}
/**
+ * getConferenceParticipants
+ * @return List of conference participants.
+ */
+ public List<ConferenceParticipant> getConferenceParticipants() {
+ return null;
+ }
+
+ /**
* isIdle
*
* FIXME rename
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 727be96..3cb00d3 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -741,7 +741,7 @@
}
// We only support the AUDIO_ONLY video state in this scenario.
- ringingPhone.acceptCall(VideoProfile.VideoState.AUDIO_ONLY);
+ ringingPhone.acceptCall(VideoProfile.STATE_AUDIO_ONLY);
if (VDBG) {
Rlog.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
@@ -1023,7 +1023,7 @@
*/
public Connection dial(Phone phone, String dialString, UUSInfo uusInfo, int videoState)
throws CallStateException {
- return phone.dial(dialString, uusInfo, videoState);
+ return phone.dial(dialString, uusInfo, videoState, null);
}
/**
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 6087124..57cbeb8 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -21,6 +21,13 @@
*/
public class CallStateException extends Exception
{
+ private int mError = ERROR_INVALID;
+
+ /** The error code is not valid (Not received a disconnect cause) */
+ public static final int ERROR_INVALID = -1;
+
+ public static final int ERROR_DISCONNECTED = 1;
+
public
CallStateException()
{
@@ -31,4 +38,15 @@
{
super(string);
}
+
+ public
+ CallStateException(int error, String string)
+ {
+ super(string);
+ mError = error;
+ }
+
+ public int getError() {
+ return mError;
+ }
}
diff --git a/src/java/com/android/internal/telephony/CarrierAppUtils.java b/src/java/com/android/internal/telephony/CarrierAppUtils.java
new file mode 100644
index 0000000..4be914c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for handling carrier applications.
+ * @hide
+ */
+public final class CarrierAppUtils {
+ private static final String TAG = "CarrierAppUtils";
+
+ private static final boolean DEBUG = false; // STOPSHIP if true
+
+ private CarrierAppUtils() {}
+
+ /**
+ * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
+ *
+ * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
+ * want to disable each such application which is present on the system image until the user
+ * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
+ * without interfering with the user if they opt to enable/disable the app explicitly.
+ *
+ * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
+ * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
+ * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
+ *
+ * When enabling a carrier app we also grant it default permissions.
+ *
+ * This method is idempotent and is safe to be called at any time; it should be called once at
+ * system startup prior to any application running, as well as any time the set of carrier
+ * privileged apps may have changed.
+ */
+ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+ }
+ String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
+ systemCarrierAppsDisabledUntilUsed);
+ }
+
+ // Must be public b/c framework unit tests can't access package-private methods.
+ @VisibleForTesting
+ public static void disableCarrierAppsUntilPrivileged(String callingPackage,
+ IPackageManager packageManager, TelephonyManager telephonyManager, int userId,
+ String[] systemCarrierAppsDisabledUntilUsed) {
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+ userId, systemCarrierAppsDisabledUntilUsed);
+ if (candidates == null || candidates.isEmpty()) {
+ return;
+ }
+
+ List<String> enabledCarrierPackages = new ArrayList<>();
+
+ try {
+ for (ApplicationInfo ai : candidates) {
+ String packageName = ai.packageName;
+ boolean hasPrivileges =
+ telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+
+ // Only update enabled state for the app on /system. Once it has been updated we
+ // shouldn't touch it.
+ if (!ai.isUpdatedSystemApp()) {
+ if (hasPrivileges
+ && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ + userId);
+ packageManager.setApplicationEnabledSetting(packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP, userId, callingPackage);
+ } else if (!hasPrivileges
+ && ai.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ Slog.i(TAG, "Update state(" + packageName
+ + "): DISABLED_UNTIL_USED for user " + userId);
+ packageManager.setApplicationEnabledSetting(packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+ userId, callingPackage);
+ }
+ }
+
+ // Always re-grant default permissions to carrier apps w/ privileges.
+ if (hasPrivileges) {
+ enabledCarrierPackages.add(ai.packageName);
+ }
+ }
+
+ if (!enabledCarrierPackages.isEmpty()) {
+ // Since we enabled at least one app, ensure we grant default permissions to those
+ // apps.
+ String[] packageNames = new String[enabledCarrierPackages.size()];
+ enabledCarrierPackages.toArray(packageNames);
+ packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not reach PackageManager", e);
+ }
+ }
+
+ /**
+ * Returns the list of "default" carrier apps.
+ *
+ * This is the subset of apps returned by
+ * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+ * privileges per the SIM(s) inserted in the device.
+ */
+ public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
+ TelephonyManager telephonyManager, int userId) {
+ // Get all system apps from the default list.
+ List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+ if (candidates == null || candidates.isEmpty()) {
+ return null;
+ }
+
+ // Filter out apps without carrier privileges.
+ // Iterate from the end to avoid creating an Iterator object and because we will be removing
+ // elements from the list as we pass through it.
+ for (int i = candidates.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = candidates.get(i);
+ String packageName = ai.packageName;
+ boolean hasPrivileges =
+ telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ if (!hasPrivileges) {
+ candidates.remove(i);
+ }
+ }
+
+ return candidates;
+ }
+
+ /**
+ * Returns the list of "default" carrier app candidates.
+ *
+ * These are the apps subject to the hiding/showing logic in
+ * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
+ * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
+ * when a matching SIM is inserted.
+ *
+ * Whether or not the app is actually considered a default app depends on whether the app has
+ * carrier privileges as determined by the SIMs in the device.
+ */
+ public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
+ IPackageManager packageManager, int userId) {
+ String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+ return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
+ systemCarrierAppsDisabledUntilUsed);
+ }
+
+ private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
+ IPackageManager packageManager, int userId,
+ String[] systemCarrierAppsDisabledUntilUsed) {
+ if (systemCarrierAppsDisabledUntilUsed == null
+ || systemCarrierAppsDisabledUntilUsed.length == 0) {
+ return null;
+ }
+ List<ApplicationInfo> apps = null;
+ try {
+ apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+ for (String packageName : systemCarrierAppsDisabledUntilUsed) {
+ ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+ if (ai == null) {
+ // No app found for packageName
+ continue;
+ }
+ if (!ai.isSystemApp()) {
+ continue;
+ }
+ apps.add(ai);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not reach PackageManager", e);
+ }
+ return apps;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
new file mode 100644
index 0000000..18e1554
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -0,0 +1,306 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.internal.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.UserHandle;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.service.carrier.CarrierService;
+
+import com.android.internal.telephony.IccCardConstants;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Manages long-lived bindings to carrier services
+ * @hide
+ */
+public class CarrierServiceBindHelper {
+ private static final String LOG_TAG = CarrierServiceBindHelper.class.getSimpleName();
+
+ private Context mContext;
+ private AppBinding[] mBindings;
+ private final BroadcastReceiver mReceiver = new PackageChangedBroadcastReceiver();
+
+ private static final int EVENT_BIND = 0;
+ private static final int EVENT_UNBIND = 1;
+ private static final int EVENT_BIND_TIMEOUT = 2;
+ private static final int EVENT_PACKAGE_CHANGED = 3;
+
+ private static final int BIND_TIMEOUT_MILLIS = 10000;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ String carrierPackageName;
+ AppBinding binding;
+ log("mHandler: " + msg.what);
+
+ CarrierServiceConnection connection;
+ switch (msg.what) {
+ case EVENT_BIND:
+ binding = (AppBinding) msg.obj;
+ log("Binding to phoneId: " + binding.getPhoneId());
+ binding.bind();
+ break;
+ case EVENT_BIND_TIMEOUT:
+ binding = (AppBinding) msg.obj;
+ log("Bind timeout for phoneId: " + binding.getPhoneId());
+ binding.unbind();
+ break;
+ case EVENT_UNBIND:
+ binding = (AppBinding) msg.obj;
+ log("Unbinding for phoneId: " + binding.getPhoneId());
+ binding.unbind();
+ break;
+ case EVENT_PACKAGE_CHANGED:
+ carrierPackageName = (String) msg.obj;
+ for (AppBinding appBinding : mBindings) {
+ if (carrierPackageName.equals(appBinding.getPackage())) {
+ log(carrierPackageName + " changed and corresponds to a phone. Rebinding.");
+ appBinding.bind();
+ }
+ }
+ break;
+ }
+ }
+ };
+
+ public CarrierServiceBindHelper(Context context) {
+ mContext = context;
+
+ int numPhones = TelephonyManager.from(context).getPhoneCount();
+ mBindings = new AppBinding[numPhones];
+
+ for (int phoneId = 0; phoneId < numPhones; phoneId++) {
+ mBindings[phoneId] = new AppBinding(phoneId);
+ }
+
+ // Register for package updates. Update app or uninstall app update will have all 3 intents,
+ // in the order or removed, added, replaced, all with extra_replace set to true.
+ IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ pkgFilter.addDataScheme("package");
+ context.registerReceiverAsUser(mReceiver, UserHandle.ALL, pkgFilter, null, null);
+ }
+
+ public void updateForPhoneId(int phoneId, String simState) {
+ log("update binding for phoneId: " + phoneId + " simState: " + simState);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ return;
+ }
+ // requires Java 7 for switch on string.
+ switch (simState) {
+ case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
+ case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR:
+ case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN:
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNBIND, mBindings[phoneId]));
+ break;
+ case IccCardConstants.INTENT_VALUE_ICC_LOADED:
+ case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_BIND, mBindings[phoneId]));
+ break;
+ }
+ }
+
+ private class AppBinding {
+ private int phoneId;
+ private CarrierServiceConnection connection;
+ private int bindCount;
+ private long lastBindStartMillis;
+ private int unbindCount;
+ private long lastUnbindMillis;
+ private String carrierPackage;
+
+ public AppBinding(int phoneId) {
+ this.phoneId = phoneId;
+ }
+
+ public int getPhoneId() {
+ return phoneId;
+ }
+
+ public String getPackage() {
+ return carrierPackage;
+ }
+
+ public void handleConnectionDown() {
+ connection = null;
+ }
+
+ public boolean bind() {
+ // Make sure there is no existing binding for this phone
+ unbind();
+
+ // Get the package name for the carrier app
+ List<String> carrierPackageNames =
+ TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId
+ );
+
+ if (carrierPackageNames == null || carrierPackageNames.size() <= 0) {
+ log("No carrier app for: " + phoneId);
+ return false;
+ }
+
+ log("Found carrier app: " + carrierPackageNames);
+ carrierPackage = carrierPackageNames.get(0);
+
+ // Log debug information
+ bindCount++;
+ lastBindStartMillis = System.currentTimeMillis();
+
+ // Look up the carrier service
+ Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
+ carrierService.setPackage(carrierPackage);
+
+ ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
+ carrierService, PackageManager.GET_META_DATA);
+ Bundle metadata = null;
+ if (carrierResolveInfo != null) {
+ metadata = carrierResolveInfo.serviceInfo.metaData;
+ }
+
+ // Only bind if the service wants it
+ if (metadata == null ||
+ !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
+ log("Carrier app does not want a long lived binding");
+ return false;
+ }
+
+ log("Binding to " + carrierPackage + " for phone " + phoneId);
+ connection = new CarrierServiceConnection(this);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_BIND_TIMEOUT, this),
+ BIND_TIMEOUT_MILLIS);
+
+ String error;
+ try {
+ if (mContext.bindService(carrierService, connection, Context.BIND_AUTO_CREATE)) {
+ return true;
+ }
+
+ error = "bindService returned false";
+ } catch (SecurityException ex) {
+ error = ex.getMessage();
+ }
+
+ log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
+ ". Error: " + error);
+ return false;
+ }
+
+ public void unbind() {
+ mHandler.removeMessages(EVENT_BIND_TIMEOUT, this);
+ if (connection == null) {
+ return;
+ }
+
+ // Log debug information
+ unbindCount++;
+ lastUnbindMillis = System.currentTimeMillis();
+
+ // Actually unbind
+ log("Unbinding from carrier app");
+ mContext.unbindService(connection);
+ connection = null;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Carrier app binding for phone " + phoneId);
+ pw.println(" connection: " + connection);
+ pw.println(" bindCount: " + bindCount);
+ pw.println(" lastBindStartMillis: " + lastBindStartMillis);
+ pw.println(" unbindCount: " + unbindCount);
+ pw.println(" lastUnbindMillis: " + lastUnbindMillis);
+ pw.println();
+ }
+ }
+
+ private class CarrierServiceConnection implements ServiceConnection {
+ private IBinder service;
+ private AppBinding binding;
+
+ public CarrierServiceConnection(AppBinding binding) {
+ this.binding = binding;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ log("Connected to carrier app: " + name.flattenToString());
+ mHandler.removeMessages(EVENT_BIND_TIMEOUT, binding);
+ this.service = service;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ log("Disconnected from carrier app: " + name.flattenToString());
+ this.service = null;
+ this.binding.handleConnectionDown();
+ }
+ }
+
+ private class PackageChangedBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ log("Receive action: " + action);
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ case Intent.ACTION_PACKAGE_REPLACED:
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ String packageName = mContext.getPackageManager().getNameForUid(uid);
+ if (packageName != null) {
+ // We don't have a phoneId for arg1.
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
+ }
+ break;
+
+ }
+ }
+ }
+
+ private static void log(String message) {
+ Log.d(LOG_TAG, message);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CarrierServiceBindHelper:");
+ for (AppBinding binding : mBindings) {
+ binding.dump(fd, pw, args);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 42b0338..fd8081b 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -1898,11 +1898,6 @@
*/
int getRilVersion();
- /**
- * @return Radio access families supported by the hardware.
- */
- int getSupportedRadioAccessFamily();
-
/**
* Sets user selected subscription at Modem.
*
@@ -1971,4 +1966,56 @@
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForRadioCapabilityChanged(Handler h);
+
+ /**
+ * Start LCE (Link Capacity Estimation) service with a desired reporting interval.
+ *
+ * @param reportIntervalMs
+ * LCE info reporting interval (ms).
+ *
+ * @param result Callback message contains the current LCE status.
+ * {byte status, int actualIntervalMs}
+ */
+ public void startLceService(int reportIntervalMs, boolean pullMode, Message result);
+
+ /**
+ * Stop LCE service.
+ *
+ * @param result Callback message contains the current LCE status:
+ * {byte status, int actualIntervalMs}
+ *
+ */
+ public void stopLceService(Message result);
+
+ /**
+ * Pull LCE service for capacity data.
+ *
+ * @param result Callback message contains the capacity info:
+ * {int capacityKbps, byte confidenceLevel, byte lceSuspendedTemporarily}
+ */
+ public void pullLceData(Message result);
+
+ /**
+ * Register a LCE info listener.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForLceInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregister the LCE Info listener.
+ *
+ * @param h handle to be removed.
+ */
+ void unregisterForLceInfo(Handler h);
+
+ /**
+ *
+ * Get modem activity info and stats
+ *
+ * @param result Callback message contains the modem activity information
+ */
+ public void getModemActivityInfo(Message result);
}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 79aebec..8edf67a 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -45,10 +45,14 @@
public void onVideoStateChanged(int videoState);
public void onLocalVideoCapabilityChanged(boolean capable);
public void onRemoteVideoCapabilityChanged(boolean capable);
+ public void onWifiChanged(boolean isWifi);
public void onVideoProviderChanged(
android.telecom.Connection.VideoProvider videoProvider);
public void onAudioQualityChanged(int audioQuality);
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
+ public void onCallSubstateChanged(int callSubstate);
+ public void onMultipartyStateChanged(boolean isMultiParty);
+ public void onConferenceMergedFailed();
}
/**
@@ -62,12 +66,20 @@
@Override
public void onRemoteVideoCapabilityChanged(boolean capable) {}
@Override
+ public void onWifiChanged(boolean isWifi) {}
+ @Override
public void onVideoProviderChanged(
android.telecom.Connection.VideoProvider videoProvider) {}
@Override
public void onAudioQualityChanged(int audioQuality) {}
@Override
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
+ @Override
+ public void onCallSubstateChanged(int callSubstate) {}
+ @Override
+ public void onMultipartyStateChanged(boolean isMultiParty) {}
+ @Override
+ public void onConferenceMergedFailed() {}
}
public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -108,7 +120,9 @@
private int mVideoState;
private boolean mLocalVideoCapable;
private boolean mRemoteVideoCapable;
+ private boolean mIsWifi;
private int mAudioQuality;
+ private int mCallSubstate;
private android.telecom.Connection.VideoProvider mVideoProvider;
public Call.State mPreHandoverState = Call.State.IDLE;
@@ -242,6 +256,15 @@
public abstract int getDisconnectCause();
/**
+ * Returns a string disconnect cause which is from vendor.
+ * Vendors may use this string to explain the underline causes of failed calls.
+ * There is no guarantee that it is non-null nor it'll have meaningful stable values.
+ * Only use it when getDisconnectCause() returns a value that is not specific enough, like
+ * ERROR_UNSPECIFIED.
+ */
+ public abstract String getVendorDisconnectCause();
+
+ /**
* Returns true of this connection originated elsewhere
* ("MT" or mobile terminated; another party called this terminal)
* or false if this call originated here (MO or mobile originated).
@@ -278,6 +301,22 @@
}
/**
+ * Get the details of conference participants. Expected to be
+ * overwritten by the Connection subclasses.
+ */
+ public List<ConferenceParticipant> getConferenceParticipants() {
+ Call c;
+
+ c = getCall();
+
+ if (c == null) {
+ return null;
+ } else {
+ return c.getConferenceParticipants();
+ }
+ }
+
+ /**
* isAlive()
*
* @return true if the connection isn't disconnected
@@ -350,6 +389,10 @@
}
}
+ public final void removePostDialListener(PostDialListener listener) {
+ mPostDialListeners.remove(listener);
+ }
+
protected final void clearPostDialListeners() {
mPostDialListeners.clear();
}
@@ -487,6 +530,15 @@
}
/**
+ * Returns whether the connection is using a wifi network.
+ *
+ * @return {@code True} if the connection is using a wifi network.
+ */
+ public boolean isWifi() {
+ return mIsWifi;
+ }
+
+ /**
* Returns the {@link android.telecom.Connection.VideoProvider} for the connection.
*
* @return The {@link android.telecom.Connection.VideoProvider}.
@@ -504,6 +556,17 @@
return mAudioQuality;
}
+
+ /**
+ * Returns the current call substate of the connection.
+ *
+ * @return The call substate of the connection.
+ */
+ public int getCallSubstate() {
+ return mCallSubstate;
+ }
+
+
/**
* Sets the videoState for the current connection and reports the changes to all listeners.
* Valid video states are defined in {@link android.telecom.VideoProfile}.
@@ -542,6 +605,18 @@
}
/**
+ * Sets whether a wifi network is used for the connection.
+ *
+ * @param isWifi {@code True} if wifi is being used.
+ */
+ public void setWifi(boolean isWifi) {
+ mIsWifi = isWifi;
+ for (Listener l : mListeners) {
+ l.onWifiChanged(mIsWifi);
+ }
+ }
+
+ /**
* Set the audio quality for the connection.
*
* @param audioQuality The audio quality.
@@ -554,6 +629,19 @@
}
/**
+ * Sets the call substate for the current connection and reports the changes to all listeners.
+ * Valid call substates are defined in {@link android.telecom.Connection}.
+ *
+ * @return The call substate.
+ */
+ public void setCallSubstate(int callSubstate) {
+ mCallSubstate = callSubstate;
+ for (Listener l : mListeners) {
+ l.onCallSubstateChanged(mCallSubstate);
+ }
+ }
+
+ /**
* Sets the {@link android.telecom.Connection.VideoProvider} for the connection.
*
* @param videoProvider The video call provider.
@@ -584,6 +672,26 @@
}
/**
+ * Notifies listeners of a change to the multiparty state of the connection.
+ *
+ * @param isMultiparty The participant(s).
+ */
+ public void updateMultipartyState(boolean isMultiparty) {
+ for (Listener l : mListeners) {
+ l.onMultipartyStateChanged(isMultiparty);
+ }
+ }
+
+ /**
+ * Notifies listeners of a failure in merging this connection with the background connection.
+ */
+ public void onConferenceMergeFailed() {
+ for (Listener l : mListeners) {
+ l.onConferenceMergedFailed();
+ }
+ }
+
+ /**
* Notifies this Connection of a request to disconnect a participant of the conference managed
* by the connection.
*
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 9be78fc..8550876 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -317,6 +317,32 @@
}
/**
+ * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
+ * This method checks if the calling package or itself has the permission to send the data sms.
+ */
+ public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
+ int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.SEND_SMS,
+ "Sending SMS message");
+ sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+ deliveryIntent);
+ }
+
+ /**
+ * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
+ * This method checks only if the calling package has the permission to send the data sms.
+ */
+ public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingPermission(
+ Manifest.permission.SEND_SMS,
+ "Sending SMS message");
+ sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+ deliveryIntent);
+ }
+
+ /**
* Send a data based SMS to a specific application port.
*
* @param destAddr the address to send the message to
@@ -342,11 +368,8 @@
* raw pdu of the status report is in the extended data ("pdu").
*/
- public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
+ int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" +
@@ -361,6 +384,33 @@
}
/**
+ * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
+ * This method checks only if the calling package has the permission to send the sms.
+ */
+ public void sendText(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp) {
+ mPhone.getContext().enforceCallingPermission(
+ Manifest.permission.SEND_SMS,
+ "Sending SMS message");
+ sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+ persistMessageForNonDefaultSmsApp);
+ }
+
+ /**
+ * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
+ * This method checks if the calling package or itself has the permission to send the sms.
+ */
+ public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.SEND_SMS,
+ "Sending SMS message");
+ sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+ true /* persistMessageForNonDefaultSmsApp */);
+ }
+
+ /**
* Send a text based SMS.
*
* @param destAddr the address to send the message to
@@ -385,11 +435,9 @@
* raw pdu of the status report is in the extended data ("pdu").
*/
- public void sendText(String callingPackage, String destAddr, String scAddr,
- String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
@@ -399,9 +447,13 @@
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
+ if (!persistMessageForNonDefaultSmsApp) {
+ // Only allow carrier app to skip auto message persistence.
+ enforceCarrierPrivilege();
+ }
destAddr = filterDestAddress(destAddr);
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
- null/*messageUri*/, callingPackage);
+ null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
}
/**
@@ -452,10 +504,14 @@
public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents) {
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
mPhone.getContext().enforceCallingPermission(
Manifest.permission.SEND_SMS,
"Sending SMS message");
+ if (!persistMessageForNonDefaultSmsApp) {
+ // Only allow carrier app to skip auto message persistence.
+ enforceCarrierPrivilege();
+ }
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
int i = 0;
for (String part : parts) {
@@ -493,14 +549,15 @@
mDispatcher.sendText(destAddr, scAddr, singlePart,
singleSentIntent, singleDeliveryIntent,
- null/*messageUri*/, callingPackage);
+ null/*messageUri*/, callingPackage,
+ persistMessageForNonDefaultSmsApp);
}
return;
}
mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
(ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
- null/*messageUri*/, callingPackage);
+ null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
}
@@ -900,7 +957,8 @@
}
textAndAddress[1] = filterDestAddress(textAndAddress[1]);
mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
- sentIntent, deliveryIntent, messageUri, callingPkg);
+ sentIntent, deliveryIntent, messageUri, callingPkg,
+ true /* persistMessageForNonDefaultSmsApp */);
}
public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
@@ -955,7 +1013,8 @@
}
mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
- singleSentIntent, singleDeliveryIntent, messageUri, callingPkg);
+ singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
+ true /* persistMessageForNonDefaultSmsApp */);
}
return;
}
@@ -967,7 +1026,8 @@
(ArrayList<PendingIntent>) sentIntents,
(ArrayList<PendingIntent>) deliveryIntents,
messageUri,
- callingPkg);
+ callingPkg,
+ true /* persistMessageForNonDefaultSmsApp */);
}
private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) {
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
index 9ad7169..13ada85 100644
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
@@ -172,13 +172,14 @@
@Override
protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) {
+ ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+ boolean persistMessage) {
if (isCdmaMo()) {
mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
- parts, sentIntents, deliveryIntents, messageUri, callingPkg);
+ parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
} else {
mGsmDispatcher.sendMultipartText(destAddr, scAddr,
- parts, sentIntents, deliveryIntents, messageUri, callingPkg);
+ parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
}
}
@@ -197,14 +198,15 @@
@Override
protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+ PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+ boolean persistMessage) {
Rlog.d(TAG, "sendText");
if (isCdmaMo()) {
mCdmaDispatcher.sendText(destAddr, scAddr,
- text, sentIntent, deliveryIntent, messageUri, callingPkg);
+ text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
} else {
mGsmDispatcher.sendText(destAddr, scAddr,
- text, sentIntent, deliveryIntent, messageUri, callingPkg);
+ text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
}
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 0387555..b4cce2c 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
@@ -42,9 +43,12 @@
import android.os.AsyncResult;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
+import android.os.IDeviceIdleController;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -187,6 +191,8 @@
private UserManager mUserManager;
+ IDeviceIdleController mDeviceIdleController;
+
/**
* Create a new SMS broadcast helper.
* @param name the class name for logging
@@ -213,6 +219,8 @@
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
mWakeLock.acquire(); // wake lock released after we enter idle state
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
addState(mDefaultState);
addState(mStartupState, mDefaultState);
@@ -407,8 +415,17 @@
case EVENT_BROADCAST_SMS:
// if any broadcasts were sent, transition to waiting state
- if (processMessagePart((InboundSmsTracker) msg.obj)) {
+ InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
+ if (processMessagePart(inboundSmsTracker)) {
transitionTo(mWaitingState);
+ } else {
+ // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
+ // processMessagePart() returns false, the state machine will be stuck in
+ // DeliveringState until next message is received. Send message to
+ // transition to idle to avoid that so that wakelock can be released
+ log("No broadcast sent on processing EVENT_BROADCAST_SMS in Delivering " +
+ "state. Return to Idle state");
+ sendMessage(EVENT_RETURN_TO_IDLE);
}
return HANDLED;
@@ -746,7 +763,12 @@
int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
if (DBG) log("dispatchWapPdu() returned " + result);
// result is Activity.RESULT_OK if an ordered broadcast was sent
- return (result == Activity.RESULT_OK);
+ if (result == Activity.RESULT_OK) {
+ return true;
+ } else {
+ deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs());
+ return false;
+ }
}
List<String> carrierPackages = null;
@@ -814,7 +836,7 @@
* @param user user to deliver the intent to
*/
protected void dispatchIntent(Intent intent, String permission, int appOp,
- BroadcastReceiver resultReceiver, UserHandle user) {
+ Bundle opts, BroadcastReceiver resultReceiver, UserHandle user) {
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
if (user.equals(UserHandle.ALL)) {
@@ -843,14 +865,13 @@
}
}
// Only pass in the resultReceiver when the USER_OWNER is processed.
- mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp,
+ mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp, opts,
users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
getHandler(), Activity.RESULT_OK, null, null);
}
} else {
- mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp,
- resultReceiver,
- getHandler(), Activity.RESULT_OK, null, null);
+ mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp, opts,
+ resultReceiver, getHandler(), Activity.RESULT_OK, null, null);
}
}
@@ -866,6 +887,28 @@
}
}
+ Bundle handleSmsWhitelisting(ComponentName target) {
+ String pkgName;
+ String reason;
+ if (target != null) {
+ pkgName = target.getPackageName();
+ reason = "sms-app";
+ } else {
+ pkgName = mContext.getPackageName();
+ reason = "sms-broadcast";
+ }
+ try {
+ long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForSms(
+ pkgName, 0, reason);
+ BroadcastOptions bopts = BroadcastOptions.makeBasic();
+ bopts.setTemporaryAppWhitelistDuration(duration);
+ return bopts.toBundle();
+ } catch (RemoteException e) {
+ }
+
+ return null;
+ }
+
/**
* Creates and dispatches the intent to the default SMS app or the appropriate port.
*
@@ -910,8 +953,9 @@
intent.setComponent(null);
}
+ Bundle options = handleSmsWhitelisting(intent.getComponent());
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
- AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
+ AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.OWNER);
}
/**
@@ -1040,16 +1084,26 @@
intent.setAction(Intents.SMS_RECEIVED_ACTION);
intent.setComponent(null);
// All running users will be notified of the received sms.
+ Bundle options = handleSmsWhitelisting(null);
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
- AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.ALL);
+ AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL);
} else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
// Now dispatch the notification only intent
intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setComponent(null);
// Only the primary user will receive notification of incoming mms.
// That app will do the actual downloading of the mms.
+ Bundle options = null;
+ try {
+ long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+ mContext.getPackageName(), 0, "mms-broadcast");
+ BroadcastOptions bopts = BroadcastOptions.makeBasic();
+ bopts.setTemporaryAppWhitelistDuration(duration);
+ options = bopts.toBundle();
+ } catch (RemoteException e) {
+ }
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
- AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
+ AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.OWNER);
} else {
// Now that the intents have been deleted we can clean up the PDU data.
if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
diff --git a/src/java/com/android/internal/telephony/LastCallFailCause.java b/src/java/com/android/internal/telephony/LastCallFailCause.java
new file mode 100644
index 0000000..d06eee6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/LastCallFailCause.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+public class LastCallFailCause {
+ public int causeCode;
+ public String vendorCause;
+
+ @Override
+ public String toString() {
+ return super.toString()
+ + " causeCode: " + causeCode
+ + " vendorCause: " + vendorCause;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 33a77ff..cccd142 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -24,7 +24,6 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -32,8 +31,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+
import libcore.icu.ICU;
import libcore.icu.TimeZoneNames;
@@ -184,11 +186,8 @@
}
Slog.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);
-
- Locale locale = null;
if (mcc != 0) {
setTimezoneFromMccIfNeeded(context, mcc);
- locale = getLocaleFromMcc(context, mcc);
}
if (fromServiceState) {
setWifiCountryCodeFromMcc(context, mcc);
@@ -202,10 +201,7 @@
config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
updateConfig = true;
}
- if (locale != null) {
- config.setLocale(locale);
- updateConfig = true;
- }
+
if (updateConfig) {
Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
ActivityManagerNative.getDefault().updateConfiguration(config);
@@ -224,26 +220,39 @@
}
}
- // Bug 19232829: It is possible to get through provisioning without setting up a persistent
- // locale value. We don't modify the locale if the device has completed "provisioning" because
- // we don't want to change the locale if the user inserts a new SIM or a new version of Android
- // is better at recognizing MCC values than an older version.
- private static boolean canUpdateLocale(Context context) {
- return !(userHasPersistedLocale() || isDeviceProvisioned(context));
+ /**
+ * Maps a given locale to a fallback locale that approximates it. This is a hack.
+ */
+ private static final Map<Locale, Locale> FALLBACKS = new HashMap<Locale, Locale>();
+
+ static {
+ FALLBACKS.put(Locale.CANADA, Locale.US);
}
- private static boolean userHasPersistedLocale() {
- final String persistedLocale = SystemProperties.get("persist.sys.locale", "");
- return !persistedLocale.isEmpty();
- }
-
- private static boolean isDeviceProvisioned(Context context) {
- try {
- return Settings.Global.getInt(
- context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED) != 0;
- } catch (Settings.SettingNotFoundException e) {
- return false;
+ /**
+ * Find the best match we actually have a localization for. This function assumes we
+ * couldn't find an exact match.
+ *
+ * TODO: This should really follow the CLDR chain of parent locales! That might be a bit
+ * of a problem because we don't really have an en-001 locale on android.
+ */
+ private static Locale chooseBestFallback(Locale target, List<Locale> candidates) {
+ if (candidates.isEmpty()) {
+ return null;
}
+
+ Locale fallback = target;
+ while ((fallback = FALLBACKS.get(fallback)) != null) {
+ if (candidates.contains(fallback)) {
+ return fallback;
+ }
+ }
+
+ // Somewhat arbitrarily take the first locale for the language,
+ // unless we get a perfect match later. Note that these come back in no
+ // particular order, so there's no reason to think the first match is
+ // a particularly good match.
+ return candidates.get(0);
}
/**
@@ -265,17 +274,6 @@
country = ""; // The Locale constructor throws if passed null.
}
- // Check whether a developer is trying to test an arbitrary MCC.
- boolean debuggingMccOverride = isDebuggingMccOverride();
-
- // If this is a regular user and they already have a persisted locale, we're done.
- if (!(debuggingMccOverride || canUpdateLocale(context))) {
- Slog.d(LOG_TAG, "getLocaleForLanguageCountry: not permitted to update locale");
- return null;
- }
-
- // Find the best match we actually have a localization for.
- // TODO: this should really follow the CLDR chain of parent locales!
final Locale target = new Locale(language, country);
try {
String[] localeArray = context.getAssets().getLocales();
@@ -285,7 +283,7 @@
locales.remove("ar-XB");
locales.remove("en-XA");
- Locale firstMatch = null;
+ List<Locale> languageMatches = new ArrayList<>();
for (String locale : locales) {
final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
@@ -301,22 +299,17 @@
l.toLanguageTag());
return l;
}
- // Otherwise somewhat arbitrarily take the first locale for the language,
- // unless we get a perfect match later. Note that these come back in no
- // particular order, so there's no reason to think the first match is
- // a particularly good match.
- if (firstMatch == null) {
- firstMatch = l;
- }
+
+ // We've only matched the language, not the country.
+ languageMatches.add(l);
}
}
- // We didn't find the exact locale, so return whichever locale we saw first where
- // the language matched (if any).
- if (firstMatch != null) {
+ Locale bestMatch = chooseBestFallback(target, languageMatches);
+ if (bestMatch != null) {
Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " +
- firstMatch.toLanguageTag());
- return firstMatch;
+ bestMatch.toLanguageTag());
+ return bestMatch;
} else {
Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " +
language);
@@ -328,41 +321,6 @@
return null;
}
- private static boolean isDebuggingMccOverride() {
- if (Build.IS_DEBUGGABLE) {
- String overrideMcc = SystemProperties.get("persist.sys.override_mcc", "");
- if (!overrideMcc.isEmpty()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Utility code to set the system locale if it's not set already
- * @param context Context to act on.
- * @param language Two character language code desired
- * @param country Two character country code desired
- *
- * {@hide}
- */
- public static void setSystemLocale(Context context, String language, String country) {
- Locale locale = getLocaleForLanguageCountry(context, language, country);
- if (locale != null) {
- Configuration config = new Configuration();
- config.setLocale(locale);
- config.userSetLocale = false;
- Slog.d(LOG_TAG, "setSystemLocale: updateLocale config=" + config);
- try {
- ActivityManagerNative.getDefault().updateConfiguration(config);
- } catch (RemoteException e) {
- Slog.d(LOG_TAG, "setSystemLocale exception", e);
- }
- } else {
- Slog.d(LOG_TAG, "setSystemLocale: no locale");
- }
- }
-
/**
* If the timezone is not already set, set it based on the MCC of the SIM.
* @param context Context to act on.
@@ -384,17 +342,29 @@
/**
* Get Locale based on the MCC of the SIM.
+ *
* @param context Context to act on.
* @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
+ * @param simLanguage (nullable) the language from the SIM records (if present).
*
* @return locale for the mcc or null if none
*/
- private static Locale getLocaleFromMcc(Context context, int mcc) {
- String language = MccTable.defaultLanguageForMcc(mcc);
+ public static Locale getLocaleFromMcc(Context context, int mcc, String simLanguage) {
+ String language = (simLanguage == null) ? MccTable.defaultLanguageForMcc(mcc) : simLanguage;
String country = MccTable.countryCodeForMcc(mcc);
- Slog.d(LOG_TAG, "getLocaleFromMcc to " + language + "_" + country + " mcc=" + mcc);
- return getLocaleForLanguageCountry(context, language, country);
+ Slog.d(LOG_TAG, "getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
+ final Locale locale = getLocaleForLanguageCountry(context, language, country);
+
+ // If we couldn't find a locale that matches the SIM language, give it a go again
+ // with the "likely" language for the given country.
+ if (locale == null && simLanguage != null) {
+ language = MccTable.defaultLanguageForMcc(mcc);
+ Slog.d(LOG_TAG, "[retry ] getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
+ return getLocaleForLanguageCountry(context, null, country);
+ }
+
+ return locale;
}
/**
diff --git a/src/java/com/android/internal/telephony/OperatorInfo.java b/src/java/com/android/internal/telephony/OperatorInfo.java
deleted file mode 100644
index 49c96a9..0000000
--- a/src/java/com/android/internal/telephony/OperatorInfo.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@hide}
- */
-public class OperatorInfo implements Parcelable {
- public enum State {
- UNKNOWN,
- AVAILABLE,
- CURRENT,
- FORBIDDEN;
- }
-
- private String mOperatorAlphaLong;
- private String mOperatorAlphaShort;
- private String mOperatorNumeric;
-
- private State mState = State.UNKNOWN;
-
-
- public String
- getOperatorAlphaLong() {
- return mOperatorAlphaLong;
- }
-
- public String
- getOperatorAlphaShort() {
- return mOperatorAlphaShort;
- }
-
- public String
- getOperatorNumeric() {
- return mOperatorNumeric;
- }
-
- public State
- getState() {
- return mState;
- }
-
- OperatorInfo(String operatorAlphaLong,
- String operatorAlphaShort,
- String operatorNumeric,
- State state) {
-
- mOperatorAlphaLong = operatorAlphaLong;
- mOperatorAlphaShort = operatorAlphaShort;
- mOperatorNumeric = operatorNumeric;
-
- mState = state;
- }
-
-
- public OperatorInfo(String operatorAlphaLong,
- String operatorAlphaShort,
- String operatorNumeric,
- String stateString) {
- this (operatorAlphaLong, operatorAlphaShort,
- operatorNumeric, rilStateToState(stateString));
- }
-
- /**
- * See state strings defined in ril.h RIL_REQUEST_QUERY_AVAILABLE_NETWORKS
- */
- private static State rilStateToState(String s) {
- if (s.equals("unknown")) {
- return State.UNKNOWN;
- } else if (s.equals("available")) {
- return State.AVAILABLE;
- } else if (s.equals("current")) {
- return State.CURRENT;
- } else if (s.equals("forbidden")) {
- return State.FORBIDDEN;
- } else {
- throw new RuntimeException(
- "RIL impl error: Invalid network state '" + s + "'");
- }
- }
-
-
- @Override
- public String toString() {
- return "OperatorInfo " + mOperatorAlphaLong
- + "/" + mOperatorAlphaShort
- + "/" + mOperatorNumeric
- + "/" + mState;
- }
-
- /**
- * Parcelable interface implemented below.
- * This is a simple effort to make OperatorInfo parcelable rather than
- * trying to make the conventional containing object (AsyncResult),
- * implement parcelable. This functionality is needed for the
- * NetworkQueryService to fix 1128695.
- */
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Implement the Parcelable interface.
- * Method to serialize a OperatorInfo object.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mOperatorAlphaLong);
- dest.writeString(mOperatorAlphaShort);
- dest.writeString(mOperatorNumeric);
- dest.writeSerializable(mState);
- }
-
- /**
- * Implement the Parcelable interface
- * Method to deserialize a OperatorInfo object, or an array thereof.
- */
- public static final Creator<OperatorInfo> CREATOR =
- new Creator<OperatorInfo>() {
- @Override
- public OperatorInfo createFromParcel(Parcel in) {
- OperatorInfo opInfo = new OperatorInfo(
- in.readString(), /*operatorAlphaLong*/
- in.readString(), /*operatorAlphaShort*/
- in.readString(), /*operatorNumeric*/
- (State) in.readSerializable()); /*state*/
- return opInfo;
- }
-
- @Override
- public OperatorInfo[] newArray(int size) {
- return new OperatorInfo[size];
- }
- };
-}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 32966ae..2f13f4e 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.CellInfo;
@@ -37,6 +38,7 @@
import com.android.internal.telephony.PhoneConstants.*; // ????
import java.util.List;
+import java.util.Locale;
/**
* Internal interface used to control the phone; SDK developers cannot
@@ -110,6 +112,8 @@
static final String REASON_SINGLE_PDN_ARBITRATION = "SinglePdnArbitration";
static final String REASON_DATA_SPECIFIC_DISABLED = "specificDisabled";
static final String REASON_SIM_NOT_READY = "simNotReady";
+ static final String REASON_IWLAN_AVAILABLE = "iwlanAvailable";
+ static final String REASON_CARRIER_CHANGE = "carrierChange";
// Used for band mode selection methods
static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
@@ -372,6 +376,22 @@
void unregisterForNewRingingConnection(Handler h);
/**
+ * Notifies when phone's video capabilities changes <p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = true if phone supports video calling <p>
+ */
+ public void registerForVideoCapabilityChanged(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for video capability changed notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForVideoCapabilityChanged(Handler h);
+
+ /**
* Notifies when an incoming call rings.<p>
*
* Messages received from this:
@@ -845,15 +865,20 @@
* path is connected (or a call index has been assigned) until
* PhoneStateChanged notification has occurred.
*
+ * NOTE: If adding another parameter, consider creating a DialArgs parameter instead to
+ * encapsulate all dial arguments and decrease scaffolding headache.
+ *
* @param dialString The dial string.
* @param uusInfo The UUSInfo.
* @param videoState The desired video state for the connection.
+ * @param intentExtras The extras from the original CALL intent.
* @exception CallStateException if a new outgoing call is not currently
* possible because no more call slots exist or a call exists
* that is dialing, alerting, ringing, or waiting. Other
* errors are handled asynchronously.
*/
- Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException;
+ Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException;
/**
* Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
@@ -1406,6 +1431,11 @@
String getGroupIdLevel1();
/**
+ * Retrieves the Group Identifier Level2 for phones.
+ */
+ String getGroupIdLevel2();
+
+ /**
* Retrieves the serial number of the ICC, if applicable.
*/
String getIccSerialNumber();
@@ -1888,6 +1918,11 @@
public Phone getImsPhone();
/**
+ * Start listening for IMS service UP/DOWN events.
+ */
+ public void startMonitoringImsService();
+
+ /**
* Release the local instance of the ImsPhone and disconnect from
* the phone.
* @return the instance of the ImsPhone phone previously owned
@@ -1924,6 +1959,11 @@
public boolean isRadioAvailable();
/**
+ * Is Radio turned on
+ */
+ public boolean isRadioOn();
+
+ /**
* shutdown Radio gracefully
*/
public void shutdownRadio();
@@ -1938,6 +1978,13 @@
public void setRadioCapability(RadioCapability rc, Message response);
/**
+ * Get phone radio capability
+ *
+ * @return the capability of the radio defined in RadioCapability
+ */
+ public RadioCapability getRadioCapability();
+
+ /**
* Get phone radio access family
*
* @return a bit mask to identify the radio access family.
@@ -1945,11 +1992,21 @@
public int getRadioAccessFamily();
/**
- * Get supported phone radio access family
+ * Get the associated data modems Id.
*
- * @return a bit mask to identify the radio access family.
+ * @return a String containing the id of the data modem
*/
- public int getSupportedRadioAccessFamily();
+ public String getModemUuId();
+
+ /**
+ * The RadioCapability has changed. This comes up from the RIL and is called when radios first
+ * become available or after a capability switch. The flow is we use setRadioCapability to
+ * request a change with the RIL and get an UNSOL response with the new data which gets set
+ * here.
+ *
+ * @param rc the phone radio capability currently in effect for this phone.
+ */
+ public void radioCapabilityUpdated(RadioCapability rc);
/**
* Registers the handler when phone radio capability is changed.
@@ -1973,4 +2030,51 @@
* @return true if IMS is Registered
*/
public boolean isImsRegistered();
+
+ /**
+ * Determines if video calling is enabled for the phone.
+ *
+ * @return {@code true} if video calling is enabled, {@code false} otherwise.
+ */
+ public boolean isVideoEnabled();
+
+ /**
+ * @return {@code true} if we are in emergency call back mode. This is a period where the phone
+ * should be using as little power as possible and be ready to receive an incoming call from the
+ * emergency operator.
+ */
+ public boolean isInEcm();
+
+ /**
+ * Returns the Status of Wi-Fi Calling
+ *@hide
+ */
+ public boolean isWifiCallingEnabled();
+
+ /**
+ * Returns the Status of Volte
+ *@hide
+ */
+ public boolean isVolteEnabled();
+
+ /**
+ * @return {@code true} if video call is present, false otherwise.
+ */
+ public boolean isVideoCallPresent();
+
+ /**
+ * Returns the status of Link Capacity Estimation (LCE) service.
+ */
+ public int getLceStatus();
+
+ /**
+ * Returns the locale based on the carrier properties (such as {@code ro.carrier}) and
+ * SIM preferences.
+ */
+ public Locale getLocaleFromSimAndCarrierPrefs();
+
+ /**
+ * Returns the modem activity information
+ */
+ public void getModemActivityInfo(Message response);
}
diff --git a/src/java/com/android/internal/telephony/PhoneBase.java b/src/java/com/android/internal/telephony/PhoneBase.java
index e1444ee..0815d50 100644
--- a/src/java/com/android/internal/telephony/PhoneBase.java
+++ b/src/java/com/android/internal/telephony/PhoneBase.java
@@ -26,6 +26,7 @@
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -45,13 +46,14 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.VoLteServiceState;
+import android.telephony.ModemActivityInfo;
import android.text.TextUtils;
import com.android.ims.ImsManager;
import com.android.internal.R;
import com.android.internal.telephony.dataconnection.DcTrackerBase;
import com.android.internal.telephony.imsphone.ImsPhone;
-import com.android.internal.telephony.RadioCapability;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccFileHandler;
@@ -86,6 +88,7 @@
public abstract class PhoneBase extends Handler implements Phone {
private static final String LOG_TAG = "PhoneBase";
+ private boolean mImsIntentReceiverRegistered = false;
private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -100,12 +103,14 @@
}
}
- if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
- mImsServiceReady = true;
- updateImsPhone();
- } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
- mImsServiceReady = false;
- updateImsPhone();
+ synchronized (PhoneProxy.lockForRadioTechnologyChange) {
+ if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
+ mImsServiceReady = true;
+ updateImsPhone();
+ } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
+ mImsServiceReady = false;
+ updateImsPhone();
+ }
}
}
};
@@ -114,6 +119,8 @@
public static final String NETWORK_SELECTION_KEY = "network_selection_key";
// Key used to read and write the saved network selection operator name
public static final String NETWORK_SELECTION_NAME_KEY = "network_selection_name_key";
+ // Key used to read and write the saved network selection operator short name
+ public static final String NETWORK_SELECTION_SHORT_KEY = "network_selection_short_key";
// Key used to read/write "disable data connection on boot" pref (used for testing)
@@ -163,7 +170,10 @@
protected static final int EVENT_UNSOL_OEM_HOOK_RAW = 34;
protected static final int EVENT_GET_RADIO_CAPABILITY = 35;
protected static final int EVENT_SS = 36;
- protected static final int EVENT_LAST = EVENT_SS;
+ protected static final int EVENT_CONFIG_LCE = 37;
+ private static final int EVENT_CHECK_FOR_NETWORK_AUTOMATIC = 38;
+ protected static final int EVENT_LAST =
+ EVENT_CHECK_FOR_NETWORK_AUTOMATIC;
// For shared prefs.
private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -192,6 +202,7 @@
public Message message;
public String operatorNumeric;
public String operatorAlphaLong;
+ public String operatorAlphaShort;
}
/* Instance Variables */
@@ -204,8 +215,12 @@
int mCallRingDelay;
public boolean mIsTheCurrentActivePhone = true;
boolean mIsVoiceCapable = true;
+
+ // Variable to cache the video capability. When RAT changes, we lose this info and are unable
+ // to recover from the state. We cache it and notify listeners when they register.
+ protected boolean mIsVideoCapable = false;
protected UiccController mUiccController = null;
- public AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
+ public final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
public SmsStorageMonitor mSmsStorageMonitor;
public SmsUsageMonitor mSmsUsageMonitor;
protected AtomicReference<UiccCardApplication> mUiccApplication =
@@ -218,11 +233,16 @@
protected int mPhoneId;
- private final Object mImsLock = new Object();
private boolean mImsServiceReady = false;
protected ImsPhone mImsPhone = null;
- protected int mRadioAccessFamily = RadioAccessFamily.RAF_UNKNOWN;
+ private final AtomicReference<RadioCapability> mRadioCapability =
+ new AtomicReference<RadioCapability>();
+
+ protected static final int DEFAULT_REPORT_INTERVAL_MS = 200;
+ protected static final boolean LCE_PULL_MODE = true;
+ protected int mReportInterval = 0; // ms
+ protected int mLceStatus = RILConstants.LCE_NOT_AVAILABLE;
@Override
public String getPhoneName() {
@@ -314,6 +334,13 @@
protected final RegistrantList mSimRecordsLoadedRegistrants
= new RegistrantList();
+ protected final RegistrantList mVideoCapabilityChangedRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mEmergencyCallToggledRegistrants
+ = new RegistrantList();
+
+
protected Looper mLooper; /* to insure registrants are in correct thread*/
protected final Context mContext;
@@ -365,7 +392,7 @@
* @param ci is CommandsInterface
* @param unitTestMode when true, prevents notifications
* of state change events
- * @param subscription is current phone subscription
+ * @param phoneId the phone-id of this phone.
*/
protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,
boolean unitTestMode, int phoneId) {
@@ -415,36 +442,70 @@
TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
- if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) return;
+ if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ return;
+ }
- setPropertiesByCarrier();
+ // The locale from the "ro.carrier" system property or R.array.carrier_properties.
+ // This will be overwritten by the Locale from the SIM language settings (EF-PL, EF-LI)
+ // if applicable.
+ final Locale carrierLocale = getLocaleFromCarrierProperties(mContext);
+ if (carrierLocale != null && !TextUtils.isEmpty(carrierLocale.getCountry())) {
+ final String country = carrierLocale.getCountry();
+ try {
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_COUNTRY_CODE);
+ } catch (Settings.SettingNotFoundException e) {
+ // note this is not persisting
+ WifiManager wM = (WifiManager)
+ mContext.getSystemService(Context.WIFI_SERVICE);
+ wM.setCountryCode(country, false);
+ }
+ }
// Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
mSmsStorageMonitor = new SmsStorageMonitor(this);
mSmsUsageMonitor = new SmsUsageMonitor(context);
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
-
- // Monitor IMS service - but first poll to see if already up (could miss
- // intent)
- ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
- if (imsManager != null && imsManager.isServiceAvailable()) {
- mImsServiceReady = true;
- updateImsPhone();
+ if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
+ mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
}
- IntentFilter filter = new IntentFilter();
- filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
- filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
- mContext.registerReceiver(mImsIntentReceiver, filter);
-
- mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
mCi.setOnUnsolOemHookRaw(this, EVENT_UNSOL_OEM_HOOK_RAW, null);
+ mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
+ obtainMessage(EVENT_CONFIG_LCE));
+ }
+
+ @Override
+ public void startMonitoringImsService() {
+ if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
+ return;
+ }
+
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+ filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+ mContext.registerReceiver(mImsIntentReceiver, filter);
+ mImsIntentReceiverRegistered = true;
+
+ // Monitor IMS service - but first poll to see if already up (could miss
+ // intent)
+ ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
+ if (imsManager != null && imsManager.isServiceAvailable()) {
+ mImsServiceReady = true;
+ updateImsPhone();
+ }
+ }
}
@Override
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
- mContext.unregisterReceiver(mImsIntentReceiver);
+ if (mImsIntentReceiverRegistered) {
+ mContext.unregisterReceiver(mImsIntentReceiver);
+ mImsIntentReceiverRegistered = false;
+ }
mCi.unSetOnCallRing(this);
// Must cleanup all connectionS and needs to use sendMessage!
mDcTracker.cleanUpAllConnections(null);
@@ -455,6 +516,7 @@
mUiccController.unregisterForIccChanged(this);
mCi.unregisterForSrvccStateChanged(this);
mCi.unSetOnUnsolOemHookRaw(this);
+ mCi.stopLceService(obtainMessage(EVENT_CONFIG_LCE));
if (mTelephonyTester != null) {
mTelephonyTester.dispose();
@@ -548,7 +610,7 @@
String dialString = (String) ar.result;
if (TextUtils.isEmpty(dialString)) return;
try {
- dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY);
+ dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, null);
} catch (CallStateException e) {
Rlog.e(LOG_TAG, "silent redial failed: " + e);
}
@@ -581,14 +643,29 @@
RadioCapability rc = (RadioCapability) ar.result;
if (ar.exception != null) {
Rlog.d(LOG_TAG, "get phone radio capability fail,"
- + "no need to change mRadioAccessFamily");
+ + "no need to change mRadioCapability");
} else {
- mRadioAccessFamily = rc.getRadioAccessFamily();
+ radioCapabilityUpdated(rc);
}
Rlog.d(LOG_TAG, "EVENT_GET_RADIO_CAPABILITY :"
- + "phone RAF : " + mRadioAccessFamily);
+ + "phone rc : " + rc);
break;
+ case EVENT_CONFIG_LCE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ Rlog.d(LOG_TAG, "config LCE service failed: " + ar.exception);
+ } else {
+ final ArrayList<Integer> statusInfo = (ArrayList<Integer>)ar.result;
+ mLceStatus = statusInfo.get(0);
+ mReportInterval = statusInfo.get(1);
+ }
+ break;
+
+ case EVENT_CHECK_FOR_NETWORK_AUTOMATIC: {
+ onCheckForNetworkSelectionModeAutomatic(msg);
+ break;
+ }
default:
throw new RuntimeException("unexpected event not handled");
}
@@ -764,6 +841,24 @@
// Inherited documentation suffices.
@Override
+ public void registerForVideoCapabilityChanged(
+ Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mVideoCapabilityChangedRegistrants.addUnique(h, what, obj);
+
+ // Notify any registrants of the cached video capability as soon as they register.
+ notifyForVideoCapabilityChanged(mIsVideoCapable);
+ }
+
+ // Inherited documentation suffices.
+ @Override
+ public void unregisterForVideoCapabilityChanged(Handler h) {
+ mVideoCapabilityChangedRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ @Override
public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
mCi.registerForInCallVoicePrivacyOn(h, what, obj);
}
@@ -877,18 +972,47 @@
@Override
public void setNetworkSelectionModeAutomatic(Message response) {
- // wrap the response message in our own message along with
- // an empty string (to indicate automatic selection) for the
- // operator's id.
- NetworkSelectMessage nsm = new NetworkSelectMessage();
- nsm.message = response;
- nsm.operatorNumeric = "";
- nsm.operatorAlphaLong = "";
+ Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode");
+ // we don't want to do this unecesarily - it acutally causes
+ // the radio to repeate network selection and is costly
+ // first check if we're already in automatic mode
+ Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC);
+ msg.obj = response;
+ mCi.getNetworkSelectionMode(msg);
+ }
- Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
- mCi.setNetworkSelectionModeAutomatic(msg);
+ private void onCheckForNetworkSelectionModeAutomatic(Message fromRil) {
+ AsyncResult ar = (AsyncResult)fromRil.obj;
+ Message response = (Message)ar.userObj;
+ boolean doAutomatic = true;
+ if (ar.exception == null && ar.result != null) {
+ try {
+ int[] modes = (int[])ar.result;
+ if (modes[0] == 0) {
+ // already confirmed to be in automatic mode - don't resend
+ doAutomatic = false;
+ }
+ } catch (Exception e) {
+ // send the setting on error
+ }
+ }
+ if (doAutomatic) {
+ // wrap the response message in our own message along with
+ // an empty string (to indicate automatic selection) for the
+ // operator's id.
+ NetworkSelectMessage nsm = new NetworkSelectMessage();
+ nsm.message = response;
+ nsm.operatorNumeric = "";
+ nsm.operatorAlphaLong = "";
+ nsm.operatorAlphaShort = "";
- updateSavedNetworkOperator(nsm);
+ Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
+ mCi.setNetworkSelectionModeAutomatic(msg);
+
+ updateSavedNetworkOperator(nsm);
+ } else {
+ Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+ }
}
@Override
@@ -904,6 +1028,7 @@
nsm.message = response;
nsm.operatorNumeric = network.getOperatorNumeric();
nsm.operatorAlphaLong = network.getOperatorAlphaLong();
+ nsm.operatorAlphaShort = network.getOperatorAlphaShort();
Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
mCi.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg);
@@ -911,6 +1036,23 @@
updateSavedNetworkOperator(nsm);
}
+ /**
+ * Registration point for emergency call/callback mode start. Message.obj is AsyncResult and
+ * Message.obj.result will be Integer indicating start of call by value 1 or end of call by
+ * value 0
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj.userObj
+ */
+ public void registerForEmergencyCallToggle(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mEmergencyCallToggledRegistrants.add(r);
+ }
+
+ public void unregisterForEmergencyCallToggle(Handler h) {
+ mEmergencyCallToggledRegistrants.remove(h);
+ }
+
private void updateSavedNetworkOperator(NetworkSelectMessage nsm) {
int subId = getSubId();
if (SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -920,6 +1062,7 @@
SharedPreferences.Editor editor = sp.edit();
editor.putString(NETWORK_SELECTION_KEY + subId, nsm.operatorNumeric);
editor.putString(NETWORK_SELECTION_NAME_KEY + subId, nsm.operatorAlphaLong);
+ editor.putString(NETWORK_SELECTION_SHORT_KEY + subId, nsm.operatorAlphaShort);
// commit and log the result.
if (!editor.commit()) {
@@ -953,12 +1096,15 @@
}
/**
- * Method to retrieve the saved operator id from the Shared Preferences
+ * Method to retrieve the saved operator from the Shared Preferences
*/
- private String getSavedNetworkSelection() {
+ private OperatorInfo getSavedNetworkSelection() {
// open the shared preferences and search with our key.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
- return sp.getString(NETWORK_SELECTION_KEY + getSubId(), "");
+ String numeric = sp.getString(NETWORK_SELECTION_KEY + getSubId(), "");
+ String name = sp.getString(NETWORK_SELECTION_NAME_KEY + getSubId(), "");
+ String shrt = sp.getString(NETWORK_SELECTION_SHORT_KEY + getSubId(), "");
+ return new OperatorInfo(numeric, name, shrt);
}
/**
@@ -967,14 +1113,14 @@
* preferences.
*/
public void restoreSavedNetworkSelection(Message response) {
- // retrieve the operator id
- String networkSelection = getSavedNetworkSelection();
+ // retrieve the operator
+ OperatorInfo networkSelection = getSavedNetworkSelection();
// set to auto if the id is empty, otherwise select the network.
- if (TextUtils.isEmpty(networkSelection)) {
- mCi.setNetworkSelectionModeAutomatic(response);
+ if (networkSelection == null || TextUtils.isEmpty(networkSelection.getOperatorNumeric())) {
+ setNetworkSelectionModeAutomatic(response);
} else {
- mCi.setNetworkSelectionModeManual(networkSelection, response);
+ selectNetworkManually(networkSelection, response);
}
}
@@ -1091,37 +1237,23 @@
* Set the properties by matching the carrier string in
* a string-array resource
*/
- private void setPropertiesByCarrier() {
+ private static Locale getLocaleFromCarrierProperties(Context ctx) {
String carrier = SystemProperties.get("ro.carrier");
if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) {
- return;
+ return null;
}
- CharSequence[] carrierLocales = mContext.
- getResources().getTextArray(R.array.carrier_properties);
+ CharSequence[] carrierLocales = ctx.getResources().getTextArray(R.array.carrier_properties);
for (int i = 0; i < carrierLocales.length; i+=3) {
String c = carrierLocales[i].toString();
if (carrier.equals(c)) {
- final Locale l = Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
- final String country = l.getCountry();
- MccTable.setSystemLocale(mContext, l.getLanguage(), country);
-
- if (!country.isEmpty()) {
- try {
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_COUNTRY_CODE);
- } catch (Settings.SettingNotFoundException e) {
- // note this is not persisting
- WifiManager wM = (WifiManager)
- mContext.getSystemService(Context.WIFI_SERVICE);
- wM.setCountryCode(country, false);
- }
- }
- return;
+ return Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
}
}
+
+ return null;
}
/**
@@ -1216,6 +1348,7 @@
* @return the original list with CDMA lat/long cleared if necessary
*/
private List<CellInfo> privatizeCellInfoList(List<CellInfo> cellInfoList) {
+ if (cellInfoList == null) return null;
int mode = Settings.Secure.getInt(getContext().getContentResolver(),
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
if (mode == Settings.Secure.LOCATION_MODE_OFF) {
@@ -1304,7 +1437,33 @@
*/
@Override
public void setPreferredNetworkType(int networkType, Message response) {
- mCi.setPreferredNetworkType(networkType, response);
+ // Only set preferred network types to that which the modem supports
+ int modemRaf = getRadioAccessFamily();
+ int rafFromType = RadioAccessFamily.getRafFromNetworkType(networkType);
+
+ if (modemRaf == RadioAccessFamily.RAF_UNKNOWN
+ || rafFromType == RadioAccessFamily.RAF_UNKNOWN) {
+ Rlog.d(LOG_TAG, "setPreferredNetworkType: Abort, unknown RAF: "
+ + modemRaf + " " + rafFromType);
+ if (response != null) {
+ CommandException ex;
+
+ ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
+ AsyncResult.forMessage(response, null, ex);
+ response.sendToTarget();
+ }
+ return;
+ }
+
+ int filteredRaf = (rafFromType & modemRaf);
+ int filteredType = RadioAccessFamily.getNetworkTypeFromRaf(filteredRaf);
+
+ Rlog.d(LOG_TAG, "setPreferredNetworkType: networkType = " + networkType
+ + " modemRaf = " + modemRaf
+ + " rafFromType = " + rafFromType
+ + " filteredType = " + filteredType);
+
+ mCi.setPreferredNetworkType(filteredType, response);
}
@Override
@@ -1447,14 +1606,40 @@
}
/**
- * @return true if we are in the emergency call back mode. This is a period where
- * the phone should be using as little power as possible and be ready to receive an
- * incoming call from the emergency operator.
+ * @return {@code true} if we are in emergency call back mode. This is a period where the phone
+ * should be using as little power as possible and be ready to receive an incoming call from the
+ * emergency operator.
*/
public boolean isInEcm() {
return false;
}
+ private static int getVideoState(Call call) {
+ int videoState = VideoProfile.STATE_AUDIO_ONLY;
+ ImsPhoneConnection conn = (ImsPhoneConnection) call.getEarliestConnection();
+ if (conn != null) {
+ videoState = conn.getVideoState();
+ }
+ return videoState;
+ }
+
+ private boolean isVideoCall(Call call) {
+ int videoState = getVideoState(call);
+ return (VideoProfile.isVideo(videoState));
+ }
+
+ @Override
+ public boolean isVideoCallPresent() {
+ boolean isVideoCallActive = false;
+ if (mImsPhone != null) {
+ isVideoCallActive = isVideoCall(mImsPhone.getForegroundCall()) ||
+ isVideoCall(mImsPhone.getBackgroundCall()) ||
+ isVideoCall(mImsPhone.getRingingCall());
+ }
+ Rlog.d(LOG_TAG, "isVideoCallActive: " + isVideoCallActive);
+ return isVideoCallActive;
+ }
+
@Override
public abstract int getPhoneType();
@@ -1479,14 +1664,15 @@
String subscriberId = sp.getString(VM_ID, null);
String currentSubscriberId = getSubscriberId();
- Rlog.d(LOG_TAG, "Voicemail count retrieval for subscriberId = " + subscriberId +
- " current subscriberId = " + currentSubscriberId);
-
if ((subscriberId != null) && (currentSubscriberId != null)
&& (currentSubscriberId.equals(subscriberId))) {
// get voice mail count from preferences
countVoiceMessages = sp.getInt(VM_COUNT, 0);
Rlog.d(LOG_TAG, "Voice Mail Count from preference = " + countVoiceMessages);
+ } else {
+ Rlog.d(LOG_TAG, "Voicemail count retrieval returning 0 as count for matching " +
+ "subscriberId not found");
+
}
return countVoiceMessages;
}
@@ -1759,6 +1945,18 @@
mNewRingingConnectionRegistrants.notifyRegistrants(ar);
}
+
+ /**
+ * Notify registrants if phone is video capable.
+ */
+ public void notifyForVideoCapabilityChanged(boolean isVideoCallCapable) {
+ // Cache the current video capability so that we don't lose the information.
+ mIsVideoCapable = isVideoCallCapable;
+
+ AsyncResult ar = new AsyncResult(null, isVideoCallCapable, null);
+ mVideoCapabilityChangedRegistrants.notifyRegistrants(ar);
+ }
+
/**
* Notify registrants of a RING event.
*/
@@ -1904,10 +2102,15 @@
@Override
public ImsPhone relinquishOwnershipOfImsPhone() {
- synchronized (mImsLock) {
+ synchronized (PhoneProxy.lockForRadioTechnologyChange) {
if (mImsPhone == null)
return null;
+ if (mImsIntentReceiverRegistered) {
+ mContext.unregisterReceiver(mImsIntentReceiver);
+ mImsIntentReceiverRegistered = false;
+ }
+
ImsPhone imsPhone = mImsPhone;
mImsPhone = null;
@@ -1920,7 +2123,7 @@
@Override
public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) {
- synchronized (mImsLock) {
+ synchronized (PhoneProxy.lockForRadioTechnologyChange) {
if (imsPhone == null)
return;
@@ -1945,26 +2148,24 @@
}
protected void updateImsPhone() {
- synchronized (mImsLock) {
- Rlog.d(LOG_TAG, "updateImsPhone"
- + " mImsServiceReady=" + mImsServiceReady);
+ Rlog.d(LOG_TAG, "updateImsPhone"
+ + " mImsServiceReady=" + mImsServiceReady);
- if (mImsServiceReady && (mImsPhone == null)) {
- mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
- CallManager.getInstance().registerPhone(mImsPhone);
- mImsPhone.registerForSilentRedial(
- this, EVENT_INITIATE_SILENT_REDIAL, null);
- } else if (!mImsServiceReady && (mImsPhone != null)) {
- CallManager.getInstance().unregisterPhone(mImsPhone);
- mImsPhone.unregisterForSilentRedial(this);
+ if (mImsServiceReady && (mImsPhone == null)) {
+ mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
+ CallManager.getInstance().registerPhone(mImsPhone);
+ mImsPhone.registerForSilentRedial(
+ this, EVENT_INITIATE_SILENT_REDIAL, null);
+ } else if (!mImsServiceReady && (mImsPhone != null)) {
+ CallManager.getInstance().unregisterPhone(mImsPhone);
+ mImsPhone.unregisterForSilentRedial(this);
- mImsPhone.dispose();
- // Potential GC issue if someone keeps a reference to ImsPhone.
- // However: this change will make sure that such a reference does
- // not access functions through NULL pointer.
- //mImsPhone.removeReferences();
- mImsPhone = null;
- }
+ mImsPhone.dispose();
+ // Potential GC issue if someone keeps a reference to ImsPhone.
+ // However: this change will make sure that such a reference does
+ // not access functions through NULL pointer.
+ //mImsPhone.removeReferences();
+ mImsPhone = null;
}
}
@@ -1974,10 +2175,12 @@
* @param dialString The number to dial.
* @param uusInfo The UUSInfo.
* @param videoState The video state for the call.
+ * @param intentExtras Extras from the original CALL intent.
* @return The Connection.
* @throws CallStateException
*/
- protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState)
+ protected Connection dialInternal(
+ String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
throws CallStateException {
// dialInternal shall be overriden by GSMPhone and CDMAPhone
return null;
@@ -2086,6 +2289,34 @@
return isImsRegistered;
}
+ /**
+ * Get Wifi Calling Feature Availability
+ */
+ @Override
+ public boolean isWifiCallingEnabled() {
+ ImsPhone imsPhone = mImsPhone;
+ boolean isWifiCallingEnabled = false;
+ if (imsPhone != null) {
+ isWifiCallingEnabled = imsPhone.isVowifiEnabled();
+ }
+ Rlog.d(LOG_TAG, "isWifiCallingEnabled =" + isWifiCallingEnabled);
+ return isWifiCallingEnabled;
+ }
+
+ /**
+ * Get Volte Feature Availability
+ */
+ @Override
+ public boolean isVolteEnabled() {
+ ImsPhone imsPhone = mImsPhone;
+ boolean isVolteEnabled = false;
+ if (imsPhone != null) {
+ isVolteEnabled = imsPhone.isVolteEnabled();
+ }
+ Rlog.d(LOG_TAG, "isImsRegistered =" + isVolteEnabled);
+ return isVolteEnabled;
+ }
+
private boolean getRoamingOverrideHelper(String prefix, String key) {
String iccId = getIccSerialNumber();
if (TextUtils.isEmpty(iccId) || TextUtils.isEmpty(key)) {
@@ -2106,6 +2337,11 @@
}
@Override
+ public boolean isRadioOn() {
+ return mCi.getRadioState().isOn();
+ }
+
+ @Override
public void shutdownRadio() {
getServiceStateTracker().requestShutdown();
}
@@ -2117,12 +2353,49 @@
@Override
public int getRadioAccessFamily() {
- return mRadioAccessFamily;
+ final RadioCapability rc = getRadioCapability();
+ return (rc == null ? RadioAccessFamily.RAF_UNKNOWN : rc.getRadioAccessFamily());
}
@Override
- public int getSupportedRadioAccessFamily() {
- return mCi.getSupportedRadioAccessFamily();
+ public String getModemUuId() {
+ final RadioCapability rc = getRadioCapability();
+ return (rc == null ? "" : rc.getLogicalModemUuid());
+ }
+
+ @Override
+ public RadioCapability getRadioCapability() {
+ return mRadioCapability.get();
+ }
+
+ @Override
+ public void radioCapabilityUpdated(RadioCapability rc) {
+ // Called when radios first become available or after a capability switch
+ // Update the cached value
+ mRadioCapability.set(rc);
+
+ if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+ sendSubscriptionSettings(true);
+ }
+ }
+
+ public void sendSubscriptionSettings(boolean restoreNetworkSelection) {
+ // Send settings down
+ int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
+ setPreferredNetworkType(type, null);
+
+ if (restoreNetworkSelection) {
+ restoreSavedNetworkSelection(null);
+ }
+ mDcTracker.setDataEnabled(getDataEnabled());
+ }
+
+ protected void setPreferredNetworkTypeIfSimLoaded() {
+ int subId = getSubId();
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
+ setPreferredNetworkType(type, null);
+ }
}
@Override
@@ -2135,6 +2408,67 @@
mCi.unregisterForRadioCapabilityChanged(this);
}
+ /**
+ * Determines if IMS is enabled for call.
+ *
+ * @return {@code true} if IMS calling is enabled.
+ */
+ public boolean isImsUseEnabled() {
+ boolean imsUseEnabled =
+ ((ImsManager.isVolteEnabledByPlatform(mContext) &&
+ ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext)) ||
+ (ImsManager.isWfcEnabledByPlatform(mContext) &&
+ ImsManager.isWfcEnabledByUser(mContext)) &&
+ ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext));
+ return imsUseEnabled;
+ }
+
+ /**
+ * Determines if video calling is enabled for the IMS phone.
+ *
+ * @return {@code true} if video calling is enabled.
+ */
+ @Override
+ public boolean isVideoEnabled() {
+ ImsPhone imsPhone = mImsPhone;
+ if ((imsPhone != null)
+ && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+ return imsPhone.isVideoEnabled();
+ }
+ return false;
+ }
+
+ @Override
+ public int getLceStatus() {
+ return mLceStatus;
+ }
+
+ @Override
+ public void getModemActivityInfo(Message response) {
+ mCi.getModemActivityInfo(response);
+ }
+
+ /**
+ * Starts LCE service after radio becomes available.
+ * LCE service state may get destroyed on the modem when radio becomes unavailable.
+ */
+ public void startLceAfterRadioIsAvailable() {
+ if (mIsTheCurrentActivePhone) {
+ mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
+ obtainMessage(EVENT_CONFIG_LCE));
+ }
+ }
+
+ @Override
+ public Locale getLocaleFromSimAndCarrierPrefs() {
+ final IccRecords records = mIccRecords.get();
+ if (records != null && records.getSimLanguage() != null) {
+ return new Locale(records.getSimLanguage());
+ }
+
+ return getLocaleFromCarrierProperties(mContext);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("PhoneBase: subId=" + getSubId());
pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 9f02433..b4c1c94 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -29,6 +29,7 @@
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.LocalLog;
import com.android.internal.telephony.cdma.CDMALTEPhone;
import com.android.internal.telephony.cdma.CDMAPhone;
@@ -42,9 +43,11 @@
import com.android.internal.telephony.sip.SipPhoneFactory;
import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.HashMap;
/**
* {@hide}
@@ -53,6 +56,7 @@
static final String LOG_TAG = "PhoneFactory";
static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
static final int SOCKET_OPEN_MAX_RETRY = 3;
+ static final boolean DBG = false;
//***** Class Variables
@@ -73,6 +77,8 @@
static private PhoneNotifier sPhoneNotifier;
static private Context sContext;
+ static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
+
//***** Class Methods
public static void makeDefaultPhones(Context context) {
@@ -151,9 +157,11 @@
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
phone = new GSMPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
+ phone.startMonitoringImsService();
} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
phone = new CDMALTEPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
+ phone.startMonitoringImsService();
}
Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
@@ -226,15 +234,18 @@
throw new IllegalStateException("Default phones haven't been made yet!");
// CAF_MSIM FIXME need to introduce default phone id ?
} else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
- dbgInfo = "phoneId == DEFAULT_PHONE_ID return sProxyPhone";
+ if (DBG) dbgInfo = "phoneId == DEFAULT_PHONE_ID return sProxyPhone";
phone = sProxyPhone;
} else {
- dbgInfo = "phoneId != DEFAULT_PHONE_ID return sProxyPhones[phoneId]";
+ if (DBG) dbgInfo = "phoneId != DEFAULT_PHONE_ID return sProxyPhones[phoneId]";
phone = (((phoneId >= 0)
&& (phoneId < TelephonyManager.getDefault().getPhoneCount()))
? sProxyPhones[phoneId] : null);
}
- Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId + " phone=" + phone);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId +
+ " phone=" + phone);
+ }
return phone;
}
}
@@ -276,7 +287,7 @@
// Update MCC MNC device configuration information
String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumericForPhone(phoneId);
- Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
+ if (DBG) Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
MccTable.updateMccMncConfiguration(sContext, defaultMccMnc, false);
// Broadcast an Intent for default sub change
@@ -406,6 +417,43 @@
return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
}
+ /**
+ * Adds a local log category.
+ *
+ * Only used within the telephony process. Use localLog to add log entries.
+ *
+ * TODO - is there a better way to do this? Think about design when we have a minute.
+ *
+ * @param key the name of the category - will be the header in the service dump.
+ * @param size the number of lines to maintain in this category
+ */
+ public static void addLocalLog(String key, int size) {
+ synchronized(sLocalLogs) {
+ if (sLocalLogs.containsKey(key)) {
+ throw new IllegalArgumentException("key " + key + " already present");
+ }
+ sLocalLogs.put(key, new LocalLog(size));
+ }
+ }
+
+ /**
+ * Add a line to the named Local Log.
+ *
+ * This will appear in the TelephonyDebugService dump.
+ *
+ * @param key the name of the log category to put this in. Must be created
+ * via addLocalLog.
+ * @param log the string to add to the log.
+ */
+ public static void localLog(String key, String log) {
+ synchronized(sLocalLogs) {
+ if (sLocalLogs.containsKey(key) == false) {
+ throw new IllegalArgumentException("key " + key + " not found");
+ }
+ sLocalLogs.get(key).log(log);
+ }
+ }
+
public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("PhoneFactory:");
PhoneProxy [] phones = (PhoneProxy[])PhoneFactory.getPhones();
@@ -454,5 +502,25 @@
e.printStackTrace();
}
pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+
+ try {
+ sSubInfoRecordUpdater.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+
+ pw.println("++++++++++++++++++++++++++++++++");
+ synchronized (sLocalLogs) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ for (String key : sLocalLogs.keySet()) {
+ ipw.println(key);
+ ipw.increaseIndent();
+ sLocalLogs.get(key).dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
+ ipw.flush();
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/PhoneProxy.java b/src/java/com/android/internal/telephony/PhoneProxy.java
index 3c71012..13428ec 100644
--- a/src/java/com/android/internal/telephony/PhoneProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneProxy.java
@@ -24,10 +24,13 @@
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.Rlog;
@@ -40,7 +43,6 @@
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.cdma.CDMALTEPhone;
-import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IsimRecords;
@@ -50,7 +52,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
-import java.util.Set;
+import java.util.Locale;
import com.android.internal.telephony.dataconnection.DctController;
@@ -177,13 +179,20 @@
if (mActivePhone != null) {
// Check for a voice over lte replacement
- if ((newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) ||
- (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
- int volteReplacementRat = mActivePhone.getContext().getResources().getInteger(
- com.android.internal.R.integer.config_volte_replacement_rat);
- logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat);
- if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
- newVoiceRadioTech = volteReplacementRat;
+ if ((newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)
+ || (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
+ CarrierConfigManager configMgr = (CarrierConfigManager)
+ mActivePhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configMgr.getConfigForSubId(mActivePhone.getSubId());
+ if (b != null) {
+ int volteReplacementRat =
+ b.getInt(CarrierConfigManager.KEY_VOLTE_REPLACEMENT_RAT_INT);
+ logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat);
+ if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+ newVoiceRadioTech = volteReplacementRat;
+ }
+ } else {
+ loge("phoneObjectUpdater: didn't get volteReplacementRat from carrier config");
}
}
@@ -301,6 +310,7 @@
if (imsPhone != null) {
mActivePhone.acquireOwnershipOfImsPhone(imsPhone);
}
+ mActivePhone.startMonitoringImsService();
mActivePhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
}
@@ -333,6 +343,11 @@
}
@Override
+ public boolean isVideoCallPresent() {
+ return mActivePhone.isVideoCallPresent();
+ }
+
+ @Override
public void updatePhoneObject(int voiceRadioTech) {
logd("updatePhoneObject: radioTechnology=" + voiceRadioTech);
sendMessage(obtainMessage(EVENT_UPDATE_PHONE_OBJECT, voiceRadioTech, 0, null));
@@ -480,6 +495,17 @@
}
@Override
+ public void registerForVideoCapabilityChanged(
+ Handler h, int what, Object obj) {
+ mActivePhone.registerForVideoCapabilityChanged(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForVideoCapabilityChanged(Handler h) {
+ mActivePhone.unregisterForVideoCapabilityChanged(h);
+ }
+
+ @Override
public void registerForIncomingRing(Handler h, int what, Object obj) {
mActivePhone.registerForIncomingRing(h, what, obj);
}
@@ -739,8 +765,9 @@
}
@Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {
- return mActivePhone.dial(dialString, uusInfo, videoState);
+ public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException {
+ return mActivePhone.dial(dialString, uusInfo, videoState, intentExtras);
}
@Override
@@ -1048,6 +1075,11 @@
}
@Override
+ public String getGroupIdLevel2() {
+ return mActivePhone.getGroupIdLevel2();
+ }
+
+ @Override
public String getIccSerialNumber() {
return mActivePhone.getIccSerialNumber();
}
@@ -1444,6 +1476,9 @@
public ImsPhone relinquishOwnershipOfImsPhone() { return null; }
@Override
+ public void startMonitoringImsService() {}
+
+ @Override
public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) { }
@Override
@@ -1470,6 +1505,11 @@
}
@Override
+ public boolean isRadioOn() {
+ return mCommandsInterface.getRadioState().isOn();
+ }
+
+ @Override
public void shutdownRadio() {
mActivePhone.shutdownRadio();
}
@@ -1485,8 +1525,18 @@
}
@Override
- public int getSupportedRadioAccessFamily() {
- return mCommandsInterface.getSupportedRadioAccessFamily();
+ public String getModemUuId() {
+ return mActivePhone.getModemUuId();
+ }
+
+ @Override
+ public RadioCapability getRadioCapability() {
+ return mActivePhone.getRadioCapability();
+ }
+
+ @Override
+ public void radioCapabilityUpdated(RadioCapability rc) {
+ mActivePhone.radioCapabilityUpdated(rc);
}
@Override
@@ -1507,6 +1557,52 @@
return mActivePhone.isImsRegistered();
}
+ /**
+ * Determines if video calling is enabled for the IMS phone.
+ *
+ * @return {@code true} if video calling is enabled.
+ */
+ @Override
+ public boolean isVideoEnabled() {
+ return mActivePhone.isVideoEnabled();
+ }
+
+ /**
+ * Returns the status of Link Capacity Estimation (LCE) service.
+ */
+ @Override
+ public int getLceStatus() {
+ return mActivePhone.getLceStatus();
+ }
+
+ @Override
+ public Locale getLocaleFromSimAndCarrierPrefs() {
+ return mActivePhone.getLocaleFromSimAndCarrierPrefs();
+ }
+
+ @Override
+ public void getModemActivityInfo(Message response) {
+ mActivePhone.getModemActivityInfo(response);
+ }
+
+ /**
+ * @return true if we are in the emergency call back mode. This is a period where
+ * the phone should be using as little power as possible and be ready to receive an
+ * incoming call from the emergency operator.
+ */
+ @Override
+ public boolean isInEcm() {
+ return mActivePhone.isInEcm();
+ }
+
+ public boolean isVolteEnabled() {
+ return mActivePhone.isVolteEnabled();
+ }
+
+ public boolean isWifiCallingEnabled() {
+ return mActivePhone.isWifiCallingEnabled();
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try {
((PhoneBase)mActivePhone).dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfo.java b/src/java/com/android/internal/telephony/PhoneSubInfo.java
index 264d335..71222ae 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -18,10 +18,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.RemoteException;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
@@ -36,6 +36,7 @@
private Phone mPhone;
private Context mContext;
+ private AppOpsManager mAppOps;
private static final String READ_PHONE_STATE =
android.Manifest.permission.READ_PHONE_STATE;
// TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
@@ -47,6 +48,7 @@
public PhoneSubInfo(Phone phone) {
mPhone = phone;
mContext = phone.getContext();
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
}
public void dispose() {
@@ -65,24 +67,30 @@
/**
* Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
*/
- public String getDeviceId() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getDeviceId(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getDeviceId")) {
+ return null;
+ }
return mPhone.getDeviceId();
}
/**
* Retrieves the IMEI.
*/
- public String getImei() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getImei(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getImei")) {
+ return null;
+ }
return mPhone.getImei();
}
/**
* Retrieves the NAI.
*/
- public String getNai() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getNai(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getNai")) {
+ return null;
+ }
return mPhone.getNai();
}
@@ -90,64 +98,82 @@
* Retrieves the software version number for the device, e.g., IMEI/SV
* for GSM phones.
*/
- public String getDeviceSvn() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getDeviceSvn(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
+ return null;
+ }
+
return mPhone.getDeviceSvn();
}
/**
* Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
*/
- public String getSubscriberId() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getSubscriberId(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
+ return null;
+ }
return mPhone.getSubscriberId();
}
/**
* Retrieves the Group Identifier Level1 for GSM phones.
*/
- public String getGroupIdLevel1() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getGroupIdLevel1(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
+ return null;
+ }
return mPhone.getGroupIdLevel1();
}
/**
* Retrieves the serial number of the ICC, if applicable.
*/
- public String getIccSerialNumber() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getIccSerialNumber(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
+ return null;
+ }
return mPhone.getIccSerialNumber();
}
/**
* Retrieves the phone number string for line 1.
*/
- public String getLine1Number() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getLine1Number(String callingPackage) {
+ // This is open to apps with WRITE_SMS.
+ if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
+ return null;
+ }
return mPhone.getLine1Number();
}
/**
* Retrieves the alpha identifier for line 1.
*/
- public String getLine1AlphaTag() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getLine1AlphaTag(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
+ return null;
+ }
return mPhone.getLine1AlphaTag();
}
/**
* Retrieves the MSISDN string.
*/
- public String getMsisdn() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getMsisdn(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
+ return null;
+ }
return mPhone.getMsisdn();
}
/**
* Retrieves the voice mail number.
*/
- public String getVoiceMailNumber() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getVoiceMailNumber(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
+ return null;
+ }
String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
if (VDBG) log("VM: PhoneSubInfo.getVoiceMailNUmber: " + number);
return number;
@@ -169,8 +195,10 @@
/**
* Retrieves the alpha identifier associated with the voice mail number.
*/
- public String getVoiceMailAlphaTag() {
- mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ public String getVoiceMailAlphaTag(String callingPackage) {
+ if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
+ return null;
+ }
return mPhone.getVoiceMailAlphaTag();
}
@@ -336,4 +364,40 @@
pw.println(" Phone Type = " + mPhone.getPhoneName());
pw.println(" Device ID = " + mPhone.getDeviceId());
}
+
+ private boolean checkReadPhoneState(String callingPackage, String message) {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+
+ // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
+ return true;
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+ message);
+ }
+
+ return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) == AppOpsManager.MODE_ALLOWED;
+ }
+
+
+ /**
+ * Besides READ_PHONE_STATE, WRITE_SMS also allows apps to get phone numbers.
+ */
+ private boolean checkReadPhoneNumber(String callingPackage, String message) {
+ // Default SMS app can always read it.
+ if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
+ Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ try {
+ return checkReadPhoneState(callingPackage, message);
+ } catch (SecurityException e) {
+ // Can be read with READ_SMS too.
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_SMS, message);
+ return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
+ Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 3394d20..abd7860 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -18,6 +18,9 @@
package com.android.internal.telephony;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.SubscriptionManager;
@@ -25,30 +28,67 @@
import android.telephony.TelephonyManager;
import java.lang.NullPointerException;
-import java.lang.ArrayIndexOutOfBoundsException;
-
-import com.android.internal.telephony.IPhoneSubInfo;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneSubInfoProxy;
public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
private static final String TAG = "PhoneSubInfoController";
- private Phone[] mPhone;
+ private final Phone[] mPhone;
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
- public PhoneSubInfoController(Phone[] phone) {
- mPhone = phone;
+ public PhoneSubInfoController(Phone[] phones) {
+ mPhone = phones;
+ Context context = null;
+ AppOpsManager appOpsManager = null;
+ for (Phone phone : mPhone) {
+ if (phone != null) {
+ context = phone.getContext();
+ appOpsManager = context.getSystemService(AppOpsManager.class);
+ break;
+ }
+ }
+ mContext = context;
+ mAppOps = appOpsManager;
if (ServiceManager.getService("iphonesubinfo") == null) {
ServiceManager.addService("iphonesubinfo", this);
}
}
+ // try-state
+ // either have permission (true), don't (exception), or explicitly turned off (false)
+ private boolean canReadPhoneState(String callingPackage, String message) {
+ if (mContext == null) return false;
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
- public String getDeviceId() {
- return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()));
+ // SKIP checking for run-time permission since caller or self has PRIVILEDGED permission
+ return true;
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+ message);
+ }
+
+
+
+ if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+
+ return true;
}
- public String getDeviceIdForPhone(int phoneId) {
- Phone phone = getPhone(phoneId);
+ public String getDeviceId(String callingPackage) {
+ return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()),
+ callingPackage);
+ }
+
+ public String getDeviceIdForPhone(int phoneId, String callingPackage) {
+ if (!canReadPhoneState(callingPackage, "getDeviceId")) {
+ return null;
+ }
+
+ final Phone phone = getPhone(phoneId);
if (phone != null) {
phone.getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PHONE_STATE,
@@ -60,10 +100,10 @@
}
}
- public String getNaiForSubscriber(int subId) {
+ public String getNaiForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getNai();
+ return phoneSubInfoProxy.getNai(callingPackage);
} else {
Rlog.e(TAG,"getNai phoneSubInfoProxy is null" +
" for Subscription:" + subId);
@@ -71,10 +111,10 @@
}
}
- public String getImeiForSubscriber(int subId) {
+ public String getImeiForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getImei();
+ return phoneSubInfoProxy.getImei(callingPackage);
} else {
Rlog.e(TAG,"getDeviceId phoneSubInfoProxy is null" +
" for Subscription:" + subId);
@@ -82,28 +122,28 @@
}
}
- public String getDeviceSvn() {
- return getDeviceSvnUsingSubId(getDefaultSubscription());
+ public String getDeviceSvn(String callingPackage) {
+ return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
}
- public String getDeviceSvnUsingSubId(int subId) {
+ public String getDeviceSvnUsingSubId(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getDeviceSvn();
+ return phoneSubInfoProxy.getDeviceSvn(callingPackage);
} else {
Rlog.e(TAG,"getDeviceSvn phoneSubInfoProxy is null");
return null;
}
}
- public String getSubscriberId() {
- return getSubscriberIdForSubscriber(getDefaultSubscription());
+ public String getSubscriberId(String callingPackage) {
+ return getSubscriberIdForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getSubscriberIdForSubscriber(int subId) {
+ public String getSubscriberIdForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getSubscriberId();
+ return phoneSubInfoProxy.getSubscriberId(callingPackage);
} else {
Rlog.e(TAG,"getSubscriberId phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -114,14 +154,14 @@
/**
* Retrieves the serial number of the ICC, if applicable.
*/
- public String getIccSerialNumber() {
- return getIccSerialNumberForSubscriber(getDefaultSubscription());
+ public String getIccSerialNumber(String callingPackage) {
+ return getIccSerialNumberForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getIccSerialNumberForSubscriber(int subId) {
+ public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getIccSerialNumber();
+ return phoneSubInfoProxy.getIccSerialNumber(callingPackage);
} else {
Rlog.e(TAG,"getIccSerialNumber phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -129,14 +169,14 @@
}
}
- public String getLine1Number() {
- return getLine1NumberForSubscriber(getDefaultSubscription());
+ public String getLine1Number(String callingPackage) {
+ return getLine1NumberForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getLine1NumberForSubscriber(int subId) {
+ public String getLine1NumberForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getLine1Number();
+ return phoneSubInfoProxy.getLine1Number(callingPackage);
} else {
Rlog.e(TAG,"getLine1Number phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -144,14 +184,14 @@
}
}
- public String getLine1AlphaTag() {
- return getLine1AlphaTagForSubscriber(getDefaultSubscription());
+ public String getLine1AlphaTag(String callingPackage) {
+ return getLine1AlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getLine1AlphaTagForSubscriber(int subId) {
+ public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getLine1AlphaTag();
+ return phoneSubInfoProxy.getLine1AlphaTag(callingPackage);
} else {
Rlog.e(TAG,"getLine1AlphaTag phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -159,14 +199,14 @@
}
}
- public String getMsisdn() {
- return getMsisdnForSubscriber(getDefaultSubscription());
+ public String getMsisdn(String callingPackage) {
+ return getMsisdnForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getMsisdnForSubscriber(int subId) {
+ public String getMsisdnForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getMsisdn();
+ return phoneSubInfoProxy.getMsisdn(callingPackage);
} else {
Rlog.e(TAG,"getMsisdn phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -174,14 +214,14 @@
}
}
- public String getVoiceMailNumber() {
- return getVoiceMailNumberForSubscriber(getDefaultSubscription());
+ public String getVoiceMailNumber(String callingPackage) {
+ return getVoiceMailNumberForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getVoiceMailNumberForSubscriber(int subId) {
+ public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getVoiceMailNumber();
+ return phoneSubInfoProxy.getVoiceMailNumber(callingPackage);
} else {
Rlog.e(TAG,"getVoiceMailNumber phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -204,14 +244,14 @@
}
}
- public String getVoiceMailAlphaTag() {
- return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription());
+ public String getVoiceMailAlphaTag(String callingPackage) {
+ return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getVoiceMailAlphaTagForSubscriber(int subId) {
+ public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getVoiceMailAlphaTag();
+ return phoneSubInfoProxy.getVoiceMailAlphaTag(callingPackage);
} else {
Rlog.e(TAG,"getVoiceMailAlphaTag phoneSubInfoProxy is" +
" null for Subscription:" + subId);
@@ -283,14 +323,14 @@
return phoneSubInfoProxy.getIccSimChallengeResponse(subId, appType, data);
}
- public String getGroupIdLevel1() {
- return getGroupIdLevel1ForSubscriber(getDefaultSubscription());
+ public String getGroupIdLevel1(String callingPackage) {
+ return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
}
- public String getGroupIdLevel1ForSubscriber(int subId) {
+ public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
if (phoneSubInfoProxy != null) {
- return phoneSubInfoProxy.getGroupIdLevel1();
+ return phoneSubInfoProxy.getGroupIdLevel1(callingPackage);
} else {
Rlog.e(TAG,"getGroupIdLevel1 phoneSubInfoProxy is" +
" null for Subscription:" + subId);
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index cedc0dc..92a1ab0 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -35,76 +35,76 @@
}
@Override
- public String getDeviceId() {
- return mPhoneSubInfo.getDeviceId();
+ public String getDeviceId(String callingPackage) {
+ return mPhoneSubInfo.getDeviceId(callingPackage);
}
- public String getImei() {
- return mPhoneSubInfo.getImei();
+ public String getImei(String callingPackage) {
+ return mPhoneSubInfo.getImei(callingPackage);
}
- public String getNai() {
- return mPhoneSubInfo.getNai();
+ public String getNai(String callingPackage) {
+ return mPhoneSubInfo.getNai(callingPackage);
}
@Override
- public String getDeviceSvn() {
- return mPhoneSubInfo.getDeviceSvn();
+ public String getDeviceSvn(String callingPackage) {
+ return mPhoneSubInfo.getDeviceSvn(callingPackage);
}
/**
* Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
*/
@Override
- public String getSubscriberId() {
- return mPhoneSubInfo.getSubscriberId();
+ public String getSubscriberId(String callingPackage) {
+ return mPhoneSubInfo.getSubscriberId(callingPackage);
}
/**
* Retrieves the Group Identifier Level1 for GSM phones.
*/
- public String getGroupIdLevel1() {
- return mPhoneSubInfo.getGroupIdLevel1();
+ public String getGroupIdLevel1(String callingPackage) {
+ return mPhoneSubInfo.getGroupIdLevel1(callingPackage);
}
/**
* Retrieves the serial number of the ICC, if applicable.
*/
@Override
- public String getIccSerialNumber() {
- return mPhoneSubInfo.getIccSerialNumber();
+ public String getIccSerialNumber(String callingPackage) {
+ return mPhoneSubInfo.getIccSerialNumber(callingPackage);
}
/**
* Retrieves the phone number string for line 1.
*/
@Override
- public String getLine1Number() {
- return mPhoneSubInfo.getLine1Number();
+ public String getLine1Number(String callingPackage) {
+ return mPhoneSubInfo.getLine1Number(callingPackage);
}
/**
* Retrieves the alpha identifier for line 1.
*/
@Override
- public String getLine1AlphaTag() {
- return mPhoneSubInfo.getLine1AlphaTag();
+ public String getLine1AlphaTag(String callingPackage) {
+ return mPhoneSubInfo.getLine1AlphaTag(callingPackage);
}
/**
* Retrieves the MSISDN Number.
*/
@Override
- public String getMsisdn() {
- return mPhoneSubInfo.getMsisdn();
+ public String getMsisdn(String callingPackage) {
+ return mPhoneSubInfo.getMsisdn(callingPackage);
}
/**
* Retrieves the voice mail number.
*/
@Override
- public String getVoiceMailNumber() {
- return mPhoneSubInfo.getVoiceMailNumber();
+ public String getVoiceMailNumber(String callingPackage) {
+ return mPhoneSubInfo.getVoiceMailNumber(callingPackage);
}
/**
@@ -119,8 +119,8 @@
* Retrieves the alpha identifier associated with the voice mail number.
*/
@Override
- public String getVoiceMailAlphaTag() {
- return mPhoneSubInfo.getVoiceMailAlphaTag();
+ public String getVoiceMailAlphaTag(String callingPackage) {
+ return mPhoneSubInfo.getVoiceMailAlphaTag(callingPackage);
}
/**
@@ -152,67 +152,70 @@
}
@Override
- public String getDeviceIdForPhone(int phoneId) throws RemoteException {
+ public String getDeviceIdForPhone(int phoneId, String callingPackage) throws RemoteException {
// FIXME: getDeviceIdForPhone
return null;
}
@Override
- public String getImeiForSubscriber(int subId) throws RemoteException {
+ public String getImeiForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getImeiForSubscriber
return null;
}
@Override
- public String getDeviceSvnUsingSubId(int subId) throws RemoteException {
+ public String getDeviceSvnUsingSubId(int subId, String callingPackage) throws RemoteException {
// FIXME: getDeviceSvnUsingSubId
return null;
}
@Override
- public String getNaiForSubscriber(int subId) throws RemoteException {
+ public String getNaiForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: NaiForSubscriber
return null;
}
@Override
- public String getSubscriberIdForSubscriber(int subId) throws RemoteException {
+ public String getSubscriberIdForSubscriber(int subId, String callingPackage)
+ throws RemoteException {
// FIXME: getSubscriberIdForSubscriber
return null;
}
@Override
- public String getGroupIdLevel1ForSubscriber(int subId) throws RemoteException {
+ public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage)
+ throws RemoteException {
// FIXME: getGroupIdLevel1ForSubscriber
return null;
}
@Override
- public String getIccSerialNumberForSubscriber(int subId) throws RemoteException {
+ public String getIccSerialNumberForSubscriber(int subId, String callingPackage)
+ throws RemoteException {
// FIXME: getIccSerialNumberForSubscriber
return null;
}
@Override
- public String getLine1NumberForSubscriber(int subId) throws RemoteException {
+ public String getLine1NumberForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getLine1NumberForSubscriber
return null;
}
@Override
- public String getLine1AlphaTagForSubscriber(int subId) throws RemoteException {
+ public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getLine1AlphaTagForSubscriber
return null;
}
@Override
- public String getMsisdnForSubscriber(int subId) throws RemoteException {
+ public String getMsisdnForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getMsisdnForSubscriber
return null;
}
@Override
- public String getVoiceMailNumberForSubscriber(int subId) throws RemoteException {
+ public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getVoiceMailNumberForSubscriber
return null;
}
@@ -224,7 +227,7 @@
}
@Override
- public String getVoiceMailAlphaTagForSubscriber(int subId) throws RemoteException {
+ public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) throws RemoteException {
// FIXME: getVoiceMailAlphaTagForSubscriber
return null;
}
@@ -253,7 +256,7 @@
* Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
* @return the response of ISIM Authetification, or null if not available
* @deprecated
- * @see getIccSimChallengeResponse
+ * @see #getIccSimChallengeResponse
*/
public String getIsimChallengeResponse(String nonce) {
return mPhoneSubInfo.getIsimChallengeResponse(nonce);
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index aa4976a..0b84800 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -29,6 +29,7 @@
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
+import android.util.Log;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Phone;
@@ -42,6 +43,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashSet;
public class ProxyController {
static final String LOG_TAG = "ProxyController";
@@ -50,6 +52,7 @@
private static final int EVENT_START_RC_RESPONSE = 2;
private static final int EVENT_APPLY_RC_RESPONSE = 3;
private static final int EVENT_FINISH_RC_RESPONSE = 4;
+ private static final int EVENT_TIMEOUT = 5;
private static final int SET_RC_STATUS_IDLE = 0;
private static final int SET_RC_STATUS_STARTING = 1;
@@ -90,8 +93,10 @@
// record each phone's set radio capability status
private int[] mSetRadioAccessFamilyStatus;
private int mRadioAccessFamilyStatusCounter;
+ private boolean mTransactionFailed = false;
- private String[] mLogicalModemIds;
+ private String[] mCurrentLogicalModemIds;
+ private String[] mNewLogicalModemIds;
// Allows the generation of unique Id's for radio capability request session id
private AtomicInteger mUniqueIdGenerator = new AtomicInteger(new Random().nextInt());
@@ -105,8 +110,6 @@
private int[] mNewRadioAccessFamily;
private int[] mOldRadioAccessFamily;
- // runnable for radio capability request timeout handling
- RadioCapabilityRunnable mSetRadioCapabilityRunnable;
//***** Class Methods
public static ProxyController getInstance(Context context, PhoneProxy[] phoneProxy,
@@ -137,14 +140,8 @@
mSetRadioAccessFamilyStatus = new int[mProxyPhones.length];
mNewRadioAccessFamily = new int[mProxyPhones.length];
mOldRadioAccessFamily = new int[mProxyPhones.length];
- mLogicalModemIds = new String[mProxyPhones.length];
-
- // TODO Get logical modem ids assume its just the phoneId as a string for now
- for (int i = 0; i < mProxyPhones.length; i++) {
- mLogicalModemIds[i] = Integer.toString(i);
- }
-
- mSetRadioCapabilityRunnable = new RadioCapabilityRunnable();
+ mCurrentLogicalModemIds = new String[mProxyPhones.length];
+ mNewLogicalModemIds = new String[mProxyPhones.length];
// wake lock for set radio capability
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -200,7 +197,8 @@
Phone activePhone = mProxyPhones[phoneId].getActivePhone();
return ((PhoneBase) activePhone).mDcTracker.isDisconnected();
} else {
- return false;
+ // if we can't find a phone for the given subId, it is disconnected.
+ return true;
}
}
@@ -230,13 +228,10 @@
if (rafs.length != mProxyPhones.length) {
throw new RuntimeException("Length of input rafs must equal to total phone count");
}
-
// Check if there is any ongoing transaction and throw an exception if there
// is one as this is a programming error.
synchronized (mSetRadioAccessFamilyStatus) {
for (int i = 0; i < mProxyPhones.length; i++) {
- logd("setRadioCapability: mSetRadioAccessFamilyStatus[" + i + "]="
- + mSetRadioAccessFamilyStatus[i]);
if (mSetRadioAccessFamilyStatus[i] != SET_RC_STATUS_IDLE) {
// TODO: The right behaviour is to cancel previous request and send this.
loge("setRadioCapability: Phone[" + i + "] is not idle. Rejecting request.");
@@ -245,23 +240,41 @@
}
}
+ // Check we actually need to do anything
+ boolean same = true;
+ for (int i = 0; i < mProxyPhones.length; i++) {
+ if (mProxyPhones[i].getRadioAccessFamily() != rafs[i].getRadioAccessFamily()) {
+ same = false;
+ }
+ }
+ if (same) {
+ // All phones are already set to the requested raf
+ logd("setRadioCapability: Already in requested configuration, nothing to do.");
+ // It isn't really an error, so return true - everything is OK.
+ return true;
+ }
+
// Clear to be sure we're in the initial state
clearTransaction();
- // A new sessionId for this transaction
- mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
-
// Keep a wake lock until we finish radio capability changed
mWakeLock.acquire();
+ return doSetRadioCapabilities(rafs);
+ }
+
+ private boolean doSetRadioCapabilities(RadioAccessFamily[] rafs) {
+ // A new sessionId for this transaction
+ mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
// Start timer to make sure all phones respond within a specific time interval.
// Will send FINISH if a timeout occurs.
- mSetRadioCapabilityRunnable.setTimeoutState(mRadioCapabilitySessionId);
- mHandler.postDelayed(mSetRadioCapabilityRunnable, SET_RC_TIMEOUT_WAITING_MSEC);
+ Message msg = mHandler.obtainMessage(EVENT_TIMEOUT, mRadioCapabilitySessionId, 0);
+ mHandler.sendMessageDelayed(msg, SET_RC_TIMEOUT_WAITING_MSEC);
synchronized (mSetRadioAccessFamilyStatus) {
logd("setRadioCapability: new request session id=" + mRadioCapabilitySessionId);
- mRadioAccessFamilyStatusCounter = rafs.length;
+ resetRadioAccessFamilyStatusCounter();
for (int i = 0; i < rafs.length; i++) {
int phoneId = rafs[i].getPhoneId();
logd("setRadioCapability: phoneId=" + phoneId + " status=STARTING");
@@ -269,9 +282,14 @@
mOldRadioAccessFamily[phoneId] = mProxyPhones[phoneId].getRadioAccessFamily();
int requestedRaf = rafs[i].getRadioAccessFamily();
// TODO Set the new radio access family to the maximum of the requested & supported
- // int supportedRaf = mProxyPhones[i].getSupportedRadioAccessFamily();
+ // int supportedRaf = mProxyPhones[i].getRadioAccessFamily();
// mNewRadioAccessFamily[phoneId] = requestedRaf & supportedRaf;
mNewRadioAccessFamily[phoneId] = requestedRaf;
+
+ mCurrentLogicalModemIds[phoneId] = mProxyPhones[phoneId].getModemUuId();
+ // get the logical mode corresponds to new raf requested and pass the
+ // same as part of SET_RADIO_CAP APPLY phase
+ mNewLogicalModemIds[phoneId] = getLogicalModemIdFromRaf(requestedRaf);
logd("setRadioCapability: mOldRadioAccessFamily[" + phoneId + "]="
+ mOldRadioAccessFamily[phoneId]);
logd("setRadioCapability: mNewRadioAccessFamily[" + phoneId + "]="
@@ -281,7 +299,7 @@
mRadioCapabilitySessionId,
RadioCapability.RC_PHASE_START,
mOldRadioAccessFamily[phoneId],
- mLogicalModemIds[phoneId],
+ mCurrentLogicalModemIds[phoneId],
RadioCapability.RC_STATUS_NONE,
EVENT_START_RC_RESPONSE);
}
@@ -311,6 +329,10 @@
onFinishRadioCapabilityResponse(msg);
break;
+ case EVENT_TIMEOUT:
+ onTimeoutRadioCapability(msg);
+ break;
+
default:
break;
}
@@ -323,6 +345,16 @@
*/
private void onStartRadioCapabilityResponse(Message msg) {
synchronized (mSetRadioAccessFamilyStatus) {
+ AsyncResult ar = (AsyncResult)msg.obj;
+ if (ar.exception != null) {
+ // just abort now. They didn't take our start so we don't have to revert
+ logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
+ mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+ Intent intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
+ mContext.sendBroadcast(intent);
+ clearTransaction();
+ return;
+ }
RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
@@ -335,27 +367,35 @@
logd("onStartRadioCapabilityResponse: Error response session=" + rc.getSession());
logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+ mTransactionFailed = true;
} else {
logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED;
}
if (mRadioAccessFamilyStatusCounter == 0) {
- resetRadioAccessFamilyStatusCounter();
- boolean success = checkAllRadioCapabilitySuccess();
- logd("onStartRadioCapabilityResponse: success=" + success);
- if (!success) {
- issueFinish(RadioCapability.RC_STATUS_FAIL,
- mRadioCapabilitySessionId);
+ HashSet<String> modemsInUse = new HashSet<String>(mNewLogicalModemIds.length);
+ for (String modemId : mNewLogicalModemIds) {
+ if (!modemsInUse.add(modemId)) {
+ mTransactionFailed = true;
+ Log.wtf(LOG_TAG, "ERROR: sending down the same id for different phones");
+ }
+ }
+ logd("onStartRadioCapabilityResponse: success=" + !mTransactionFailed);
+ if (mTransactionFailed) {
+ // Sends a variable number of requests, so don't resetRadioAccessFamilyCounter
+ // here.
+ issueFinish(mRadioCapabilitySessionId);
} else {
// All logical modem accepted the new radio access family, issue the APPLY
+ resetRadioAccessFamilyStatusCounter();
for (int i = 0; i < mProxyPhones.length; i++) {
sendRadioCapabilityRequest(
i,
mRadioCapabilitySessionId,
RadioCapability.RC_PHASE_APPLY,
mNewRadioAccessFamily[i],
- mLogicalModemIds[i],
+ mNewLogicalModemIds[i],
RadioCapability.RC_STATUS_NONE,
EVENT_APPLY_RC_RESPONSE);
@@ -385,6 +425,7 @@
int id = rc.getPhoneId();
logd("onApplyRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+ mTransactionFailed = true;
}
} else {
logd("onApplyRadioCapabilityResponse: Valid start expecting notification rc=" + rc);
@@ -416,25 +457,20 @@
(rc.getStatus() == RadioCapability.RC_STATUS_FAIL)) {
logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=FAIL");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+ mTransactionFailed = true;
} else {
logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=SUCCESS");
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS;
+ // The modems may have been restarted and forgotten this
+ mDctController.retryAttach(id);
+ mProxyPhones[id].radioCapabilityUpdated(rc);
}
mRadioAccessFamilyStatusCounter--;
if (mRadioAccessFamilyStatusCounter == 0) {
- logd("onNotificationRadioCapabilityChanged: removing callback from handler");
- mHandler.removeCallbacks(mSetRadioCapabilityRunnable);
- resetRadioAccessFamilyStatusCounter();
- boolean success = checkAllRadioCapabilitySuccess();
- logd("onNotificationRadioCapabilityChanged: APPLY URC success=" + success);
- int status;
- if (success) {
- status = RadioCapability.RC_STATUS_SUCCESS;
- } else {
- status = RadioCapability.RC_STATUS_FAIL;
- }
- issueFinish(status, mRadioCapabilitySessionId);
+ logd("onNotificationRadioCapabilityChanged: APPLY URC success=" +
+ mTransactionFailed);
+ issueFinish(mRadioCapabilitySessionId);
}
}
}
@@ -460,29 +496,49 @@
}
}
- private void issueFinish(int status, int sessionId) {
+ private void onTimeoutRadioCapability(Message msg) {
+ if (msg.arg1 != mRadioCapabilitySessionId) {
+ logd("RadioCapability timeout: Ignore msg.arg1=" + msg.arg1 +
+ "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
+ return;
+ }
+
+ synchronized(mSetRadioAccessFamilyStatus) {
+ // timed-out. Clean up as best we can
+ for (int i = 0; i < mProxyPhones.length; i++) {
+ logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
+ mSetRadioAccessFamilyStatus[i]);
+ }
+
+ // Increment the sessionId as we are completing the transaction below
+ // so we don't want it completed when the FINISH phase is done.
+ int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
+ // send FINISH request with fail status and then uniqueDifferentId
+ mTransactionFailed = true;
+ issueFinish(uniqueDifferentId);
+ }
+ }
+
+ private void issueFinish(int sessionId) {
// Issue FINISH
synchronized(mSetRadioAccessFamilyStatus) {
for (int i = 0; i < mProxyPhones.length; i++) {
- if (mSetRadioAccessFamilyStatus[i] != SET_RC_STATUS_FAIL) {
- logd("issueFinish: phoneId=" + i + " sessionId=" + sessionId
- + " status=" + status);
- sendRadioCapabilityRequest(
+ logd("issueFinish: phoneId=" + i + " sessionId=" + sessionId
+ + " mTransactionFailed=" + mTransactionFailed);
+ mRadioAccessFamilyStatusCounter++;
+ sendRadioCapabilityRequest(
i,
sessionId,
RadioCapability.RC_PHASE_FINISH,
mOldRadioAccessFamily[i],
- mLogicalModemIds[i],
- status,
+ mCurrentLogicalModemIds[i],
+ (mTransactionFailed ? RadioCapability.RC_STATUS_FAIL :
+ RadioCapability.RC_STATUS_SUCCESS),
EVENT_FINISH_RC_RESPONSE);
- if (status == RadioCapability.RC_STATUS_FAIL) {
- logd("issueFinish: phoneId: " + i + " status: FAIL");
- // At least one failed, mark them all failed.
- mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_FAIL;
- }
- } else {
- logd("issueFinish: Ignore already FAIL, Phone" + i + " sessionId=" + sessionId
- + " status=" + status);
+ if (mTransactionFailed) {
+ logd("issueFinish: phoneId: " + i + " status: FAIL");
+ // At least one failed, mark them all failed.
+ mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_FAIL;
}
}
}
@@ -491,9 +547,8 @@
private void completeRadioCapabilityTransaction() {
// Create the intent to broadcast
Intent intent;
- boolean success = checkAllRadioCapabilitySuccess();
- logd("onFinishRadioCapabilityResponse: success=" + success);
- if (success) {
+ logd("onFinishRadioCapabilityResponse: success=" + !mTransactionFailed);
+ if (!mTransactionFailed) {
ArrayList<RadioAccessFamily> phoneRAFList = new ArrayList<RadioAccessFamily>();
for (int i = 0; i < mProxyPhones.length; i++) {
int raf = mProxyPhones[i].getRadioAccessFamily();
@@ -504,12 +559,23 @@
intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE);
intent.putParcelableArrayListExtra(TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY,
phoneRAFList);
+
+ // make messages about the old transaction obsolete (specifically the timeout)
+ mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
+ // Reinitialize
+ clearTransaction();
} else {
intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
- }
- // Reinitialize
- clearTransaction();
+ // now revert.
+ mTransactionFailed = false;
+ RadioAccessFamily[] rafs = new RadioAccessFamily[mProxyPhones.length];
+ for (int phoneId = 0; phoneId < mProxyPhones.length; phoneId++) {
+ rafs[phoneId] = new RadioAccessFamily(phoneId, mOldRadioAccessFamily[phoneId]);
+ }
+ doSetRadioCapabilities(rafs);
+ }
// Broadcast that we're done
mContext.sendBroadcast(intent);
@@ -524,6 +590,7 @@
mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_IDLE;
mOldRadioAccessFamily[i] = 0;
mNewRadioAccessFamily[i] = 0;
+ mTransactionFailed = false;
}
if (mWakeLock.isHeld()) {
@@ -532,17 +599,6 @@
}
}
- private boolean checkAllRadioCapabilitySuccess() {
- synchronized(mSetRadioAccessFamilyStatus) {
- for (int i = 0; i < mProxyPhones.length; i++) {
- if (mSetRadioAccessFamilyStatus[i] == SET_RC_STATUS_FAIL) {
- return false;
- }
- }
- return true;
- }
- }
-
private void resetRadioAccessFamilyStatusCounter() {
mRadioAccessFamilyStatusCounter = mProxyPhones.length;
}
@@ -555,47 +611,53 @@
requestRC, mHandler.obtainMessage(eventId));
}
- /**
- * RadioCapabilityRunnable is used to check
- * if radio capability request's response is out of date.
- * <p>
- * Note that the setRadioCapability will be stopped directly and send FINISH
- * with fail status to all logical modems. and send out fail intent
- *
- */
- private class RadioCapabilityRunnable implements Runnable {
- private int mSessionId;
- public RadioCapabilityRunnable() {
- }
+ // This method will return max number of raf bits supported from the raf
+ // values currently stored in all phone objects
+ public int getMaxRafSupported() {
+ int[] numRafSupported = new int[mProxyPhones.length];
+ int maxNumRafBit = 0;
+ int maxRaf = RadioAccessFamily.RAF_UNKNOWN;
- public void setTimeoutState(int sessionId) {
- mSessionId = sessionId;
- }
-
- @Override
- public void run() {
- if (mSessionId != mRadioCapabilitySessionId) {
- logd("RadioCapability timeout: Ignore mSessionId=" + mSessionId
- + "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
- return;
- }
-
- synchronized(mSetRadioAccessFamilyStatus) {
- for (int i = 0; i < mProxyPhones.length; i++) {
- logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
- mSetRadioAccessFamilyStatus[i]);
- }
-
- // Increment the sessionId as we are completing the transaction below
- // so we don't want it completed when the FINISH phase is done.
- int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
-
- // send FINISH request with fail status and then uniqueDifferentId
- issueFinish(RadioCapability.RC_STATUS_FAIL,
- uniqueDifferentId);
- completeRadioCapabilityTransaction();
+ for (int len = 0; len < mProxyPhones.length; len++) {
+ numRafSupported[len] = Integer.bitCount(mProxyPhones[len].getRadioAccessFamily());
+ if (maxNumRafBit < numRafSupported[len]) {
+ maxNumRafBit = numRafSupported[len];
+ maxRaf = mProxyPhones[len].getRadioAccessFamily();
}
}
+
+ return maxRaf;
+ }
+
+ // This method will return minimum number of raf bits supported from the raf
+ // values currently stored in all phone objects
+ public int getMinRafSupported() {
+ int[] numRafSupported = new int[mProxyPhones.length];
+ int minNumRafBit = 0;
+ int minRaf = RadioAccessFamily.RAF_UNKNOWN;
+
+ for (int len = 0; len < mProxyPhones.length; len++) {
+ numRafSupported[len] = Integer.bitCount(mProxyPhones[len].getRadioAccessFamily());
+ if ((minNumRafBit == 0) || (minNumRafBit > numRafSupported[len])) {
+ minNumRafBit = numRafSupported[len];
+ minRaf = mProxyPhones[len].getRadioAccessFamily();
+ }
+ }
+ return minRaf;
+ }
+
+ // This method checks current raf values stored in all phones and
+ // whicheve phone raf matches with input raf, returns modemId from that phone
+ private String getLogicalModemIdFromRaf(int raf) {
+ String modemUuid = null;
+
+ for (int phoneId = 0; phoneId < mProxyPhones.length; phoneId++) {
+ if (mProxyPhones[phoneId].getRadioAccessFamily() == raf) {
+ modemUuid = mProxyPhones[phoneId].getModemUuId();
+ break;
+ }
+ }
+ return modemUuid;
}
private void logd(String string) {
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index fad1228..d834eef 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -40,18 +40,22 @@
import android.os.Message;
import android.os.Parcel;
import android.os.PowerManager;
+import android.os.BatteryManager;
import android.os.SystemProperties;
import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
import android.provider.Settings.SettingNotFoundException;
import android.telephony.CellInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneNumberUtils;
+import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ModemActivityInfo;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.Display;
@@ -81,6 +85,7 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -227,6 +232,10 @@
static final String RILJ_LOG_TAG = "RILJ";
static final boolean RILJ_LOGD = true;
static final boolean RILJ_LOGV = false; // STOPSHIP if true
+ static final int RADIO_SCREEN_UNSET = -1;
+ static final int RADIO_SCREEN_OFF = 0;
+ static final int RADIO_SCREEN_ON = 1;
+
/**
* Wake lock timeout should be longer than the longest timeout in
@@ -243,6 +252,8 @@
RILReceiver mReceiver;
Display mDefaultDisplay;
int mDefaultDisplayState = Display.STATE_UNKNOWN;
+ int mRadioScreenState = RADIO_SCREEN_UNSET;
+ boolean mIsDevicePlugged = false;
WakeLock mWakeLock;
final int mWakeLockTimeout;
// The number of wakelock requests currently active. Don't release the lock
@@ -291,6 +302,22 @@
@Override
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
+ final int oldState = mDefaultDisplayState;
+ mDefaultDisplayState = mDefaultDisplay.getState();
+ if (mDefaultDisplayState != oldState) {
+ updateScreenState();
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBatteryStateListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean oldState = mIsDevicePlugged;
+ // 0 means it's on battery
+ mIsDevicePlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ if (mIsDevicePlugged != oldState) {
updateScreenState();
}
}
@@ -641,6 +668,13 @@
Context.DISPLAY_SERVICE);
mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
dm.registerDisplayListener(mDisplayListener, null);
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = context.registerReceiver(mBatteryStateListener, filter);
+ if (batteryStatus != null) {
+ // 0 means it's on battery
+ mIsDevicePlugged = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ }
}
TelephonyDevController tdc = TelephonyDevController.getInstance();
@@ -712,7 +746,10 @@
// FIXME This API should take an AID and slot ID
public void setDataAllowed(boolean allowed, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_ALLOW_DATA, result);
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) +
+ " allowed: " + allowed);
+ }
rr.mParcel.writeInt(1);
rr.mParcel.writeInt(allowed ? 1 : 0);
@@ -2213,17 +2250,20 @@
// message should be deleted and replaced with more precise messages to control
// behavior such as signal strength reporting or power managements based on
// more robust signals.
+ /**
+ * Update the screen state. Send screen state ON if the default display is ON or the device
+ * is plugged.
+ */
private void updateScreenState() {
- final int oldState = mDefaultDisplayState;
- mDefaultDisplayState = mDefaultDisplay.getState();
- if (mDefaultDisplayState != oldState) {
- if (oldState != Display.STATE_ON
- && mDefaultDisplayState == Display.STATE_ON) {
- sendScreenState(true);
- } else if ((oldState == Display.STATE_ON || oldState == Display.STATE_UNKNOWN)
- && mDefaultDisplayState != Display.STATE_ON) {
- sendScreenState(false);
+ final int oldState = mRadioScreenState;
+ mRadioScreenState = (mDefaultDisplayState == Display.STATE_ON || mIsDevicePlugged)
+ ? RADIO_SCREEN_ON : RADIO_SCREEN_OFF;
+ if (mRadioScreenState != oldState) {
+ if (RILJ_LOGV) {
+ riljLog("defaultDisplayState: " + mDefaultDisplayState
+ + ", isDevicePlugged: " + mIsDevicePlugged);
}
+ sendScreenState(mRadioScreenState == RADIO_SCREEN_ON);
}
}
@@ -2442,7 +2482,7 @@
case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret = responseVoid(p); break;
case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break;
case RIL_REQUEST_UDUB: ret = responseVoid(p); break;
- case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break;
+ case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseFailCause(p); break;
case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break;
case RIL_REQUEST_VOICE_REGISTRATION_STATE: ret = responseStrings(p); break;
case RIL_REQUEST_DATA_REGISTRATION_STATE: ret = responseStrings(p); break;
@@ -2554,6 +2594,10 @@
case RIL_REQUEST_SHUTDOWN: ret = responseVoid(p); break;
case RIL_REQUEST_GET_RADIO_CAPABILITY: ret = responseRadioCapability(p); break;
case RIL_REQUEST_SET_RADIO_CAPABILITY: ret = responseRadioCapability(p); break;
+ case RIL_REQUEST_START_LCE: ret = responseLceStatus(p); break;
+ case RIL_REQUEST_STOP_LCE: ret = responseLceStatus(p); break;
+ case RIL_REQUEST_PULL_LCEDATA: ret = responseLceData(p); break;
+ case RIL_REQUEST_GET_ACTIVITY_INFO: ret = responseActivityData(p); break;
default:
throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
//break;
@@ -2611,10 +2655,29 @@
mIccStatusChangedRegistrants.notifyRegistrants();
}
break;
+ case RIL_REQUEST_GET_RADIO_CAPABILITY: {
+ // Ideally RIL's would support this or at least give NOT_SUPPORTED
+ // but the hammerhead RIL reports GENERIC :(
+ // TODO - remove GENERIC_FAILURE catching: b/21079604
+ if (REQUEST_NOT_SUPPORTED == error ||
+ GENERIC_FAILURE == error) {
+ // we should construct the RAF bitmask the radio
+ // supports based on preferred network bitmasks
+ ret = makeStaticRadioCapability();
+ error = 0;
+ }
+ break;
+ }
+ case RIL_REQUEST_GET_ACTIVITY_INFO:
+ ret = new ModemActivityInfo(0, 0, 0,
+ new int [ModemActivityInfo.TX_POWER_LEVELS], 0, 0);
+ error = 0;
+ break;
}
- rr.onError(error, ret);
- } else {
+ if (error != 0) rr.onError(error, ret);
+ }
+ if (error == 0) {
if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+ " " + retToString(rr.mRequest, ret));
@@ -2627,6 +2690,21 @@
return rr;
}
+ private RadioCapability makeStaticRadioCapability() {
+ // default to UNKNOWN so we fail fast.
+ int raf = RadioAccessFamily.RAF_UNKNOWN;
+
+ String rafString = mContext.getResources().getString(
+ com.android.internal.R.string.config_radio_access_family);
+ if (TextUtils.isEmpty(rafString) == false) {
+ raf = RadioAccessFamily.rafTypeFromString(rafString);
+ }
+ RadioCapability rc = new RadioCapability(mInstanceId.intValue(), 0, 0, raf,
+ "", RadioCapability.RC_STATUS_SUCCESS);
+ if (RILJ_LOGD) riljLog("Faking RIL_REQUEST_GET_RADIO_CAPABILITY response using " + raf);
+ return rc;
+ }
+
static String
retToString(int req, Object ret) {
if (ret == null) return "";
@@ -2769,6 +2847,7 @@
ret = responseRadioCapability(p); break;
case RIL_UNSOL_ON_SS: ret = responseSsData(p); break;
case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: ret = responseString(p); break;
+ case RIL_UNSOL_LCEDATA_RECV: ret = responseLceData(p); break;
default:
throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -3062,7 +3141,7 @@
break;
case RIL_UNSOL_OEM_HOOK_RAW:
- if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
+ if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[]) ret));
if (mUnsolOemHookRawRegistrant != null) {
mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
}
@@ -3125,11 +3204,8 @@
case RIL_UNSOL_RIL_CONNECTED: {
if (RILJ_LOGD) unsljLogRet(response, ret);
- getRadioCapability(mSupportedRafHandler.obtainMessage());
-
// Initial conditions
setRadioPower(false, null);
- setPreferredNetworkType(mPreferredNetworkType, null);
setCdmaSubscriptionSource(mCdmaSubscription, null);
setCellInfoListRate(Integer.MAX_VALUE, null);
notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
@@ -3194,27 +3270,17 @@
new AsyncResult (null, ret, null));
}
break;
+ case RIL_UNSOL_LCEDATA_RECV:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mLceInfoRegistrant != null) {
+ mLceInfoRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
+ }
+ break;
}
}
/**
- * Receives and stores the capabilities supported by the modem.
- */
- private Handler mSupportedRafHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- AsyncResult ar = (AsyncResult) msg.obj;
- RadioCapability rc = (RadioCapability) ar.result;
- if (ar.exception != null) {
- if (RILJ_LOGD) riljLog("Get supported radio access family fail");
- } else {
- mSupportedRaf = rc.getRadioAccessFamily();
- if (RILJ_LOGD) riljLog("Supported radio access family=" + mSupportedRaf);
- }
- }
- };
-
- /**
* Notifiy all registrants that the ril has connected or disconnected.
*
* @param rilVer is the version of the ril or -1 if disconnected.
@@ -3243,6 +3309,15 @@
return response;
}
+ private Object
+ responseFailCause(Parcel p) {
+ LastCallFailCause failCause = new LastCallFailCause();
+ failCause.causeCode = p.readInt();
+ if (p.dataAvail() > 0) {
+ failCause.vendorCause = p.readString();
+ }
+ return failCause;
+ }
private Object
responseVoid(Parcel p) {
@@ -3370,8 +3445,8 @@
+ " 0x" + Integer.toHexString(sw2) + " "
+ s);
-
- return new IccIoResult(sw1, sw2, android.util.Base64.decode(s, android.util.Base64.DEFAULT));
+ return new IccIoResult(sw1, sw2, (s != null)
+ ? android.util.Base64.decode(s, android.util.Base64.DEFAULT) : (byte[]) null);
}
private Object
@@ -3922,6 +3997,55 @@
return rc;
}
+ private Object responseLceData(Parcel p) {
+ final ArrayList<Integer> capacityResponse = new ArrayList<Integer>();
+ final int capacityDownKbps = p.readInt();
+ final int confidenceLevel = p.readByte();
+ final int lceSuspended = p.readByte();
+
+ riljLog("LCE capacity information received:" +
+ " capacity=" + capacityDownKbps +
+ " confidence=" + confidenceLevel +
+ " lceSuspended=" + lceSuspended);
+
+ capacityResponse.add(capacityDownKbps);
+ capacityResponse.add(confidenceLevel);
+ capacityResponse.add(lceSuspended);
+ return capacityResponse;
+ }
+
+ private Object responseLceStatus(Parcel p) {
+ final ArrayList<Integer> statusResponse = new ArrayList<Integer>();
+ final int lceStatus = (int)p.readByte();
+ final int actualInterval = p.readInt();
+
+ riljLog("LCE status information received:" +
+ " lceStatus=" + lceStatus +
+ " actualInterval=" + actualInterval);
+ statusResponse.add(lceStatus);
+ statusResponse.add(actualInterval);
+ return statusResponse;
+ }
+
+ private Object responseActivityData(Parcel p) {
+ final int sleepModeTimeMs = p.readInt();
+ final int idleModeTimeMs = p.readInt();
+ int [] txModeTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ txModeTimeMs[i] = p.readInt();
+ }
+ final int rxModeTimeMs = p.readInt();
+
+ riljLog("Modem activity info received:" +
+ " sleepModeTimeMs=" + sleepModeTimeMs +
+ " idleModeTimeMs=" + idleModeTimeMs +
+ " txModeTimeMs[]=" + Arrays.toString(txModeTimeMs) +
+ " rxModeTimeMs=" + rxModeTimeMs);
+
+ return new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
+ idleModeTimeMs, txModeTimeMs, rxModeTimeMs, 0);
+ }
+
static String
requestToString(int request) {
/*
@@ -4061,6 +4185,10 @@
return "RIL_REQUEST_SET_RADIO_CAPABILITY";
case RIL_REQUEST_GET_RADIO_CAPABILITY:
return "RIL_REQUEST_GET_RADIO_CAPABILITY";
+ case RIL_REQUEST_START_LCE: return "RIL_REQUEST_START_LCE";
+ case RIL_REQUEST_STOP_LCE: return "RIL_REQUEST_STOP_LCE";
+ case RIL_REQUEST_PULL_LCEDATA: return "RIL_REQUEST_PULL_LCEDATA";
+ case RIL_REQUEST_GET_ACTIVITY_INFO: return "RIL_REQUEST_GET_ACTIVITY_INFO";
default: return "<unknown request>";
}
}
@@ -4122,6 +4250,7 @@
return "RIL_UNSOL_RADIO_CAPABILITY";
case RIL_UNSOL_ON_SS: return "UNSOL_ON_SS";
case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: return "UNSOL_STK_CC_ALPHA_NOTIFY";
+ case RIL_UNSOL_LCEDATA_RECV: return "UNSOL_LCE_INFO_RECV";
default: return "<unknown response>";
}
}
@@ -4666,4 +4795,48 @@
send(rr);
}
+
+ @Override
+ public void startLceService(int reportIntervalMs, boolean pullMode, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_START_LCE, response);
+ /** solicited command argument: reportIntervalMs, pullMode. */
+ rr.mParcel.writeInt(2);
+ rr.mParcel.writeInt(reportIntervalMs);
+ rr.mParcel.writeInt(pullMode ? 1: 0); // PULL mode: 1; PUSH mode: 0;
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ send(rr);
+ }
+
+ @Override
+ public void stopLceService(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_STOP_LCE, response);
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+ send(rr);
+ }
+
+ @Override
+ public void pullLceData(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_PULL_LCEDATA, response);
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+ send(rr);
+ }
+
+ /**
+ * @hide
+ */
+ public void getModemActivityInfo(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_ACTIVITY_INFO, response);
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+ send(rr);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index a12c3fd..015305e 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.telephony;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
@@ -354,6 +355,13 @@
}
}
+ private static int getSendSmsFlag(@Nullable PendingIntent deliveryIntent) {
+ if (deliveryIntent == null) {
+ return 0;
+ }
+ return CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS;
+ }
+
/**
* Use the carrier messaging service to send a text SMS.
*/
@@ -370,7 +378,8 @@
if (text != null) {
try {
carrierMessagingService.sendTextSms(text, getSubId(),
- mTracker.mDestAddress, mSenderCallback);
+ mTracker.mDestAddress, getSendSmsFlag(mTracker.mDeliveryIntent),
+ mSenderCallback);
} catch (RemoteException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendSmsComplete(
@@ -402,7 +411,8 @@
if (data != null) {
try {
carrierMessagingService.sendDataSms(data, getSubId(),
- mTracker.mDestAddress, destPort, mSenderCallback);
+ mTracker.mDestAddress, destPort,
+ getSendSmsFlag(mTracker.mDeliveryIntent), mSenderCallback);
} catch (RemoteException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendSmsComplete(
@@ -528,7 +538,8 @@
protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
try {
carrierMessagingService.sendMultipartTextSms(
- mParts, getSubId(), mTrackers[0].mDestAddress, mSenderCallback);
+ mParts, getSubId(), mTrackers[0].mDestAddress,
+ getSendSmsFlag(mTrackers[0].mDeliveryIntent), mSenderCallback);
} catch (RemoteException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendMultipartSmsComplete(
@@ -765,10 +776,12 @@
* broadcast when the message is delivered to the recipient. The
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
+ * @param persistMessage whether to save the sent message into SMS DB for a
+ * non-default SMS app.
*/
protected abstract void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
- String callingPkg);
+ String callingPkg, boolean persistMessage);
/**
* Inject an SMS PDU into the android platform.
@@ -818,10 +831,13 @@
* to the recipient. The raw pdu of the status report is in the
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
+ * @param persistMessage whether to save the sent message into SMS DB for a
+ * non-default SMS app.
*/
protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) {
+ ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+ boolean persistMessage) {
final String fullMessageText = getMultipartMessageText(parts);
int refNumber = getNextConcatenatedRef() & 0x00FF;
int msgCount = parts.size();
@@ -879,6 +895,7 @@
getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding,
sentIntent, deliveryIntent, (i == (msgCount - 1)),
unsentPartCount, anyPartFailed, messageUri, fullMessageText);
+ trackers[i].mPersistMessage = persistMessage;
}
if (parts == null || trackers == null || trackers.length == 0
@@ -1265,7 +1282,7 @@
}
sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
- null/*messageUri*/, null/*callingPkg*/);
+ null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
}
/**
@@ -1304,11 +1321,13 @@
// If this is a text message (instead of data message)
private boolean mIsText;
+ private boolean mPersistMessage;
+
private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
- boolean isText) {
+ boolean isText, boolean persistMessage) {
mData = data;
mSentIntent = sentIntent;
mDeliveryIntent = deliveryIntent;
@@ -1326,6 +1345,7 @@
mFullMessageText = fullMessageText;
mSubId = subId;
mIsText = isText;
+ mPersistMessage = persistMessage;
}
/**
@@ -1386,7 +1406,7 @@
* @return The telephony provider URI if stored
*/
private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
- if (!mIsText ||
+ if (!mIsText || !mPersistMessage ||
!SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
return null;
}
@@ -1529,7 +1549,7 @@
protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
- boolean isExpectMore, String fullMessageText, boolean isText) {
+ boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
// Get calling app package name via UID from Binder call
PackageManager pm = mContext.getPackageManager();
String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
@@ -1549,15 +1569,15 @@
String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
- fullMessageText, getSubId(), isText);
+ fullMessageText, getSubId(), isText, persistMessage);
}
protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
- String fullMessageText, boolean isText) {
+ String fullMessageText, boolean isText, boolean persistMessage) {
return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
- fullMessageText, isText);
+ fullMessageText, isText, persistMessage);
}
protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index e7ec0ea..cdd65e5 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncResult;
+import android.os.BaseBundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -28,6 +29,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.CellInfo;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -39,9 +41,13 @@
import android.util.Log;
import android.util.Pair;
import android.util.TimeUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.content.Context;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -177,6 +183,7 @@
protected static final int EVENT_CHANGE_IMS_STATE = 45;
protected static final int EVENT_IMS_STATE_CHANGED = 46;
protected static final int EVENT_IMS_STATE_DONE = 47;
+ protected static final int EVENT_IMS_CAPABILITY_CHANGED = 48;
protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -228,10 +235,11 @@
/** Keep track of SPN display rules, so we only broadcast intent if something changes. */
protected boolean mSpnUpdatePending = false;
protected String mCurSpn = null;
+ protected String mCurDataSpn = null;
protected String mCurPlmn = null;
protected boolean mCurShowPlmn = false;
protected boolean mCurShowSpn = false;
-
+ protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mImsRegistered = false;
@@ -254,17 +262,12 @@
if (mPreviousSubId.getAndSet(subId) != subId) {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
Context context = mPhoneBase.getContext();
- int networkType = PhoneFactory.calculatePreferredNetworkType(context, subId);
- mCi.setPreferredNetworkType(networkType, null);
mPhoneBase.notifyCallForwardingIndicator();
- boolean skipRestoringSelection = context.getResources().getBoolean(
+ boolean restoreSelection = !context.getResources().getBoolean(
com.android.internal.R.bool.skip_restoring_network_selection);
- if (!skipRestoringSelection) {
- // restore the previous network selection.
- mPhoneBase.restoreSavedNetworkSelection(null);
- }
+ mPhoneBase.sendSubscriptionSettings(restoreSelection);
mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
ServiceState.rilRadioTechnologyToString(mSS.getRilDataRadioTechnology()));
@@ -280,21 +283,32 @@
// older build that did not include subId in the names.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
context);
- String oldNetworkSelectionName = sp.getString(PhoneBase.
- NETWORK_SELECTION_NAME_KEY, "");
- String oldNetworkSelection = sp.getString(PhoneBase.NETWORK_SELECTION_KEY,
- "");
- if (!TextUtils.isEmpty(oldNetworkSelectionName) ||
- !TextUtils.isEmpty(oldNetworkSelection)) {
+ String oldNetworkSelection = sp.getString(
+ PhoneBase.NETWORK_SELECTION_KEY, "");
+ String oldNetworkSelectionName = sp.getString(
+ PhoneBase.NETWORK_SELECTION_NAME_KEY, "");
+ String oldNetworkSelectionShort = sp.getString(
+ PhoneBase.NETWORK_SELECTION_SHORT_KEY, "");
+ if (!TextUtils.isEmpty(oldNetworkSelection) ||
+ !TextUtils.isEmpty(oldNetworkSelectionName) ||
+ !TextUtils.isEmpty(oldNetworkSelectionShort)) {
SharedPreferences.Editor editor = sp.edit();
- editor.putString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId,
- oldNetworkSelectionName);
editor.putString(PhoneBase.NETWORK_SELECTION_KEY + subId,
oldNetworkSelection);
- editor.remove(PhoneBase.NETWORK_SELECTION_NAME_KEY);
+ editor.putString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId,
+ oldNetworkSelectionName);
+ editor.putString(PhoneBase.NETWORK_SELECTION_SHORT_KEY + subId,
+ oldNetworkSelectionShort);
editor.remove(PhoneBase.NETWORK_SELECTION_KEY);
+ editor.remove(PhoneBase.NETWORK_SELECTION_NAME_KEY);
+ editor.remove(PhoneBase.NETWORK_SELECTION_SHORT_KEY);
editor.commit();
}
+
+ // Once sub id becomes valid, we need to update the service provider name
+ // displayed on the UI again. The old SPN update intents sent to
+ // MobileSignalController earlier were actually ignored due to invalid sub id.
+ updateSpnDisplay();
}
}
}
@@ -635,6 +649,7 @@
public abstract boolean isConcurrentVoiceAndDataAllowed();
public abstract void setImsRegistrationState(boolean registered);
+ public void onImsCapabilityChanged() {}
public abstract void pollState();
/**
@@ -1058,4 +1073,73 @@
protected int getPhoneId() {
return mPhoneBase.getPhoneId();
}
+
+ /* Reset Service state when IWLAN is enabled as polling in airplane mode
+ * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF
+ */
+ protected void resetServiceStateInIwlanMode() {
+ if (mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+ boolean resetIwlanRatVal = false;
+ log("set service state as POWER_OFF");
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mNewSS.getRilDataRadioTechnology()) {
+ log("pollStateDone: mNewSS = " + mNewSS);
+ log("pollStateDone: reset iwlan RAT value");
+ resetIwlanRatVal = true;
+ }
+ mNewSS.setStateOff();
+ if (resetIwlanRatVal) {
+ mNewSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+ mNewSS.setDataRegState(ServiceState.STATE_IN_SERVICE);
+ log("pollStateDone: mNewSS = " + mNewSS);
+ }
+ }
+ }
+
+ /**
+ * Check if device is non-roaming and always on home network.
+ *
+ * @param b carrier config bundle obtained from CarrierConfigManager
+ * @return true if network is always on home network, false otherwise
+ * @see CarrierConfigManager
+ */
+ protected final boolean alwaysOnHomeNetwork(BaseBundle b) {
+ return b.getBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL);
+ }
+
+ /**
+ * Check if the network identifier has membership in the set of
+ * network identifiers stored in the carrier config bundle.
+ *
+ * @param b carrier config bundle obtained from CarrierConfigManager
+ * @param network The network identifier to check network existence in bundle
+ * @param key The key to index into the bundle presenting a string array of
+ * networks to check membership
+ * @return true if network has membership in bundle networks, false otherwise
+ * @see CarrierConfigManager
+ */
+ private boolean isInNetwork(BaseBundle b, String network, String key) {
+ String[] networks = b.getStringArray(key);
+
+ if (networks != null && Arrays.asList(networks).contains(network)) {
+ return true;
+ }
+ return false;
+ }
+
+ protected final boolean isRoamingInGsmNetwork(BaseBundle b, String network) {
+ return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY);
+ }
+
+ protected final boolean isNonRoamingInGsmNetwork(BaseBundle b, String network) {
+ return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY);
+ }
+
+ protected final boolean isRoamingInCdmaNetwork(BaseBundle b, String network) {
+ return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY);
+ }
+
+ protected final boolean isNonRoamingInCdmaNetwork(BaseBundle b, String network) {
+ return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsApplication.java b/src/java/com/android/internal/telephony/SmsApplication.java
index 56ff988..5a95394 100644
--- a/src/java/com/android/internal/telephony/SmsApplication.java
+++ b/src/java/com/android/internal/telephony/SmsApplication.java
@@ -17,19 +17,17 @@
package com.android.internal.telephony;
import android.Manifest.permission;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
@@ -58,6 +56,7 @@
private static final String PHONE_PACKAGE_NAME = "com.android.phone";
private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
+ private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
private static final String SCHEME_SMS = "sms";
private static final String SCHEME_SMSTO = "smsto";
@@ -385,51 +384,15 @@
configurePreferredActivity(packageManager, new ComponentName(
applicationData.mPackageName, applicationData.mSendToClass),
userId);
- // Verify that the phone, BT app and MmsService have permissions
- try {
- PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
- int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- PHONE_PACKAGE_NAME);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- Rlog.e(LOG_TAG, PHONE_PACKAGE_NAME + " lost OP_WRITE_SMS: (fixing)");
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- }
- } catch (NameNotFoundException e) {
- // No phone app on this device (unexpected, even for non-phone devices)
- Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
- applicationData = null;
- }
-
- try {
- PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
- int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- BLUETOOTH_PACKAGE_NAME);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- Rlog.e(LOG_TAG, BLUETOOTH_PACKAGE_NAME + " lost OP_WRITE_SMS: (fixing)");
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- }
- } catch (NameNotFoundException e) {
- // No BT app on this device
- Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
- }
-
- try {
- PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
- int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- MMS_SERVICE_PACKAGE_NAME);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- Rlog.e(LOG_TAG, MMS_SERVICE_PACKAGE_NAME + " lost OP_WRITE_SMS: (fixing)");
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- }
- } catch (NameNotFoundException e) {
- // No phone app on this device (unexpected, even for non-phone devices)
- Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
- applicationData = null;
- }
-
+ // Assign permission to special system apps
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ PHONE_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ BLUETOOTH_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ MMS_SERVICE_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ TELEPHONY_PROVIDER_PACKAGE_NAME);
}
}
if (DEBUG_MULTIUSER) {
@@ -500,39 +463,53 @@
appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
- // Phone needs to always have this permission to write to the sms database
- try {
- PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- } catch (NameNotFoundException e) {
- // No phone app on this device (unexpected, even for non-phone devices)
- Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
- }
-
- // BT needs to always have this permission to write to the sms database
- try {
- PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- } catch (NameNotFoundException e) {
- // No BT app on this device
- Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
- }
-
- // MmsService needs to always have this permission to write to the sms database
- try {
- PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
- appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
- } catch (NameNotFoundException e) {
- // No phone app on this device (unexpected, even for non-phone devices)
- Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
- }
+ // Assign permission to special system apps
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ PHONE_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ BLUETOOTH_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ MMS_SERVICE_PACKAGE_NAME);
+ assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+ TELEPHONY_PROVIDER_PACKAGE_NAME);
}
}
/**
+ * Assign WRITE_SMS AppOps permission to some special system apps.
+ *
+ * @param context The context
+ * @param packageManager The package manager instance
+ * @param appOps The AppOps manager instance
+ * @param packageName The package name of the system app
+ */
+ private static void assignWriteSmsPermissionToSystemApp(Context context,
+ PackageManager packageManager, AppOpsManager appOps, String packageName) {
+ // First check package signature matches the caller's package signature.
+ // Since this class is only used internally by the system, this check makes sure
+ // the package signature matches system signature.
+ final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
+ if (result != PackageManager.SIGNATURE_MATCH) {
+ Rlog.e(LOG_TAG, packageName + " does not have system signature");
+ return;
+ }
+ try {
+ PackageInfo info = packageManager.getPackageInfo(packageName, 0);
+ int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+ packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)");
+ appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+ packageName, AppOpsManager.MODE_ALLOWED);
+ }
+ } catch (NameNotFoundException e) {
+ // No whitelisted system app on this device
+ Rlog.e(LOG_TAG, "Package not found: " + packageName);
+ }
+
+ }
+
+ /**
* Tracks package changes and ensures that the default SMS app is always configured to be the
* preferred activity for SENDTO sms/mms intents.
*/
@@ -735,24 +712,36 @@
* Caller must pass in the correct user context if calling from a singleton service.
*/
public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
- if (packageName == null) return true;
-
if (SmsManager.getDefault().getAutoPersisting()) {
return true;
}
+ return !isDefaultSmsApplication(context, packageName);
+ }
- String defaultSmsPackage = null;
- ComponentName component = getDefaultSmsApplication(context, false);
- if (component != null) {
- defaultSmsPackage = component.getPackageName();
+ /**
+ * Check if a package is default sms app (or equivalent, like bluetooth)
+ *
+ * @param context context from the calling app
+ * @param packageName the name of the package to be checked
+ * @return true if the package is default sms app or bluetooth
+ */
+ public static boolean isDefaultSmsApplication(Context context, String packageName) {
+ if (packageName == null) {
+ return false;
}
-
- if ((defaultSmsPackage == null || !defaultSmsPackage.equals(packageName)) &&
- !packageName.equals(BLUETOOTH_PACKAGE_NAME)) {
- // To write the message for someone other than the default SMS and BT app
+ final String defaultSmsPackage = getDefaultSmsApplicationPackageName(context);
+ if ((defaultSmsPackage != null && defaultSmsPackage.equals(packageName))
+ || BLUETOOTH_PACKAGE_NAME.equals(packageName)) {
return true;
}
-
return false;
}
+
+ private static String getDefaultSmsApplicationPackageName(Context context) {
+ final ComponentName component = getDefaultSmsApplication(context, false);
+ if (component != null) {
+ return component.getPackageName();
+ }
+ return null;
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsMessageBase.java b/src/java/com/android/internal/telephony/SmsMessageBase.java
index b027b1e..1d50834 100644
--- a/src/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/src/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,11 +16,14 @@
package com.android.internal.telephony;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
+import java.text.BreakIterator;
import java.util.Arrays;
import android.provider.Telephony;
+import android.telephony.SmsMessage;
/**
* Base class declaring the specific methods and members for SmsMessage.
@@ -346,4 +349,70 @@
mIsEmail = Telephony.Mms.isEmailAddress(mEmailFrom);
}
+ /**
+ * Find the next position to start a new fragment of a multipart SMS.
+ *
+ * @param currentPosition current start position of the fragment
+ * @param byteLimit maximum number of bytes in the fragment
+ * @param msgBody text of the SMS in UTF-16 encoding
+ * @return the position to start the next fragment
+ */
+ public static int findNextUnicodePosition(
+ int currentPosition, int byteLimit, CharSequence msgBody) {
+ int nextPos = Math.min(currentPosition + byteLimit / 2, msgBody.length());
+ // Check whether the fragment ends in a character boundary. Some characters take 4-bytes
+ // in UTF-16 encoding. Many carriers cannot handle
+ // a fragment correctly if it does not end at a character boundary.
+ if (nextPos < msgBody.length()) {
+ BreakIterator breakIterator = BreakIterator.getCharacterInstance();
+ breakIterator.setText(msgBody.toString());
+ if (!breakIterator.isBoundary(nextPos)) {
+ nextPos = breakIterator.preceding(nextPos);
+ }
+ }
+ return nextPos;
+ }
+
+ /**
+ * Calculate the TextEncodingDetails of a message encoded in Unicode.
+ */
+ public static TextEncodingDetails calcUnicodeEncodingDetails(CharSequence msgBody) {
+ TextEncodingDetails ted = new TextEncodingDetails();
+ int octets = msgBody.length() * 2;
+ ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
+ ted.codeUnitCount = msgBody.length();
+ if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
+ // If EMS is not supported, break down EMS into single segment SMS
+ // and add page info " x/y".
+ // In the case of UCS2 encoding type, we need 8 bytes for this
+ // but we only have 6 bytes from UDH, so truncate the limit for
+ // each segment by 2 bytes (1 char).
+ int maxUserDataBytesWithHeader = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+ if (!SmsMessage.hasEmsSupport()) {
+ // make sure total number of segments is less than 10
+ if (octets <= 9 * (maxUserDataBytesWithHeader - 2)) {
+ maxUserDataBytesWithHeader -= 2;
+ }
+ }
+
+ int pos = 0; // Index in code units.
+ int msgCount = 0;
+ while (pos < msgBody.length()) {
+ int nextPos = findNextUnicodePosition(pos, maxUserDataBytesWithHeader,
+ msgBody);
+ if (nextPos == msgBody.length()) {
+ ted.codeUnitsRemaining = pos + maxUserDataBytesWithHeader / 2 -
+ msgBody.length();
+ }
+ pos = nextPos;
+ msgCount++;
+ }
+ ted.msgCount = msgCount;
+ } else {
+ ted.msgCount = 1;
+ ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets) / 2;
+ }
+
+ return ted;
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsNumberUtils.java b/src/java/com/android/internal/telephony/SmsNumberUtils.java
index 2d03f17..f530ade 100644
--- a/src/java/com/android/internal/telephony/SmsNumberUtils.java
+++ b/src/java/com/android/internal/telephony/SmsNumberUtils.java
@@ -534,7 +534,7 @@
if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + destAddr + "\"" );
if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
- Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number!");
+ Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number! Nothing changed.");
return destAddr;
}
@@ -551,7 +551,10 @@
}
}
- if (DBG) Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + result + "\"" );
+ if (DBG) {
+ Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+ Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? result : destAddr) + "\"" );
+ }
return result != null ? result : destAddr;
}
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index b9a0af1..73e9a42 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -46,6 +46,7 @@
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
@@ -445,7 +446,7 @@
try {
infile = mPolicyFile.openRead();
final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(infile, null);
+ parser.setInput(infile, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY);
@@ -502,7 +503,7 @@
outfile = mPolicyFile.startWrite();
XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outfile, "utf-8");
+ out.setOutput(outfile, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 57172bb..ba5e037 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -38,7 +39,7 @@
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
-
+import java.util.Objects;
import com.android.internal.telephony.IccCardConstants.State;
import java.io.FileDescriptor;
@@ -46,12 +47,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* SubscriptionController to provide an inter-process communication to
@@ -124,8 +126,11 @@
protected TelephonyManager mTelephonyManager;
protected CallManager mCM;
+ private final AppOpsManager mAppOps;
+
// FIXME: Does not allow for multiple subs in a slot and change to SparseArray
- private static HashMap<Integer, Integer> mSlotIdxToSubId = new HashMap<Integer, Integer>();
+ private static Map<Integer, Integer> sSlotIdxToSubId =
+ new ConcurrentHashMap<Integer, Integer>();
private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
@@ -166,6 +171,7 @@
mContext = c;
mCM = CallManager.getInstance();
mTelephonyManager = TelephonyManager.from(mContext);
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
if(ServiceManager.getService("isub") == null) {
ServiceManager.addService("isub", this);
@@ -175,12 +181,13 @@
}
private boolean isSubInfoReady() {
- return mSlotIdxToSubId.size() > 0;
+ return sSlotIdxToSubId.size() > 0;
}
private SubscriptionController(Phone phone) {
mContext = phone.getContext();
mCM = CallManager.getInstance();
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
if(ServiceManager.getService("isub") == null) {
ServiceManager.addService("isub", this);
@@ -190,13 +197,34 @@
}
/**
- * Make sure the caller has the READ_PHONE_STATE permission.
+ * Make sure the caller can read phone state which requires holding the
+ * READ_PHONE_STATE permission and the OP_READ_PHONE_STATE app op being
+ * set to MODE_ALLOWED.
*
- * @throws SecurityException if the caller does not have the required permission
+ * @param callingPackage The package claiming to make the IPC.
+ * @param message The name of the access protected method.
+ *
+ * @throws SecurityException if the caller does not have READ_PHONE_STATE permission.
*/
- private void enforceSubscriptionPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
- "Requires READ_PHONE_STATE");
+ private boolean canReadPhoneState(String callingPackage, String message) {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+
+ // SKIP checking run-time permission since self or using PRIVILEDGED permission
+ return true;
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+ message);
+ }
+
+ return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ private void enforceModifyPhoneState(String message) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE, message);
}
/**
@@ -210,22 +238,7 @@
mContext.sendBroadcast(intent);
}
- private boolean checkNotifyPermission(String method) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (DBG) {
- logd("checkNotifyPermission Permission Denial: " + method + " from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
- }
- return false;
- }
-
public void notifySubscriptionInfoChanged() {
- if (!checkNotifyPermission("notifySubscriptionInfoChanged")) {
- return;
- }
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
try {
@@ -280,9 +293,9 @@
+ " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso);
}
+ // If line1number has been set to a different number, use it instead.
String line1Number = mTelephonyManager.getLine1NumberForSubscriber(id);
if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
- logd("Line1Number is different: " + line1Number);
number = line1Number;
}
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
@@ -345,10 +358,11 @@
/**
* Find unused color to be set for new SubInfoRecord
+ * @param callingPackage The package making the IPC.
* @return RGB integer value of color
*/
- private int getUnusedColor() {
- List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList();
+ private int getUnusedColor(String callingPackage) {
+ List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage);
colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
int colorIdx = 0;
@@ -372,187 +386,266 @@
/**
* Get the active SubscriptionInfo with the subId key
* @param subId The unique SubscriptionInfo key in database
+ * @param callingPackage The package making the IPC.
* @return SubscriptionInfo, maybe null if its not active
*/
@Override
- public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
- enforceSubscriptionPermission();
+ public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
+ if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfo")) {
+ return null;
+ }
- List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (si.getSubscriptionId() == subId) {
- if (DBG) logd("[getActiveSubInfoForSubscriber]+ subId=" + subId + " subInfo=" + si);
- return si;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
+ if (subList != null) {
+ for (SubscriptionInfo si : subList) {
+ if (si.getSubscriptionId() == subId) {
+ if (DBG)
+ logd("[getActiveSubInfoForSubscriber]+ subId=" + subId
+ + " subInfo=" + si);
+ return si;
+ }
}
}
+ if (DBG) {
+ logd("[getActiveSubInfoForSubscriber]- subId=" + subId
+ + " subList=" + subList + " subInfo=null");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- if (DBG) {
- logd("[getActiveSubInfoForSubscriber]- subId=" + subId
- + " subList=" + subList + " subInfo=null");
- }
+
return null;
}
/**
* Get the active SubscriptionInfo associated with the iccId
* @param iccId the IccId of SIM card
+ * @param callingPackage The package making the IPC.
* @return SubscriptionInfo, maybe null if its not active
*/
@Override
- public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId) {
- enforceSubscriptionPermission();
+ public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) {
+ if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForIccId")) {
+ return null;
+ }
- List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (si.getIccId() == iccId) {
- if (DBG) logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
- return si;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
+ if (subList != null) {
+ for (SubscriptionInfo si : subList) {
+ if (si.getIccId() == iccId) {
+ if (DBG)
+ logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
+ return si;
+ }
}
}
+ if (DBG) {
+ logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
+ + " subList=" + subList + " subInfo=null");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- if (DBG) {
- logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
- + " subList=" + subList + " subInfo=null");
- }
+
return null;
}
/**
* Get the active SubscriptionInfo associated with the slotIdx
* @param slotIdx the slot which the subscription is inserted
+ * @param callingPackage The package making the IPC.
* @return SubscriptionInfo, maybe null if its not active
*/
@Override
- public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx) {
- enforceSubscriptionPermission();
+ public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx,
+ String callingPackage) {
+ if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
+ return null;
+ }
- List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
- if (subList != null) {
- for (SubscriptionInfo si : subList) {
- if (si.getSimSlotIndex() == slotIdx) {
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
- + " subId=" + si);
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
+ if (subList != null) {
+ for (SubscriptionInfo si : subList) {
+ if (si.getSimSlotIndex() == slotIdx) {
+ if (DBG) {
+ logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
+ + " subId=" + si);
+ }
+ return si;
}
- return si;
+ }
+ if (DBG) {
+ logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
+ + " subId=null");
+ }
+ } else {
+ if (DBG) {
+ logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
}
}
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
- + " subId=null");
- }
- } else {
- if (DBG) {
- logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
- }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+
return null;
}
/**
+ * @param callingPackage The package making the IPC.
* @return List of all SubscriptionInfo records in database,
* include those that were inserted before, maybe empty but not null.
* @hide
*/
@Override
- public List<SubscriptionInfo> getAllSubInfoList() {
+ public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) {
if (DBG) logd("[getAllSubInfoList]+");
- enforceSubscriptionPermission();
- List<SubscriptionInfo> subList = null;
- subList = getSubInfo(null, null);
- if (subList != null) {
- if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
- } else {
- if (DBG) logd("[getAllSubInfoList]- no info return");
+ if (!canReadPhoneState(callingPackage, "getAllSubInfoList")) {
+ return null;
}
- return subList;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> subList = null;
+ subList = getSubInfo(null, null);
+ if (subList != null) {
+ if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
+ } else {
+ if (DBG) logd("[getAllSubInfoList]- no info return");
+ }
+ return subList;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Get the SubInfoRecord(s) of the currently inserted SIM(s)
+ * @param callingPackage The package making the IPC.
* @return Array list of currently inserted SubInfoRecord(s)
*/
@Override
- public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
- enforceSubscriptionPermission();
+ public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
if (DBG) logdl("[getActiveSubInfoList]+");
- List<SubscriptionInfo> subList = null;
-
- if (!isSubInfoReady()) {
- if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
- return subList;
+ if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoList")) {
+ return null;
}
- subList = getSubInfo(SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
- if (subList != null) {
- // FIXME: Unnecessary when an insertion sort is used!
- Collections.sort(subList, new Comparator<SubscriptionInfo>() {
- @Override
- public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
- // Primary sort key on SimSlotIndex
- int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
- if (flag == 0) {
- // Secondary sort on SubscriptionId
- return arg0.getSubscriptionId() - arg1.getSubscriptionId();
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (!isSubInfoReady()) {
+ if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
+ return null;
+ }
+
+ List<SubscriptionInfo> subList = getSubInfo(
+ SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
+
+ if (subList != null) {
+ // FIXME: Unnecessary when an insertion sort is used!
+ Collections.sort(subList, new Comparator<SubscriptionInfo>() {
+ @Override
+ public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
+ // Primary sort key on SimSlotIndex
+ int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
+ if (flag == 0) {
+ // Secondary sort on SubscriptionId
+ return arg0.getSubscriptionId() - arg1.getSubscriptionId();
+ }
+ return flag;
}
- return flag;
- }
- });
+ });
- if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
- } else {
- if (DBG) logdl("[getActiveSubInfoList]- no info return");
+ if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
+ } else {
+ if (DBG) logdl("[getActiveSubInfoList]- no info return");
+ }
+
+ return subList;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
-
- return subList;
}
/**
* Get the SUB count of active SUB(s)
+ * @param callingPackage The package making the IPC.
* @return active SIM count
*/
@Override
- public int getActiveSubInfoCount() {
+ public int getActiveSubInfoCount(String callingPackage) {
if (DBG) logd("[getActiveSubInfoCount]+");
- List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
- if (records == null) {
- if (DBG) logd("[getActiveSubInfoCount] records null");
+
+ if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
return 0;
}
- if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
- return records.size();
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
+ if (records == null) {
+ if (DBG) logd("[getActiveSubInfoCount] records null");
+ return 0;
+ }
+ if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+ return records.size();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Get the SUB count of all SUB(s) in SubscriptoinInfo database
+ * @param callingPackage The package making the IPC.
* @return all SIM count in database, include what was inserted before
*/
@Override
- public int getAllSubInfoCount() {
+ public int getAllSubInfoCount(String callingPackage) {
if (DBG) logd("[getAllSubInfoCount]+");
- enforceSubscriptionPermission();
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, null, null, null);
- try {
- if (cursor != null) {
- int count = cursor.getCount();
- if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
- return count;
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
+ if (!canReadPhoneState(callingPackage, "getAllSubInfoCount")) {
+ return 0;
}
- if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
- return 0;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+ null, null, null, null);
+ try {
+ if (cursor != null) {
+ int count = cursor.getCount();
+ if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
+ return count;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
+
+ return 0;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -573,142 +666,158 @@
@Override
public int addSubInfoRecord(String iccId, int slotId) {
if (DBG) logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
- enforceSubscriptionPermission();
- if (iccId == null) {
- if (DBG) logdl("[addSubInfoRecord]- null iccId");
- return -1;
- }
+ enforceModifyPhoneState("addSubInfoRecord");
- int[] subIds = getSubId(slotId);
- if (subIds == null || subIds.length == 0) {
- if (DBG) {
- logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
- + subIds);
- }
- return -1;
- }
-
- String nameToSet;
- String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);
-
- if (!TextUtils.isEmpty(simCarrierName)) {
- nameToSet = simCarrierName;
- } else {
- nameToSet = "CARD " + Integer.toString(slotId + 1);
- }
- if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
- if (DBG) logdl("[addSubInfoRecord] carrier name = " + simCarrierName);
-
- ContentResolver resolver = mContext.getContentResolver();
- Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
- new String[] {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
- SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
- SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null);
-
- int color = getUnusedColor();
-
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
try {
- if (cursor == null || !cursor.moveToFirst()) {
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.ICC_ID, iccId);
- // default SIM color differs between slots
- value.put(SubscriptionManager.COLOR, color);
- value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
- value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- value.put(SubscriptionManager.CARRIER_NAME, "");
- Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
- if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
- } else {
- int subId = cursor.getInt(0);
- int oldSimInfoId = cursor.getInt(1);
- int nameSource = cursor.getInt(2);
- ContentValues value = new ContentValues();
+ if (iccId == null) {
+ if (DBG) logdl("[addSubInfoRecord]- null iccId");
+ return -1;
+ }
- if (slotId != oldSimInfoId) {
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
+ new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+ SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
+ SubscriptionManager.ICC_ID + "=?", new String[]{iccId}, null);
+
+ int color = getUnusedColor(mContext.getOpPackageName());
+ boolean setDisplayName = false;
+ try {
+ if (cursor == null || !cursor.moveToFirst()) {
+ setDisplayName = true;
+ ContentValues value = new ContentValues();
+ value.put(SubscriptionManager.ICC_ID, iccId);
+ // default SIM color differs between slots
+ value.put(SubscriptionManager.COLOR, color);
value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
- }
+ value.put(SubscriptionManager.CARRIER_NAME, "");
+ Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
+ if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
+ } else {
+ int subId = cursor.getInt(0);
+ int oldSimInfoId = cursor.getInt(1);
+ int nameSource = cursor.getInt(2);
+ ContentValues value = new ContentValues();
- if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
- value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- }
-
- if (value.size() > 0) {
- resolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
- "=" + Long.toString(subId), null);
- }
-
- if (DBG) logdl("[addSubInfoRecord] Record already exists");
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
- SubscriptionManager.SIM_SLOT_INDEX + "=?",
- new String[] {String.valueOf(slotId)}, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- do {
- int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
- // If mSlotIdToSubIdMap already has a valid subId for a slotId/phoneId,
- // do not add another subId for same slotId/phoneId.
- Integer currentSubId = mSlotIdxToSubId.get(slotId);
- if (currentSubId == null
- || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
- // TODO While two subs active, if user deactivats first
- // one, need to update the default subId with second one.
-
- // FIXME: Currently we assume phoneId == slotId which in the future
- // may not be true, for instance with multiple subs per slot.
- // But is true at the moment.
- mSlotIdxToSubId.put(slotId, subId);
- int subIdCountMax = getActiveSubInfoCountMax();
- int defaultSubId = getDefaultSubId();
- if (DBG) {
- logdl("[addSubInfoRecord]"
- + " mSlotIdxToSubId.size=" + mSlotIdxToSubId.size()
- + " slotId=" + slotId + " subId=" + subId
- + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
- }
-
- // Set the default sub if not set or if single sim device
- if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
- || subIdCountMax == 1) {
- setDefaultFallbackSubId(subId);
- }
- // If single sim device, set this subscription as the default for everything
- if (subIdCountMax == 1) {
- if (DBG) {
- logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
- }
- setDefaultDataSubId(subId);
- setDefaultSmsSubId(subId);
- setDefaultVoiceSubId(subId);
- }
- } else {
- if (DBG) {
- logdl("[addSubInfoRecord] currentSubId != null"
- + " && currentSubId is valid, IGNORE");
- }
+ if (slotId != oldSimInfoId) {
+ value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
}
- if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
- } while (cursor.moveToNext());
+
+ if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+ setDisplayName = true;
+ }
+
+ if (value.size() > 0) {
+ resolver.update(SubscriptionManager.CONTENT_URI, value,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+ "=" + Long.toString(subId), null);
+ }
+
+ if (DBG) logdl("[addSubInfoRecord] Record already exists");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
+
+ cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
+ SubscriptionManager.SIM_SLOT_INDEX + "=?",
+ new String[] {String.valueOf(slotId)}, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
+ // If sSlotIdxToSubId already has a valid subId for a slotId/phoneId,
+ // do not add another subId for same slotId/phoneId.
+ Integer currentSubId = sSlotIdxToSubId.get(slotId);
+ if (currentSubId == null
+ || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
+ // TODO While two subs active, if user deactivats first
+ // one, need to update the default subId with second one.
+
+ // FIXME: Currently we assume phoneId == slotId which in the future
+ // may not be true, for instance with multiple subs per slot.
+ // But is true at the moment.
+ sSlotIdxToSubId.put(slotId, subId);
+ int subIdCountMax = getActiveSubInfoCountMax();
+ int defaultSubId = getDefaultSubId();
+ if (DBG) {
+ logdl("[addSubInfoRecord]"
+ + " sSlotIdxToSubId.size=" + sSlotIdxToSubId.size()
+ + " slotId=" + slotId + " subId=" + subId
+ + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
+ }
+
+ // Set the default sub if not set or if single sim device
+ if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
+ || subIdCountMax == 1) {
+ setDefaultFallbackSubId(subId);
+ }
+ // If single sim device, set this subscription as the default for everything
+ if (subIdCountMax == 1) {
+ if (DBG) {
+ logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
+ }
+ setDefaultDataSubId(subId);
+ setDefaultSmsSubId(subId);
+ setDefaultVoiceSubId(subId);
+ }
+ } else {
+ if (DBG) {
+ logdl("[addSubInfoRecord] currentSubId != null"
+ + " && currentSubId is valid, IGNORE");
+ }
+ }
+ if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
+ } while (cursor.moveToNext());
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ // Set Display name after sub id is set above so as to get valid simCarrierName
+ int[] subIds = getSubId(slotId);
+ if (subIds == null || subIds.length == 0) {
+ if (DBG) {
+ logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
+ + subIds);
+ }
+ return -1;
+ }
+ if (setDisplayName) {
+ String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);
+ String nameToSet;
+
+ if (!TextUtils.isEmpty(simCarrierName)) {
+ nameToSet = simCarrierName;
+ } else {
+ nameToSet = "CARD " + Integer.toString(slotId + 1);
+ }
+
+ ContentValues value = new ContentValues();
+ value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+ resolver.update(SubscriptionManager.CONTENT_URI, value,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+ "=" + Long.toString(subIds[0]), null);
+
+ if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
+ }
+
+ // Once the records are loaded, notify DcTracker
+ updateAllDataConnectionTrackers();
+
+ if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIdxToSubId.size());
+
} finally {
- if (cursor != null) {
- cursor.close();
- }
+ Binder.restoreCallingIdentity(identity);
}
-
- // Once the records are loaded, notify DcTracker
- updateAllDataConnectionTrackers();
-
- if (DBG) logdl("[addSubInfoRecord]- info size=" + mSlotIdxToSubId.size());
return 0;
}
@@ -740,11 +849,13 @@
if (showPlmn) {
carrierText = plmn;
if (showSpn) {
- // Need to show both plmn and spn.
- String separator = mContext.getString(
- com.android.internal.R.string.kg_text_message_separator).toString();
- carrierText = new StringBuilder().append(carrierText).append(separator)
- .append(spn).toString();
+ // Need to show both plmn and spn if both are not same.
+ if(!Objects.equals(spn, plmn)) {
+ String separator = mContext.getString(
+ com.android.internal.R.string.kg_text_message_separator).toString();
+ carrierText = new StringBuilder().append(carrierText).append(separator)
+ .append(spn).toString();
+ }
}
} else if (showSpn) {
carrierText = spn;
@@ -764,16 +875,24 @@
*/
private int setCarrierText(String text, int subId) {
if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
- enforceSubscriptionPermission();
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.CARRIER_NAME, text);
+ enforceModifyPhoneState("setCarrierText");
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
- notifySubscriptionInfoChanged();
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.CARRIER_NAME, text);
- return result;
+ int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+ value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+ Long.toString(subId), null);
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -785,18 +904,26 @@
@Override
public int setIconTint(int tint, int subId) {
if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
- enforceSubscriptionPermission();
- validateSubId(subId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.COLOR, tint);
- if (DBG) logd("[setIconTint]- tint:" + tint + " set");
+ enforceModifyPhoneState("setIconTint");
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
- notifySubscriptionInfoChanged();
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subId);
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.COLOR, tint);
+ if (DBG) logd("[setIconTint]- tint:" + tint + " set");
- return result;
+ int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+ value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+ Long.toString(subId), null);
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -824,28 +951,36 @@
logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId
+ " nameSource:" + nameSource);
}
- enforceSubscriptionPermission();
- validateSubId(subId);
- String nameToSet;
- if (displayName == null) {
- nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
- } else {
- nameToSet = displayName;
+ enforceModifyPhoneState("setDisplayNameUsingSrc");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subId);
+ String nameToSet;
+ if (displayName == null) {
+ nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
+ } else {
+ nameToSet = displayName;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+ if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
+ if (DBG) logd("Set nameSource=" + nameSource);
+ value.put(SubscriptionManager.NAME_SOURCE, nameSource);
+ }
+ if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
+
+ int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+ value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+ Long.toString(subId), null);
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
- if (DBG) logd("Set nameSource=" + nameSource);
- value.put(SubscriptionManager.NAME_SOURCE, nameSource);
- }
- if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
-
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
- notifySubscriptionInfoChanged();
-
- return result;
}
/**
@@ -857,31 +992,38 @@
@Override
public int setDisplayNumber(String number, int subId) {
if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
- enforceSubscriptionPermission();
- validateSubId(subId);
- int result;
- int phoneId = getPhoneId(subId);
+ enforceModifyPhoneState("setDisplayNumber");
- if (number == null || phoneId < 0 ||
- phoneId >= mTelephonyManager.getPhoneCount()) {
- if (DBG) logd("[setDispalyNumber]- fail");
- return -1;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subId);
+ int result;
+ int phoneId = getPhoneId(subId);
+
+ if (number == null || phoneId < 0 ||
+ phoneId >= mTelephonyManager.getPhoneCount()) {
+ if (DBG) logd("[setDispalyNumber]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.NUMBER, number);
+
+ // This function had a call to update number on the SIM (Phone.setLine1Number()) but
+ // that was removed as there doesn't seem to be a reason for that. If it is added
+ // back, watch out for deadlocks.
+
+ result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ + "=" + Long.toString(subId), null);
+ if (DBG) logd("[setDisplayNumber]- update result :" + result);
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.NUMBER, number);
-
- // This function had a call to update number on the SIM (Phone.setLine1Number()) but that
- // was removed as there doesn't seem to be a reason for that. If it is added back, watch out
- // for deadlocks.
-
- result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
- + "=" + Long.toString(subId), null);
- if (DBG) logd("[setDisplayNumber]- update result :" + result);
- notifySubscriptionInfoChanged();
-
- return result;
}
/**
@@ -893,22 +1035,30 @@
@Override
public int setDataRoaming(int roaming, int subId) {
if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
- enforceSubscriptionPermission();
- validateSubId(subId);
- if (roaming < 0) {
- if (DBG) logd("[setDataRoaming]- fail");
- return -1;
+ enforceModifyPhoneState("setDataRoaming");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subId);
+ if (roaming < 0) {
+ if (DBG) logd("[setDataRoaming]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.DATA_ROAMING, roaming);
+ if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
+
+ int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+ value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+ Long.toString(subId), null);
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.DATA_ROAMING, roaming);
- if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
-
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
- notifySubscriptionInfoChanged();
-
- return result;
}
/**
@@ -951,7 +1101,7 @@
return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
}
- int size = mSlotIdxToSubId.size();
+ int size = sSlotIdxToSubId.size();
if (size == 0)
{
@@ -959,7 +1109,7 @@
return SubscriptionManager.SIM_NOT_INSERTED;
}
- for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+ for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
int sim = entry.getKey();
int sub = entry.getValue();
@@ -1000,10 +1150,10 @@
}
// Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate.
- int size = mSlotIdxToSubId.size();
+ int size = sSlotIdxToSubId.size();
if (size == 0) {
if (DBG) {
- logd("[getSubId]- mSlotIdToSubIdMap.size == 0, return DummySubIds slotIdx="
+ logd("[getSubId]- sSlotIdxToSubId.size == 0, return DummySubIds slotIdx="
+ slotIdx);
}
return getDummySubIds(slotIdx);
@@ -1011,7 +1161,7 @@
// Create an array of subIds that are in this slot?
ArrayList<Integer> subIds = new ArrayList<Integer>();
- for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+ for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
int slot = entry.getKey();
int sub = entry.getValue();
if (slotIdx == slot) {
@@ -1052,7 +1202,7 @@
return SubscriptionManager.INVALID_PHONE_INDEX;
}
- int size = mSlotIdxToSubId.size();
+ int size = sSlotIdxToSubId.size();
if (size == 0) {
phoneId = mDefaultPhoneId;
if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
@@ -1060,7 +1210,7 @@
}
// FIXME: Assumes phoneId == slotId
- for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+ for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
int sim = entry.getKey();
int sub = entry.getValue();
@@ -1089,7 +1239,7 @@
for (int i = 0; i < numSubs; i++) {
dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx;
}
- if (DBG) {
+ if (VDBG) {
logd("getDummySubIds: slotIdx=" + slotIdx
+ " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
}
@@ -1104,19 +1254,26 @@
*/
@Override
public int clearSubInfo() {
- enforceSubscriptionPermission();
- if (DBG) logd("[clearSubInfo]+");
+ enforceModifyPhoneState("clearSubInfo");
- int size = mSlotIdxToSubId.size();
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) logd("[clearSubInfo]+");
- if (size == 0) {
- if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
- return 0;
+ int size = sSlotIdxToSubId.size();
+
+ if (size == 0) {
+ if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
+ return 0;
+ }
+
+ sSlotIdxToSubId.clear();
+ if (DBG) logdl("[clearSubInfo]- clear size=" + size);
+ return size;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
-
- mSlotIdxToSubId.clear();
- if (DBG) logdl("[clearSubInfo]- clear size=" + size);
- return size;
}
private void logvl(String msg) {
@@ -1162,7 +1319,7 @@
subId = getDefaultDataSubId();
if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
}
- if ( ! isActiveSubId(subId)) {
+ if (!isActiveSubId(subId)) {
subId = mDefaultFallbackSubId;
if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
}
@@ -1172,6 +1329,8 @@
@Override
public void setDefaultSmsSubId(int subId) {
+ enforceModifyPhoneState("setDefaultSmsSubId");
+
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
}
@@ -1201,6 +1360,8 @@
@Override
public void setDefaultVoiceSubId(int subId) {
+ enforceModifyPhoneState("setDefaultVoiceSubId");
+
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
}
@@ -1239,37 +1400,42 @@
@Override
public void setDefaultDataSubId(int subId) {
+ enforceModifyPhoneState("setDefaultDataSubId");
+
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
}
if (DBG) logdl("[setDefaultDataSubId] subId=" + subId);
+ ProxyController proxyController = ProxyController.getInstance();
int len = sProxyPhones.length;
logdl("[setDefaultDataSubId] num phones=" + len);
- RadioAccessFamily[] rafs = new RadioAccessFamily[len];
- for (int phoneId = 0; phoneId < len; phoneId++) {
- PhoneProxy phone = sProxyPhones[phoneId];
- int raf = phone.getRadioAccessFamily();
- int id = phone.getSubId();
- logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
- raf |= RadioAccessFamily.RAF_GSM;
- if (id == subId) {
- raf |= RadioAccessFamily.RAF_UMTS;
- } else {
- raf &= ~RadioAccessFamily.RAF_UMTS;
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ // Only re-map modems if the new default data sub is valid
+ RadioAccessFamily[] rafs = new RadioAccessFamily[len];
+ boolean atLeastOneMatch = false;
+ for (int phoneId = 0; phoneId < len; phoneId++) {
+ PhoneProxy phone = sProxyPhones[phoneId];
+ int raf;
+ int id = phone.getSubId();
+ if (id == subId) {
+ // TODO Handle the general case of N modems and M subscriptions.
+ raf = proxyController.getMaxRafSupported();
+ atLeastOneMatch = true;
+ } else {
+ // TODO Handle the general case of N modems and M subscriptions.
+ raf = proxyController.getMinRafSupported();
+ }
+ logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
+ rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
}
- logdl("[setDefaultDataSubId] reqRAF=" + raf);
-
- // Set the raf to the maximum of the requested and the user's preferred.
- int networkType = PhoneFactory.calculatePreferredNetworkType(mContext, id);
- logdl("[setDefaultDataSubId] networkType=" + networkType);
- raf &= RadioAccessFamily.getRafFromNetworkType(networkType);
-
- logdl("[setDefaultDataSubId] newRAF=" + raf);
- rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
+ if (atLeastOneMatch) {
+ proxyController.setRadioCapability(rafs);
+ } else {
+ if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating.");
+ }
}
- ProxyController.getInstance().setRadioCapability(rafs);
// FIXME is this still needed?
updateAllDataConnectionTrackers();
@@ -1322,8 +1488,8 @@
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
if (DBG) {
- logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" + phoneId
- + " subId=" + subId);
+ logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" +
+ phoneId + " subId=" + subId);
}
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} else {
@@ -1337,19 +1503,28 @@
@Override
public void clearDefaultsForInactiveSubIds() {
- final List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
- if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
- if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
- if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
- setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- }
- if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
- if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
- setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- }
- if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
- if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
- setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ enforceModifyPhoneState("clearDefaultsForInactiveSubIds");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
+ if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
+ if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
+ if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
+ setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
+ if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
+ setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
+ if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
+ setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -1390,49 +1565,57 @@
return getSubId(slotId);
}
- public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) {
+ public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck,
+ String callingPackage) {
if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
- enforceSubscriptionPermission();
- if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
- slotId = getSlotId(getDefaultSubId());
- }
- if (!SubscriptionManager.isValidSlotId(slotId)) {
- if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
+ if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
return null;
}
- if (needCheck && !isSubInfoReady()) {
- if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
- return null;
- }
-
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
- new String[] {String.valueOf(slotId)}, null);
- ArrayList<SubscriptionInfo> subList = null;
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
try {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- SubscriptionInfo subInfo = getSubInfoRecord(cursor);
- if (subInfo != null)
- {
- if (subList == null)
- {
- subList = new ArrayList<SubscriptionInfo>();
+ if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+ slotId = getSlotId(getDefaultSubId());
+ }
+ if (!SubscriptionManager.isValidSlotId(slotId)) {
+ if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
+ return null;
+ }
+
+ if (needCheck && !isSubInfoReady()) {
+ if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
+ return null;
+ }
+
+ Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+ null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
+ new String[]{String.valueOf(slotId)}, null);
+ ArrayList<SubscriptionInfo> subList = null;
+ try {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SubscriptionInfo subInfo = getSubInfoRecord(cursor);
+ if (subInfo != null) {
+ if (subList == null) {
+ subList = new ArrayList<SubscriptionInfo>();
+ }
+ subList.add(subInfo);
}
- subList.add(subInfo);
}
}
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
+ if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
- return subList;
+ return subList;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private void validateSubId(int subId) {
@@ -1453,7 +1636,7 @@
*/
@Override
public int[] getActiveSubIdList() {
- Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
+ Set<Entry<Integer, Integer>> simInfoSet = sSlotIdxToSubId.entrySet();
if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);
int[] subIdArr = new int[simInfoSet.size()];
@@ -1468,39 +1651,28 @@
return subIdArr;
}
- private boolean isActiveSubId(int subId) {
- boolean retVal = false;
-
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
- if (VDBG) logdl("[isActiveSubId] simInfoSet=" + simInfoSet);
-
- for (Entry<Integer, Integer> entry: simInfoSet) {
- if (subId == entry.getValue()) {
- retVal = true;
- break;
- }
- }
- }
+ @Override
+ public boolean isActiveSubId(int subId) {
+ boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
+ && sSlotIdxToSubId.containsValue(subId);
if (VDBG) logdl("[isActiveSubId]- " + retVal);
return retVal;
}
/**
- * Get the SIM state for the subscriber
+ * Get the SIM state for the slot idx
* @return SIM state as the ordinal of {@See IccCardConstants.State}
*/
@Override
- public int getSimStateForSubscriber(int subId) {
+ public int getSimStateForSlotIdx(int slotIdx) {
State simState;
String err;
- int phoneIdx = getPhoneId(subId);
- if (phoneIdx < 0) {
+ if (slotIdx < 0) {
simState = IccCardConstants.State.UNKNOWN;
- err = "invalid PhoneIdx";
+ err = "invalid slotIdx";
} else {
- Phone phone = PhoneFactory.getPhone(phoneIdx);
+ Phone phone = PhoneFactory.getPhone(slotIdx);
if (phone == null) {
simState = IccCardConstants.State.UNKNOWN;
err = "phone == null";
@@ -1515,11 +1687,106 @@
}
}
}
- if (DBG) logd("getSimStateForSubscriber: " + err + " simState=" + simState
+ if (DBG) logd("getSimStateForSlotIdx: " + err + " simState=" + simState
+ " ordinal=" + simState.ordinal());
return simState.ordinal();
}
+ /**
+ * Store properties associated with SubscriptionInfo in database
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in database associated with SubscriptionInfo
+ * @param propValue Value to store in DB for particular subId & column name
+ * @hide
+ */
+ @Override
+ public void setSubscriptionProperty(int subId, String propKey, String propValue) {
+ enforceModifyPhoneState("setSubscriptionProperty");
+ final long token = Binder.clearCallingIdentity();
+ ContentResolver resolver = mContext.getContentResolver();
+ ContentValues value = new ContentValues();
+ switch (propKey) {
+ case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
+ case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
+ case SubscriptionManager.CB_AMBER_ALERT:
+ case SubscriptionManager.CB_EMERGENCY_ALERT:
+ case SubscriptionManager.CB_ALERT_SOUND_DURATION:
+ case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
+ case SubscriptionManager.CB_ALERT_VIBRATE:
+ case SubscriptionManager.CB_ALERT_SPEECH:
+ case SubscriptionManager.CB_ETWS_TEST_ALERT:
+ case SubscriptionManager.CB_CHANNEL_50_ALERT:
+ case SubscriptionManager.CB_CMAS_TEST_ALERT:
+ case SubscriptionManager.CB_OPT_OUT_DIALOG:
+ value.put(propKey, Integer.parseInt(propValue));
+ break;
+ default:
+ if(DBG) logd("Invalid column name");
+ break;
+ }
+
+ resolver.update(SubscriptionManager.CONTENT_URI, value,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+ "=" + Integer.toString(subId), null);
+ Binder.restoreCallingIdentity(token);
+ }
+
+ /**
+ * Store properties associated with SubscriptionInfo in 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) {
+ if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
+ return null;
+ }
+ String resultValue = null;
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
+ new String[]{propKey},
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+ new String[]{subId + ""}, null);
+
+ try {
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ switch (propKey) {
+ case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
+ case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
+ case SubscriptionManager.CB_AMBER_ALERT:
+ case SubscriptionManager.CB_EMERGENCY_ALERT:
+ case SubscriptionManager.CB_ALERT_SOUND_DURATION:
+ case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
+ case SubscriptionManager.CB_ALERT_VIBRATE:
+ case SubscriptionManager.CB_ALERT_SPEECH:
+ case SubscriptionManager.CB_ETWS_TEST_ALERT:
+ case SubscriptionManager.CB_CHANNEL_50_ALERT:
+ case SubscriptionManager.CB_CMAS_TEST_ALERT:
+ case SubscriptionManager.CB_OPT_OUT_DIALOG:
+ resultValue = cursor.getInt(0) + "";
+ break;
+ default:
+ if(DBG) logd("Invalid column name");
+ break;
+ }
+ } else {
+ if(DBG) logd("Valid row not present in db");
+ }
+ } else {
+ if(DBG) logd("Query failed");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
+ return resultValue;
+ }
+
private static void printStackTrace(String msg) {
RuntimeException re = new RuntimeException();
slogd("StackTrace - " + msg);
@@ -1553,13 +1820,14 @@
.from(mContext).getDefaultSmsPhoneId());
pw.flush();
- for (Entry<Integer, Integer> entry : mSlotIdxToSubId.entrySet()) {
- pw.println(" mSlotIdToSubIdMap[" + entry.getKey() + "]: subId=" + entry.getValue());
+ for (Entry<Integer, Integer> entry : sSlotIdxToSubId.entrySet()) {
+ pw.println(" sSlotIdxToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
}
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
- List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList();
+ List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(
+ mContext.getOpPackageName());
if (sirl != null) {
pw.println(" ActiveSubInfoList:");
for (SubscriptionInfo entry : sirl) {
@@ -1571,7 +1839,7 @@
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
- sirl = getAllSubInfoList();
+ sirl = getAllSubInfoList(mContext.getOpPackageName());
if (sirl != null) {
pw.println(" AllSubInfoList:");
for (SubscriptionInfo entry : sirl) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 736cf7c..64eaf15 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.READ_PHONE_STATE;
import android.app.ActivityManagerNative;
+import android.app.IUserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContentResolver;
@@ -26,14 +27,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.IPackageManager;
import android.os.AsyncResult;
import android.os.Handler;
+import android.os.IRemoteCallback;
import android.os.Message;
-import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.Rlog;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
@@ -42,8 +47,11 @@
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
+
import android.text.TextUtils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -58,6 +66,8 @@
private static final int EVENT_SIM_LOADED = 3;
private static final int EVENT_SIM_ABSENT = 4;
private static final int EVENT_SIM_LOCKED = 5;
+ private static final int EVENT_SIM_IO_ERROR = 6;
+ private static final int EVENT_SIM_UNKNOWN = 7;
private static final String ICCID_STRING_FOR_NO_SIM = "";
/**
@@ -91,6 +101,10 @@
private static String mIccId[] = new String[PROJECT_SIM_NUM];
private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
private SubscriptionManager mSubscriptionManager = null;
+ private IPackageManager mPackageManager;
+ // The current foreground user ID.
+ private int mCurrentlyActiveUserId;
+ private CarrierServiceBindHelper mCarrierServiceBindHelper;
public SubscriptionInfoUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {
logd("Constructor invoked");
@@ -98,11 +112,56 @@
mContext = context;
mPhone = phoneProxy;
mSubscriptionManager = SubscriptionManager.from(mContext);
+ mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
mContext.registerReceiver(sReceiver, intentFilter);
- intentFilter = new IntentFilter(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mContext.registerReceiver(sReceiver, intentFilter);
+
+ mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
+ initializeCarrierApps();
+ }
+
+ private void initializeCarrierApps() {
+ // Initialize carrier apps:
+ // -Now (on system startup)
+ // -Whenever new carrier privilege rules might change (new SIM is loaded)
+ // -Whenever we switch to a new user
+ mCurrentlyActiveUserId = 0;
+ try {
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(
+ new IUserSwitchObserver.Stub() {
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply)
+ throws RemoteException {
+ mCurrentlyActiveUserId = newUserId;
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
+
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ // Ignore.
+ }
+
+ @Override
+ public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {
+ // Ignore.
+ }
+ });
+ mCurrentlyActiveUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ logd("Couldn't get current user ID; guessing it's 0: " + e.getMessage());
+ }
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
}
private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
@@ -130,6 +189,10 @@
if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotId, -1));
+ } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
+ sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotId, -1));
+ } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
+ sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotId, -1));
} else {
logd("Ignoring simStatus: " + simStatus);
}
@@ -208,6 +271,9 @@
}
broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED,
uObj.reason);
+ if (!ICCID_STRING_FOR_NO_SIM.equals(mIccId[slotId])) {
+ updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+ }
break;
}
@@ -237,6 +303,14 @@
handleSimLocked(msg.arg1, (String) msg.obj);
break;
+ case EVENT_SIM_UNKNOWN:
+ updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
+ break;
+
+ case EVENT_SIM_IO_ERROR:
+ updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+ break;
+
default:
logd("Unknown msg:" + msg.what);
}
@@ -271,6 +345,8 @@
new QueryIccIdUserObj(reason, slotId)));
} else {
logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
+ updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+ broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
}
} else {
logd("sFh[" + slotId + "] is null, ignore");
@@ -373,7 +449,19 @@
logd("Invalid subId, could not update ContentResolver");
}
+ // Update set of enabled carrier apps now that the privilege rules may have changed.
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
+
broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
+ updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
+ }
+
+ private void updateCarrierServices(int slotId, String simState) {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ configManager.updateConfigForPhoneId(slotId, simState);
+ mCarrierServiceBindHelper.updateForPhoneId(slotId, simState);
}
private void handleSimAbsent(int slotId) {
@@ -384,6 +472,7 @@
if (isAllIccIdQueryDone()) {
updateSubscriptionInfoByIccId();
}
+ updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
}
/**
@@ -428,7 +517,8 @@
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
oldIccId[i] = null;
List<SubscriptionInfo> oldSubInfo =
- SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false);
+ SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false,
+ mContext.getOpPackageName());
if (oldSubInfo != null) {
oldIccId[i] = oldSubInfo.get(0).getIccId();
logd("updateSubscriptionInfoByIccId: oldSubId = "
@@ -523,6 +613,9 @@
}
}
+ // Ensure the modems are mapped correctly
+ mSubscriptionManager.setDefaultDataSubId(mSubscriptionManager.getDefaultDataSubId());
+
SubscriptionController.getInstance().notifySubscriptionInfoChanged();
logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete");
}
@@ -567,4 +660,9 @@
private void logd(String message) {
Rlog.d(LOG_TAG, message);
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("SubscriptionInfoUpdater:");
+ mCarrierServiceBindHelper.dump(fd, pw, args);
+ }
}
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 5f86aa5..fecd851 100755
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -18,6 +18,7 @@
package com.android.internal.telephony;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
@@ -25,7 +26,9 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
+import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -49,13 +52,7 @@
}
}
- public boolean
- updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu)
- throws android.os.RemoteException {
- return updateMessageOnIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage,
- index, status, pdu);
- }
-
+ @Override
public boolean
updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
byte[] pdu) throws android.os.RemoteException {
@@ -63,53 +60,39 @@
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
} else {
- Rlog.e(LOG_TAG,"updateMessageOnIccEf iccSmsIntMgr is null" +
+ Rlog.e(LOG_TAG,"updateMessageOnIccEfForSubscriber iccSmsIntMgr is null" +
" for Subscription: " + subId);
return false;
}
}
- public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc)
- throws android.os.RemoteException {
- return copyMessageToIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage, status,
- pdu, smsc);
- }
-
+ @Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
byte[] pdu, byte[] smsc) throws android.os.RemoteException {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
} else {
- Rlog.e(LOG_TAG,"copyMessageToIccEf iccSmsIntMgr is null" +
+ Rlog.e(LOG_TAG,"copyMessageToIccEfForSubscriber iccSmsIntMgr is null" +
" for Subscription: " + subId);
return false;
}
}
- public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage)
- throws android.os.RemoteException {
- return getAllMessagesFromIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage);
- }
-
+ @Override
public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage)
throws android.os.RemoteException {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
} else {
- Rlog.e(LOG_TAG,"getAllMessagesFromIccEf iccSmsIntMgr is" +
+ Rlog.e(LOG_TAG,"getAllMessagesFromIccEfForSubscriber iccSmsIntMgr is" +
" null for Subscription: " + subId);
return null;
}
}
- public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- sendDataForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
- destPort, data, sentIntent, deliveryIntent);
- }
-
+ @Override
public void sendDataForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
@@ -118,6 +101,21 @@
iccSmsIntMgr.sendData(callingPackage, destAddr, scAddr, destPort, data,
sentIntent, deliveryIntent);
} else {
+ Rlog.e(LOG_TAG,"sendDataForSubscriber iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ // TODO: Use a more specific error code to replace RESULT_ERROR_GENERIC_FAILURE.
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ }
+ }
+
+ public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPackage,
+ String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ iccSmsIntMgr.sendDataWithSelfPermissions(callingPackage, destAddr, scAddr, destPort, data,
+ sentIntent, deliveryIntent);
+ } else {
Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
" Subscription: " + subId);
}
@@ -126,15 +124,31 @@
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
- text, sentIntent, deliveryIntent);
+ text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
}
+ @Override
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
- String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessageForNonDefaultSmsApp) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
- deliveryIntent);
+ deliveryIntent, persistMessageForNonDefaultSmsApp);
+ } else {
+ Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" +
+ " Subscription: " + subId);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ }
+ }
+
+ public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPackage,
+ String destAddr, String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ iccSmsIntMgr.sendTextWithSelfPermissions(callingPackage, destAddr, scAddr, text,
+ sentIntent, deliveryIntent);
} else {
Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
" Subscription: " + subId);
@@ -145,83 +159,67 @@
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
- scAddr, parts, sentIntents, deliveryIntents);
+ scAddr, parts, sentIntents, deliveryIntents,
+ true /* persistMessageForNonDefaultSmsApp */);
}
+ @Override
public void sendMultipartTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents)
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
throws android.os.RemoteException {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
- deliveryIntents);
+ deliveryIntents, persistMessageForNonDefaultSmsApp);
} else {
- Rlog.e(LOG_TAG,"sendMultipartText iccSmsIntMgr is null for" +
+ Rlog.e(LOG_TAG,"sendMultipartTextForSubscriber iccSmsIntMgr is null for" +
" Subscription: " + subId);
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
- public boolean enableCellBroadcast(int messageIdentifier, int ranType)
- throws android.os.RemoteException {
- return enableCellBroadcastForSubscriber(getPreferredSmsSubscription(), messageIdentifier,
- ranType);
- }
-
+ @Override
public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
throws android.os.RemoteException {
return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
- public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType)
- throws android.os.RemoteException {
- return enableCellBroadcastRangeForSubscriber(getPreferredSmsSubscription(), startMessageId,
- endMessageId, ranType);
- }
-
+ @Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
int endMessageId, int ranType) throws android.os.RemoteException {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId, ranType);
} else {
- Rlog.e(LOG_TAG,"enableCellBroadcast iccSmsIntMgr is null for" +
+ Rlog.e(LOG_TAG,"enableCellBroadcastRangeForSubscriber iccSmsIntMgr is null for" +
" Subscription: " + subId);
}
return false;
}
- public boolean disableCellBroadcast(int messageIdentifier, int ranType)
- throws android.os.RemoteException {
- return disableCellBroadcastForSubscriber(getPreferredSmsSubscription(), messageIdentifier,
- ranType);
- }
-
+ @Override
public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
throws android.os.RemoteException {
return disableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
- public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType)
- throws android.os.RemoteException {
- return disableCellBroadcastRangeForSubscriber(getPreferredSmsSubscription(), startMessageId,
- endMessageId, ranType);
- }
-
+ @Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
int endMessageId, int ranType) throws android.os.RemoteException {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId, ranType);
} else {
- Rlog.e(LOG_TAG,"disableCellBroadcast iccSmsIntMgr is null for" +
+ Rlog.e(LOG_TAG,"disableCellBroadcastRangeForSubscriber iccSmsIntMgr is null for" +
" Subscription:"+subId);
}
return false;
}
+ @Override
public int getPremiumSmsPermission(String packageName) {
return getPremiumSmsPermissionForSubscriber(getPreferredSmsSubscription(), packageName);
}
@@ -232,12 +230,13 @@
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.getPremiumSmsPermission(packageName);
} else {
- Rlog.e(LOG_TAG, "getPremiumSmsPermission iccSmsIntMgr is null");
+ Rlog.e(LOG_TAG, "getPremiumSmsPermissionForSubscriber iccSmsIntMgr is null");
}
//TODO Rakesh
return 0;
}
+ @Override
public void setPremiumSmsPermission(String packageName, int permission) {
setPremiumSmsPermissionForSubscriber(getPreferredSmsSubscription(), packageName, permission);
}
@@ -248,21 +247,17 @@
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.setPremiumSmsPermission(packageName, permission);
} else {
- Rlog.e(LOG_TAG, "setPremiumSmsPermission iccSmsIntMgr is null");
+ Rlog.e(LOG_TAG, "setPremiumSmsPermissionForSubscriber iccSmsIntMgr is null");
}
}
- public boolean isImsSmsSupported() {
- return isImsSmsSupportedForSubscriber(getPreferredSmsSubscription());
- }
-
@Override
public boolean isImsSmsSupportedForSubscriber(int subId) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.isImsSmsSupported();
} else {
- Rlog.e(LOG_TAG, "isImsSmsSupported iccSmsIntMgr is null");
+ Rlog.e(LOG_TAG, "isImsSmsSupportedForSubscriber iccSmsIntMgr is null");
}
return false;
}
@@ -300,35 +295,39 @@
return false;
}
- public String getImsSmsFormat() {
- return getImsSmsFormatForSubscriber(getPreferredSmsSubscription());
- }
-
@Override
public String getImsSmsFormatForSubscriber(int subId) {
- IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.getImsSmsFormat();
} else {
- Rlog.e(LOG_TAG, "getImsSmsFormat iccSmsIntMgr is null");
+ Rlog.e(LOG_TAG, "getImsSmsFormatForSubscriber iccSmsIntMgr is null");
}
return null;
}
@Override
- public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- injectSmsPdu(SubscriptionManager.getDefaultSmsSubId(), pdu, format, receivedIntent);
- }
-
- // FIXME: Add injectSmsPdu to ISms.aidl
- public void injectSmsPdu(int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
- getIccSmsInterfaceManager(subId).injectSmsPdu(pdu, format, receivedIntent);
+ public void injectSmsPduForSubscriber(
+ int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ iccSmsIntMgr.injectSmsPdu(pdu, format, receivedIntent);
+ } else {
+ Rlog.e(LOG_TAG, "injectSmsPduForSubscriber iccSmsIntMgr is null");
+ // RESULT_SMS_GENERIC_ERROR is documented for injectSmsPdu
+ sendErrorInPendingIntent(receivedIntent, Intents.RESULT_SMS_GENERIC_ERROR);
+ }
}
/**
* get sms interface manager object based on subscription.
**/
- private IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+ private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+ if (!isActiveSubId(subId)) {
+ Rlog.e(LOG_TAG, "Subscription " + subId + " is inactive.");
+ return null;
+ }
+
int phoneId = SubscriptionController.getInstance().getPhoneId(subId) ;
//Fixme: for multi-subscription case
if (!SubscriptionManager.isValidPhoneId(phoneId)
@@ -341,24 +340,26 @@
((PhoneProxy)mPhone[(int)phoneId]).getIccSmsInterfaceManager();
} catch (NullPointerException e) {
Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
- e.printStackTrace(); //This will print stact trace
+ e.printStackTrace();
return null;
} catch (ArrayIndexOutOfBoundsException e) {
Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
- e.printStackTrace(); //This will print stack trace
+ e.printStackTrace();
return null;
}
}
/**
Gets User preferred SMS subscription */
+ @Override
public int getPreferredSmsSubscription() {
- return SubscriptionController.getInstance().getDefaultSmsSubId();
+ return SubscriptionController.getInstance().getDefaultSmsSubId();
}
/**
* Get SMS prompt property, enabled or not
**/
+ @Override
public boolean isSMSPromptEnabled() {
return PhoneFactory.isSMSPromptEnabled();
}
@@ -372,6 +373,7 @@
deliveryIntent);
} else {
Rlog.e(LOG_TAG,"sendStoredText iccSmsIntMgr is null for subscription: " + subId);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
@@ -386,7 +388,29 @@
} else {
Rlog.e(LOG_TAG,"sendStoredMultipartText iccSmsIntMgr is null for subscription: "
+ subId);
+ sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
+ /*
+ * @return true if the subId is active.
+ */
+ private boolean isActiveSubId(int subId) {
+ return SubscriptionController.getInstance().isActiveSubId(subId);
+ }
+
+ private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
+ if (intent != null) {
+ try {
+ intent.send(errorCode);
+ } catch (PendingIntent.CanceledException ex) {
+ }
+ }
+ }
+
+ private void sendErrorInPendingIntents(List<PendingIntent> intents, int errorCode) {
+ for (PendingIntent intent : intents) {
+ sendErrorInPendingIntent(intent, errorCode);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index a89e2f0..8b9d629 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -21,6 +21,7 @@
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
import android.app.Activity;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -34,7 +35,9 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.IDeviceIdleController;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
@@ -60,9 +63,12 @@
*/
public class WapPushOverSms implements ServiceConnection {
private static final String TAG = "WAP PUSH";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private final Context mContext;
+ IDeviceIdleController mDeviceIdleController;
+
+ private String mWapPushManagerPackage;
/** Assigned from ServiceConnection callback on main threaad. */
private volatile IWapPushManager mWapPushManager;
@@ -81,12 +87,15 @@
public WapPushOverSms(Context context) {
mContext = context;
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
Intent intent = new Intent(IWapPushManager.class.getName());
ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !context.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
Rlog.e(TAG, "bindService() for wappush manager failed");
} else {
+ mWapPushManagerPackage = comp.getPackageName();
if (DBG) Rlog.v(TAG, "bindService() for wappush manager succeeded");
}
}
@@ -230,6 +239,9 @@
if (wapPushMan == null) {
if (DBG) Rlog.w(TAG, "wap push manager not found!");
} else {
+ mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+ mWapPushManagerPackage, 0, "mms-mgr");
+
Intent intent = new Intent();
intent.putExtra("transactionId", transactionId);
intent.putExtra("pduType", pduType);
@@ -283,14 +295,23 @@
// Direct the intent to only the default MMS app. If we can't find a default MMS app
// then sent it to all broadcast receivers.
ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
+ Bundle options = null;
if (componentName != null) {
// Deliver MMS message only to this receiver
intent.setComponent(componentName);
if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
+ try {
+ long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+ componentName.getPackageName(), 0, "mms-app");
+ BroadcastOptions bopts = BroadcastOptions.makeBasic();
+ bopts.setTemporaryAppWhitelistDuration(duration);
+ options = bopts.toBundle();
+ } catch (RemoteException e) {
+ }
}
- handler.dispatchIntent(intent, permission, appOp, receiver, UserHandle.OWNER);
+ handler.dispatchIntent(intent, permission, appOp, options, receiver, UserHandle.OWNER);
return Activity.RESULT_OK;
} catch (ArrayIndexOutOfBoundsException aie) {
// 0-byte WAP PDU or other unexpected WAP PDU contents can easily throw this;
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 098b481..c4f1dfc 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -961,6 +961,7 @@
case PRFRMD_WITH_MODIFICATION:
case PRFRMD_NAA_NOT_ACTIVE:
case PRFRMD_TONE_NOT_PLAYED:
+ case LAUNCH_BROWSER_ERROR:
case TERMINAL_CRNTLY_UNABLE_TO_PROCESS:
switch (type) {
case SET_UP_MENU:
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 882741f..ad84a82 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -65,6 +65,22 @@
static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
+ // As per TS 102.223 Annex C, Structure of CAT communications,
+ // the APDU length can be max 255 bytes. This leaves only 239 bytes for user
+ // input string. CMD details TLV + Device IDs TLV + Result TLV + Other
+ // details of TextString TLV not including user input take 16 bytes.
+ //
+ // If UCS2 encoding is used, maximum 118 UCS2 chars can be encoded in 238 bytes.
+ // Each UCS2 char takes 2 bytes. Byte Order Mask(BOM), 0xFEFF takes 2 bytes.
+ //
+ // If GSM 7 bit default(use 8 bits to represent a 7 bit char) format is used,
+ // maximum 239 chars can be encoded in 239 bytes since each char takes 1 byte.
+ //
+ // No issues for GSM 7 bit packed format encoding.
+
+ private static final int MAX_GSM7_DEFAULT_CHARS = 239;
+ private static final int MAX_UCS2_CHARS = 118;
+
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
IccFileHandler fh) {
if (sInstance != null) {
@@ -499,6 +515,18 @@
input.packed = (cmdDet.commandQualifier & 0x08) != 0;
input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
+ // Truncate the maxLen if it exceeds the max number of chars that can
+ // be encoded. Limit depends on DCS in Command Qualifier.
+ if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) {
+ CatLog.d(this, "UCS2: received maxLen = " + input.maxLen +
+ ", truncating to " + MAX_UCS2_CHARS);
+ input.maxLen = MAX_UCS2_CHARS;
+ } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) {
+ CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen +
+ ", truncating to " + MAX_GSM7_DEFAULT_CHARS);
+ input.maxLen = MAX_GSM7_DEFAULT_CHARS;
+ }
+
mCmdParams = new GetInputParams(cmdDet, input);
if (iconId != null) {
diff --git a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 21cabf6..f3f04ae 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -237,6 +237,12 @@
return (mSimRecords != null) ? mSimRecords.getGid1() : "";
}
+ // return GID2 from USIM
+ @Override
+ public String getGroupIdLevel2() {
+ return (mSimRecords != null) ? mSimRecords.getGid2() : "";
+ }
+
@Override
public String getImei() {
return mImei;
diff --git a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 0fdd3db..6bed5ac 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -24,6 +24,7 @@
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -70,7 +71,6 @@
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.uicc.IccException;
import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.RuimRecords;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
@@ -407,33 +407,60 @@
@Override
public Connection
dial (String dialString, int videoState) throws CallStateException {
- ImsPhone imsPhone = mImsPhone;
+ return dial(dialString, null, videoState, null);
+ }
- boolean imsUseEnabled =
- ImsManager.isVolteEnabledByPlatform(mContext) &&
- ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
- ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext);
- if (!imsUseEnabled) {
- Rlog.w(LOG_TAG, "IMS is disabled: forced to CS");
+
+ @Override
+ protected Connection
+ dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException {
+ // Need to make sure dialString gets parsed properly
+ String newDialString = PhoneNumberUtils.stripSeparators(dialString);
+ return mCT.dial(newDialString);
+ }
+
+ @Override
+ public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException {
+ if (uusInfo != null) {
+ throw new CallStateException("Sending UUS information NOT supported in CDMA!");
}
+ boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
+ ImsPhone imsPhone = mImsPhone;
+
+ boolean imsUseEnabled = isImsUseEnabled()
+ && imsPhone != null
+ && (imsPhone.isVolteEnabled() || imsPhone.isVowifiEnabled())
+ && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
+
+ boolean useImsForEmergency = ImsManager.isVolteEnabledByPlatform(mContext)
+ && imsPhone != null
+ && isEmergency
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.useImsAlwaysForEmergencyCall)
+ && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
+ && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
+
if (DBG) {
- Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone
+ Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled
+ + ", useImsForEmergency=" + useImsForEmergency
+ + ", imsPhone=" + imsPhone
+ ", imsPhone.isVolteEnabled()="
+ ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+ + ", imsPhone.isVowifiEnabled()="
+ + ((imsPhone != null) ? imsPhone.isVowifiEnabled() : "N/A")
+ ", imsPhone.getServiceState().getState()="
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
- if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled()
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
- && !PhoneNumberUtils.isEmergencyNumber(dialString))
- || (PhoneNumberUtils.isEmergencyNumber(dialString)
- && mContext.getResources().getBoolean(
- com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) {
+ ImsPhone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
+
+ if (imsUseEnabled || useImsForEmergency) {
try {
if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call");
- return imsPhone.dial(dialString, videoState);
+ return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
} catch (CallStateException e) {
if (DBG) Rlog.d(LOG_TAG, "IMS PS call exception " + e +
"imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
@@ -445,24 +472,12 @@
}
}
+ if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
+ && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
+ throw new CallStateException("cannot dial in current state");
+ }
if (DBG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call");
- return dialInternal(dialString, null, videoState);
- }
-
-
- @Override
- protected Connection
- dialInternal (String dialString, UUSInfo uusInfo,
- int videoState) throws CallStateException {
- // Need to make sure dialString gets parsed properly
- String newDialString = PhoneNumberUtils.stripSeparators(dialString);
- return mCT.dial(newDialString);
- }
-
- @Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
- throws CallStateException {
- throw new CallStateException("Sending UUS information NOT supported in CDMA!");
+ return dialInternal(dialString, null, videoState, intentExtras);
}
@Override
@@ -614,6 +629,12 @@
}
@Override
+ public String getGroupIdLevel2() {
+ Rlog.e(LOG_TAG, "GID2 is not available in CDMA");
+ return null;
+ }
+
+ @Override
public String getImei() {
Rlog.e(LOG_TAG, "getImei() called for CDMAPhone");
return mImei;
@@ -1123,9 +1144,14 @@
sendEmergencyCallbackModeChange();
// Re-initiate data connection
mDcTracker.setInternalDataEnabled(true);
+ notifyEmergencyCallRegistrants(false);
}
}
+ protected void notifyEmergencyCallRegistrants(boolean started) {
+ mEmergencyCallToggledRegistrants.notifyResult(started ? 1 : 0);
+ }
+
/**
* Handle to cancel or restart Ecm timer in emergency call back mode
* if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled;
@@ -1195,6 +1221,7 @@
mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
+ startLceAfterRadioIsAvailable();
}
break;
@@ -1257,6 +1284,9 @@
case EVENT_RADIO_ON:{
Rlog.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
+ // If this is on APM off, SIM may already be loaded. Send setPreferredNetworkType
+ // request to RIL to preserve user setting across APM toggling
+ setPreferredNetworkTypeIfSimLoaded();
}
break;
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 59115f5..3903a2f 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -36,6 +36,7 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DriverCall;
+import com.android.internal.telephony.LastCallFailCause;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneConstants;
@@ -687,6 +688,7 @@
if( mPendingCallInEcm) {
mPendingCallInEcm = false;
}
+ checkAndEnableDataCallAfterEmergencyCallDropped();
}
if (newRinging != null) {
@@ -1006,6 +1008,7 @@
case EVENT_GET_LAST_CALL_FAIL_CAUSE:
int causeCode;
+ String vendorCause = null;
ar = (AsyncResult)msg.obj;
operationComplete();
@@ -1017,7 +1020,9 @@
Rlog.i(LOG_TAG,
"Exception during getLastCallFailCause, assuming normal disconnect");
} else {
- causeCode = ((int[])ar.result)[0];
+ LastCallFailCause failCause = (LastCallFailCause)ar.result;
+ causeCode = failCause.causeCode;
+ vendorCause = failCause.vendorCause;
}
for (int i = 0, s = mDroppedDuringPoll.size()
@@ -1025,7 +1030,7 @@
) {
CdmaConnection conn = mDroppedDuringPoll.get(i);
- conn.onRemoteDisconnect(causeCode);
+ conn.onRemoteDisconnect(causeCode, vendorCause);
}
updatePhoneState();
@@ -1118,6 +1123,7 @@
if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
mIsInEmergencyCall = true;
mPhone.mDcTracker.setInternalDataEnabled(false);
+ mPhone.notifyEmergencyCallRegistrants(true);
}
}
@@ -1135,6 +1141,7 @@
if (inEcm.compareTo("false") == 0) {
// Re-initiate data connection
mPhone.mDcTracker.setInternalDataEnabled(true);
+ mPhone.notifyEmergencyCallRegistrants(false);
}
}
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 57389b4..9ad3a99 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -64,6 +64,7 @@
int mCause = DisconnectCause.NOT_DISCONNECTED;
PostDialState mPostDialState = PostDialState.NOT_STARTED;
int mPreciseCause = 0;
+ String mVendorCause;
Handler mHandler;
@@ -136,9 +137,10 @@
mHandler = new MyHandler(mOwner.getLooper());
mDialString = dialString;
- Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString);
+ Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + maskDialString(dialString));
dialString = formatDialString(dialString);
- Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString);
+ Rlog.d(LOG_TAG,
+ "[CDMAConn] CdmaConnection:formated dialString=" + maskDialString(dialString));
mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
@@ -322,6 +324,7 @@
onHangupLocal() {
mCause = DisconnectCause.LOCAL;
mPreciseCause = 0;
+ mVendorCause = null;
}
/**
@@ -399,8 +402,9 @@
}
/*package*/ void
- onRemoteDisconnect(int causeCode) {
+ onRemoteDisconnect(int causeCode, String vendorCause) {
this.mPreciseCause = causeCode;
+ this.mVendorCause = vendorCause;
onDisconnect(disconnectCauseFromCode(causeCode));
}
@@ -911,6 +915,14 @@
Rlog.d(LOG_TAG, "[CDMAConn] " + msg);
}
+ private String maskDialString(String dialString) {
+ if (VDBG) {
+ return dialString;
+ }
+
+ return "<MASKED>";
+ }
+
@Override
public int getNumberPresentation() {
return mNumberPresentation;
@@ -927,6 +939,11 @@
}
@Override
+ public String getVendorDisconnectCause() {
+ return mVendorCause;
+ }
+
+ @Override
public Connection getOrigConnection() {
return null;
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
index 981dec6..7531bb9 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
@@ -167,7 +167,7 @@
public String toString() {
return "CdmaNumberInfoRec: {" +
" id: " + CdmaInformationRecords.idToString(id) +
- ", number: " + number +
+ ", number: <MASKED>" +
", numberType: " + numberType +
", numberPlan: " + numberPlan +
", pi: " + pi +
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index fac1012b..18689e2 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -25,7 +25,10 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.dataconnection.DcTrackerBase;
+import com.android.internal.telephony.PhoneConstants;
import android.telephony.CellInfo;
import android.telephony.CellInfoLte;
@@ -276,8 +279,10 @@
setSignalStrengthDefaultValues();
mGotCountryCode = false;
- pollStateDone();
- break;
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ != mSS.getRilDataRadioTechnology()) {
+ pollStateDone();
+ }
default:
// Issue all poll-related commands at once, then count
@@ -303,22 +308,7 @@
@Override
protected void pollStateDone() {
- log("pollStateDone: lte 1 ss=[" + mSS + "] newSS=[" + mNewSS + "]");
-
- if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric()) ||
- mPhone.isSidMarkedAsNonRoaming(mNewSS.getSystemId())) {
- log("pollStateDone: override - marked as non-roaming.");
- mNewSS.setVoiceRoaming(false);
- mNewSS.setDataRoaming(false);
- mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
- } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric()) ||
- mPhone.isSidMarkedAsRoaming(mNewSS.getSystemId())) {
- log("pollStateDone: override - marked as roaming.");
- mNewSS.setVoiceRoaming(true);
- mNewSS.setDataRoaming(true);
- mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
- mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
- }
+ updateRoamingState();
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
mNewSS.setVoiceRoaming(true);
@@ -326,6 +316,8 @@
}
useDataRegStateForDataOnlyDevices();
+ resetServiceStateInIwlanMode();
+ log("pollStateDone: lte 1 ss=[" + mSS + "] newSS=[" + mNewSS + "]");
boolean hasRegistered = mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
&& mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
@@ -427,7 +419,12 @@
if (hasDataRadioTechnologyChanged) {
tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
- }
+
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ log("pollStateDone: IWLAN enabled");
+ }
+ }
if (hasRegistered) {
mNetworkAttachedRegistrants.notifyRegistrants();
@@ -463,9 +460,11 @@
}
if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY &&
- mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)) {
+ mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)
+ && mSS.getRilVoiceRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
// SIM is found on the device. If ERI roaming is OFF, and SID/NID matches
- // one configured in SIM, use operator name from CSIM record.
+ // one configured in SIM, use operator name from CSIM record. Note that ERI, SID,
+ // and NID are CDMA only, not applicable to LTE.
boolean showSpn =
((RuimRecords)mIccRecords).getCsimSpnDisplayCondition();
int iconIndex = mSS.getCdmaEriIconIndex();
@@ -538,7 +537,12 @@
if ((hasCdmaDataConnectionChanged || hasDataRadioTechnologyChanged)) {
notifyDataRegStateRilRadioTechnologyChanged();
- mPhone.notifyDataConnection(null);
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+ } else {
+ mPhone.notifyDataConnection(null);
+ }
}
if (hasVoiceRoamingOn) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index f585c0f..8660d98 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -109,32 +109,44 @@
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, destPort, data, (deliveryIntent != null));
- HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
- SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
- false /*isText*/);
+ if (pdu != null) {
+ HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
+ SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+ null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
+ false /*isText*/, true /*persistMessage*/);
- String carrierPackage = getCarrierAppPackageName();
- if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
- DataSmsSender smsSender = new DataSmsSender(tracker);
- smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+ String carrierPackage = getCarrierAppPackageName();
+ if (carrierPackage != null) {
+ Rlog.d(TAG, "Found carrier package.");
+ DataSmsSender smsSender = new DataSmsSender(tracker);
+ smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+ } else {
+ Rlog.v(TAG, "No carrier package.");
+ sendSubmitPdu(tracker);
+ }
} else {
- Rlog.v(TAG, "No carrier package.");
- sendSubmitPdu(tracker);
+ Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null");
+ if (sentIntent != null) {
+ try {
+ sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (CanceledException ex) {
+ Rlog.e(TAG, "Intent has been canceled!");
+ }
+ }
}
}
/** {@inheritDoc} */
@Override
protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+ PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+ boolean persistMessage) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null), null);
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- messageUri, false /*isExpectMore*/, text, true /*isText*/);
+ messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
@@ -147,6 +159,13 @@
}
} else {
Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");
+ if (sentIntent != null) {
+ try {
+ sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (CanceledException ex) {
+ Rlog.e(TAG, "Intent has been canceled!");
+ }
+ }
}
}
@@ -191,7 +210,8 @@
message, submitPdu);
return getSmsTracker(map, sentIntent, deliveryIntent,
getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
- false /*isExpextMore*/, fullMessageText, true /*isText*/);
+ false /*isExpextMore*/, fullMessageText, true /*isText*/,
+ true /*persistMessage*/);
}
@Override
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 2ff9c51..d036165 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -25,9 +25,12 @@
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -49,6 +52,7 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.CommandsInterface.RadioState;
import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -568,7 +572,13 @@
String plmn = mSS.getOperatorAlphaLong();
boolean showPlmn = false;
- if (!TextUtils.equals(plmn, mCurPlmn)) {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+
+ if (mSubId != subId || !TextUtils.equals(plmn, mCurPlmn)) {
// Allow A blank plmn, "" to set showPlmn to true. Previously, we
// would set showPlmn to true only if plmn was not empty, i.e. was not
// null and not blank. But this would cause us to incorrectly display
@@ -576,7 +586,7 @@
showPlmn = plmn != null;
if (DBG) {
log(String.format("updateSpnDisplay: changed sending intent" +
- " showPlmn='%b' plmn='%s'", showPlmn, plmn));
+ " showPlmn='%b' plmn='%s' subId='%d'", showPlmn, plmn, subId));
}
Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -593,6 +603,7 @@
}
}
+ mSubId = subId;
mCurShowSpn = false;
mCurShowPlmn = showPlmn;
mCurSpn = "";
@@ -815,12 +826,6 @@
return;
}
- if (!mCi.getRadioState().isOn()) {
- // Radio has crashed or turned off.
- cancelPollState();
- return;
- }
-
if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
loge("handlePollStateResult: RIL returned an error where it must succeed"
+ ar.exception);
@@ -1022,8 +1027,10 @@
setSignalStrengthDefaultValues();
mGotCountryCode = false;
- pollStateDone();
- break;
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ != mSS.getRilDataRadioTechnology()) {
+ pollStateDone();
+ }
default:
// Issue all poll-related commands at once, then count
@@ -1108,30 +1115,67 @@
}
}
- protected void pollStateDone() {
- if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
+ /**
+ * Query the carrier configuration to determine if there are any network overrides
+ * for roaming or not roaming for the current service state.
+ */
+ protected void updateRoamingState() {
+ // Save the roaming state before carrier config possibly overrides it.
+ mNewSS.setDataRoamingFromRegistration(mNewSS.getDataRoaming());
- if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric()) ||
- mPhone.isSidMarkedAsNonRoaming(mNewSS.getSystemId())) {
- log("pollStateDone: override - marked as non-roaming.");
- mNewSS.setVoiceRoaming(false);
- mNewSS.setDataRoaming(false);
- mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
- } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric()) ||
- mPhone.isSidMarkedAsRoaming(mNewSS.getSystemId())) {
- log("pollStateDone: override - marked as roaming.");
- mNewSS.setVoiceRoaming(true);
- mNewSS.setDataRoaming(true);
- mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
- mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
+ ICarrierConfigLoader configLoader =
+ (ICarrierConfigLoader) ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
+ if (configLoader != null) {
+ try {
+ PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
+ String systemId = Integer.toString(mNewSS.getSystemId());
+
+ if (alwaysOnHomeNetwork(b)) {
+ log("updateRoamingState: carrier config override always on home network");
+ setRoamingOff();
+ } else if (isNonRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
+ || isNonRoamingInCdmaNetwork(b, systemId)) {
+ log("updateRoamingState: carrier config override set non-roaming:"
+ + mNewSS.getOperatorNumeric() + ", " + systemId);
+ setRoamingOff();
+ } else if (isRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
+ || isRoamingInCdmaNetwork(b, systemId)) {
+ log("updateRoamingState: carrier config override set roaming:"
+ + mNewSS.getOperatorNumeric() + ", " + systemId);
+ setRoamingOn();
+ }
+ } catch (RemoteException e) {
+ loge("updateRoamingState: unable to access carrier config service");
+ }
+ } else {
+ log("updateRoamingState: no carrier config service available");
}
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
mNewSS.setVoiceRoaming(true);
mNewSS.setDataRoaming(true);
}
+ }
+
+ private void setRoamingOn() {
+ mNewSS.setVoiceRoaming(true);
+ mNewSS.setDataRoaming(true);
+ mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
+ mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
+ }
+
+ private void setRoamingOff() {
+ mNewSS.setVoiceRoaming(false);
+ mNewSS.setDataRoaming(false);
+ mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
+ }
+
+ protected void pollStateDone() {
+ updateRoamingState();
useDataRegStateForDataOnlyDevices();
+ resetServiceStateInIwlanMode();
+ if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
boolean hasRegistered =
mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
@@ -1198,6 +1242,11 @@
if (hasRilDataRadioTechnologyChanged) {
tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
+
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ log("pollStateDone: IWLAN enabled");
+ }
}
if (hasRegistered) {
@@ -1283,7 +1332,12 @@
if (hasCdmaDataConnectionChanged || hasRilDataRadioTechnologyChanged) {
notifyDataRegStateRilRadioTechnologyChanged();
- mPhone.notifyDataConnection(null);
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+ } else {
+ mPhone.notifyDataConnection(null);
+ }
}
if (hasVoiceRoamingOn) {
diff --git a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java b/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 05800b5..58c6e55 100644
--- a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -24,9 +24,10 @@
import android.telephony.Rlog;
import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
@@ -489,31 +490,7 @@
isEntireMsg) {
// We don't support single-segment EMS, so calculate for 16-bit
// TODO: Consider supporting single-segment EMS
- ted.codeUnitCount = msg.length();
- int octets = ted.codeUnitCount * 2;
- if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
- // If EMS is not supported, break down EMS into single segment SMS
- // and add page info " x/y".
- // In the case of UCS2 encoding type, we need 8 bytes for this
- // but we only have 6 bytes from UDH, so truncate the limit for
- // each segment by 2 bytes (1 char).
- int max_user_data_bytes_with_header =
- SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
- if (!android.telephony.SmsMessage.hasEmsSupport()) {
- // make sure total number of segments is less than 10
- if (octets <= 9 * (max_user_data_bytes_with_header - 2))
- max_user_data_bytes_with_header -= 2;
- }
-
- ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
- max_user_data_bytes_with_header;
- ted.codeUnitsRemaining = ((ted.msgCount *
- max_user_data_bytes_with_header) - octets) / 2;
- } else {
- ted.msgCount = 1;
- ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets)/2;
- }
- ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
+ return SmsMessageBase.calcUnicodeEncodingDetails(msg);
}
}
return ted;
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 2a1f1c5..081e9b7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -18,12 +18,17 @@
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
import android.net.NetworkConfig;
import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
+import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -118,6 +123,14 @@
mDcAc = dcac;
}
+ public synchronized void releaseDataConnection(String reason) {
+ if (mDcAc != null) {
+ mDcAc.tearDown(this, reason, null);
+ mDcAc = null;
+ }
+ setState(DctConstants.State.IDLE);
+ }
+
public synchronized PendingIntent getReconnectIntent() {
return mReconnectAlarmIntent;
}
@@ -127,12 +140,12 @@
}
public synchronized ApnSetting getApnSetting() {
- log("getApnSetting: apnSetting=" + mApnSetting);
+ if (DBG) log("getApnSetting: apnSetting=" + mApnSetting);
return mApnSetting;
}
public synchronized void setApnSetting(ApnSetting apnSetting) {
- log("setApnSetting: apnSetting=" + apnSetting);
+ if (DBG) log("setApnSetting: apnSetting=" + apnSetting);
mApnSetting = apnSetting;
}
@@ -262,43 +275,148 @@
public boolean isProvisioningApn() {
String provisioningApn = mContext.getResources()
.getString(R.string.mobile_provisioning_apn);
- if ((mApnSetting != null) && (mApnSetting.apn != null)) {
+ if (!TextUtils.isEmpty(provisioningApn) &&
+ (mApnSetting != null) && (mApnSetting.apn != null)) {
return (mApnSetting.apn.equals(provisioningApn));
} else {
return false;
}
}
- public void incRefCount() {
+ private final ArrayList<LocalLog> mLocalLogs = new ArrayList<LocalLog>();
+
+ public void requestLog(String str) {
synchronized (mRefCountLock) {
+ for (LocalLog l : mLocalLogs) {
+ l.log(str);
+ }
+ }
+ }
+
+ public void incRefCount(LocalLog log) {
+ synchronized (mRefCountLock) {
+ if (mRefCount == 0) {
+ // we wanted to leave the last in so it could actually capture the tear down
+ // of the network
+ requestLog("clearing log with size=" + mLocalLogs.size());
+ mLocalLogs.clear();
+ }
+ if (mLocalLogs.contains(log)) {
+ log.log("ApnContext.incRefCount has duplicate add - " + mRefCount);
+ } else {
+ mLocalLogs.add(log);
+ log.log("ApnContext.incRefCount - " + mRefCount);
+ }
if (mRefCount++ == 0) {
mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true);
}
}
}
- public void decRefCount() {
+ public void decRefCount(LocalLog log) {
synchronized (mRefCountLock) {
+ // leave the last log alive to capture the actual tear down
+ if (mRefCount != 1) {
+ if (mLocalLogs.remove(log)) {
+ log.log("ApnContext.decRefCount - " + mRefCount);
+ } else {
+ log.log("ApnContext.decRefCount didn't find log - " + mRefCount);
+ }
+ } else {
+ log.log("ApnContext.decRefCount - 1");
+ }
if (mRefCount-- == 1) {
mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);
}
}
}
+ private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
+
+ public void resetErrorCodeRetries() {
+ requestLog("ApnContext.resetErrorCodeRetries");
+ if (DBG) log("ApnContext.resetErrorCodeRetries");
+
+ String[] config = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_cell_retries_per_error_code);
+ synchronized (mRetriesLeftPerErrorCode) {
+ mRetriesLeftPerErrorCode.clear();
+
+ for (String c : config) {
+ String errorValue[] = c.split(",");
+ if (errorValue != null && errorValue.length == 2) {
+ int count = 0;
+ int errorCode = 0;
+ try {
+ errorCode = Integer.parseInt(errorValue[0]);
+ count = Integer.parseInt(errorValue[1]);
+ } catch (NumberFormatException e) {
+ log("Exception parsing config_retries_per_error_code: " + e);
+ continue;
+ }
+ if (count > 0 && errorCode > 0) {
+ mRetriesLeftPerErrorCode.put(errorCode, count);
+ }
+ } else {
+ log("Exception parsing config_retries_per_error_code: " + c);
+ }
+ }
+ }
+ }
+
+ public boolean restartOnError(int errorCode) {
+ boolean result = false;
+ int retriesLeft = 0;
+ synchronized(mRetriesLeftPerErrorCode) {
+ retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
+ switch (retriesLeft) {
+ case 0: {
+ // not set, never restart modem
+ break;
+ }
+ case 1: {
+ resetErrorCodeRetries();
+ result = true;
+ break;
+ }
+ default: {
+ mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
+ result = false;
+ }
+ }
+ }
+ String str = "ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft +
+ " and returned " + result;
+ if (DBG) log(str);
+ requestLog(str);
+ return result;
+ }
+
@Override
public synchronized String toString() {
// We don't print mDataConnection because its recursive.
- return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" + mWaitingApns +
- "} mWaitingApnsPermanentFailureCountDown=" + mWaitingApnsPermanentFailureCountDown +
- " mApnSetting={" + mApnSetting + "} mReason=" + mReason +
- " mDataEnabled=" + mDataEnabled + " mDependencyMet=" + mDependencyMet + "}";
+ return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" +
+ mWaitingApns + "} mWaitingApnsPermanentFailureCountDown=" +
+ mWaitingApnsPermanentFailureCountDown + " mApnSetting={" + mApnSetting +
+ "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" +
+ mDependencyMet + "}";
}
- protected void log(String s) {
+ private void log(String s) {
Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("ApnContext: " + this.toString());
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ synchronized (mRefCountLock) {
+ pw.println(toString());
+ if (mRefCount > 0) {
+ pw.increaseIndent();
+ for (LocalLog l : mLocalLogs) {
+ l.dump(fd, pw, args);
+ }
+ pw.decreaseIndent();
+ }
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index c759413..4cc7550 100755
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -16,10 +16,12 @@
package com.android.internal.telephony.dataconnection;
+import android.telephony.ServiceState;
import android.text.TextUtils;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.uicc.IccRecords;
import java.util.ArrayList;
import java.util.List;
@@ -56,12 +58,20 @@
*/
public final boolean carrierEnabled;
/**
+ * Radio Access Technology info
+ * To check what values can hold, refer to ServiceState.java.
+ * This should be spread to other technologies,
+ * but currently only used for LTE(14) and EHRPD(13).
+ */
+ private final int bearer;
+ /**
* Radio Access Technology info
- * To check what values can hold, refer to ServiceState.java.
+ * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
+ * technologies in ServiceState.
* This should be spread to other technologies,
* but currently only used for LTE(14) and EHRPD(13).
*/
- public final int bearer;
+ public final int bearerBitmask;
/* ID of the profile in the modem */
public final int profileId;
@@ -90,8 +100,8 @@
String mmsc, String mmsProxy, String mmsPort,
String user, String password, int authType, String[] types,
String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
- int profileId, boolean modemCognitive, int maxConns, int waitTime, int maxConnsTime,
- int mtu, String mvnoType, String mvnoMatchData) {
+ int bearerBitmask, int profileId, boolean modemCognitive, int maxConns, int waitTime,
+ int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
this.id = id;
this.numeric = numeric;
this.carrier = carrier;
@@ -112,6 +122,7 @@
this.roamingProtocol = roamingProtocol;
this.carrierEnabled = carrierEnabled;
this.bearer = bearer;
+ this.bearerBitmask = (bearerBitmask | ServiceState.getBitmaskForTech(bearer));
this.profileId = profileId;
this.modemCognitive = modemCognitive;
this.maxConns = maxConns;
@@ -139,12 +150,12 @@
* v2 format:
* [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
* <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
+ * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
*
* v3 format:
* [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
* <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
+ * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
* <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
* <mvnoType>, <mvnoMatchData>
*
@@ -181,7 +192,7 @@
String[] typeArray;
String protocol, roamingProtocol;
boolean carrierEnabled;
- int bearer = 0;
+ int bearerBitmask = 0;
int profileId = 0;
boolean modemCognitive = false;
int maxConns = 0;
@@ -196,7 +207,6 @@
protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
carrierEnabled = true;
- bearer = 0;
} else {
if (a.length < 18) {
return null;
@@ -206,10 +216,7 @@
roamingProtocol = a[15];
carrierEnabled = Boolean.parseBoolean(a[16]);
- try {
- bearer = Integer.parseInt(a[17]);
- } catch (NumberFormatException ex) {
- }
+ bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
if (a.length > 22) {
modemCognitive = Boolean.parseBoolean(a[19]);
@@ -234,8 +241,8 @@
}
return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
- a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,bearer,
- profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
+ a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,0,
+ bearerBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
mvnoType, mvnoMatchData);
}
@@ -286,6 +293,7 @@
sb.append(", ").append(roamingProtocol);
sb.append(", ").append(carrierEnabled);
sb.append(", ").append(bearer);
+ sb.append(", ").append(bearerBitmask);
sb.append(", ").append(profileId);
sb.append(", ").append(modemCognitive);
sb.append(", ").append(maxConns);
@@ -318,6 +326,52 @@
return false;
}
+ private static boolean imsiMatches(String imsiDB, String imsiSIM) {
+ // Note: imsiDB value has digit number or 'x' character for seperating USIM information
+ // for MVNO operator. And then digit number is matched at same order and 'x' character
+ // could replace by any digit number.
+ // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
+ // that means first 6 digits, 8th and 9th digit
+ // should be set in USIM for GG Operator.
+ int len = imsiDB.length();
+ int idxCompare = 0;
+
+ if (len <= 0) return false;
+ if (len > imsiSIM.length()) return false;
+
+ for (int idx=0; idx<len; idx++) {
+ char c = imsiDB.charAt(idx);
+ if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
+ if (mvnoType.equalsIgnoreCase("spn")) {
+ if ((r.getServiceProviderName() != null) &&
+ r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ } else if (mvnoType.equalsIgnoreCase("imsi")) {
+ String imsiSIM = r.getIMSI();
+ if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
+ return true;
+ }
+ } else if (mvnoType.equalsIgnoreCase("gid")) {
+ String gid1 = r.getGid1();
+ int mvno_match_data_length = mvnoMatchData.length();
+ if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
+ gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// TODO - if we have this function we should also have hashCode.
// Also should handle changes in type order and perhaps case-insensitivity
@Override
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 09b3c9b..4a84b6d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony.dataconnection;
-
+import com.android.internal.telephony.CallTracker;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
@@ -24,6 +24,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Protocol;
@@ -210,8 +211,13 @@
static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11;
static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12;
static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13;
+ static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14;
+ static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15;
+ static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16;
- private static final int CMD_TO_STRING_COUNT = EVENT_DATA_CONNECTION_ROAM_OFF - BASE + 1;
+ private static final int CMD_TO_STRING_COUNT =
+ EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE + 1;
+
private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
static {
sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
@@ -230,6 +236,11 @@
"EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED";
sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON";
sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF";
+ sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE";
+ sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] =
+ "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
+ sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
+ "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
}
// Convert cmd to string or null if unknown
static String cmdToString(int cmd) {
@@ -505,6 +516,7 @@
if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
+ "' APN='" + mApnSetting.apn
+ "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
+ if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
// Check if we should fake an error.
if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) {
@@ -549,7 +561,7 @@
}
String protocol;
- if (mPhone.getServiceState().getDataRoaming()) {
+ if (mPhone.getServiceState().getDataRoamingFromRegistration()) {
protocol = mApnSetting.roamingProtocol;
} else {
protocol = mApnSetting.protocol;
@@ -572,21 +584,28 @@
*/
private void tearDownData(Object o) {
int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+ ApnContext apnContext = null;
if ((o != null) && (o instanceof DisconnectParams)) {
DisconnectParams dp = (DisconnectParams)o;
-
+ apnContext = dp.mApnContext;
if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
} else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
}
}
- if (mPhone.mCi.getRadioState().isOn()) {
- if (DBG) log("tearDownData radio is on, call deactivateDataCall");
+ if (mPhone.mCi.getRadioState().isOn()
+ || (mPhone.getServiceState().getRilDataRadioTechnology()
+ == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN )) {
+ String str = "tearDownData radio is on, call deactivateDataCall";
+ if (DBG) log(str);
+ if (apnContext != null) apnContext.requestLog(str);
mPhone.mCi.deactivateDataCall(mCid, discReason,
obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
} else {
- if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
+ String str = "tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately";
+ if (DBG) log(str);
+ if (apnContext != null) apnContext.requestLog(str);
AsyncResult ar = new AsyncResult(o, null, null);
sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
}
@@ -1221,6 +1240,7 @@
mNetworkInfo.setSubtype(networkType,
TelephonyManager.getNetworkTypeName(networkType));
if (mNetworkAgent != null) {
+ updateNetworkInfoSuspendState();
mNetworkAgent.sendNetworkCapabilities(makeNetworkCapabilities());
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent.sendLinkProperties(mLinkProperties);
@@ -1246,6 +1266,33 @@
return retVal;
}
}
+
+ private boolean updateNetworkInfoSuspendState() {
+ final NetworkInfo.DetailedState oldState = mNetworkInfo.getDetailedState();
+
+ // this is only called when we are either connected or suspended. Decide which.
+ if (mNetworkAgent == null) {
+ Rlog.e(getName(), "Setting suspend state without a NetworkAgent");
+ }
+
+ // if we are not in-service change to SUSPENDED
+ final ServiceStateTracker sst = mPhone.getServiceStateTracker();
+ if (sst.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+ } else {
+ // check for voice call and concurrency issues
+ if (sst.isConcurrentVoiceAndDataAllowed() == false) {
+ final CallTracker ct = mPhone.getCallTracker();
+ if (ct.getState() != PhoneConstants.State.IDLE) {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+ return (oldState != NetworkInfo.DetailedState.SUSPENDED);
+ }
+ }
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ }
+ return (oldState != mNetworkInfo.getDetailedState());
+ }
+
private DcDefaultState mDefaultState = new DcDefaultState();
/**
@@ -1565,6 +1612,9 @@
log("DcActivatingState onSetupConnectionCompleted result=" + result
+ " dc=" + DataConnection.this);
}
+ if (cp.mApnContext != null) {
+ cp.mApnContext.requestLog("onSetupConnectionCompleted result=" + result);
+ }
switch (result) {
case SUCCESS:
// All is well
@@ -1591,17 +1641,20 @@
case ERR_RilError:
int delay = mDcRetryAlarmController.getSuggestedRetryTime(
DataConnection.this, ar);
- if (DBG) {
- log("DcActivatingState: ERR_RilError "
- + " delay=" + delay
- + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
- + " result=" + result
- + " result.isRestartRadioFail=" +
- result.mFailCause.isRestartRadioFail()
- + " result.isPermanentFail=" +
- mDct.isPermanentFail(result.mFailCause));
- }
- if (result.mFailCause.isRestartRadioFail()) {
+ String str = "DcActivatingState: ERR_RilError "
+ + " delay=" + delay
+ + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
+ + " result=" + result
+ + " result.isRestartRadioFail=" +
+ result.mFailCause.isRestartRadioFail()
+ + " result.isPermanentFail=" +
+ mDct.isPermanentFail(result.mFailCause);
+ if (DBG) log(str);
+ if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
+ if (result.mFailCause.isRestartRadioFail() ||
+ (cp.mApnContext != null &&
+ cp.mApnContext.restartOnError(
+ result.mFailCause.getErrorCode()))) {
if (DBG) log("DcActivatingState: ERR_RilError restart radio");
mDct.sendRestartRadio();
mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
@@ -1720,6 +1773,11 @@
// If we were retrying there maybe more than one, otherwise they'll only be one.
notifyAllOfConnected(Phone.REASON_CONNECTED);
+ mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
+ DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
+ mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(),
+ DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null);
+
// If the EVENT_CONNECT set the current max retry restore it here
// if it didn't then this is effectively a NOP.
mRetryManager.restoreCurMaxRetryCount();
@@ -1740,8 +1798,19 @@
@Override
public void exit() {
if (DBG) log("DcActiveState: exit dc=" + this);
+ String reason = mNetworkInfo.getReason();
+ if(mDcController.isExecutingCarrierChange()) {
+ reason = Phone.REASON_CARRIER_CHANGE;
+ } else if (mDisconnectParams != null && mDisconnectParams.mReason != null) {
+ reason = mDisconnectParams.mReason;
+ } else if (mDcFailCause != null) {
+ reason = mDcFailCause.toString();
+ }
+ mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
+ mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
- mNetworkInfo.getReason(), mNetworkInfo.getExtraInfo());
+ reason, mNetworkInfo.getExtraInfo());
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent = null;
}
@@ -1847,6 +1916,33 @@
retVal = HANDLED;
break;
}
+ case EVENT_BW_REFRESH_RESPONSE: {
+ AsyncResult ar = (AsyncResult)msg.obj;
+ if (ar.exception != null) {
+ log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
+ } else {
+ final ArrayList<Integer> capInfo = (ArrayList<Integer>)ar.result;
+ final int lceBwDownKbps = capInfo.get(0);
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
+ nc.setLinkDownstreamBandwidthKbps(lceBwDownKbps);
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(nc);
+ }
+ }
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
+ case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: {
+ if (updateNetworkInfoSuspendState()) {
+ // state changed
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+ retVal = HANDLED;
+ break;
+ }
default:
if (VDBG) {
log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -1876,10 +1972,14 @@
break;
case EVENT_DEACTIVATE_DONE:
- if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
- + mApnContexts.size());
AsyncResult ar = (AsyncResult) msg.obj;
DisconnectParams dp = (DisconnectParams) ar.userObj;
+
+ String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
+ + mApnContexts.size();
+ if (DBG) log(str);
+ if (dp.mApnContext != null) dp.mApnContext.requestLog(str);
+
if (dp.mTag == mTag) {
// Transition to inactive but send notifications after
// we've entered the mInactive state.
@@ -1918,10 +2018,10 @@
AsyncResult ar = (AsyncResult) msg.obj;
ConnectionParams cp = (ConnectionParams) ar.userObj;
if (cp.mTag == mTag) {
- if (DBG) {
- log("DcDisconnectionErrorCreatingConnection" +
- " msg.what=EVENT_DEACTIVATE_DONE");
- }
+ String str = "DcDisconnectionErrorCreatingConnection" +
+ " msg.what=EVENT_DEACTIVATE_DONE";
+ if (DBG) log(str);
+ if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
// Transition to inactive but send notifications after
// we've entered the mInactive state.
@@ -1975,6 +2075,13 @@
obtainMessage(EVENT_DISCONNECT, dp));
}
}
+
+ @Override
+ protected void pollLceData() {
+ if(mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) { // active LCE service
+ mPhone.mCi.pullLceData(DataConnection.this.obtainMessage(EVENT_BW_REFRESH_RESPONSE));
+ }
+ }
}
// ******* "public" interface
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
index 45a6b6b..f459f27 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
@@ -72,9 +72,9 @@
DataProfile(ApnSetting apn, boolean isRoaming) {
this(apn.profileId, apn.apn, isRoaming? apn.protocol : apn.roamingProtocol,
- apn.authType, apn.user, apn.password, apn.bearer == 0 ? TYPE_COMMON :
- (ServiceState.isCdma(apn.bearer) ? TYPE_3GPP2 : TYPE_3GPP), apn.maxConnsTime,
- apn.maxConns, apn.waitTime, apn.carrierEnabled);
+ apn.authType, apn.user, apn.password, apn.bearerBitmask == 0 ? TYPE_COMMON :
+ (ServiceState.hasCdma(apn.bearerBitmask) ? TYPE_3GPP2 : TYPE_3GPP),
+ apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled);
}
public static Parcel toParcel(Parcel pc, DataProfile[] dps) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index c7ef159..98d1c3b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.dataconnection;
+import android.content.Context;
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.net.LinkProperties.CompareResult;
@@ -25,6 +26,8 @@
import android.os.Message;
import android.os.SystemClock;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneStateListener;
import android.telephony.Rlog;
import com.android.internal.telephony.DctConstants;
@@ -73,6 +76,13 @@
private DccDefaultState mDccDefaultState = new DccDefaultState();
+ TelephonyManager mTelephonyManager;
+ private PhoneStateListener mPhoneStateListener;
+
+ //mExecutingCarrierChange tracks whether the phone is currently executing
+ //carrier network change
+ private volatile boolean mExecutingCarrierChange;
+
/**
* Constructor.
*
@@ -91,6 +101,19 @@
addState(mDccDefaultState);
setInitialState(mDccDefaultState);
log("X ctor");
+
+ mPhoneStateListener = new PhoneStateListener(handler.getLooper()) {
+ @Override
+ public void onCarrierNetworkChange(boolean active) {
+ mExecutingCarrierChange = active;
+ }
+ };
+
+ mTelephonyManager = (TelephonyManager) phone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ if(mTelephonyManager != null) {
+ mTelephonyManager.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
+ }
}
static DcController makeDcc(PhoneBase phone, DcTrackerBase dct, Handler handler) {
@@ -101,6 +124,7 @@
void dispose() {
log("dispose: call quiteNow()");
+ if(mTelephonyManager != null) mTelephonyManager.listen(mPhoneStateListener, 0);
quitNow();
}
@@ -127,6 +151,10 @@
}
}
+ boolean isExecutingCarrierChange() {
+ return mExecutingCarrierChange;
+ }
+
private class DccDefaultState extends State {
@Override
public void enter() {
@@ -351,28 +379,28 @@
}
}
- // Temporary notification until RIL implementation is complete.
- if (mOverallDataConnectionActiveState != newOverallDataConnectionActiveState) {
- mOverallDataConnectionActiveState = newOverallDataConnectionActiveState;
- long time = SystemClock.elapsedRealtimeNanos();
- int dcPowerState;
- switch (mOverallDataConnectionActiveState) {
- case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE:
- case DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT:
- dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
- break;
- case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
- dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
- break;
- default:
- dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN;
- break;
- }
- DataConnectionRealTimeInfo dcRtInfo =
- new DataConnectionRealTimeInfo(time , dcPowerState);
- log("onDataStateChanged: notify DcRtInfo changed dcRtInfo=" + dcRtInfo);
- mPhone.notifyDataConnectionRealTimeInfo(dcRtInfo);
- }
+ // TODO: b/23319188 Enable/Disable this based on enable/disable of dormancy indications
+ //if (mOverallDataConnectionActiveState != newOverallDataConnectionActiveState) {
+ // mOverallDataConnectionActiveState = newOverallDataConnectionActiveState;
+ // long time = SystemClock.elapsedRealtimeNanos();
+ // int dcPowerState;
+ // switch (mOverallDataConnectionActiveState) {
+ // case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE:
+ // case DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT:
+ // dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ // break;
+ // case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
+ // dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+ // break;
+ // default:
+ // dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN;
+ // break;
+ // }
+ // DataConnectionRealTimeInfo dcRtInfo =
+ // new DataConnectionRealTimeInfo(time , dcPowerState);
+ // log("onDataStateChanged: notify DcRtInfo changed dcRtInfo=" + dcRtInfo);
+ // mPhone.notifyDataConnectionRealTimeInfo(dcRtInfo);
+ //}
if (DBG) {
lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
index 95e3ab7..461249a 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
@@ -16,13 +16,14 @@
package com.android.internal.telephony.dataconnection;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-import com.android.internal.telephony.PhoneConstants;
-
import android.net.NetworkRequest;
import android.os.Message;
import android.telephony.Rlog;
+import android.util.LocalLog;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.telephony.PhoneConstants;
public class DcSwitchAsyncChannel extends AsyncChannel {
private static final boolean DBG = true;
@@ -34,50 +35,61 @@
// ***** Event codes for driving the state machine
private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00002000;
- static final int REQ_CONNECT = BASE + 0;
- static final int RSP_CONNECT = BASE + 1;
- static final int REQ_DISCONNECT = BASE + 2;
- static final int RSP_DISCONNECT = BASE + 3;
- static final int REQ_DISCONNECT_ALL = BASE + 4;
- static final int RSP_DISCONNECT_ALL = BASE + 5;
- static final int REQ_IS_IDLE_STATE = BASE + 6;
- static final int RSP_IS_IDLE_STATE = BASE + 7;
- static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 8;
- static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 9;
- static final int EVENT_DATA_ATTACHED = BASE + 10;
- static final int EVENT_DATA_DETACHED = BASE + 11;
+ static final int REQ_CONNECT = BASE + 0;
+ static final int REQ_RETRY_CONNECT = BASE + 1;
+ static final int REQ_DISCONNECT_ALL = BASE + 2;
+ static final int REQ_IS_IDLE_STATE = BASE + 3;
+ static final int RSP_IS_IDLE_STATE = BASE + 4;
+ static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 5;
+ static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 6;
+ static final int EVENT_DATA_ATTACHED = BASE + 7;
+ static final int EVENT_DATA_DETACHED = BASE + 8;
+ static final int EVENT_EMERGENCY_CALL_STARTED = BASE + 9;
+ static final int EVENT_EMERGENCY_CALL_ENDED = BASE + 10;
- private static final int CMD_TO_STRING_COUNT = EVENT_DATA_DETACHED - BASE + 1;
+ private static final int CMD_TO_STRING_COUNT = EVENT_EMERGENCY_CALL_ENDED - BASE + 1;
private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
static {
sCmdToString[REQ_CONNECT - BASE] = "REQ_CONNECT";
- sCmdToString[RSP_CONNECT - BASE] = "RSP_CONNECT";
- sCmdToString[REQ_DISCONNECT - BASE] = "REQ_DISCONNECT";
- sCmdToString[RSP_DISCONNECT - BASE] = "RSP_DISCONNECT";
+ sCmdToString[REQ_RETRY_CONNECT - BASE] = "REQ_RETRY_CONNECT";
sCmdToString[REQ_DISCONNECT_ALL - BASE] = "REQ_DISCONNECT_ALL";
- sCmdToString[RSP_DISCONNECT_ALL - BASE] = "RSP_DISCONNECT_ALL";
sCmdToString[REQ_IS_IDLE_STATE - BASE] = "REQ_IS_IDLE_STATE";
sCmdToString[RSP_IS_IDLE_STATE - BASE] = "RSP_IS_IDLE_STATE";
sCmdToString[REQ_IS_IDLE_OR_DETACHING_STATE - BASE] = "REQ_IS_IDLE_OR_DETACHING_STATE";
sCmdToString[RSP_IS_IDLE_OR_DETACHING_STATE - BASE] = "RSP_IS_IDLE_OR_DETACHING_STATE";
sCmdToString[EVENT_DATA_ATTACHED - BASE] = "EVENT_DATA_ATTACHED";
sCmdToString[EVENT_DATA_DETACHED - BASE] = "EVENT_DATA_DETACHED";
+ sCmdToString[EVENT_EMERGENCY_CALL_STARTED - BASE] = "EVENT_EMERGENCY_CALL_STARTED";
+ sCmdToString[EVENT_EMERGENCY_CALL_ENDED - BASE] = "EVENT_EMERGENCY_CALL_ENDED";
}
public static class RequestInfo {
boolean executed;
- NetworkRequest request;
- int priority;
+ final NetworkRequest request;
+ final int priority;
+ final int phoneId;
+ private final LocalLog requestLog;
- public RequestInfo(NetworkRequest request, int priority) {
+ public RequestInfo(NetworkRequest request, int priority, LocalLog l, int phoneId) {
this.request = request;
this.priority = priority;
+ this.requestLog = l;
+ this.executed = false;
+ this.phoneId = phoneId;
+ }
+
+ public void log(String str) {
+ requestLog.log(str);
+ }
+
+ public LocalLog getLog() {
+ return requestLog;
}
@Override
public String toString() {
return "[ request=" + request + ", executed=" + executed +
- ", priority=" + priority + "]";
+ ", priority=" + priority + ", phoneId=" + phoneId + "]";
}
}
@@ -95,62 +107,34 @@
tagId = id;
}
- private int rspConnect(Message response) {
- int retVal = response.arg1;
- if (DBG) log("rspConnect=" + retVal);
- return retVal;
+ public int connect(RequestInfo apnRequest) {
+ sendMessage(REQ_CONNECT, apnRequest);
+ return PhoneConstants.APN_REQUEST_STARTED;
}
- public int connectSync(RequestInfo apnRequest) {
- Message response = sendMessageSynchronously(REQ_CONNECT, apnRequest);
- if ((response != null) && (response.what == RSP_CONNECT)) {
- return rspConnect(response);
- } else {
- if (DBG) log("rspConnect error response=" + response);
- return PhoneConstants.APN_REQUEST_FAILED;
- }
+ public void retryConnect() {
+ sendMessage(REQ_RETRY_CONNECT);
}
- private int rspDisconnect(Message response) {
- int retVal = response.arg1;
- if (DBG) log("rspDisconnect=" + retVal);
- return retVal;
- }
-
- public int disconnectSync(RequestInfo apnRequest) {
- Message response = sendMessageSynchronously(REQ_DISCONNECT, apnRequest);
- if ((response != null) && (response.what == RSP_DISCONNECT)) {
- return rspDisconnect(response);
- } else {
- if (DBG) log("rspDisconnect error response=" + response);
- return PhoneConstants.APN_REQUEST_FAILED;
- }
- }
-
- private int rspDisconnectAll(Message response) {
- int retVal = response.arg1;
- if (DBG) log("rspDisconnectAll=" + retVal);
- return retVal;
- }
-
- public int disconnectAllSync() {
- Message response = sendMessageSynchronously(REQ_DISCONNECT_ALL);
- if ((response != null) && (response.what == RSP_DISCONNECT_ALL)) {
- return rspDisconnectAll(response);
- } else {
- if (DBG) log("rspDisconnectAll error response=" + response);
- return PhoneConstants.APN_REQUEST_FAILED;
- }
+ public int disconnectAll() {
+ sendMessage(REQ_DISCONNECT_ALL);
+ return PhoneConstants.APN_REQUEST_STARTED;
}
public void notifyDataAttached() {
sendMessage(EVENT_DATA_ATTACHED);
- if (DBG) log("notifyDataAttached");
}
public void notifyDataDetached() {
sendMessage(EVENT_DATA_DETACHED);
- if (DBG) log("EVENT_DATA_DETACHED");
+ }
+
+ public void notifyEmergencyCallToggled(int start) {
+ if (start != 0) {
+ sendMessage(EVENT_EMERGENCY_CALL_STARTED);
+ } else {
+ sendMessage(EVENT_EMERGENCY_CALL_ENDED);
+ }
}
private boolean rspIsIdle(Message response) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
index ed87a99..ae2d2ff 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
@@ -17,15 +17,18 @@
package com.android.internal.telephony.dataconnection;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.IState;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
+import android.os.AsyncResult;
import android.os.Message;
import android.telephony.Rlog;
@@ -36,18 +39,26 @@
// ***** Event codes for driving the state machine
private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00001000;
- private static final int EVENT_CONNECTED = BASE + 0;
+ private static final int EVENT_CONNECTED = BASE + 0;
+ private static final int EVENT_DATA_ALLOWED = BASE + 1;
+ private static final int CMD_RETRY_ATTACH = BASE + 2;
+ private static final int EVENT_DATA_DISALLOWED = BASE + 3;
private int mId;
private Phone mPhone;
private AsyncChannel mAc;
private IdleState mIdleState = new IdleState();
+ private EmergencyState mEmergencyState = new EmergencyState();
private AttachingState mAttachingState = new AttachingState();
private AttachedState mAttachedState = new AttachedState();
private DetachingState mDetachingState = new DetachingState();
private DefaultState mDefaultState = new DefaultState();
+ // In case of transition to emergency state, this tracks the state of the state machine prior
+ // to entering emergency state
+ private IState mPreEmergencyState;
+
protected DcSwitchStateMachine(Phone phone, String name, int id) {
super(name);
if (DBG) log("DcSwitchState constructor E");
@@ -56,6 +67,7 @@
addState(mDefaultState);
addState(mIdleState, mDefaultState);
+ addState(mEmergencyState, mDefaultState);
addState(mAttachingState, mDefaultState);
addState(mAttachedState, mDefaultState);
addState(mDetachingState, mDefaultState);
@@ -89,21 +101,25 @@
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
- if (DBG) {
- log("IdleState: REQ_CONNECT");
- }
-
- PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
- pb.mCi.setDataAllowed(true, null);
-
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
- PhoneConstants.APN_REQUEST_STARTED);
-
+ RequestInfo apnRequest = (RequestInfo)msg.obj;
+ apnRequest.log("DcSwitchStateMachine.IdleState: REQ_CONNECT");
+ if (DBG) log("IdleState: REQ_CONNECT, apnRequest=" + apnRequest);
transitionTo(mAttachingState);
retVal = HANDLED;
break;
}
+ case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
+ if (DBG) log("AttachingState: REQ_DISCONNECT_ALL" );
+
+ // Shouldn't have any requests, but why not try..
+ DctController.getInstance().releaseAllRequests(mId);
+
+ retVal = HANDLED;
+ break;
+ }
+
+
case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
if (DBG) {
log("AttachingState: EVENT_DATA_ATTACHED");
@@ -131,10 +147,73 @@
}
}
+ private class EmergencyState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ if (!pb.mDcTracker.isEmergency()) {
+ loge("EmergencyState: isEmergency() is false. deferMessage msg.what=0x" +
+ Integer.toHexString(msg.what));
+ deferMessage(msg);
+ transitionTo(mPreEmergencyState);
+ return HANDLED;
+ }
+
+ switch (msg.what) {
+ case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_ENDED: {
+ transitionTo(mPreEmergencyState);
+ break;
+ }
+
+ case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_STARTED: {
+ loge("EmergencyState: ignoring EVENT_EMERGENCY_CALL_STARTED");
+ break;
+ }
+
+ // explicitly call out the messages we must defer
+ // anything not listed falls through to the default state
+ case DcSwitchAsyncChannel.REQ_CONNECT:
+ case DcSwitchAsyncChannel.REQ_RETRY_CONNECT:
+ case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL:
+ case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
+ case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
+ log("EmergencyState: deferMessage msg.what=0x" + Integer.toHexString(msg.what));
+ deferMessage(msg);
+ break;
+ }
+
+ default: {
+ if (VDBG) {
+ log("EmergencyState: nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ return NOT_HANDLED;
+ }
+ }
+
+ return HANDLED;
+ }
+ }
+
private class AttachingState extends State {
+ private int mCurrentAllowedSequence = 0;
@Override
public void enter() {
log("AttachingState: enter");
+ doEnter();
+ }
+
+ private void doEnter() {
+ final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ pb.mCi.setDataAllowed(true, obtainMessage(EVENT_DATA_ALLOWED,
+ ++mCurrentAllowedSequence, 0));
+ // if we're on a carrier that unattaches us if we're idle for too long
+ // (on wifi) and they won't re-attach until we poke them. Poke them!
+ // essentially react as Attached does here in Attaching.
+ if (pb.mDcTracker.getAutoAttachOnCreation()) {
+ if (DBG) log("AttachingState executeAll due to autoAttach");
+ DctController.getInstance().executeAllRequests(mId);
+ }
}
@Override
@@ -143,36 +222,75 @@
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
- if (DBG) {
- log("AttachingState: REQ_CONNECT");
+ RequestInfo apnRequest = (RequestInfo)msg.obj;
+ apnRequest.log("DcSwitchStateMachine.AttachingState: REQ_CONNECT");
+ if (DBG) log("AttachingState: REQ_CONNECT, apnRequest=" + apnRequest);
+
+ final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ if (pb.mDcTracker.getAutoAttachOnCreation() == false) {
+ // do nothing - wait til we attach and then we'll execute all requests
+ } else {
+ apnRequest.log("DcSwitchStateMachine processing due to autoAttach");
+ DctController.getInstance().executeRequest(apnRequest);
}
-
- PhoneBase pb = (PhoneBase) ((PhoneProxy) mPhone).getActivePhone();
- pb.mCi.setDataAllowed(true, null);
-
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
- PhoneConstants.APN_REQUEST_STARTED);
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_DATA_ALLOWED: {
+ AsyncResult ar = (AsyncResult)msg.obj;
+ if (mCurrentAllowedSequence != msg.arg1) {
+ loge("EVENT_DATA_ALLOWED ignored arg1=" + msg.arg1 + ", seq=" +
+ mCurrentAllowedSequence);
+ } else if (ar.exception != null) {
+ if (ar.exception instanceof CommandException) {
+ CommandException e = (CommandException)ar.exception;
+ if (e.getCommandError() ==
+ CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ // must be on a single-sim device so stay in Attaching
+ // this is important to avoid an infinite loop
+ retVal = HANDLED;
+ break;
+ }
+ }
+ loge("EVENT_DATA_ALLOWED failed, " + ar.exception);
+ transitionTo(mIdleState);
+ }
retVal = HANDLED;
break;
}
- case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
+ case DcSwitchAsyncChannel.REQ_RETRY_CONNECT: {
+ if (DBG) log("AttachingState going to retry");
+ doEnter();
+ retVal = HANDLED;
+ break;
+ }
+
+ case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED: {
if (DBG) {
log("AttachingState: EVENT_DATA_ATTACHED");
}
transitionTo(mAttachedState);
retVal = HANDLED;
break;
+ }
case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
if (DBG) {
log("AttachingState: REQ_DISCONNECT_ALL" );
}
- DctController.getInstance().releaseAllRequests(mId);
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
- PhoneConstants.APN_REQUEST_STARTED);
+ final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+ if (pb.mDcTracker.getAutoAttachOnCreation()) {
+ // if AutoAttachOnCreation, then we may have executed requests
+ // without ever actually getting to Attached, so release the request
+ // here in that case.
+ if (DBG) log("releasingAll due to autoAttach");
+ DctController.getInstance().releaseAllRequests(mId);
+ }
- transitionTo(mDetachingState);
+ // modem gets unhappy if we try to detach while attaching
+ // wait til attach finishes.
+ deferMessage(msg);
retVal = HANDLED;
break;
}
@@ -204,27 +322,10 @@
switch (msg.what) {
case DcSwitchAsyncChannel.REQ_CONNECT: {
RequestInfo apnRequest = (RequestInfo)msg.obj;
- if (DBG) {
- log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
- }
+ apnRequest.log("DcSwitchStateMachine.AttachedState: REQ_CONNECT");
+ if (DBG) log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
DctController.getInstance().executeRequest(apnRequest);
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
- PhoneConstants.APN_REQUEST_STARTED);
-
- retVal = HANDLED;
- break;
- }
- case DcSwitchAsyncChannel.REQ_DISCONNECT: {
- RequestInfo apnRequest = (RequestInfo)msg.obj;
- if (DBG) {
- log("AttachedState: REQ_DISCONNECT apnRequest=" + apnRequest);
- }
-
- DctController.getInstance().releaseRequest(apnRequest);
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
- PhoneConstants.APN_REQUEST_STARTED);
-
retVal = HANDLED;
break;
}
@@ -234,9 +335,6 @@
log("AttachedState: REQ_DISCONNECT_ALL" );
}
DctController.getInstance().releaseAllRequests(mId);
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
- PhoneConstants.APN_REQUEST_STARTED);
-
transitionTo(mDetachingState);
retVal = HANDLED;
break;
@@ -264,12 +362,14 @@
}
private class DetachingState extends State {
+ private int mCurrentDisallowedSequence = 0;
+
@Override
public void enter() {
if (DBG) log("DetachingState: enter");
PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
- pb.mCi.setDataAllowed(false, obtainMessage(
- DcSwitchAsyncChannel.EVENT_DATA_DETACHED));
+ pb.mCi.setDataAllowed(false, obtainMessage(EVENT_DATA_DISALLOWED,
+ ++mCurrentDisallowedSequence, 0));
}
@Override
@@ -277,6 +377,17 @@
boolean retVal;
switch (msg.what) {
+ case DcSwitchAsyncChannel.REQ_CONNECT: {
+ RequestInfo apnRequest = (RequestInfo)msg.obj;
+ apnRequest.log("DcSwitchStateMachine.DetachingState: REQ_CONNECT");
+ if (DBG) log("DetachingState: REQ_CONNECT, apnRequest=" + apnRequest);
+
+ // can't process this now - wait until we return to idle
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+ }
+
case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
if (DBG) {
log("DetachingState: EVENT_DATA_DETACHED");
@@ -285,13 +396,25 @@
retVal = HANDLED;
break;
}
-
+ case EVENT_DATA_DISALLOWED: {
+ AsyncResult ar = (AsyncResult)msg.obj;
+ if (mCurrentDisallowedSequence != msg.arg1) {
+ loge("EVENT_DATA_DISALLOWED ignored arg1=" + msg.arg1 + ", seq=" +
+ mCurrentDisallowedSequence);
+ } else if (ar.exception != null) {
+ // go back to attached as we still think we are. Notifications
+ // from the ServiceStateTracker will kick us out of attached when
+ // appropriate.
+ loge("EVENT_DATA_DISALLOWED failed, " + ar.exception);
+ transitionTo(mAttachedState);
+ }
+ retVal = HANDLED;
+ break;
+ }
case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
if (DBG) {
log("DetachingState: REQ_DISCONNECT_ALL, already detaching" );
}
- mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
- PhoneConstants.APN_REQUEST_STARTED);
retVal = HANDLED;
break;
}
@@ -350,6 +473,11 @@
DcSwitchAsyncChannel.RSP_IS_IDLE_OR_DETACHING_STATE, val ? 1 : 0);
break;
}
+ case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_STARTED: {
+ mPreEmergencyState = getCurrentState();
+ transitionTo(mEmergencyState);
+ break;
+ }
default:
if (DBG) {
log("DefaultState: shouldn't happen but ignore msg.what=0x" +
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 27e3ed8..0a453b0 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -40,9 +40,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.RegistrantList;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -58,7 +56,7 @@
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
-import android.util.SparseArray;
+import android.util.LocalLog;
import android.view.WindowManager;
import android.telephony.Rlog;
@@ -85,8 +83,6 @@
import java.util.Objects;
import java.lang.StringBuilder;
-import android.provider.Settings;
-
import com.android.internal.telephony.ServiceStateTracker;
/**
* {@hide}
@@ -257,18 +253,20 @@
}
@Override
- public void incApnRefCount(String name) {
+ public void incApnRefCount(String name, LocalLog log) {
ApnContext apnContext = mApnContexts.get(name);
+ log.log("DcTracker.incApnRefCount on " + name + " found " + apnContext);
if (apnContext != null) {
- apnContext.incRefCount();
+ apnContext.incRefCount(log);
}
}
@Override
- public void decApnRefCount(String name) {
+ public void decApnRefCount(String name, LocalLog log) {
ApnContext apnContext = mApnContexts.get(name);
+ log.log("DcTracker.decApnRefCount on " + name + " found " + apnContext);
if (apnContext != null) {
- apnContext.decRefCount();
+ apnContext.decRefCount(log);
}
}
@@ -380,6 +378,14 @@
boolean dataAllowed = isEmergencyApn || isDataAllowed();
boolean possible = dataAllowed && apnTypePossible;
+ if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+ || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
+ && (mPhone.getServiceState().getRilDataRadioTechnology()
+ == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+ log("Default data call activation not possible in iwlan.");
+ possible = false;
+ }
+
if (VDBG) {
log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
"apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
@@ -646,7 +652,17 @@
}
private boolean isDataAllowed(ApnContext apnContext) {
- return apnContext.isReady() && isDataAllowed();
+ //If RAT is iwlan then dont allow default/IA PDP at all.
+ //Rest of APN types can be evaluated for remaining conditions.
+ if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+ || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
+ && (mPhone.getServiceState().getRilDataRadioTechnology()
+ == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+ log("Default data call activation not allowed in iwlan.");
+ return false;
+ } else {
+ return apnContext.isReady() && isDataAllowed();
+ }
}
//****** Called from ServiceStateTracker
@@ -679,7 +695,7 @@
notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
}
if (mAutoAttachOnCreationConfig) {
- mAutoAttachOnCreation = true;
+ mAutoAttachOnCreation.set(true);
}
setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
}
@@ -693,11 +709,16 @@
boolean attachedState = mAttached.get();
boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
+ int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
+ if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+ desiredPowerState = true;
+ }
+
IccRecords r = mIccRecords.get();
boolean recordsLoaded = false;
if (r != null) {
recordsLoaded = r.getRecordsLoaded();
- if (DBG) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
+ if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
}
//FIXME always attach
@@ -721,7 +742,7 @@
state = mPhone.getCallTracker().getState();
}
boolean allowed =
- (attachedState || mAutoAttachOnCreation) &&
+ (attachedState || mAutoAttachOnCreation.get()) &&
recordsLoaded &&
(state == PhoneConstants.State.IDLE ||
mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
@@ -733,7 +754,7 @@
desiredPowerState;
if (!allowed && DBG) {
String reason = "";
- if (!(attachedState || mAutoAttachOnCreation)) {
+ if (!(attachedState || mAutoAttachOnCreation.get())) {
reason += " - Attached= " + attachedState;
}
if (!recordsLoaded) reason += " - SIM not loaded";
@@ -770,17 +791,19 @@
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
if (DBG) log("setupDataOnConnectableApns: " + reason);
- ArrayList<ApnSetting> waitingApns = null;
for (ApnContext apnContext : mPrioritySortedApnContexts) {
+ ArrayList<ApnSetting> waitingApns = null;
+
if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
- if (apnContext.getState() == DctConstants.State.FAILED) {
+ if (apnContext.getState() == DctConstants.State.FAILED
+ || apnContext.getState() == DctConstants.State.RETRYING) {
if (retryFailures == RetryFailures.ALWAYS) {
- apnContext.setState(DctConstants.State.IDLE);
+ apnContext.releaseDataConnection(reason);
} else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
// RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
- apnContext.setState(DctConstants.State.IDLE);
+ apnContext.releaseDataConnection(reason);
} else {
// RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
@@ -789,7 +812,7 @@
waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
if (originalApns.size() != waitingApns.size() ||
originalApns.containsAll(waitingApns) == false) {
- apnContext.setState(DctConstants.State.IDLE);
+ apnContext.releaseDataConnection(reason);
}
}
}
@@ -812,6 +835,7 @@
" due to " + apnContext.getReason() + " apnContext=" + apnContext);
log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
}
+ apnContext.requestLog("trySetupData due to " + apnContext.getReason());
if (mPhone.getSimulatedRadioControl() != null) {
// Assume data is connected on the simulator
@@ -835,7 +859,9 @@
(isDataAllowed(apnContext) &&
getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
if (apnContext.getState() == DctConstants.State.FAILED) {
- if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable");
+ String str ="trySetupData: make a FAILED ApnContext IDLE so its reusable";
+ if (DBG) log(str);
+ apnContext.requestLog(str);
apnContext.setState(DctConstants.State.IDLE);
}
int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
@@ -847,7 +873,9 @@
if (waitingApns.isEmpty()) {
notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
notifyOffApnsOfAvailability(apnContext.getReason());
- if (DBG) log("trySetupData: X No APN found retValue=false");
+ String str = "trySetupData: X No APN found retValue=false";
+ if (DBG) log(str);
+ apnContext.requestLog(str);
return false;
} else {
apnContext.setWaitingApns(waitingApns);
@@ -873,7 +901,16 @@
mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
}
notifyOffApnsOfAvailability(apnContext.getReason());
- if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false");
+ String str = "trySetupData: X apnContext not 'ready' retValue=false";
+ apnContext.requestLog(str);
+ if (DBG) {
+ log(str);
+ if (!apnContext.isConnectable()) log("apnContext.isConnectable = false");
+ if (!isDataAllowed(apnContext)) log("isDataAllowed = false");
+ if (!getAnyDataEnabled(checkUserDataEnabled)) {
+ log("getAnyDataEnabled(" + checkUserDataEnabled + ") = false");
+ }
+ }
return false;
}
}
@@ -969,10 +1006,10 @@
}
DcAsyncChannel dcac = apnContext.getDcAc();
- if (DBG) {
- log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
- " apnContext=" + apnContext);
- }
+ String str = "cleanUpConnection: tearDown=" + tearDown + " reason=" +
+ apnContext.getReason();
+ if (DBG) log(str + " apnContext=" + apnContext);
+ apnContext.requestLog(str);
if (tearDown) {
if (apnContext.isDisconnected()) {
// The request is tearDown and but ApnContext is not connected.
@@ -980,10 +1017,9 @@
apnContext.setState(DctConstants.State.IDLE);
if (!apnContext.isReady()) {
if (dcac != null) {
- if (DBG) {
- log("cleanUpConnection: teardown, disconnected, !ready apnContext="
- + apnContext);
- }
+ str = "cleanUpConnection: teardown, disconnectd, !ready";
+ if (DBG) log(str + " apnContext=" + apnContext);
+ apnContext.requestLog(str);
dcac.tearDown(apnContext, "", null);
}
apnContext.setDataConnectionAc(null);
@@ -1007,10 +1043,9 @@
disconnectAll = true;
}
}
- if (DBG) {
- log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :"")
- + "apnContext=" + apnContext);
- }
+ str = "cleanUpConnection: tearing down" + (disconnectAll ? " all" : "");
+ if (DBG) log(str + "apnContext=" + apnContext);
+ apnContext.requestLog(str);
Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
if (disconnectAll) {
apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
@@ -1025,6 +1060,7 @@
// apn is connected but no reference to dcac.
// Should not be happen, but reset the state in case.
apnContext.setState(DctConstants.State.IDLE);
+ apnContext.requestLog("cleanUpConnection: connected, bug no DCAC");
mPhone.notifyDataConnection(apnContext.getReason(),
apnContext.getApnType());
}
@@ -1042,10 +1078,9 @@
if (dcac != null) {
cancelReconnectAlarm(apnContext);
}
- if (DBG) {
- log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
- " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
- }
+ str = "cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason();
+ if (DBG) log(str + " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
+ apnContext.requestLog(str);
}
/**
@@ -1093,53 +1128,6 @@
return result;
}
- private boolean imsiMatches(String imsiDB, String imsiSIM) {
- // Note: imsiDB value has digit number or 'x' character for seperating USIM information
- // for MVNO operator. And then digit number is matched at same order and 'x' character
- // could replace by any digit number.
- // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
- // that means first 6 digits, 8th and 9th digit
- // should be set in USIM for GG Operator.
- int len = imsiDB.length();
- int idxCompare = 0;
-
- if (len <= 0) return false;
- if (len > imsiSIM.length()) return false;
-
- for (int idx=0; idx<len; idx++) {
- char c = imsiDB.charAt(idx);
- if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
- continue;
- } else {
- return false;
- }
- }
- return true;
- }
-
- @Override
- protected boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
- if (mvnoType.equalsIgnoreCase("spn")) {
- if ((r.getServiceProviderName() != null) &&
- r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("imsi")) {
- String imsiSIM = r.getIMSI();
- if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("gid")) {
- String gid1 = r.getGid1();
- int mvno_match_data_length = mvnoMatchData.length();
- if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
- gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- }
- return false;
- }
-
@Override
protected boolean isPermanentFail(DcFailCause dcFailCause) {
return (dcFailCause.isPermanentFail() &&
@@ -1175,6 +1163,7 @@
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.CARRIER_ENABLED)) == 1,
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -1201,7 +1190,7 @@
}
if (apn.hasMvnoParams()) {
- if (r != null && mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+ if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
mvnoApns.add(apn);
}
} else {
@@ -1248,6 +1237,7 @@
private boolean setupData(ApnContext apnContext, int radioTech) {
if (DBG) log("setupData: apnContext=" + apnContext);
+ apnContext.requestLog("setupData");
ApnSetting apnSetting;
DcAsyncChannel dcac = null;
@@ -1323,8 +1313,8 @@
Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
msg.obj = apnContext;
- dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,
- msg);
+ dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,
+ mAutoAttachOnCreation.get(), msg);
if (DBG) log("setupData: initing!");
return true;
@@ -1544,11 +1534,12 @@
private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
boolean cleanup = false;
boolean trySetup = false;
- if (DBG) {
- log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
- "(" + apnContext.isEnabled() + "), " + met + "(" +
- apnContext.getDependencyMet() +"))");
- }
+ String str ="applyNewState(" + apnContext.getApnType() + ", " + enabled +
+ "(" + apnContext.isEnabled() + "), " + met + "(" +
+ apnContext.getDependencyMet() +"))";
+ if (DBG) log(str);
+ apnContext.requestLog(str);
+
if (apnContext.isReady()) {
cleanup = true;
if (enabled && met) {
@@ -1560,6 +1551,7 @@
case DISCONNECTING:
// We're "READY" and active so just return
if (DBG) log("applyNewState: 'ready' so return");
+ apnContext.requestLog("applyNewState state=" + state + ", so return");
return;
case IDLE:
// fall through: this is unexpected but if it happens cleanup and try setup
@@ -1607,7 +1599,10 @@
apnContext.setEnabled(enabled);
apnContext.setDependencyMet(met);
if (cleanup) cleanUpConnection(true, apnContext);
- if (trySetup) trySetupData(apnContext);
+ if (trySetup) {
+ apnContext.resetErrorCodeRetries();
+ trySetupData(apnContext);
+ }
}
private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
@@ -1625,7 +1620,6 @@
ApnContext potentialApnCtx = null;
for (ApnContext curApnCtx : mApnContexts.values()) {
DcAsyncChannel curDcac = curApnCtx.getDcAc();
- log("curDcac: " + curDcac);
if (curDcac != null) {
ApnSetting apnSetting = curApnCtx.getApnSetting();
log("apnSetting: " + apnSetting);
@@ -2245,13 +2239,14 @@
String operator = (r != null) ? r.getOperatorNumeric() : "";
if (operator != null) {
String selection = "numeric = '" + operator + "'";
+ String orderBy = "_id";
// query only enabled apn.
// carrier_enabled : 1 means enabled apn, 0 disabled apn.
// selection += " and carrier_enabled = 1";
if (DBG) log("createAllApnList: selection=" + selection);
Cursor cursor = mPhone.getContext().getContentResolver().query(
- Telephony.Carriers.CONTENT_URI, null, selection, null, null);
+ Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
if (cursor != null) {
if (cursor.getCount() > 0) {
@@ -2350,7 +2345,7 @@
xorEquals(first.proxy, second.proxy) &&
xorEquals(first.port, second.port) &&
first.carrierEnabled == second.carrierEnabled &&
- first.bearer == second.bearer &&
+ first.bearerBitmask == second.bearerBitmask &&
first.profileId == second.profileId &&
Objects.equals(first.mvnoType, second.mvnoType) &&
Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
@@ -2367,10 +2362,12 @@
}
private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
+ int id = dest.id;
ArrayList<String> resultTypes = new ArrayList<String>();
resultTypes.addAll(Arrays.asList(dest.types));
for (String srcType : src.types) {
if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
+ if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
}
String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
@@ -2380,11 +2377,13 @@
String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
dest.roamingProtocol;
+ int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
+ 0 : (dest.bearerBitmask | src.bearerBitmask);
- return new ApnSetting(dest.id, dest.numeric, dest.carrier, dest.apn,
+ return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
dest.authType, resultTypes.toArray(new String[0]), protocol,
- roamingProtocol, dest.carrierEnabled, dest.bearer, dest.profileId,
+ roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
(dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
}
@@ -2454,6 +2453,9 @@
if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
usePreferred = true;
}
+ if (usePreferred) {
+ mPreferredApn = getPreferredApn();
+ }
if (DBG) {
log("buildWaitingApns: usePreferred=" + usePreferred
+ " canSetPreferApn=" + mCanSetPreferApn
@@ -2469,7 +2471,7 @@
+ mPreferredApn.numeric + ":" + mPreferredApn);
}
if (mPreferredApn.numeric.equals(operator)) {
- if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
+ if (ServiceState.bitmaskHasTech(mPreferredApn.bearerBitmask, radioTech)) {
apnList.add(mPreferredApn);
if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
return apnList;
@@ -2487,26 +2489,23 @@
if (mAllApnSettings != null) {
if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
for (ApnSetting apn : mAllApnSettings) {
- if (DBG) log("buildWaitingApns: apn=" + apn);
if (apn.canHandleType(requestedApnType)) {
- if (apn.bearer == 0 || apn.bearer == radioTech) {
- if (DBG) log("buildWaitingApns: adding apn=" + apn.toString());
+ if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
+ if (DBG) log("buildWaitingApns: adding apn=" + apn);
apnList.add(apn);
} else {
if (DBG) {
- log("buildWaitingApns: bearer:" + apn.bearer + " != "
- + "radioTech:" + radioTech);
+ log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +
+ "not include radioTech:" + radioTech);
}
}
- } else {
- if (DBG) {
+ } else if (DBG) {
log("buildWaitingApns: couldn't handle requesedApnType="
+ requestedApnType);
}
}
- }
} else {
- loge("mAllApnSettings is empty!");
+ loge("mAllApnSettings is null!");
}
if (DBG) log("buildWaitingApns: X apnList=" + apnList);
return apnList;
@@ -2543,8 +2542,8 @@
}
private ApnSetting getPreferredApn() {
- if (mAllApnSettings.isEmpty()) {
- log("getPreferredApn: X not found mAllApnSettings.isEmpty");
+ if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
+ log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));
return null;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
index a5a0e64..5c0226e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
@@ -42,11 +42,13 @@
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;
+import android.util.LocalLog;
import android.telephony.Rlog;
import com.android.internal.R;
@@ -69,6 +71,7 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.PriorityQueue;
@@ -79,7 +82,7 @@
public abstract class DcTrackerBase extends Handler {
protected static final boolean DBG = true;
protected static final boolean VDBG = false; // STOPSHIP if true
- protected static final boolean VDBG_STALL = true; // STOPSHIP if true
+ protected static final boolean VDBG_STALL = false; // STOPSHIP if true
protected static final boolean RADIO_TESTS = false;
static boolean mIsCleanupRequired = false;
@@ -233,7 +236,7 @@
// When false we will not auto attach and manually attaching is required.
protected boolean mAutoAttachOnCreationConfig = false;
- protected boolean mAutoAttachOnCreation = false;
+ protected AtomicBoolean mAutoAttachOnCreation = new AtomicBoolean(false);
// State of screen
// (TODO: Reconsider tying directly to screen, maybe this is
@@ -615,7 +618,7 @@
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
- mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
+ mAutoAttachOnCreation.set(sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false));
mSubscriptionManager = SubscriptionManager.from(mPhone.getContext());
mSubscriptionManager
@@ -660,13 +663,9 @@
mPhone.notifyDataActivity();
}
- public void incApnRefCount(String name) {
+ abstract public void incApnRefCount(String name, LocalLog log);
- }
-
- public void decApnRefCount(String name) {
-
- }
+ abstract public void decApnRefCount(String name, LocalLog log);
public boolean isApnSupported(String name) {
return false;
@@ -693,21 +692,18 @@
log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
return null;
}
- int bearer = -1;
+ int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
ApnSetting retDunSetting = null;
String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
List<ApnSetting> dunSettings = ApnSetting.arrayFromString(apnData);
IccRecords r = mIccRecords.get();
for (ApnSetting dunSetting : dunSettings) {
String operator = (r != null) ? r.getOperatorNumeric() : "";
- if (dunSetting.bearer != 0) {
- if (bearer == -1) bearer = mPhone.getServiceState().getRilDataRadioTechnology();
- if (dunSetting.bearer != bearer) continue;
- }
+ if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
if (dunSetting.numeric.equals(operator)) {
if (dunSetting.hasMvnoParams()) {
- if (r != null &&
- mvnoMatches(r, dunSetting.mvnoType, dunSetting.mvnoMatchData)) {
+ if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
+ dunSetting.mvnoMatchData)) {
if (VDBG) {
log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
}
@@ -725,15 +721,13 @@
for (String apn : apnArrayData) {
ApnSetting dunSetting = ApnSetting.fromString(apn);
if (dunSetting != null) {
- if (dunSetting.bearer != 0) {
- if (bearer == -1) bearer = mPhone.getServiceState().getRilDataRadioTechnology();
- if (dunSetting.bearer != bearer) continue;
- }
+ if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
if (dunSetting.hasMvnoParams()) {
- if (r != null &&
- mvnoMatches(r, dunSetting.mvnoType, dunSetting.mvnoMatchData)) {
- if (VDBG) log("fetchDunApn: config_tether_apndata mvno dunSetting="
- + dunSetting);
+ if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
+ dunSetting.mvnoMatchData)) {
+ if (VDBG) {
+ log("fetchDunApn: config_tether_apndata mvno dunSetting=" + dunSetting);
+ }
return dunSetting;
}
} else {
@@ -894,7 +888,6 @@
public abstract void setDataAllowed(boolean enable, Message response);
public abstract String[] getPcscfAddress(String apnType);
public abstract void setImsRegistrationState(boolean registered);
- protected abstract boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data);
protected abstract boolean isPermanentFail(DcFailCause dcFailCause);
@Override
@@ -1310,58 +1303,7 @@
sendMessage(msg);
}
- protected void onEnableApn(int apnId, int enabled) {
- if (DBG) {
- log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
- ", enabled=" + enabled + ", dataEnabled = " + mDataEnabled[apnId] +
- ", enabledCount = " + mEnabledCount + ", isApnTypeActive = " +
- isApnTypeActive(apnIdToType(apnId)));
- }
- if (enabled == DctConstants.ENABLED) {
- synchronized (this) {
- if (!mDataEnabled[apnId]) {
- mDataEnabled[apnId] = true;
- mEnabledCount++;
- }
- }
- String type = apnIdToType(apnId);
- if (!isApnTypeActive(type)) {
- mRequestedApnType = type;
- onEnableNewApn();
- } else {
- notifyApnIdUpToCurrent(Phone.REASON_APN_SWITCHED, apnId);
- }
- } else {
- // disable
- boolean didDisable = false;
- synchronized (this) {
- if (mDataEnabled[apnId]) {
- mDataEnabled[apnId] = false;
- mEnabledCount--;
- didDisable = true;
- }
- }
- if (didDisable) {
- if ((mEnabledCount == 0) || (apnId == DctConstants.APN_DUN_ID)) {
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
- onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
- }
-
- // send the disconnect msg manually, since the normal route wont send
- // it (it's not enabled)
- notifyApnIdDisconnected(Phone.REASON_DATA_DISABLED, apnId);
- if (mDataEnabled[DctConstants.APN_DEFAULT_ID] == true
- && !isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) {
- // TODO - this is an ugly way to restore the default conn - should be done
- // by a real contention manager and policy that disconnects the lower pri
- // stuff as enable requests come in and pops them back on as we disable back
- // down to the lower pri stuff
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
- onEnableNewApn();
- }
- }
- }
- }
+ abstract void onEnableApn(int apnId, int enabled);
/**
* Called when we switch APNs.
@@ -1973,6 +1915,10 @@
sendMessage(msg);
}
+ public boolean getAutoAttachOnCreation() {
+ return mAutoAttachOnCreation.get();
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("DcTrackerBase:");
pw.println(" RADIO_TESTS=" + RADIO_TESTS);
@@ -2002,7 +1948,7 @@
pw.println(" mIsWifiConnected=" + mIsWifiConnected);
pw.println(" mReconnectIntent=" + mReconnectIntent);
pw.println(" mCidActive=" + mCidActive);
- pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation);
+ pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
pw.println(" mIsScreenOn=" + mIsScreenOn);
pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
pw.flush();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DctController.java b/src/java/com/android/internal/telephony/dataconnection/DctController.java
index 4fce2ec..fdeb77e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DctController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DctController.java
@@ -22,6 +22,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkRequest;
+import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -30,6 +31,7 @@
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.LocalLog;
import android.util.SparseArray;
import com.android.internal.telephony.Phone;
@@ -39,9 +41,11 @@
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
@@ -55,9 +59,13 @@
private static final int EVENT_EXECUTE_ALL_REQUESTS = 102;
private static final int EVENT_RELEASE_REQUEST = 103;
private static final int EVENT_RELEASE_ALL_REQUESTS = 104;
+ private static final int EVENT_RETRY_ATTACH = 105;
+ private static final int EVENT_SETTINGS_CHANGED = 106;
+ private static final int EVENT_SUBSCRIPTIONS_CHANGED = 107;
private static final int EVENT_DATA_ATTACHED = 500;
private static final int EVENT_DATA_DETACHED = 600;
+ private static final int EVENT_EMERGENCY_CALL_TOGGLED = 700;
private static DctController sDctController;
@@ -83,7 +91,7 @@
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
- onSubInfoReady();
+ DctController.this.obtainMessage(EVENT_SUBSCRIPTIONS_CHANGED).sendToTarget();
}
};
@@ -91,7 +99,7 @@
@Override
public void onChange(boolean selfChange) {
logd("Settings change");
- onSettingsChange();
+ DctController.this.obtainMessage(EVENT_SETTINGS_CHANGED).sendToTarget();
}
};
@@ -122,6 +130,8 @@
EVENT_DATA_ATTACHED + index, null);
phoneBase.getServiceStateTracker().registerForDataConnectionDetached(mRspHandler,
EVENT_DATA_DETACHED + index, null);
+ phoneBase.registerForEmergencyCallToggle(mRspHandler,
+ EVENT_EMERGENCY_CALL_TOGGLED + index, null);
ConnectivityManager cm = (ConnectivityManager)mPhones[index].getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -160,7 +170,14 @@
private Handler mRspHandler = new Handler() {
@Override
public void handleMessage(Message msg){
- if (msg.what >= EVENT_DATA_DETACHED) {
+ if (msg.what >= EVENT_EMERGENCY_CALL_TOGGLED) {
+ logd("EVENT_PHONE" + (msg.what - EVENT_EMERGENCY_CALL_TOGGLED + 1)
+ + "_EMERGENCY_CALL_END.");
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Integer toggle = (Integer) ar.result;
+ mDcSwitchAsyncChannel[msg.what - EVENT_EMERGENCY_CALL_TOGGLED].
+ notifyEmergencyCallToggled(toggle.intValue());
+ } else if (msg.what >= EVENT_DATA_DETACHED) {
logd("EVENT_PHONE" + (msg.what - EVENT_DATA_DETACHED + 1)
+ "_DATA_DETACH.");
mDcSwitchAsyncChannel[msg.what - EVENT_DATA_DETACHED].notifyDataDetached();
@@ -276,16 +293,26 @@
case EVENT_RELEASE_ALL_REQUESTS:
onReleaseAllRequests(msg.arg1);
break;
+ case EVENT_RETRY_ATTACH:
+ onRetryAttach(msg.arg1);
+ break;
+ case EVENT_SETTINGS_CHANGED:
+ onSettingsChanged();
+ break;
+ case EVENT_SUBSCRIPTIONS_CHANGED:
+ onSubInfoReady();
+ break;
default:
loge("Un-handled message [" + msg.what + "]");
}
}
- private int requestNetwork(NetworkRequest request, int priority) {
+ private int requestNetwork(NetworkRequest request, int priority, LocalLog l, int phoneId) {
logd("requestNetwork request=" + request
+ ", priority=" + priority);
+ l.log("Dctc.requestNetwork, priority=" + priority);
- RequestInfo requestInfo = new RequestInfo(request, priority);
+ RequestInfo requestInfo = new RequestInfo(request, priority, l, phoneId);
mRequestInfos.put(request.requestId, requestInfo);
processRequests();
@@ -295,6 +322,7 @@
private int releaseNetwork(NetworkRequest request) {
RequestInfo requestInfo = mRequestInfos.get(request.requestId);
logd("releaseNetwork request=" + request + ", requestInfo=" + requestInfo);
+ if (requestInfo != null) requestInfo.log("DctController.releaseNetwork");
mRequestInfos.remove(request.requestId);
releaseRequest(requestInfo);
@@ -327,6 +355,11 @@
sendMessage(obtainMessage(EVENT_RELEASE_ALL_REQUESTS, phoneId, 0));
}
+ public void retryAttach(int phoneId) {
+ logd("retryAttach, phone:" + phoneId);
+ sendMessage(obtainMessage(EVENT_RETRY_ATTACH, phoneId, 0));
+ }
+
private void onProcessRequest() {
//process all requests
//1. Check all requests and find subscription of the top priority
@@ -353,24 +386,25 @@
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
- if (getRequestPhoneId(requestInfo.request) == phoneId && !requestInfo.executed) {
- mDcSwitchAsyncChannel[phoneId].connectSync(requestInfo);
+ if (requestInfo.phoneId == phoneId && !requestInfo.executed) {
+ mDcSwitchAsyncChannel[phoneId].connect(requestInfo);
}
}
} else {
- mDcSwitchAsyncChannel[activePhoneId].disconnectAllSync();
+ mDcSwitchAsyncChannel[activePhoneId].disconnectAll();
}
}
private void onExecuteRequest(RequestInfo requestInfo) {
- logd("onExecuteRequest request=" + requestInfo);
- if (!requestInfo.executed) {
+ if (!requestInfo.executed && mRequestInfos.containsKey(requestInfo.request.requestId)) {
+ logd("onExecuteRequest request=" + requestInfo);
+ requestInfo.log("DctController.onExecuteRequest - executed=" + requestInfo.executed);
requestInfo.executed = true;
String apn = apnForNetworkRequest(requestInfo.request);
- int phoneId = getRequestPhoneId(requestInfo.request);
+ int phoneId = requestInfo.phoneId;
PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
DcTrackerBase dcTracker = phoneBase.mDcTracker;
- dcTracker.incApnRefCount(apn);
+ dcTracker.incApnRefCount(apn, requestInfo.getLog());
}
}
@@ -379,7 +413,7 @@
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
- if (getRequestPhoneId(requestInfo.request) == phoneId) {
+ if (requestInfo.phoneId == phoneId) {
onExecuteRequest(requestInfo);
}
}
@@ -387,13 +421,16 @@
private void onReleaseRequest(RequestInfo requestInfo) {
logd("onReleaseRequest request=" + requestInfo);
- if (requestInfo != null && requestInfo.executed) {
- String apn = apnForNetworkRequest(requestInfo.request);
- int phoneId = getRequestPhoneId(requestInfo.request);
- PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
- DcTrackerBase dcTracker = phoneBase.mDcTracker;
- dcTracker.decApnRefCount(apn);
- requestInfo.executed = false;
+ if (requestInfo != null) {
+ requestInfo.log("DctController.onReleaseRequest");
+ if (requestInfo.executed) {
+ String apn = apnForNetworkRequest(requestInfo.request);
+ int phoneId = requestInfo.phoneId;
+ PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
+ DcTrackerBase dcTracker = phoneBase.mDcTracker;
+ dcTracker.decApnRefCount(apn, requestInfo.getLog());
+ requestInfo.executed = false;
+ }
}
}
@@ -402,13 +439,22 @@
Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
- if (getRequestPhoneId(requestInfo.request) == phoneId) {
+ if (requestInfo.phoneId == phoneId) {
onReleaseRequest(requestInfo);
}
}
}
- private void onSettingsChange() {
+ private void onRetryAttach(int phoneId) {
+ final int topPriPhone = getTopPriorityRequestPhoneId();
+ logd("onRetryAttach phoneId=" + phoneId + " topPri phone = " + topPriPhone);
+
+ if (phoneId != -1 && phoneId == topPriPhone) {
+ mDcSwitchAsyncChannel[phoneId].retryConnect();
+ }
+ }
+
+ private void onSettingsChanged() {
//Sub Selection
long dataSubId = mSubController.getDefaultDataSubId();
@@ -437,10 +483,11 @@
String apn = apnForNetworkRequest(requestInfo.request);
logd("[setDataSubId] activePhoneId:" + activePhoneId + ", subId =" +
dataSubId);
+ requestInfo.log("DctController.onSettingsChange releasing request");
PhoneBase phoneBase =
(PhoneBase)mPhones[activePhoneId].getActivePhone();
DcTrackerBase dcTracker = phoneBase.mDcTracker;
- dcTracker.decApnRefCount(apn);
+ dcTracker.decApnRefCount(apn, requestInfo.getLog());
requestInfo.executed = false;
}
}
@@ -467,7 +514,7 @@
while (iterator.hasNext()) {
RequestInfo requestInfo = mRequestInfos.get(iterator.next());
logd("selectExecPhone requestInfo = " + requestInfo);
- if (getRequestPhoneId(requestInfo.request) == i &&
+ if (requestInfo.phoneId == i &&
priority < requestInfo.priority) {
priority = requestInfo.priority;
retRequestInfo = requestInfo;
@@ -477,6 +524,11 @@
if (retRequestInfo != null) {
phoneId = getRequestPhoneId(retRequestInfo.request);
+ } else {
+ int defaultDds = mSubController.getDefaultDataSubId();
+ phoneId = mSubController.getPhoneId(defaultDds);
+ logd("getTopPriorityRequestPhoneId: RequestInfo list is empty, " +
+ "use Dds sub phone id");
}
logd("getTopPriorityRequestPhoneId = " + phoneId
@@ -562,10 +614,14 @@
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
if (name != null) error = true;
- name = null;
- loge("EIMS APN type not yet supported");
+ name = PhoneConstants.APN_TYPE_EMERGENCY;
+ type = ConnectivityManager.TYPE_MOBILE_EMERGENCY;
}
if (error) {
+ // TODO: If this error condition is removed, the framework's handling of
+ // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
+ // say FOTA and INTERNET are marked as restricted. This is not how
+ // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
loge("Multiple apn types specified in request - result is unspecified!");
}
if (type == -1 || name == null) {
@@ -605,6 +661,21 @@
private final SparseArray<NetworkRequest> mPendingReq = new SparseArray<NetworkRequest>();
private Phone mPhone;
+ private class RequestLogger {
+ public NetworkRequest request;
+ public LocalLog log;
+
+ public RequestLogger(NetworkRequest r, LocalLog log) {
+ request = r;
+ this.log = log;
+ }
+ }
+
+ private static final int MAX_REQUESTS_LOGGED = 20;
+ private static final int MAX_LOG_LINES_PER_REQUEST = 50;
+
+ private ArrayDeque<RequestLogger> mRequestLogs = new ArrayDeque<RequestLogger>();
+
public TelephonyNetworkFactory(Looper l, Context c, String TAG, Phone phone,
NetworkCapabilities nc) {
super(l, c, TAG, nc);
@@ -612,53 +683,78 @@
log("NetworkCapabilities: " + nc);
}
+ public LocalLog requestLog(int requestId, String l) {
+ synchronized(mRequestLogs) {
+ for (RequestLogger r : mRequestLogs) {
+ if (r.request.requestId == requestId) {
+ r.log.log(l);
+ return r.log;
+ }
+ }
+ }
+ return null;
+ }
+
+ private LocalLog addLogger(NetworkRequest request) {
+ synchronized(mRequestLogs) {
+ for (RequestLogger r : mRequestLogs) {
+ if (r.request.requestId == request.requestId) {
+ return r.log;
+ }
+ }
+ LocalLog l = new LocalLog(MAX_LOG_LINES_PER_REQUEST);
+ RequestLogger logger = new RequestLogger(request, l);
+ while (mRequestLogs.size() >= MAX_REQUESTS_LOGGED) {
+ mRequestLogs.removeFirst();
+ }
+ mRequestLogs.addLast(logger);
+ return l;
+ }
+ }
+
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
// figure out the apn type and enable it
log("Cellular needs Network for " + networkRequest);
- if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {
- log("Sub Info has not been ready, pending request.");
+ final LocalLog l = addLogger(networkRequest);
+
+ if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId()) ||
+ getRequestPhoneId(networkRequest) != mPhone.getPhoneId()) {
+ final String str = "Request not useable, pending request.";
+ log(str);
+ l.log(str);
mPendingReq.put(networkRequest.requestId, networkRequest);
return;
}
- if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) {
- DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
- String apn = apnForNetworkRequest(networkRequest);
- if (dcTracker.isApnSupported(apn)) {
- requestNetwork(networkRequest, dcTracker.getApnPriority(apn));
- } else {
- log("Unsupported APN");
- }
+ DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
+ String apn = apnForNetworkRequest(networkRequest);
+ if (dcTracker.isApnSupported(apn)) {
+ requestNetwork(networkRequest, dcTracker.getApnPriority(apn), l,
+ mPhone.getPhoneId());
} else {
- log("Request not send, put to pending");
- mPendingReq.put(networkRequest.requestId, networkRequest);
+ final String str = "Unsupported APN";
+ log(str);
+ l.log(str);
}
}
@Override
protected void releaseNetworkFor(NetworkRequest networkRequest) {
- log("Cellular releasing Network for " + networkRequest);
+ String str = "Cellular releasing Network for ";
+ log(str + networkRequest);
+ final LocalLog l = requestLog(networkRequest.requestId, str);
- if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {
- log("Sub Info has not been ready, remove request.");
+ if (mPendingReq.get(networkRequest.requestId) != null) {
+ str = "Sub Info has not been ready, remove request.";
+ log(str);
+ if (l != null) l.log(str);
mPendingReq.remove(networkRequest.requestId);
return;
}
- if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) {
- DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
- String apn = apnForNetworkRequest(networkRequest);
- if (dcTracker.isApnSupported(apn)) {
- releaseNetwork(networkRequest);
- } else {
- log("Unsupported APN");
- }
-
- } else {
- log("Request not release");
- }
+ releaseNetwork(networkRequest);
}
@Override
@@ -678,6 +774,33 @@
needNetworkFor(request, 0);
}
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(fd, writer, args);
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ pw.increaseIndent();
+ pw.println("Pending Requests:");
+ pw.increaseIndent();
+ for (int i = 0; i < mPendingReq.size(); i++) {
+ NetworkRequest request = mPendingReq.valueAt(i);
+ pw.println(request);
+ }
+ pw.decreaseIndent();
+
+ pw.println("Request History:");
+ pw.increaseIndent();
+ synchronized(mRequestLogs) {
+ for (RequestLogger r : mRequestLogs) {
+ pw.println(r.request);
+ pw.increaseIndent();
+ r.log.dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -704,7 +827,7 @@
pw.flush();
pw.println("TelephonyNetworkFactories:");
for (NetworkFactory tnf : mNetworkFactory) {
- pw.println(" " + tnf);
+ tnf.dump(fd, pw, args);
}
pw.flush();
pw.println("++++++++++++++++++++++++++++++++");
diff --git a/src/java/com/android/internal/telephony/gsm/GSMPhone.java b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
index 70636ce..b861095 100755
--- a/src/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -22,11 +22,11 @@
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
-import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.telecom.VideoProfile;
@@ -36,13 +36,13 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import com.android.ims.ImsManager;
import com.android.internal.telephony.CallTracker;
import android.text.TextUtils;
import android.telephony.Rlog;
import android.util.Log;
+import com.android.ims.ImsManager;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
@@ -63,14 +63,12 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.IccPhoneBookInterfaceManager;
import com.android.internal.telephony.MmiCode;
-import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.PhoneSubInfo;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -797,39 +795,47 @@
@Override
public Connection
dial(String dialString, int videoState) throws CallStateException {
- return dial(dialString, null, videoState);
+ return dial(dialString, null, videoState, null);
}
@Override
public Connection
- dial (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {
+ dial (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException {
+ boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
ImsPhone imsPhone = mImsPhone;
- boolean imsUseEnabled =
- ImsManager.isVolteEnabledByPlatform(mContext) &&
- ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
- ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext);
- if (!imsUseEnabled) {
- Rlog.w(LOG_TAG, "IMS is disabled: forced to CS");
- }
+ boolean imsUseEnabled = isImsUseEnabled()
+ && imsPhone != null
+ && (imsPhone.isVolteEnabled() || imsPhone.isVowifiEnabled())
+ && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
+
+ boolean useImsForEmergency = ImsManager.isVolteEnabledByPlatform(mContext)
+ && imsPhone != null
+ && isEmergency
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.useImsAlwaysForEmergencyCall)
+ && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
+ && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
if (LOCAL_DEBUG) {
- Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone
+ Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled
+ + ", useImsForEmergency=" + useImsForEmergency
+ + ", imsPhone=" + imsPhone
+ ", imsPhone.isVolteEnabled()="
+ ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+ + ", imsPhone.isVowifiEnabled()="
+ + ((imsPhone != null) ? imsPhone.isVowifiEnabled() : "N/A")
+ ", imsPhone.getServiceState().getState()="
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
- if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled()
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
- && !PhoneNumberUtils.isEmergencyNumber(dialString))
- || (PhoneNumberUtils.isEmergencyNumber(dialString)
- && mContext.getResources().getBoolean(
- com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) {
+ ImsPhone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
+
+ if (imsUseEnabled || useImsForEmergency) {
try {
if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying IMS PS call");
- return imsPhone.dial(dialString, videoState);
+ return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
} catch (CallStateException e) {
if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "IMS PS call exception " + e +
"imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
@@ -841,13 +847,17 @@
}
}
+ if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
+ && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
+ throw new CallStateException("cannot dial in current state");
+ }
if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call");
- return dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY);
+ return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
}
@Override
protected Connection
- dialInternal (String dialString, UUSInfo uusInfo, int videoState)
+ dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
throws CallStateException {
// Need to make sure dialString gets parsed properly
@@ -866,9 +876,9 @@
"dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
- return mCT.dial(newDialString, uusInfo);
+ return mCT.dial(newDialString, uusInfo, intentExtras);
} else if (mmi.isTemporaryModeCLIR()) {
- return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo);
+ return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
} else {
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
@@ -1065,6 +1075,12 @@
}
@Override
+ public String getGroupIdLevel2() {
+ IccRecords r = mIccRecords.get();
+ return (r != null) ? r.getGid2() : null;
+ }
+
+ @Override
public String getLine1Number() {
IccRecords r = mIccRecords.get();
return (r != null) ? r.getMsisdnNumber() : null;
@@ -1435,11 +1451,14 @@
mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
+ startLceAfterRadioIsAvailable();
}
break;
case EVENT_RADIO_ON:
- // do-nothing
+ // If this is on APM off, SIM may already be loaded. Send setPreferredNetworkType
+ // request to RIL to preserve user setting across APM toggling
+ setPreferredNetworkTypeIfSimLoaded();
break;
case EVENT_REGISTERED_TO_NETWORK:
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 64b4155..7de8217 100755
--- a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.gsm;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -37,6 +38,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DriverCall;
import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.LastCallFailCause;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneConstants;
@@ -169,7 +171,8 @@
* clirMode is one of the CLIR_ constants
*/
synchronized Connection
- dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
+ dial (String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
+ throws CallStateException {
// note that this triggers call state changed notif
clearDisconnected();
@@ -189,6 +192,14 @@
// and we need to make sure the foreground call is clear
// for the newly dialed connection
switchWaitingOrHoldingAndActive();
+ // This is a hack to delay DIAL so that it is sent out to RIL only after
+ // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
+ // multi-way conference calls due to DIAL being sent out before SWITCH is processed
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
// Fake local state so that
// a) foregroundCall is empty for the newly dialed connection
@@ -239,13 +250,13 @@
}
Connection
- dial(String dialString, UUSInfo uusInfo) throws CallStateException {
- return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo);
+ dial(String dialString, UUSInfo uusInfo, Bundle intentExtras) throws CallStateException {
+ return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);
}
Connection
- dial(String dialString, int clirMode) throws CallStateException {
- return dial(dialString, clirMode, null);
+ dial(String dialString, int clirMode, Bundle intentExtras) throws CallStateException {
+ return dial(dialString, clirMode, null, intentExtras);
}
void
@@ -907,6 +918,7 @@
case EVENT_GET_LAST_CALL_FAIL_CAUSE:
int causeCode;
+ String vendorCause = null;
ar = (AsyncResult)msg.obj;
operationComplete();
@@ -918,7 +930,9 @@
Rlog.i(LOG_TAG,
"Exception during getLastCallFailCause, assuming normal disconnect");
} else {
- causeCode = ((int[])ar.result)[0];
+ LastCallFailCause failCause = (LastCallFailCause)ar.result;
+ causeCode = failCause.causeCode;
+ vendorCause = failCause.vendorCause;
}
// Log the causeCode if its not normal
if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
@@ -939,7 +953,7 @@
) {
GsmConnection conn = mDroppedDuringPoll.get(i);
- conn.onRemoteDisconnect(causeCode);
+ conn.onRemoteDisconnect(causeCode, vendorCause);
}
updatePhoneState();
diff --git a/src/java/com/android/internal/telephony/gsm/GsmConnection.java b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
index 3b722d3..e343c6e 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmConnection.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
@@ -64,6 +64,7 @@
PostDialState mPostDialState = PostDialState.NOT_STARTED;
UUSInfo mUusInfo;
int mPreciseCause = 0;
+ String mVendorCause;
Connection mOrigConnection;
@@ -293,6 +294,7 @@
onHangupLocal() {
mCause = DisconnectCause.LOCAL;
mPreciseCause = 0;
+ mVendorCause = null;
}
/**
@@ -375,8 +377,9 @@
}
/*package*/ void
- onRemoteDisconnect(int causeCode) {
+ onRemoteDisconnect(int causeCode, String vendorCause) {
this.mPreciseCause = causeCode;
+ this.mVendorCause = vendorCause;
onDisconnect(disconnectCauseFromCode(causeCode));
}
@@ -756,6 +759,11 @@
}
@Override
+ public String getVendorDisconnectCause() {
+ return mVendorCause;
+ }
+
+ @Override
public void migrateFrom(Connection c) {
if (c == null) return;
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 1a5ac32..8bd1ec7 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -306,7 +306,7 @@
isServiceClassVoiceorNone(ssData.serviceClass));
Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag cffEnabled: " + cffEnabled);
- if (mPhone.mIccRecords != null) {
+ if (mIccRecords != null) {
mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, null);
Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info.");
} else {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index b18fa4a..4fbc924 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -157,7 +157,7 @@
HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
- false /*isText*/);
+ false /*isText*/, true /*persistMessage*/);
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
@@ -176,13 +176,15 @@
/** {@inheritDoc} */
@Override
protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+ PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+ boolean persistMessage) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
if (pdu != null) {
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
- messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/);
+ messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
+ persistMessage);
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
@@ -226,7 +228,8 @@
message, pdu);
return getSmsTracker(map, sentIntent,
deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
- smsHeader, !lastPart, fullMessageText, true /*isText*/);
+ smsHeader, !lastPart, fullMessageText, true /*isText*/,
+ false /*persistMessage*/);
} else {
Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
return null;
diff --git a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 1541c49..133e628 100755
--- a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -31,7 +31,10 @@
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -45,7 +48,6 @@
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.CellLocation;
-import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -58,11 +60,10 @@
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ProxyController;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RestrictedState;
import com.android.internal.telephony.ServiceStateTracker;
@@ -70,10 +71,10 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.DcTrackerBase;
+import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.SIMRecords;
-import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
@@ -183,21 +184,6 @@
mAlarmSwitch = false;
DcTrackerBase dcTracker = mPhone.mDcTracker;
powerOffRadioSafely(dcTracker);
- } else if (intent.getAction().equals(
- TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE)) {
- if (DBG) {
- log("Received Intent ACTION_SET_RADIO_CAPABILITY_DONE");
- }
- ArrayList<RadioAccessFamily> newPhoneRcList =
- intent.getParcelableArrayListExtra(
- TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY);
- if (newPhoneRcList == null || newPhoneRcList.size() == 0) {
- if (DBG) {
- log("EXTRA_RADIO_ACCESS_FAMILY not present.");
- }
- } else {
- onSetPhoneRCDone(newPhoneRcList);
- }
}
}
};
@@ -260,7 +246,6 @@
filter = new IntentFilter();
Context context = phone.getContext();
filter.addAction(ACTION_RADIO_OFF);
- filter.addAction(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE);
context.registerReceiver(mIntentReceiver, filter);
}
@@ -503,6 +488,11 @@
setPowerStateToDesired();
break;
+ case EVENT_IMS_CAPABILITY_CHANGED:
+ if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED");
+ updateSpnDisplay();
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -626,35 +616,59 @@
}
// The value of spn/showSpn are same in different scenarios.
- // EXTRA_SHOW_SPN = depending on IccRecords rule
+ // EXTRA_SHOW_SPN = depending on IccRecords rule and radio/IMS state
// EXTRA_SPN = spn
+ // EXTRA_DATA_SPN = dataSpn
String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
+ String dataSpn = spn;
boolean showSpn = !TextUtils.isEmpty(spn)
&& ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
== SIMRecords.SPN_RULE_SHOW_SPN);
- // airplane mode or spn equals plmn, do not show spn
- if (mSS.getVoiceRegState() == ServiceState.STATE_POWER_OFF
+ if (!TextUtils.isEmpty(spn)
+ && mPhone.getImsPhone() != null
+ && ((ImsPhone) mPhone.getImsPhone()).isVowifiEnabled()) {
+ // In Wi-Fi Calling mode show SPN+WiFi
+ String formatVoice = mPhone.getContext().getText(
+ com.android.internal.R.string.wfcSpnFormat).toString();
+ String formatData = mPhone.getContext().getText(
+ com.android.internal.R.string.wfcDataSpnFormat).toString();
+ String originalSpn = spn.trim();
+ spn = String.format(formatVoice, originalSpn);
+ dataSpn = String.format(formatData, originalSpn);
+ showSpn = true;
+ showPlmn = false;
+ } else if (mSS.getVoiceRegState() == ServiceState.STATE_POWER_OFF
|| (showPlmn && TextUtils.equals(spn, plmn))) {
+ // airplane mode or spn equals plmn, do not show spn
spn = null;
showSpn = false;
}
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+
// Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
- if (showPlmn != mCurShowPlmn
+ if (mSubId != subId ||
+ showPlmn != mCurShowPlmn
|| showSpn != mCurShowSpn
|| !TextUtils.equals(spn, mCurSpn)
+ || !TextUtils.equals(dataSpn, mCurDataSpn)
|| !TextUtils.equals(plmn, mCurPlmn)) {
if (DBG) {
log(String.format("updateSpnDisplay: changed" +
" sending intent rule=" + rule +
- " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
- showPlmn, plmn, showSpn, spn));
+ " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s' dataSpn='%s' subId='%d'",
+ showPlmn, plmn, showSpn, spn, dataSpn, subId));
}
Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
+ intent.putExtra(TelephonyIntents.EXTRA_DATA_SPN, dataSpn);
intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
@@ -666,9 +680,11 @@
}
}
+ mSubId = subId;
mCurShowSpn = showSpn;
mCurShowPlmn = showPlmn;
mCurSpn = spn;
+ mCurDataSpn = dataSpn;
mCurPlmn = plmn;
}
@@ -696,12 +712,6 @@
return;
}
- if (!mCi.getRadioState().isOn()) {
- // Radio has crashed or turned off
- cancelPollState();
- return;
- }
-
if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
loge("RIL implementation has returned an error where it must succeed" +
ar.exception);
@@ -842,39 +852,69 @@
mPollingContext[0]--;
if (mPollingContext[0] == 0) {
- /**
- * Since the roaming state of gsm service (from +CREG) and
- * data service (from +CGREG) could be different, the new SS
- * is set to roaming when either is true.
- *
- * There are exceptions for the above rule.
- * The new SS is not set as roaming while gsm service reports
- * roaming but indeed it is same operator.
- * And the operator is considered non roaming.
- *
- * The test for the operators is to handle special roaming
- * agreements and MVNO's.
- */
- boolean roaming = (mGsmRoaming || mDataRoaming);
- if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
- (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
- roaming = false;
- }
-
- if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric())) {
- roaming = false;
- } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric())) {
- roaming = true;
- }
-
- mNewSS.setVoiceRoaming(roaming);
- mNewSS.setDataRoaming(roaming);
+ updateRoamingState();
mNewSS.setEmergencyOnly(mEmergencyOnly);
pollStateDone();
}
}
/**
+ * Query the carrier configuration to determine if there any network overrides
+ * for roaming or not roaming for the current service state.
+ */
+ protected void updateRoamingState() {
+ /**
+ * Since the roaming state of gsm service (from +CREG) and
+ * data service (from +CGREG) could be different, the new SS
+ * is set to roaming when either is true.
+ *
+ * There are exceptions for the above rule.
+ * The new SS is not set as roaming while gsm service reports
+ * roaming but indeed it is same operator.
+ * And the operator is considered non roaming.
+ *
+ * The test for the operators is to handle special roaming
+ * agreements and MVNO's.
+ */
+ boolean roaming = (mGsmRoaming || mDataRoaming);
+ if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
+ (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+ roaming = false;
+ }
+
+ // Save the roaming state before carrier config possibly overrides it.
+ mNewSS.setDataRoamingFromRegistration(roaming);
+
+ ICarrierConfigLoader configLoader =
+ (ICarrierConfigLoader) ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
+ if (configLoader != null) {
+ try {
+ PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
+
+ if (alwaysOnHomeNetwork(b)) {
+ log("updateRoamingState: carrier config override always on home network");
+ roaming = false;
+ } else if (isNonRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())) {
+ log("updateRoamingState: carrier config override set non roaming:"
+ + mNewSS.getOperatorNumeric());
+ roaming = false;
+ } else if (isRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())) {
+ log("updateRoamingState: carrier config override set roaming:"
+ + mNewSS.getOperatorNumeric());
+ roaming = true;
+ }
+ } catch (RemoteException e) {
+ loge("updateRoamingState: unable to access carrier config service");
+ }
+ } else {
+ log("updateRoamingState: no carrier config service available");
+ }
+
+ mNewSS.setVoiceRoaming(roaming);
+ mNewSS.setDataRoaming(roaming);
+ }
+
+ /**
* Set both voice and data roaming type,
* judging from the ISO country of SIM VS network.
*/
@@ -950,8 +990,10 @@
setSignalStrengthDefaultValues();
mGotCountryCode = false;
mNitzUpdatedTime = false;
- pollStateDone();
- break;
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ != mSS.getRilDataRadioTechnology()) {
+ pollStateDone();
+ }
default:
// Issue all poll-related commands at once
@@ -982,6 +1024,13 @@
}
private void pollStateDone() {
+ if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
+ mNewSS.setVoiceRoaming(true);
+ mNewSS.setDataRoaming(true);
+ }
+ useDataRegStateForDataOnlyDevices();
+ resetServiceStateInIwlanMode();
+
if (DBG) {
log("Poll ServiceState done: " +
" oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" +
@@ -991,13 +1040,6 @@
" mNewReasonDataDenied=" + mNewReasonDataDenied);
}
- if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
- mNewSS.setVoiceRoaming(true);
- mNewSS.setDataRoaming(true);
- }
-
- useDataRegStateForDataOnlyDevices();
-
boolean hasRegistered =
mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
&& mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
@@ -1037,7 +1079,6 @@
boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
-
TelephonyManager tm =
(TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
@@ -1092,6 +1133,11 @@
if (hasRilDataRadioTechnologyChanged) {
tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilVoiceRadioTechnology());
+
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ log("pollStateDone: IWLAN enabled");
+ }
}
if (hasRegistered) {
@@ -1250,7 +1296,13 @@
if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
notifyDataRegStateRilRadioTechnologyChanged();
- mPhone.notifyDataConnection(null);
+
+ if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ == mSS.getRilDataRadioTechnology()) {
+ mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+ } else {
+ mPhone.notifyDataConnection(null);
+ }
}
if (hasVoiceRoamingOn) {
@@ -1922,7 +1974,6 @@
* @param notifyType is one state of PS/CS_*_ENABLE/DISABLE
*/
private void setNotification(int notifyType) {
-
if (DBG) log("setNotification: create notification " + notifyType);
// Needed because sprout RIL sends these when they shouldn't?
@@ -1935,13 +1986,6 @@
Context context = mPhone.getContext();
- mNotification = new Notification();
- mNotification.when = System.currentTimeMillis();
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
- Intent intent = new Intent();
- mNotification.contentIntent = PendingIntent
- .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
CharSequence details = "";
CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle);
@@ -1974,11 +2018,16 @@
}
if (DBG) log("setNotification: put notification " + title + " / " +details);
- mNotification.tickerText = title;
- mNotification.color = context.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color);
- mNotification.setLatestEventInfo(context, title, details,
- mNotification.contentIntent);
+ mNotification = new Notification.Builder(context)
+ .setWhen(System.currentTimeMillis())
+ .setAutoCancel(true)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+ .setTicker(title)
+ .setColor(context.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(details)
+ .build();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -2068,6 +2117,7 @@
pw.println(" mNotification=" + mNotification);
pw.println(" mWakeLock=" + mWakeLock);
pw.println(" mCurSpn=" + mCurSpn);
+ pw.println(" mCurDataSpn=" + mCurDataSpn);
pw.println(" mCurShowSpn=" + mCurShowSpn);
pw.println(" mCurPlmn=" + mCurPlmn);
pw.println(" mCurShowPlmn=" + mCurShowPlmn);
@@ -2146,53 +2196,7 @@
mImsRegistrationOnOff = registered;
}
- public void onSetPhoneRCDone(ArrayList<RadioAccessFamily> phoneRcs) {
- int INVALID = -1;
- int size = 0;
- boolean needToChangeNetworkMode = false;
- RadioAccessFamily phoneRaf = null;
- int myPhoneId = mPhone.getPhoneId();
- int newCapability = 0;
- int networkMode = INVALID;
-
- if (phoneRcs == null) return;
- size = phoneRcs.size();
- for (int i = 0; i < size; i++) {
- phoneRaf = phoneRcs.get(i);
- if (myPhoneId == phoneRaf.getPhoneId()) {
- needToChangeNetworkMode = true;
- newCapability = phoneRaf.getRadioAccessFamily();
- break;
- }
- }
-
- if (needToChangeNetworkMode) {
- if ((newCapability & RadioAccessFamily.RAF_LTE)
- == RadioAccessFamily.RAF_LTE) {
- networkMode = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
- } else if ((newCapability & RadioAccessFamily.RAF_UMTS)
- == RadioAccessFamily.RAF_UMTS) {
- networkMode = RILConstants.NETWORK_MODE_WCDMA_PREF;
- } else if ((newCapability & RadioAccessFamily.RAF_GSM)
- == RadioAccessFamily.RAF_GSM) {
- networkMode = RILConstants.NETWORK_MODE_GSM_ONLY;
- } else {
- networkMode = INVALID;
- log("Error: capability is not define");
- }
-
- if (DBG) log("myPhoneId=" + myPhoneId + " newCapability=" + newCapability
- + " networkMode=" + networkMode);
-
- if (networkMode != INVALID) {
- //FIXME : update preferred network mode
- //TelephonyManager.putIntAtIndex(mPhone.getContext().getContentResolver(),
- // Settings.Global.PREFERRED_NETWORK_MODE, myPhoneId, networkMode);
- //networkMode = PhoneFactory.calculatePreferredNetworkType(mPhone.getContext());
- //FIXME : update preferred network mode
-
- mCi.setPreferredNetworkType(networkMode, null);
- }
- }
- }
+ public void onImsCapabilityChanged() {
+ sendMessage(obtainMessage(EVENT_IMS_CAPABILITY_CHANGED));
+ }
}
diff --git a/src/java/com/android/internal/telephony/gsm/SmsMessage.java b/src/java/com/android/internal/telephony/gsm/SmsMessage.java
index a1f029f..0d9bed4 100644
--- a/src/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/src/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -777,7 +777,8 @@
}
/**
- * Calculate the number of septets needed to encode the message.
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message.
*
* @param msgBody the message to encode
* @param use7bitOnly ignore (but still count) illegal characters if true
@@ -795,31 +796,7 @@
}
TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(newMsgBody, use7bitOnly);
if (ted == null) {
- ted = new TextEncodingDetails();
- int octets = newMsgBody.length() * 2;
- ted.codeUnitCount = newMsgBody.length();
- if (octets > MAX_USER_DATA_BYTES) {
- // If EMS is not supported, break down EMS into single segment SMS
- // and add page info " x/y".
- // In the case of UCS2 encoding type, we need 8 bytes for this
- // but we only have 6 bytes from UDH, so truncate the limit for
- // each segment by 2 bytes (1 char).
- int max_user_data_bytes_with_header = MAX_USER_DATA_BYTES_WITH_HEADER;
- if (!android.telephony.SmsMessage.hasEmsSupport()) {
- // make sure total number of segments is less than 10
- if (octets <= 9 * (max_user_data_bytes_with_header - 2))
- max_user_data_bytes_with_header -= 2;
- }
-
- ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
- max_user_data_bytes_with_header;
- ted.codeUnitsRemaining = ((ted.msgCount *
- max_user_data_bytes_with_header) - octets) / 2;
- } else {
- ted.msgCount = 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
- }
- ted.codeUnitSize = ENCODING_16BIT;
+ return SmsMessageBase.calcUnicodeEncodingDetails(newMsgBody);
}
return ted;
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index efc53c4..48d0d37 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -16,10 +16,16 @@
package com.android.internal.telephony.imsphone;
+import android.app.Activity;
import android.app.ActivityManagerNative;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -37,9 +43,11 @@
import com.android.ims.ImsCallForwardInfo;
import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConfig;
import com.android.ims.ImsEcbm;
import com.android.ims.ImsEcbmStateListener;
import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
import com.android.ims.ImsReasonInfo;
import com.android.ims.ImsSsInfo;
import com.android.ims.ImsUtInterface;
@@ -77,8 +85,10 @@
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
import com.android.internal.telephony.uicc.IccRecords;
@@ -98,9 +108,15 @@
protected static final int EVENT_GET_CALL_BARRING_DONE = EVENT_LAST + 2;
protected static final int EVENT_SET_CALL_WAITING_DONE = EVENT_LAST + 3;
protected static final int EVENT_GET_CALL_WAITING_DONE = EVENT_LAST + 4;
+ protected static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED = EVENT_LAST + 5;
public static final String CS_FALLBACK = "cs_fallback";
+ public static final String EXTRA_KEY_ALERT_TITLE = "alertTitle";
+ public static final String EXTRA_KEY_ALERT_MESSAGE = "alertMessage";
+ public static final String EXTRA_KEY_ALERT_SHOW = "alertShow";
+ public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage";
+
static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
@@ -128,6 +144,7 @@
private final RegistrantList mSilentRedialRegistrants = new RegistrantList();
private boolean mImsRegistered = false;
+
// A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@Override
@@ -171,12 +188,34 @@
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
mWakeLock.setReferenceCounted(false);
+
+ if (mDefaultPhone.getServiceStateTracker() != null) {
+ mDefaultPhone.getServiceStateTracker()
+ .registerForDataRegStateOrRatChanged(this,
+ EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
+ }
+ updateDataServiceState();
}
public void updateParentPhone(PhoneBase parentPhone) {
// synchronization is managed at the PhoneBase scope (which calls this function)
+ if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
+ mDefaultPhone.getServiceStateTracker().
+ unregisterForDataRegStateOrRatChanged(this);
+ }
mDefaultPhone = parentPhone;
mPhoneId = mDefaultPhone.getPhoneId();
+ if (mDefaultPhone.getServiceStateTracker() != null) {
+ mDefaultPhone.getServiceStateTracker()
+ .registerForDataRegStateOrRatChanged(this,
+ EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
+ }
+ updateDataServiceState();
+
+ // When the parent phone is updated, we need to notify listeners of the cached video
+ // capability.
+ Rlog.d(LOG_TAG, "updateParentPhone - Notify video capability changed " + mIsVideoCapable);
+ notifyForVideoCapabilityChanged(mIsVideoCapable);
}
@Override
@@ -188,6 +227,10 @@
mCT.dispose();
//Force all referenced classes to unregister their former registered events
+ if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
+ mDefaultPhone.getServiceStateTracker().
+ unregisterForDataRegStateOrRatChanged(this);
+ }
}
@Override
@@ -207,6 +250,7 @@
/* package */ void setServiceState(int state) {
mSS.setState(state);
+ updateDataServiceState();
}
@Override
@@ -463,14 +507,42 @@
mDefaultPhone.notifyNewRingingConnectionP(c);
}
+ public static void checkWfcWifiOnlyModeBeforeDial(ImsPhone imsPhone, Context context)
+ throws CallStateException {
+ if (imsPhone == null ||
+ !imsPhone.isVowifiEnabled()) {
+ boolean wfcWiFiOnly = (ImsManager.isWfcEnabledByPlatform(context) &&
+ ImsManager.isWfcEnabledByUser(context) &&
+ (ImsManager.getWfcMode(context) ==
+ ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+ if (wfcWiFiOnly) {
+ throw new CallStateException(
+ CallStateException.ERROR_DISCONNECTED,
+ "WFC Wi-Fi Only Mode: IMS not registered");
+ }
+ }
+ }
+
+ public void notifyForVideoCapabilityChanged(boolean isVideoCapable) {
+ mIsVideoCapable = isVideoCapable;
+ mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable);
+ }
@Override
public Connection
dial(String dialString, int videoState) throws CallStateException {
- return dialInternal(dialString, videoState);
+ return dialInternal(dialString, videoState, null);
}
- protected Connection dialInternal(String dialString, int videoState)
+ @Override
+ public Connection
+ dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+ throws CallStateException {
+ // ignore UUSInfo
+ return dialInternal (dialString, videoState, intentExtras);
+ }
+
+ protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -481,7 +553,7 @@
}
if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- return mCT.dial(dialString, videoState);
+ return mCT.dial(dialString, videoState, intentExtras);
}
// Only look at the Network portion for mmi
@@ -492,9 +564,9 @@
"dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
- return mCT.dial(dialString, videoState);
+ return mCT.dial(dialString, videoState, intentExtras);
} else if (mmi.isTemporaryModeCLIR()) {
- return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState);
+ return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState, intentExtras);
} else if (!mmi.isSupportedOverImsPhone()) {
// If the mmi is not supported by IMS service,
// try to initiate dialing with default phone
@@ -668,8 +740,18 @@
String dialingNumber,
int timerSeconds,
Message onComplete) {
+ setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber,
+ CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
+ }
+
+ public void setCallForwardingOption(int commandInterfaceCFAction,
+ int commandInterfaceCFReason,
+ String dialingNumber,
+ int serviceClass,
+ int timerSeconds,
+ Message onComplete) {
if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
- + ", reason=" + commandInterfaceCFReason);
+ + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
(isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
Message resp;
@@ -684,6 +766,7 @@
ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
getConditionFromCFReason(commandInterfaceCFReason),
dialingNumber,
+ serviceClass,
timerSeconds,
onComplete);
} catch (ImsException e) {
@@ -710,13 +793,17 @@
@Override
public void setCallWaiting(boolean enable, Message onComplete) {
+ setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+ }
+
+ public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
Message resp;
resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
try {
ImsUtInterface ut = mCT.getUtInterface();
- ut.updateCallWaiting(enable, resp);
+ ut.updateCallWaiting(enable, serviceClass, resp);
} catch (ImsException e) {
sendErrorResponse(onComplete, e);
}
@@ -833,6 +920,8 @@
case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
error = CommandException.Error.PASSWORD_INCORRECT;
break;
+ case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
+ error = CommandException.Error.RADIO_NOT_AVAILABLE;
default:
break;
}
@@ -1039,22 +1128,24 @@
sendResponse(Message onComplete, Object result, Throwable e) {
if (onComplete != null) {
CommandException ex = null;
- ImsException imsEx = null;
if (e != null) {
- if (e instanceof ImsException) {
- imsEx = (ImsException) e;
- AsyncResult.forMessage(onComplete, result, imsEx);
- } else {
- ex = getCommandException(e);
- AsyncResult.forMessage(onComplete, result, ex);
- }
- } else {
- AsyncResult.forMessage(onComplete, result, null);
+ ex = getCommandException(e);
}
+ AsyncResult.forMessage(onComplete, result, ex);
onComplete.sendToTarget();
}
}
+ private void updateDataServiceState() {
+ if (mSS != null && mDefaultPhone.getServiceStateTracker() != null
+ && mDefaultPhone.getServiceStateTracker().mSS != null) {
+ ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
+ mSS.setDataRegState(ss.getDataRegState());
+ mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology());
+ Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
+ }
+ }
+
@Override
public void handleMessage (Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
@@ -1097,6 +1188,11 @@
sendResponse((Message) ar.userObj, null, ar.exception);
break;
+ case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
+ if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
+ updateDataServiceState();
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -1234,12 +1330,20 @@
mEcmExitRespRegistrant.clear();
}
+ public void onFeatureCapabilityChanged() {
+ mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
+ }
+
public boolean isVolteEnabled() {
return mCT.isVolteEnabled();
}
- public boolean isVtEnabled() {
- return mCT.isVtEnabled();
+ public boolean isVowifiEnabled() {
+ return mCT.isVowifiEnabled();
+ }
+
+ public boolean isVideoCallEnabled() {
+ return mCT.isVideoCallEnabled();
}
public Phone getDefaultPhone() {
@@ -1257,4 +1361,120 @@
public void callEndCleanupHandOverCallIfAny() {
mCT.callEndCleanupHandOverCallIfAny();
}
+
+ private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Add notification only if alert was not shown by WfcSettings
+ if (getResultCode() == Activity.RESULT_OK) {
+ // Default result code (as passed to sendOrderedBroadcast)
+ // means that intent was not received by WfcSettings.
+
+ CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE);
+ CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE);
+ CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE);
+
+ Intent resultIntent = new Intent(Intent.ACTION_MAIN);
+ resultIntent.setClassName("com.android.settings",
+ "com.android.settings.Settings$WifiCallingSettingsActivity");
+ resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true);
+ resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
+ resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
+ PendingIntent resultPendingIntent =
+ PendingIntent.getActivity(
+ mContext,
+ 0,
+ resultIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ );
+
+ final Notification notification =
+ new Notification.Builder(mContext)
+ .setSmallIcon(android.R.drawable.stat_sys_warning)
+ .setContentTitle(title)
+ .setContentText(messageNotification)
+ .setAutoCancel(true)
+ .setContentIntent(resultPendingIntent)
+ .setStyle(new Notification.BigTextStyle().bigText(messageNotification))
+ .build();
+ final String notificationTag = "wifi_calling";
+ final int notificationId = 1;
+
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(notificationTag, notificationId,
+ notification);
+ }
+ }
+ };
+
+ /**
+ * Show notification in case of some error codes.
+ */
+ public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
+ if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
+ && imsReasonInfo.mExtraMessage != null) {
+
+ final String[] wfcOperatorErrorCodes =
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.wfcOperatorErrorCodes);
+ final String[] wfcOperatorErrorAlertMessages =
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.wfcOperatorErrorAlertMessages);
+ final String[] wfcOperatorErrorNotificationMessages =
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
+
+ for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
+ // Match error code.
+ if (!imsReasonInfo.mExtraMessage.startsWith(
+ wfcOperatorErrorCodes[i])) {
+ continue;
+ }
+ // If there is no delimiter at the end of error code string
+ // then we need to verify that we are not matching partial code.
+ // EXAMPLE: "REG9" must not match "REG99".
+ // NOTE: Error code must not be empty.
+ int codeStringLength = wfcOperatorErrorCodes[i].length();
+ char lastChar = wfcOperatorErrorCodes[i].charAt(codeStringLength-1);
+ if (Character.isLetterOrDigit(lastChar)) {
+ if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
+ char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
+ if (Character.isLetterOrDigit(nextChar)) {
+ continue;
+ }
+ }
+ }
+
+ final CharSequence title = mContext.getText(
+ com.android.internal.R.string.wfcRegErrorTitle);
+
+ CharSequence messageAlert = imsReasonInfo.mExtraMessage;
+ CharSequence messageNotification = imsReasonInfo.mExtraMessage;
+ if (!wfcOperatorErrorAlertMessages[i].isEmpty()) {
+ messageAlert = wfcOperatorErrorAlertMessages[i];
+ }
+ if (!wfcOperatorErrorNotificationMessages[i].isEmpty()) {
+ messageNotification = wfcOperatorErrorNotificationMessages[i];
+ }
+
+ // UX requirement is to disable WFC in case of "permanent" registration failures.
+ ImsManager.setWfcSetting(mContext, false);
+
+ // If WfcSettings are active then alert will be shown
+ // otherwise notification will be added.
+ Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
+ intent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
+ intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
+ intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
+ mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
+ null, Activity.RESULT_OK, null, null);
+
+ // We can only match a single error code
+ // so should break the loop after a successful match.
+ break;
+ }
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 22eb63d..8ad7472 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.LinkProperties;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -66,13 +67,6 @@
}
@Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
- throws CallStateException {
- // ignore UUSInfo
- return dial(dialString, videoState);
- }
-
- @Override
public void migrateFrom(PhoneBase from) {
super.migrateFrom(from);
migrate(mRingbackRegistrants, ((ImsPhoneBase)from).mRingbackRegistrants);
@@ -341,6 +335,11 @@
}
@Override
+ public String getGroupIdLevel2() {
+ return null;
+ }
+
+ @Override
public String getIccSerialNumber() {
return null;
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index 5098bbb..c6f118a 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -16,8 +16,10 @@
package com.android.internal.telephony.imsphone;
+import android.telecom.ConferenceParticipant;
import android.telephony.Rlog;
import android.telephony.DisconnectCause;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -34,22 +36,40 @@
* {@hide}
*/
public class ImsPhoneCall extends Call {
- /*************************** Instance Variables **************************/
-
private static final String LOG_TAG = "ImsPhoneCall";
+ // This flag is meant to be used as a debugging tool to quickly see all logs
+ // regardless of the actual log level set on this component.
+ private static final boolean FORCE_DEBUG = false; /* STOPSHIP if true */
+ private static final boolean DBG = FORCE_DEBUG || Rlog.isLoggable(LOG_TAG, Log.DEBUG);
+ private static final boolean VDBG = FORCE_DEBUG || Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+ /*************************** Instance Variables **************************/
+ public static final String CONTEXT_UNKNOWN = "UK";
+ public static final String CONTEXT_RINGING = "RG";
+ public static final String CONTEXT_FOREGROUND = "FG";
+ public static final String CONTEXT_BACKGROUND = "BG";
+ public static final String CONTEXT_HANDOVER = "HO";
+
/*package*/ ImsPhoneCallTracker mOwner;
private boolean mRingbackTonePlayed = false;
+ // Determines what type of ImsPhoneCall this is. ImsPhoneCallTracker uses instances of
+ // ImsPhoneCall to for fg, bg, etc calls. This is used as a convenience for logging so that it
+ // can be made clear whether a call being logged is the foreground, background, etc.
+ private final String mCallContext;
+
/****************************** Constructors *****************************/
/*package*/
ImsPhoneCall() {
+ mCallContext = CONTEXT_UNKNOWN;
}
/*package*/
- ImsPhoneCall(ImsPhoneCallTracker owner) {
+ ImsPhoneCall(ImsPhoneCallTracker owner, String context) {
mOwner = owner;
+ mCallContext = context;
}
public void dispose() {
@@ -101,21 +121,53 @@
}
@Override
- public String
- toString() {
- return mState.toString();
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ImsPhoneCall ");
+ sb.append(mCallContext);
+ sb.append(" state: ");
+ sb.append(mState.toString());
+ sb.append(" ");
+ if (mConnections.size() > 1) {
+ sb.append(" ERROR_MULTIPLE ");
+ }
+ for (Connection conn : mConnections) {
+ sb.append(conn);
+ sb.append(" ");
+ }
+
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public List<ConferenceParticipant> getConferenceParticipants() {
+ ImsCall call = getImsCall();
+ if (call == null) {
+ return null;
+ }
+ return call.getConferenceParticipants();
}
//***** Called from ImsPhoneConnection
/*package*/ void
attach(Connection conn) {
+ if (VDBG) {
+ Rlog.v(LOG_TAG, "attach : " + mCallContext + " conn = " + conn);
+ }
clearDisconnected();
mConnections.add(conn);
+
+ mOwner.logState();
}
/*package*/ void
attach(Connection conn, State state) {
+ if (VDBG) {
+ Rlog.v(LOG_TAG, "attach : " + mCallContext + " state = " +
+ state.toString());
+ }
this.attach(conn);
mState = state;
}
@@ -153,8 +205,13 @@
/*package*/ void
detach(ImsPhoneConnection conn) {
+ if (VDBG) {
+ Rlog.v(LOG_TAG, "detach : " + mCallContext + " conn = " + conn);
+ }
mConnections.remove(conn);
clearDisconnected();
+
+ mOwner.logState();
}
/**
@@ -227,13 +284,15 @@
long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
if (conferenceConnectTime > 0) {
imsPhoneConnection.setConnectTime(conferenceConnectTime);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "merge: conference connect time is 0");
+ }
}
}
-
- ImsPhoneConnection[] cc = that.mConnections.toArray(
- new ImsPhoneConnection[that.mConnections.size()]);
- for (ImsPhoneConnection c : cc) {
- c.update(null, state);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "merge(" + mCallContext + "): " + that + "state = "
+ + state);
}
}
@@ -302,12 +361,16 @@
}
void switchWith(ImsPhoneCall that) {
+ if (VDBG) {
+ Rlog.v(LOG_TAG, "switchWith : switchCall = " + this + " withCall = " + that);
+ }
synchronized (ImsPhoneCall.class) {
ImsPhoneCall tmp = new ImsPhoneCall();
tmp.takeOver(this);
this.takeOver(that);
that.takeOver(tmp);
}
+ mOwner.logState();
}
private void takeOver(ImsPhoneCall that) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index d148713..1515e9c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -28,6 +28,7 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -74,8 +75,13 @@
private static final boolean DBG = true;
- private boolean mIsVolteEnabled = false;
- private boolean mIsVtEnabled = false;
+ // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
+ // calls. This is helpful for debugging.
+ private static final boolean VERBOSE_STATE_LOGGING = false; /* stopship if true */
+
+ //Indices map to ImsConfig.FeatureConstants
+ private boolean[] mImsFeatureEnabled = {false, false, false, false};
+ private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi"};
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -105,13 +111,7 @@
ImsPhoneCallTracker.this, mRingingCall);
addConnection(conn);
- IImsVideoCallProvider imsVideoCallProvider =
- imsCall.getCallSession().getVideoCallProvider();
- if (imsVideoCallProvider != null) {
- ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
- new ImsVideoCallProviderWrapper(imsVideoCallProvider);
- conn.setVideoProvider(imsVideoCallProviderWrapper);
- }
+ setVideoCallProvider(conn, imsCall);
if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
(mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
@@ -147,10 +147,10 @@
private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
- ImsPhoneCall mRingingCall = new ImsPhoneCall(this);
- ImsPhoneCall mForegroundCall = new ImsPhoneCall(this);
- ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this);
- ImsPhoneCall mHandoverCall = new ImsPhoneCall(this);
+ final ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING);
+ final ImsPhoneCall mForegroundCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_FOREGROUND);
+ final ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_BACKGROUND);
+ final ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER);
private ImsPhoneConnection mPendingMO;
private int mClirMode = CommandsInterface.CLIR_DEFAULT;
@@ -174,7 +174,7 @@
private boolean mIsInEmergencyCall = false;
private int pendingCallClirMode;
- private int pendingCallVideoState;
+ private int mPendingCallVideoState;
private boolean pendingCallInEcm = false;
private boolean mSwitchingFgAndBgCalls = false;
private ImsCall mCallExpectedToResume = null;
@@ -275,17 +275,18 @@
}
Connection
- dial(String dialString, int videoState) throws CallStateException {
+ dial(String dialString, int videoState, Bundle intentExtras) throws CallStateException {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
- return dial(dialString, oirMode, videoState);
+ return dial(dialString, oirMode, videoState, intentExtras);
}
/**
* oirMode is one of the CLIR_ constants
*/
synchronized Connection
- dial(String dialString, int clirMode, int videoState) throws CallStateException {
+ dial(String dialString, int clirMode, int videoState, Bundle intentExtras)
+ throws CallStateException {
boolean isPhoneInEcmMode = SystemProperties.getBoolean(
TelephonyProperties.PROPERTY_INECM_MODE, false);
boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);
@@ -319,6 +320,8 @@
}
// foreground call is empty for the newly dialed connection
holdBeforeDial = true;
+ // Cache the video state for pending MO call.
+ mPendingCallVideoState = videoState;
switchWaitingOrHoldingAndActive();
}
@@ -360,7 +363,7 @@
}
mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
pendingCallClirMode = clirMode;
- pendingCallVideoState = videoState;
+ mPendingCallVideoState = videoState;
pendingCallInEcm = true;
}
}
@@ -414,13 +417,7 @@
callees, mImsCallListener);
conn.setImsCall(imsCall);
- IImsVideoCallProvider imsVideoCallProvider =
- imsCall.getCallSession().getVideoCallProvider();
- if (imsVideoCallProvider != null) {
- ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
- new ImsVideoCallProviderWrapper(imsVideoCallProvider);
- conn.setVideoProvider(imsVideoCallProviderWrapper);
- }
+ setVideoCallProvider(conn, imsCall);
} catch (ImsException e) {
loge("dialInternal : " + e);
conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
@@ -447,6 +444,8 @@
if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
&& mForegroundCall.getState().isAlive()) {
setMute(false);
+ // Cache video state for pending MT call.
+ mPendingCallVideoState = videoState;
switchWaitingOrHoldingAndActive();
} else if (mRingingCall.getState().isRinging()) {
if (DBG) log("acceptCall: incoming...");
@@ -478,6 +477,17 @@
}
}
+
+ private void switchAfterConferenceSuccess() {
+ if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() +
+ ", bg = " + mBackgroundCall.getState());
+
+ if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
+ log("switchAfterConferenceSuccess");
+ mForegroundCall.switchWith(mBackgroundCall);
+ }
+ }
+
void
switchWaitingOrHoldingAndActive() throws CallStateException {
if (DBG) log("switchWaitingOrHoldingAndActive");
@@ -502,6 +512,11 @@
// be resumed.
try {
imsCall.hold();
+
+ // If there is no background call to resume, then don't expect there to be a switch.
+ if (mCallExpectedToResume == null) {
+ mSwitchingFgAndBgCalls = false;
+ }
} catch (ImsException e) {
mForegroundCall.switchWith(mBackgroundCall);
throw new CallStateException(e.getMessage());
@@ -529,8 +544,21 @@
// Keep track of the connect time of the earliest call so that it can be set on the
// {@code ImsConference} when it is created.
- long conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
- mBackgroundCall.getEarliestConnectTime());
+ long foregroundConnectTime = mForegroundCall.getEarliestConnectTime();
+ long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime();
+ long conferenceConnectTime;
+ if (foregroundConnectTime > 0 && backgroundConnectTime > 0) {
+ conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
+ mBackgroundCall.getEarliestConnectTime());
+ log("conference - using connect time = " + conferenceConnectTime);
+ } else if (foregroundConnectTime > 0) {
+ log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime);
+ conferenceConnectTime = foregroundConnectTime;
+ } else {
+ log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime);
+ conferenceConnectTime = backgroundConnectTime;
+ }
+
ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
if (foregroundConnection != null) {
foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
@@ -811,7 +839,10 @@
} else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
//accept waiting call after holding background call
ImsCall imsCall = mRingingCall.getImsCall();
- if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE);
+ if (imsCall != null) {
+ imsCall.accept(
+ ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState));
+ }
} else {
//Just resume background call.
//To distinguish resuming call with swapping calls
@@ -862,7 +893,7 @@
}
- private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) {
+ private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
for (ImsPhoneConnection conn : mConnections) {
if (conn.getImsCall() == imsCall) {
return conn;
@@ -881,6 +912,19 @@
private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);
+ // This method is called on onCallUpdate() where there is not necessarily a call state
+ // change. In these situations, we'll ignore the state related updates and only process
+ // the change in media capabilities (as expected). The default is to not ignore state
+ // changes so we do not change existing behavior.
+ processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
+ }
+
+ private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
+ boolean ignoreState) {
+ if (DBG) {
+ log("processCallStateChange state=" + state + " cause=" + cause
+ + " ignoreState=" + ignoreState);
+ }
if (imsCall == null) return;
@@ -892,8 +936,17 @@
return;
}
- changed = conn.update(imsCall, state);
+ // processCallStateChange is triggered for onCallUpdated as well.
+ // onCallUpdated should not modify the state of the call
+ // It should modify only other capabilities of call through updateMediaCapabilities
+ // State updates will be triggered through individual callbacks
+ // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update
+ if (ignoreState) {
+ conn.updateMediaCapabilities(imsCall);
+ return;
+ }
+ changed = conn.update(imsCall, state);
if (state == ImsPhoneCall.State.DISCONNECTED) {
changed = conn.onDisconnect(cause) || changed;
//detach the disconnected connections
@@ -991,6 +1044,19 @@
DisconnectCause.NOT_DISCONNECTED);
}
+ @Override
+ public void onCallUpdated(ImsCall imsCall) {
+ if (DBG) log("onCallUpdated");
+ if (imsCall == null) {
+ return;
+ }
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ processCallStateChange(imsCall, conn.getCall().mState,
+ DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
+ }
+ }
+
/**
* onCallStartFailed will be invoked when:
* case 1) Dialing fails
@@ -1048,7 +1114,13 @@
@Override
public void onCallHeld(ImsCall imsCall) {
- if (DBG) log("onCallHeld");
+ if (DBG) {
+ if (mForegroundCall.getImsCall() == imsCall) {
+ log("onCallHeld (fg) " + imsCall);
+ } else if (mBackgroundCall.getImsCall() == imsCall) {
+ log("onCallHeld (bg) " + imsCall);
+ }
+ }
synchronized (mSyncHold) {
ImsPhoneCall.State oldState = mBackgroundCall.getState();
@@ -1115,6 +1187,10 @@
// is not the one we expected, we likely had a resume failure and we need to swap the
// FG and BG calls back.
if (mSwitchingFgAndBgCalls && imsCall != mCallExpectedToResume) {
+ if (DBG) {
+ log("onCallResumed : switching " + mForegroundCall + " with "
+ + mBackgroundCall);
+ }
mForegroundCall.switchWith(mBackgroundCall);
mSwitchingFgAndBgCalls = false;
mCallExpectedToResume = null;
@@ -1125,10 +1201,13 @@
@Override
public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
- // TODO : What should be done?
// If we are in the midst of swapping the FG and BG calls and we got a resume fail, we
// need to swap back the FG and BG calls.
if (mSwitchingFgAndBgCalls && imsCall == mCallExpectedToResume) {
+ if (DBG) {
+ log("onCallResumeFailed : switching " + mForegroundCall + " with "
+ + mBackgroundCall);
+ }
mForegroundCall.switchWith(mBackgroundCall);
mCallExpectedToResume = null;
mSwitchingFgAndBgCalls = false;
@@ -1160,27 +1239,75 @@
}
@Override
- public void onCallMerged(ImsCall call, boolean swapCalls) {
+ public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) {
if (DBG) log("onCallMerged");
- mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
+ ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall();
+ ImsPhoneConnection peerConnection = findConnection(peerCall);
+ ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null
+ : peerConnection.getCall();
+
if (swapCalls) {
- try {
- switchWaitingOrHoldingAndActive();
- } catch (CallStateException e) {
- if (Phone.DEBUG_PHONE) {
- loge("Failed swap fg and bg calls on merge exception=" + e);
+ switchAfterConferenceSuccess();
+ }
+ foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE);
+
+ // TODO Temporary code. Remove the try-catch block from the runnable once thread
+ // synchronization is fixed.
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final ImsPhoneConnection conn = findConnection(call);
+ log("onCallMerged: ImsPhoneConnection=" + conn);
+ log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
+ setVideoCallProvider(conn, call);
+ log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
+ } catch (Exception e) {
+ loge("onCallMerged: exception " + e);
}
}
+ };
+
+ ImsPhoneCallTracker.this.post(r);
+
+ // After merge complete, update foreground as Active
+ // and background call as Held, if background call exists
+ processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE,
+ DisconnectCause.NOT_DISCONNECTED);
+ if (peerConnection != null) {
+ processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING,
+ DisconnectCause.NOT_DISCONNECTED);
}
- updatePhoneState();
- mPhone.notifyPreciseCallStateChanged();
+
+ // Check if the merge was requested by an existing conference call. In that
+ // case, no further action is required.
+ if (!call.isMergeRequestedByConf()) {
+ log("onCallMerged :: calling onMultipartyStateChanged()");
+ onMultipartyStateChanged(call, true);
+ } else {
+ log("onCallMerged :: Merge requested by existing conference.");
+ // Reset the flag.
+ call.resetIsMergeRequestedByConf(false);
+ }
+ logState();
}
@Override
public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo);
+
+ // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog
+ // We should move this into the InCallService so that it is handled appropriately
+ // based on the user facing UI.
mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
+
+ // Start plumbing this even through Telecom so other components can take
+ // appropriate action.
+ ImsPhoneConnection conn = findConnection(call);
+ if (conn != null) {
+ conn.onConferenceMergeFailed();
+ }
}
/**
@@ -1204,6 +1331,42 @@
public void onCallSessionTtyModeReceived(ImsCall call, int mode) {
mPhone.onTtyModeReceived(mode);
}
+
+ @Override
+ public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
+ targetAccessTech + ", reasonInfo=" + reasonInfo);
+ }
+ }
+
+ @Override
+ public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (DBG) {
+ log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech +
+ ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo);
+ }
+ }
+
+ /**
+ * Handles a change to the multiparty state for an {@code ImsCall}. Notifies the associated
+ * {@link ImsPhoneConnection} of the change.
+ *
+ * @param imsCall The IMS call.
+ * @param isMultiParty {@code true} if the call became multiparty, {@code false}
+ * otherwise.
+ */
+ @Override
+ public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) {
+ if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N"));
+
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ conn.updateMultipartyState(isMultiParty);
+ }
+ }
};
/**
@@ -1282,10 +1445,16 @@
}
@Override
- public void onImsDisconnected() {
- if (DBG) log("onImsDisconnected");
+ public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
+ if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
mPhone.setImsRegistered(false);
+ mPhone.processDisconnectReason(imsReasonInfo);
+ }
+
+ @Override
+ public void onImsProgressing() {
+ if (DBG) log("onImsProgressing");
}
@Override
@@ -1304,25 +1473,41 @@
public void onFeatureCapabilityChanged(int serviceClass,
int[] enabledFeatures, int[] disabledFeatures) {
if (serviceClass == ImsServiceClass.MMTEL) {
- if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
- ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
- mIsVolteEnabled = true;
+ boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
+ // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+ for (int i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
+ i <= ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI; i++) {
+ if (enabledFeatures[i] == i) {
+ // If the feature is set to its own integer value it is enabled.
+ if (DBG) log("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] + "): value=true");
+ mImsFeatureEnabled[i] = true;
+ } else if (enabledFeatures[i]
+ == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+ // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
+ if (DBG) log("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] + "): value=false");
+ mImsFeatureEnabled[i] = false;
+ } else {
+ // Feature has unknown state; it is not its own value or -1.
+ if (DBG) {
+ loge("onFeatureCapabilityChanged(" + i + ", " +mImsFeatureStrings[i] + "): unexpectedValue="
+ + enabledFeatures[i]);
+ }
+ }
}
- if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
- ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
- mIsVtEnabled = true;
+ if (tmpIsVideoCallEnabled != isVideoCallEnabled()) {
+ mPhone.notifyForVideoCapabilityChanged(isVideoCallEnabled());
}
- if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
- ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
- mIsVolteEnabled = false;
+
+ // TODO: Use the ImsCallSession or ImsCallProfile to tell the initial Wifi state and
+ // {@link ImsCallSession.Listener#callSessionHandover} to listen for changes to
+ // wifi capability caused by a handover.
+ if (DBG) log("onFeatureCapabilityChanged: isVowifiEnabled=" + isVowifiEnabled());
+ for (ImsPhoneConnection connection : mConnections) {
+ connection.updateWifiState();
}
- if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
- ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
- mIsVtEnabled = false;
- }
+
+ mPhone.onFeatureCapabilityChanged();
}
- if (DBG) log("onFeatureCapabilityChanged, mIsVolteEnabled = " + mIsVolteEnabled
- + " mIsVtEnabled = " + mIsVtEnabled);
}
};
@@ -1407,13 +1592,14 @@
}
break;
case EVENT_DIAL_PENDINGMO:
- dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY);
+ dialInternal(mPendingMO, mClirMode, mPendingCallVideoState);
break;
case EVENT_EXIT_ECM_RESPONSE_CDMA:
// no matter the result, we still do the same here
if (pendingCallInEcm) {
- dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState);
+ dialInternal(mPendingMO, pendingCallClirMode,
+ mPendingCallVideoState);
pendingCallInEcm = false;
}
mPhone.unsetOnEcbModeExitResponse(this);
@@ -1430,6 +1616,33 @@
Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
}
+ /**
+ * Logs the current state of the ImsPhoneCallTracker. Useful for debugging issues with
+ * call tracking.
+ */
+ /* package */
+ void logState() {
+ if (!VERBOSE_STATE_LOGGING) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Current IMS PhoneCall State:\n");
+ sb.append(" Foreground: ");
+ sb.append(mForegroundCall);
+ sb.append("\n");
+ sb.append(" Background: ");
+ sb.append(mBackgroundCall);
+ sb.append("\n");
+ sb.append(" Ringing: ");
+ sb.append(mRingingCall);
+ sb.append("\n");
+ sb.append(" Handover: ");
+ sb.append(mHandoverCall);
+ sb.append("\n");
+ Rlog.v(LOG_TAG, sb.toString());
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("ImsPhoneCallTracker extends:");
@@ -1466,14 +1679,31 @@
}
public boolean isVolteEnabled() {
- return mIsVolteEnabled;
+ return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE];
}
- public boolean isVtEnabled() {
- return mIsVtEnabled;
+ public boolean isVowifiEnabled() {
+ return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI];
}
+
+ public boolean isVideoCallEnabled() {
+ return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE]
+ || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]);
+ }
+
@Override
public PhoneConstants.State getState() {
return mState;
}
+
+ private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
+ throws RemoteException {
+ IImsVideoCallProvider imsVideoCallProvider =
+ imsCall.getCallSession().getVideoCallProvider();
+ if (imsVideoCallProvider != null) {
+ ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
+ new ImsVideoCallProviderWrapper(imsVideoCallProvider);
+ conn.setVideoProvider(imsVideoCallProviderWrapper);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index cd0920b..7172993 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -608,4 +608,20 @@
@Override
public void getRadioCapability(Message response) {
}
+
+ @Override
+ public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+ }
+
+ @Override
+ public void stopLceService(Message result) {
+ }
+
+ @Override
+ public void pullLceData(Message result) {
+ }
+
+ @Override
+ public void getModemActivityInfo(Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 2c93f5a..bd2dd40 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -29,6 +29,7 @@
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.text.TextUtils;
import com.android.ims.ImsException;
import com.android.ims.ImsStreamMediaProfile;
@@ -116,7 +117,8 @@
/** This is probably an MT call */
/*package*/
- ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+ ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct,
+ ImsPhoneCall parent) {
createWakeLock(context);
acquireWakeLock();
@@ -131,7 +133,6 @@
imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
mCnapNamePresentation = ImsCallProfile.OIRToPresentation(
imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
-
updateMediaCapabilities(imsCall);
} else {
mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
@@ -144,13 +145,16 @@
//mIndex = index;
+ updateWifiState();
+
mParent = parent;
mParent.attach(this, ImsPhoneCall.State.INCOMING);
}
/** This is an MO call, created when dialing */
/*package*/
- ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+ ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct,
+ ImsPhoneCall parent) {
createWakeLock(context);
acquireWakeLock();
@@ -221,6 +225,11 @@
mCause = cause;
}
+ @Override
+ public String getVendorDisconnectCause() {
+ return null;
+ }
+
public ImsPhoneCallTracker getOwner () {
return mOwner;
}
@@ -467,7 +476,8 @@
// arg1 is the character that was/is being processed
notifyMessage.arg1 = c;
- //Rlog.v(LOG_TAG, "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
+ //Rlog.v(LOG_TAG,
+ // "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
notifyMessage.sendToTarget();
}
}
@@ -535,6 +545,21 @@
return mImsCall != null && mImsCall.isMultiparty();
}
+ /**
+ * Where {@link #isMultiparty()} is {@code true}, determines if this {@link ImsCall} is the
+ * origin of the conference call (i.e. {@code #isConferenceHost()} is {@code true}), or if this
+ * {@link ImsCall} is a member of a conference hosted on another device.
+ *
+ * @return {@code true} if this call is the origin of the conference call it is a member of,
+ * {@code false} otherwise.
+ */
+ public boolean isConferenceHost() {
+ if (mImsCall == null) {
+ return false;
+ }
+ return mImsCall.isConferenceHost();
+ }
+
/*package*/ ImsCall getImsCall() {
return mImsCall;
}
@@ -553,6 +578,15 @@
*/
/*package*/ boolean update(ImsCall imsCall, ImsPhoneCall.State state) {
if (state == ImsPhoneCall.State.ACTIVE) {
+ // If the state of the call is active, but there is a pending request to the RIL to hold
+ // the call, we will skip this update. This is really a signalling delay or failure
+ // from the RIL, but we will prevent it from going through as we will end up erroneously
+ // making this call active when really it should be on hold.
+ if (imsCall.isPendingHold()) {
+ Rlog.w(LOG_TAG, "update : state is ACTIVE, but call is pending hold, skipping");
+ return false;
+ }
+
if (mParent.getState().isRinging() || mParent.getState().isDialing()) {
onConnectedInOrOut();
}
@@ -572,7 +606,10 @@
boolean updateParent = mParent.update(this, imsCall, state);
boolean updateMediaCapabilities = updateMediaCapabilities(imsCall);
- return updateParent || updateMediaCapabilities;
+ boolean updateWifiState = updateWifiState();
+ boolean updateAddressDisplay = updateAddressDisplay(imsCall);
+
+ return updateParent || updateMediaCapabilities || updateWifiState || updateAddressDisplay;
}
@Override
@@ -619,13 +656,63 @@
}
/**
+ * Check for a change in the address display related fields for the {@link ImsCall}, and
+ * update the {@link ImsPhoneConnection} with this information.
+ *
+ * @param imsCall The call to check for changes in address display fields.
+ * @return Whether the address display fields have been changed.
+ */
+ private boolean updateAddressDisplay(ImsCall imsCall) {
+ if (imsCall == null) {
+ return false;
+ }
+
+ boolean changed = false;
+ ImsCallProfile callProfile = imsCall.getCallProfile();
+ if (callProfile != null) {
+ String address = callProfile.getCallExtra(ImsCallProfile.EXTRA_OI);
+ String name = callProfile.getCallExtra(ImsCallProfile.EXTRA_CNA);
+ int nump = ImsCallProfile.OIRToPresentation(
+ callProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
+ int namep = ImsCallProfile.OIRToPresentation(
+ callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
+ if (Phone.DEBUG_PHONE) {
+ Rlog.d(LOG_TAG, "address = " + address + " name = " + name +
+ " nump = " + nump + " namep = " + namep);
+ }
+ if(equalsHandlesNulls(mAddress, address)) {
+ mAddress = address;
+ changed = true;
+ }
+ if (TextUtils.isEmpty(name)) {
+ if (!TextUtils.isEmpty(mCnapName)) {
+ mCnapName = "";
+ changed = true;
+ }
+ } else if (!name.equals(mCnapName)) {
+ mCnapName = name;
+ changed = true;
+ }
+ if (mNumberPresentation != nump) {
+ mNumberPresentation = nump;
+ changed = true;
+ }
+ if (mCnapNamePresentation != namep) {
+ mCnapNamePresentation = namep;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ /**
* Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and
* update the {@link ImsPhoneConnection} with this information.
*
* @param imsCall The call to check for changes in media capabilities.
* @return Whether the media capabilities have been changed.
*/
- private boolean updateMediaCapabilities(ImsCall imsCall) {
+ public boolean updateMediaCapabilities(ImsCall imsCall) {
if (imsCall == null) {
return false;
}
@@ -641,10 +728,11 @@
ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile();
if (negotiatedCallProfile != null) {
- int callType = negotiatedCallProfile.mCallType;
+ int oldVideoState = getVideoState();
+ int newVideoState = ImsCallProfile
+ .getVideoStateFromImsCallProfile(negotiatedCallProfile);
- int newVideoState = ImsCallProfile.getVideoStateFromCallType(callType);
- if (getVideoState() != newVideoState) {
+ if (oldVideoState != newVideoState) {
setVideoState(newVideoState);
changed = true;
}
@@ -660,6 +748,16 @@
}
}
+ if (remoteCallProfile != null) {
+ boolean newRemoteVideoCapable = remoteCallProfile.mCallType
+ == ImsCallProfile.CALL_TYPE_VT;
+
+ if (isRemoteVideoCapable() != newRemoteVideoCapable) {
+ setRemoteVideoCapable(newRemoteVideoCapable);
+ changed = true;
+ }
+ }
+
int newAudioQuality =
getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
if (getAudioQuality() != newAudioQuality) {
@@ -674,6 +772,21 @@
}
/**
+ * Check for a change in the wifi state of the ImsPhoneCallTracker and update the
+ * {@link ImsPhoneConnection} with this information.
+ *
+ * @return Whether the ImsPhoneCallTracker's usage of wifi has been changed.
+ */
+ public boolean updateWifiState() {
+ Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled());
+ if (isWifi() != mOwner.isVowifiEnabled()) {
+ setWifi(mOwner.isVowifiEnabled());
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Determines the {@link ImsPhoneConnection} audio quality based on the local and remote
* {@link ImsCallProfile}. If indicate a HQ audio call if the local stream profile
* indicates AMR_WB or EVRC_WB and there is no remote restrict cause.
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index fbdd572..ac7f76e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -706,10 +706,10 @@
throw new CallStateException(ImsPhone.CS_FALLBACK);
} else if (isServiceCodeCallForwarding(mSc)) {
Rlog.d(LOG_TAG, "is CF");
- // service group is not supported
String dialingNumber = mSia;
int reason = scToCallForwardReason(mSc);
+ int serviceClass = siToServiceClass(mSib);
int time = siToTime(mSic);
if (isInterrogate()) {
@@ -750,7 +750,7 @@
Rlog.d(LOG_TAG, "is CF setCallForward");
mPhone.setCallForwardingOption(cfAction, reason,
- dialingNumber, time, obtainMessage(
+ dialingNumber, serviceClass, time, obtainMessage(
EVENT_SET_CFF_COMPLETE,
isSettingUnconditional,
isEnableDesired, this));
@@ -890,9 +890,10 @@
}
} else if (mSc != null && mSc.equals(SC_WAIT)) {
// sia = basic service group
- // service group is not supported
+ int serviceClass = siToServiceClass(mSib);
+
if (isActivate() || isDeactivate()) {
- mPhone.setCallWaiting(isActivate(),
+ mPhone.setCallWaiting(isActivate(), serviceClass,
obtainMessage(EVENT_SET_COMPLETE, this));
} else if (isInterrogate()) {
mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 889cc8f..8fd036d 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -610,4 +610,21 @@
@Override
public void requestShutdown(Message result) {
}
+
+ @Override
+ public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+ }
+
+ @Override
+ public void stopLceService(Message result) {
+ }
+
+ @Override
+ public void pullLceData(Message result) {
+ }
+
+ @Override
+ public void getModemActivityInfo(Message result) {
+ }
+
}
diff --git a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
index 8d8a554..c182981 100644
--- a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -141,6 +141,11 @@
}
@Override
+ public String getVendorDisconnectCause() {
+ return null;
+ }
+
+ @Override
public PostDialState getPostDialState() {
if (VDBG) log("getPostDialState: ret=" + mPostDialState);
return mPostDialState;
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index 6edee55..beb02c6 100644
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -26,6 +26,7 @@
import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Message;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
@@ -213,7 +214,7 @@
@Override
public void switchHoldingAndActive() throws CallStateException {
- if (DBG) log("dialInternal: switch fg and bg");
+ if (DBG) log("switchHoldingAndActive: switch fg and bg");
synchronized (SipPhone.class) {
mForegroundCall.switchWith(mBackgroundCall);
if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold();
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index c8a6d93..bcd520f 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.LinkProperties;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -70,7 +71,7 @@
public abstract Call getRingingCall();
@Override
- public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
+ public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
throws CallStateException {
// ignore UUSInfo
return dial(dialString, videoState);
@@ -304,6 +305,11 @@
}
@Override
+ public String getGroupIdLevel2() {
+ return null;
+ }
+
+ @Override
public String getIccSerialNumber() {
return null;
}
@@ -502,6 +508,16 @@
return null;
}
+ /**
+ * Determines if video calling is enabled. Always {@code false} for SIP.
+ *
+ * @return {@code false} since SIP does not support video calling.
+ */
+ @Override
+ public boolean isVideoEnabled() {
+ return false;
+ }
+
void updatePhoneState() {
PhoneConstants.State oldState = mState;
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 71fd66e..e0c2b79 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1767,4 +1767,24 @@
public void requestShutdown(Message result) {
setRadioState(RadioState.RADIO_UNAVAILABLE);
}
+
+ @Override
+ public void startLceService(int report_interval_ms, boolean pullMode, Message result) {
+ unimplemented(result);
+ }
+
+ @Override
+ public void stopLceService(Message result) {
+ unimplemented(result);
+ }
+
+ @Override
+ public void pullLceData(Message result) {
+ unimplemented(result);
+ }
+
+ @Override
+ public void getModemActivityInfo(Message result) {
+ unimplemented(result);
+ }
}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
index 8df8216..5e3dcb4 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
@@ -24,6 +24,8 @@
import android.os.Message;
import android.telephony.Rlog;
+import com.android.internal.telephony.uicc.IccConstants;
+
public class AdnRecordLoader extends Handler {
final static String LOG_TAG = "AdnRecordLoader";
final static boolean VDBG = false;
@@ -64,6 +66,14 @@
mFh = fh;
}
+ private String getEFPath(int efid) {
+ if (efid == IccConstants.EF_ADN) {
+ return IccConstants.MF_SIM + IccConstants.DF_TELECOM;
+ }
+
+ return null;
+ }
+
/**
* Resulting AdnRecord is placed in response.obj.result
* or response.obj.exception is set
@@ -76,10 +86,9 @@
mRecordNumber = recordNumber;
mUserResponse = response;
- mFh.loadEFLinearFixed(
- ef, recordNumber,
- obtainMessage(EVENT_ADN_LOAD_DONE));
-
+ mFh.loadEFLinearFixed(
+ ef, getEFPath(ef), recordNumber,
+ obtainMessage(EVENT_ADN_LOAD_DONE));
}
@@ -94,10 +103,13 @@
mExtensionEF = extensionEF;
mUserResponse = response;
+ /* If we are loading from EF_ADN, specifically
+ * specify the path as well, since, on some cards,
+ * the fileid is not unique.
+ */
mFh.loadEFLinearFixedAll(
- ef,
- obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
-
+ ef, getEFPath(ef),
+ obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
}
/**
@@ -120,10 +132,10 @@
mRecordNumber = recordNumber;
mUserResponse = response;
mPin2 = pin2;
-
- mFh.getEFLinearRecordSize( ef,
- obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
- }
+
+ mFh.getEFLinearRecordSize( ef, getEFPath(ef),
+ obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
+ }
//***** Overridden from Handler
@@ -163,7 +175,8 @@
ar.exception);
}
- mFh.updateEFLinearFixed(mEf, mRecordNumber,
+
+ mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
mPendingExtLoads = 1;
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index f18640e..ce8a671 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -362,7 +362,6 @@
mIccRecords = newRecords;
registerUiccCardEvents();
}
-
updateExternalState();
}
}
@@ -382,7 +381,15 @@
}
private void updateExternalState() {
- if (mUiccCard == null || mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
+
+ // mUiccCard could be null at bootup, before valid card states have
+ // been received from UiccController.
+ if (mUiccCard == null) {
+ setExternalState(State.NOT_READY);
+ return;
+ }
+
+ if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
if (mRadioOn) {
setExternalState(State.ABSENT);
} else {
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 8a9fc6b..061b28f 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -24,10 +24,12 @@
static final int EF_ADN = 0x6F3A;
static final int EF_FDN = 0x6F3B;
static final int EF_GID1 = 0x6F3E;
+ static final int EF_GID2 = 0x6F3F;
static final int EF_SDN = 0x6F49;
static final int EF_EXT1 = 0x6F4A;
static final int EF_EXT2 = 0x6F4B;
static final int EF_EXT3 = 0x6F4C;
+ static final int EF_EXT5 = 0x6F4E;
static final int EF_EXT6 = 0x6fc8; // Ext record for EF[MBDN]
static final int EF_MWIS = 0x6FCA;
static final int EF_MBDN = 0x6fc7;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index a460819..881b4c3 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -102,6 +102,7 @@
int mEfid;
int mRecordNum, mRecordSize, mCountRecords;
boolean mLoadAll;
+ String mPath;
Message mOnLoaded;
@@ -112,6 +113,23 @@
mRecordNum = recordNum;
mOnLoaded = onLoaded;
mLoadAll = false;
+ mPath = null;
+ }
+
+ LoadLinearFixedContext(int efid, int recordNum, String path, Message onLoaded) {
+ mEfid = efid;
+ mRecordNum = recordNum;
+ mOnLoaded = onLoaded;
+ mLoadAll = false;
+ mPath = path;
+ }
+
+ LoadLinearFixedContext(int efid, String path, Message onLoaded) {
+ mEfid = efid;
+ mRecordNum = 1;
+ mLoadAll = true;
+ mOnLoaded = onLoaded;
+ mPath = path;
}
LoadLinearFixedContext(int efid, Message onLoaded) {
@@ -119,6 +137,7 @@
mRecordNum = 1;
mLoadAll = true;
mOnLoaded = onLoaded;
+ mPath = null;
}
}
@@ -140,6 +159,27 @@
* Load a record from a SIM Linear Fixed EF
*
* @param fileid EF id
+ * @param path Path of the EF on the card
+ * @param recordNum 1-based (not 0-based) record number
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+ *
+ */
+ public void loadEFLinearFixed(int fileid, String path, int recordNum, Message onLoaded) {
+ String efPath = (path == null) ? getEFPath(fileid) : path;
+ Message response
+ = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
+ new LoadLinearFixedContext(fileid, recordNum, efPath, onLoaded));
+
+ mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ }
+
+ /**
+ * Load a record from a SIM Linear Fixed EF
+ *
+ * @param fileid EF id
* @param recordNum 1-based (not 0-based) record number
* @param onLoaded
*
@@ -147,12 +187,7 @@
*
*/
public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) {
- Message response
- = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
- new LoadLinearFixedContext(fileid, recordNum, onLoaded));
-
- mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ loadEFLinearFixed(fileid, getEFPath(fileid), recordNum, onLoaded);
}
/**
@@ -179,17 +214,51 @@
* get record size for a linear fixed EF
*
* @param fileid EF id
+ * @param path Path of the EF on the card
+ * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
+ * int[0] is the record length int[1] is the total length of the EF
+ * file int[3] is the number of records in the EF file So int[0] *
+ * int[3] = int[1]
+ */
+ public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) {
+ String efPath = (path == null) ? getEFPath(fileid) : path;
+ Message response
+ = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
+ new LoadLinearFixedContext(fileid, efPath, onLoaded));
+ mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ }
+
+ /**
+ * get record size for a linear fixed EF
+ *
+ * @param fileid EF id
* @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
* int[0] is the record length int[1] is the total length of the EF
* file int[3] is the number of records in the EF file So int[0] *
* int[3] = int[1]
*/
public void getEFLinearRecordSize(int fileid, Message onLoaded) {
- Message response
- = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
- new LoadLinearFixedContext(fileid, onLoaded));
- mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ getEFLinearRecordSize(fileid, getEFPath(fileid), onLoaded);
+ }
+
+ /**
+ * Load all records from a SIM Linear Fixed EF
+ *
+ * @param fileid EF id
+ * @param path Path of the EF on the card
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]>
+ *
+ */
+ public void loadEFLinearFixedAll(int fileid, String path, Message onLoaded) {
+ String efPath = (path == null) ? getEFPath(fileid) : path;
+ Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
+ new LoadLinearFixedContext(fileid, efPath, onLoaded));
+
+ mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
}
/**
@@ -202,11 +271,7 @@
*
*/
public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
- Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
- new LoadLinearFixedContext(fileid,onLoaded));
-
- mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ loadEFLinearFixedAll(fileid, getEFPath(fileid), onLoaded);
}
/**
@@ -277,6 +342,24 @@
/**
* Update a record in a linear fixed EF
* @param fileid EF id
+ * @param path Path of the EF on the card
+ * @param recordNum 1-based (not 0-based) record number
+ * @param data must be exactly as long as the record in the EF
+ * @param pin2 for CHV2 operations, otherwist must be null
+ * @param onComplete onComplete.obj will be an AsyncResult
+ * onComplete.obj.userObj will be a IccIoResult on success
+ */
+ public void updateEFLinearFixed(int fileid, String path, int recordNum, byte[] data,
+ String pin2, Message onComplete) {
+ String efPath = (path == null) ? getEFPath(fileid) : path;
+ mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, efPath,
+ recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
+ IccUtils.bytesToHexString(data), pin2, mAid, onComplete);
+ }
+
+ /**
+ * Update a record in a linear fixed EF
+ * @param fileid EF id
* @param recordNum 1-based (not 0-based) record number
* @param data must be exactly as long as the record in the EF
* @param pin2 for CHV2 operations, otherwist must be null
@@ -348,6 +431,7 @@
int size;
int fileid;
int recordSize[];
+ String path = null;
try {
switch (msg.what) {
@@ -389,6 +473,7 @@
}
data = result.payload;
+ path = lc.mPath;
if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
throw new IccFileTypeMismatch();
@@ -409,7 +494,10 @@
lc.results = new ArrayList<byte[]>(lc.mCountRecords);
}
- mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, getEFPath(lc.mEfid),
+ if (path == null) {
+ path = getEFPath(lc.mEfid);
+ }
+ mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,
lc.mRecordNum,
READ_RECORD_MODE_ABSOLUTE,
lc.mRecordSize, null, null, mAid,
@@ -452,6 +540,7 @@
lc = (LoadLinearFixedContext) ar.userObj;
result = (IccIoResult) ar.result;
response = lc.mOnLoaded;
+ path = lc.mPath;
if (processException(response, (AsyncResult) msg.obj)) {
break;
@@ -467,7 +556,11 @@
if (lc.mRecordNum > lc.mCountRecords) {
sendResult(response, lc.results, null);
} else {
- mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, getEFPath(lc.mEfid),
+ if (path == null) {
+ path = getEFPath(lc.mEfid);
+ }
+
+ mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,
lc.mRecordNum,
READ_RECORD_MODE_ABSOLUTE,
lc.mRecordSize, null, null, mAid,
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 77de1fa..d91c4e7 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -23,12 +23,17 @@
import android.os.Registrant;
import android.os.RegistrantList;
+import android.telephony.Rlog;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -36,6 +41,7 @@
*/
public abstract class IccRecords extends Handler implements IccConstants {
protected static final boolean DBG = true;
+ protected static final boolean VDBG = false; // STOPSHIP if true
// ***** Instance Variables
protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
@@ -78,6 +84,8 @@
private String mSpn;
protected String mGid1;
+ protected String mGid2;
+ protected String mPrefLang;
private final Object mLock = new Object();
@@ -124,7 +132,7 @@
+ " newVoiceMailNum=" + mNewVoiceMailNum
+ " newVoiceMailTag=" + mNewVoiceMailTag
+ " isVoiceMailFixed=" + mIsVoiceMailFixed
- + " mImsi=" + mImsi
+ + (VDBG ? (" mImsi=" + mImsi) : "")
+ " mncLength=" + mMncLength
+ " mailboxIndex=" + mMailboxIndex
+ " spn=" + mSpn;
@@ -284,6 +292,14 @@
}
/**
+ * Get the Group Identifier Level 2 (GID2) on a SIM.
+ * @return null if SIM is not yet ready
+ */
+ public String getGid2() {
+ return null;
+ }
+
+ /**
* Set subscriber number to SIM record
*
* The subscriber number is stored in EF_MSISDN (TS 51.011)
@@ -481,6 +497,49 @@
}
}
+ /**
+ * Returns the SIM language derived from the EF-LI and EF-PL sim records.
+ */
+ public String getSimLanguage() {
+ return mPrefLang;
+ }
+
+ protected void setSimLanguage(byte[] efLi, byte[] efPl) {
+ String[] locales = mContext.getAssets().getLocales();
+ try {
+ mPrefLang = findBestLanguage(efLi, locales);
+ } catch (UnsupportedEncodingException uee) {
+ log("Unable to parse EF-LI: " + Arrays.toString(efLi));
+ }
+
+ if (mPrefLang == null) {
+ try {
+ mPrefLang = findBestLanguage(efPl, locales);
+ } catch (UnsupportedEncodingException uee) {
+ log("Unable to parse EF-PL: " + Arrays.toString(efLi));
+ }
+ }
+ }
+
+ protected static String findBestLanguage(byte[] languages, String[] locales)
+ throws UnsupportedEncodingException {
+ if ((languages == null) || (locales == null)) return null;
+
+ // Each 2-bytes consists of one language
+ for (int i = 0; (i + 1) < languages.length; i += 2) {
+ String lang = new String(languages, i, 2, "ISO-8859-1");
+ for (int j = 0; j < locales.length; j++) {
+ if (locales[j] != null && locales[j].length() >= 2 &&
+ locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
+ return lang;
+ }
+ }
+ }
+
+ // no match found. return null
+ return null;
+ }
+
protected abstract void onRecordLoaded();
protected abstract void onAllRecordsLoaded();
@@ -656,14 +715,18 @@
pw.println(" mRecordsToLoad=" + mRecordsToLoad);
pw.println(" mRdnCache=" + mAdnCache);
pw.println(" iccid=" + mIccId);
- pw.println(" mMsisdn=" + mMsisdn);
+ if (TextUtils.isEmpty(mMsisdn)) {
+ pw.println(" mMsisdn=null");
+ } else {
+ pw.println(" mMsisdn=" + (VDBG ? mMsisdn : "XXX"));
+ }
pw.println(" mMsisdnTag=" + mMsisdnTag);
pw.println(" mVoiceMailNum=" + mVoiceMailNum);
pw.println(" mVoiceMailTag=" + mVoiceMailTag);
pw.println(" mNewVoiceMailNum=" + mNewVoiceMailNum);
pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
- pw.println(" mImsi=" + mImsi);
+ if (VDBG) pw.println(" mImsi=" + mImsi);
pw.println(" mMncLength=" + mMncLength);
pw.println(" mMailboxIndex=" + mMailboxIndex);
pw.println(" mSpn=" + mSpn);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index ff6edae..057674d 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -47,7 +47,8 @@
protected static final String LOG_TAG = "IsimUiccRecords";
private static final boolean DBG = true;
- private static final boolean DUMP_RECORDS = true; // Note: PII is logged when this is true
+ private static final boolean DUMP_RECORDS = false; // Note: PII is logged when this is true
+ // STOPSHIP if true
public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
private static final int EVENT_APP_READY = 1;
@@ -58,8 +59,8 @@
private String mIsimImpi; // IMS private user identity
private String mIsimDomain; // IMS home network domain name
private String[] mIsimImpu; // IMS public user identity(s)
- private String mIsimIst; // IMS Service Table
- private String[] mIsimPcscf; // IMS Proxy Call Session Control Function
+ private String mIsimIst; // IMS Service Table
+ private String[] mIsimPcscf; // IMS Proxy Call Session Control Function
private String auth_rsp;
private final Object mLock = new Object();
@@ -69,11 +70,11 @@
@Override
public String toString() {
return "IsimUiccRecords: " + super.toString()
- + " mIsimImpi=" + mIsimImpi
+ + (DUMP_RECORDS ? (" mIsimImpi=" + mIsimImpi
+ " mIsimDomain=" + mIsimDomain
+ " mIsimImpu=" + mIsimImpu
+ " mIsimIst=" + mIsimIst
- + " mIsimPcscf=" + mIsimPcscf;
+ + " mIsimPcscf=" + mIsimPcscf) : "");
}
public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -500,11 +501,13 @@
pw.println("IsimRecords: " + this);
pw.println(" extends:");
super.dump(fd, pw, args);
- pw.println(" mIsimImpi=" + mIsimImpi);
- pw.println(" mIsimDomain=" + mIsimDomain);
- pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
- pw.println(" mIsimIst" + mIsimIst);
- pw.println(" mIsimPcscf"+mIsimPcscf);
+ if (DUMP_RECORDS) {
+ pw.println(" mIsimImpi=" + mIsimImpi);
+ pw.println(" mIsimDomain=" + mIsimDomain);
+ pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
+ pw.println(" mIsimIst" + mIsimIst);
+ pw.println(" mIsimPcscf" + mIsimPcscf);
+ }
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 3b91de4..965fd63 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -29,7 +29,6 @@
import android.os.SystemProperties;
import android.telephony.SubscriptionManager;
import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.content.res.Resources;
@@ -373,7 +372,7 @@
@Override
public void onRecordLoaded(AsyncResult ar) {
byte[] data = (byte[]) ar.result;
- if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
+ if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
// C.S0065 section 5.2.2 for IMSI_M encoding
// C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
boolean provisioned = ((data[7] & 0x80) == 0x80);
@@ -738,56 +737,6 @@
return localeLangs;
}
- private String findBestLanguage(byte[] languages) {
- final String[] assetLanguages = getAssetLanguages(mContext);
-
- if ((languages == null) || (assetLanguages == null)) return null;
-
- // Each 2-bytes consists of one language
- for (int i = 0; (i + 1) < languages.length; i += 2) {
- final String lang;
- try {
- lang = new String(languages, i, 2, "ISO-8859-1");
- } catch(java.io.UnsupportedEncodingException e) {
- log("Failed to parse SIM language records");
- continue;
- }
-
- for (int j = 0; j < assetLanguages.length; j++) {
- if (assetLanguages[j].equals(lang)) {
- return lang;
- }
- }
- }
-
- // no match found. return null
- return null;
- }
-
- private void setLocaleFromCsim() {
- String prefLang = null;
- // check EFli then EFpl
- prefLang = findBestLanguage(mEFli);
-
- if (prefLang == null) {
- prefLang = findBestLanguage(mEFpl);
- }
-
- if (prefLang != null) {
- // check country code from SIM
- String imsi = getIMSI();
- String country = null;
- if (imsi != null) {
- country = MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0,3)));
- }
- log("Setting locale to " + prefLang + "_" + country);
- MccTable.setSystemLocale(mContext, prefLang, country);
- } else {
- log ("No suitable CSIM selected locale");
- }
- }
-
@Override
protected void onRecordLoaded() {
// One record loaded successfully or failed, In either case
@@ -823,7 +772,7 @@
}
if (!TextUtils.isEmpty(mImsi)) {
- log("onAllRecordsLoaded set mcc imsi=" + mImsi);
+ log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : ""));
mTelephonyManager.setSimCountryIsoForPhone(
mParentApp.getPhoneId(),
MccTable.countryCodeForMcc(
@@ -833,7 +782,11 @@
}
}
- setLocaleFromCsim();
+ Resources resource = Resources.getSystem();
+ if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
+ setSimLanguage(mEFli, mEFpl);
+ }
+
mRecordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
@@ -869,16 +822,13 @@
obtainMessage(EVENT_GET_ICCID_DONE));
mRecordsToLoad++;
- Resources resource = Resources.getSystem();
- if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
- mFh.loadEFTransparent(EF_PL,
- obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
- mRecordsToLoad++;
+ mFh.loadEFTransparent(EF_PL,
+ obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
+ mRecordsToLoad++;
- mFh.loadEFTransparent(EF_CSIM_LI,
- obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
- mRecordsToLoad++;
- }
+ mFh.loadEFTransparent(EF_CSIM_LI,
+ obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
+ mRecordsToLoad++;
mFh.loadEFTransparent(EF_CSIM_SPN,
obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
diff --git a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
index c1db5f3..8412b4e 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
@@ -55,6 +55,7 @@
case EF_SST:
case EF_CFIS:
case EF_GID1:
+ case EF_GID2:
return MF_SIM + DF_GSM;
case EF_MAILBOX_CPHS:
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index f43ab8e..3acbd4d 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -163,6 +163,7 @@
private static final int EVENT_GET_CSP_CPHS_DONE = 33;
private static final int EVENT_GET_GID1_DONE = 34;
private static final int EVENT_APP_LOCKED = 35;
+ private static final int EVENT_GET_GID2_DONE = 36;
// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
@@ -242,6 +243,7 @@
mSpdiNetworks = null;
mPnnHomeName = null;
mGid1 = null;
+ mGid2 = null;
mAdnCache.reset();
@@ -279,10 +281,32 @@
}
@Override
+ public String getGid2() {
+ return mGid2;
+ }
+
+ @Override
public UsimServiceTable getUsimServiceTable() {
return mUsimServiceTable;
}
+ private int getExtFromEf(int ef) {
+ int ext;
+ switch (ef) {
+ case EF_MSISDN:
+ /* For USIM apps use EXT5. (TS 31.102 Section 4.2.37) */
+ if (mParentApp.getType() == AppType.APPTYPE_USIM) {
+ ext = EF_EXT5;
+ } else {
+ ext = EF_EXT1;
+ }
+ break;
+ default:
+ ext = EF_EXT1;
+ }
+ return ext;
+ }
+
/**
* Set subscriber number to SIM record
*
@@ -309,10 +333,9 @@
if(DBG) log("Set MSISDN: " + mNewMsisdnTag + " " + /*mNewMsisdn*/ "xxxxxxx");
-
AdnRecord adn = new AdnRecord(mNewMsisdnTag, mNewMsisdn);
- new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
+ new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, getExtFromEf(EF_MSISDN), 1, null,
obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
}
@@ -1196,6 +1219,22 @@
break;
+ case EVENT_GET_GID2_DONE:
+ isRecordLoadResponse = true;
+
+ ar = (AsyncResult)msg.obj;
+ data =(byte[])ar.result;
+
+ if (ar.exception != null) {
+ loge("Exception in get GID2 " + ar.exception);
+ mGid2 = null;
+ break;
+ }
+ mGid2 = IccUtils.bytesToHexString(data);
+ log("GID2: " + mGid2);
+
+ break;
+
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
@@ -1254,6 +1293,24 @@
if (DBG) log("SIM Refresh called for EF_FDN");
mParentApp.queryFdn();
break;
+ case EF_MSISDN:
+ mRecordsToLoad++;
+ log("SIM Refresh called for EF_MSISDN");
+ new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
+ obtainMessage(EVENT_GET_MSISDN_DONE));
+ break;
+ case EF_CFIS:
+ mRecordsToLoad++;
+ log("SIM Refresh called for EF_CFIS");
+ mFh.loadEFLinearFixed(EF_CFIS,
+ 1, obtainMessage(EVENT_GET_CFIS_DONE));
+ break;
+ case EF_CFF_CPHS:
+ mRecordsToLoad++;
+ log("SIM Refresh called for EF_CFF_CPHS");
+ mFh.loadEFTransparent(EF_CFF_CPHS,
+ obtainMessage(EVENT_GET_CFF_DONE));
+ break;
default:
// For now, fetch all records if this is not a
// voicemail number.
@@ -1362,56 +1419,6 @@
}
}
- private String findBestLanguage(byte[] languages) {
- String bestMatch = null;
- String[] locales = mContext.getAssets().getLocales();
-
- if ((languages == null) || (locales == null)) return null;
-
- // Each 2-bytes consists of one language
- for (int i = 0; (i + 1) < languages.length; i += 2) {
- try {
- String lang = new String(languages, i, 2, "ISO-8859-1");
- if (DBG) log ("languages from sim = " + lang);
- for (int j = 0; j < locales.length; j++) {
- if (locales[j] != null && locales[j].length() >= 2 &&
- locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
- return lang;
- }
- }
- if (bestMatch != null) break;
- } catch(java.io.UnsupportedEncodingException e) {
- log ("Failed to parse USIM language records" + e);
- }
- }
- // no match found. return null
- return null;
- }
-
- private void setLocaleFromUsim() {
- String prefLang = null;
- // check EFli then EFpl
- prefLang = findBestLanguage(mEfLi);
-
- if (prefLang == null) {
- prefLang = findBestLanguage(mEfPl);
- }
-
- if (prefLang != null) {
- // check country code from SIM
- String imsi = getIMSI();
- String country = null;
- if (imsi != null) {
- country = MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0,3)));
- }
- if (DBG) log("Setting locale to " + prefLang + "_" + country);
- MccTable.setSystemLocale(mContext, prefLang, country);
- } else {
- if (DBG) log ("No suitable USIM selected locale");
- }
- }
-
@Override
protected void onRecordLoaded() {
// One record loaded successfully or failed, In either case
@@ -1431,7 +1438,12 @@
protected void onAllRecordsLoaded() {
if (DBG) log("record load complete");
- setLocaleFromUsim();
+ Resources resource = Resources.getSystem();
+ if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
+ setSimLanguage(mEfLi, mEfPl);
+ } else {
+ if (DBG) log ("Not using EF LI/EF PL");
+ }
if (mParentApp.getState() == AppState.APPSTATE_PIN ||
mParentApp.getState() == AppState.APPSTATE_PUK) {
@@ -1457,7 +1469,7 @@
}
if (!TextUtils.isEmpty(mImsi)) {
- log("onAllRecordsLoaded set mcc imsi=" + mImsi);
+ log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + mImsi) : ""));
mTelephonyManager.setSimCountryIsoForPhone(
mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
Integer.parseInt(mImsi.substring(0,3))));
@@ -1502,12 +1514,6 @@
}
private void loadEfLiAndEfPl() {
- Resources resource = Resources.getSystem();
- if (!resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
- if (DBG) log ("Not using EF LI/EF PL");
- return;
- }
-
if (mParentApp.getType() == AppType.APPTYPE_USIM) {
mRecordsRequested = true;
mFh.loadEFTransparent(EF_LI,
@@ -1533,7 +1539,7 @@
// FIXME should examine EF[MSISDN]'s capability configuration
// to determine which is the voice/data/fax line
- new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
+ new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
obtainMessage(EVENT_GET_MSISDN_DONE));
mRecordsToLoad++;
@@ -1586,6 +1592,9 @@
mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
mRecordsToLoad++;
+ mFh.loadEFTransparent(EF_GID2, obtainMessage(EVENT_GET_GID2_DONE));
+ mRecordsToLoad++;
+
loadEfLiAndEfPl();
// XXX should seek instead of examining them all
@@ -1920,6 +1929,7 @@
pw.println(" mPnnHomeName=" + mPnnHomeName);
pw.println(" mUsimServiceTable=" + mUsimServiceTable);
pw.println(" mGid1=" + mGid1);
+ pw.println(" mGid2=" + mGid2);
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index f77a16c..f40e7a8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -27,6 +27,7 @@
import android.content.pm.Signature;
import android.content.res.Resources;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -36,6 +37,7 @@
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.LocalLog;
import android.view.WindowManager;
import com.android.internal.telephony.CommandsInterface;
@@ -94,6 +96,8 @@
private static final int EVENT_SIM_IO_DONE = 19;
private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 20;
+ private static final LocalLog mLocalLog = new LocalLog(100);
+
private int mPhoneId;
public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
@@ -364,8 +368,8 @@
case EVENT_SIM_IO_DONE:
AsyncResult ar = (AsyncResult)msg.obj;
if (ar.exception != null) {
- if (DBG)
- log("Error in SIM access with exception" + ar.exception);
+ loglocal("Exception: " + ar.exception);
+ log("Error in SIM access with exception" + ar.exception);
}
AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
((Message)ar.userObj).sendToTarget();
@@ -483,6 +487,8 @@
* Exposes {@link CommandsInterface.iccOpenLogicalChannel}
*/
public void iccOpenLogicalChannel(String AID, Message response) {
+ loglocal("Open Logical Channel: " + AID + " by pid:" + Binder.getCallingPid()
+ + " uid:" + Binder.getCallingUid());
mCi.iccOpenLogicalChannel(AID,
mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
}
@@ -491,6 +497,7 @@
* Exposes {@link CommandsInterface.iccCloseLogicalChannel}
*/
public void iccCloseLogicalChannel(int channel, Message response) {
+ loglocal("Close Logical Channel: " + channel);
mCi.iccCloseLogicalChannel(channel,
mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
}
@@ -639,6 +646,10 @@
Rlog.e(LOG_TAG, msg);
}
+ private void loglocal(String msg) {
+ if (DBG) mLocalLog.log(msg);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UiccCard:");
pw.println(" mCi=" + mCi);
@@ -699,5 +710,8 @@
+ ((Registrant)mCarrierPrivilegeRegistrants.get(i)).getHandler());
}
pw.flush();
+ pw.println("mLocalLog:");
+ mLocalLog.dump(fd, pw, args);
+ pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 0aec4d1..aa0bd64 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -29,6 +29,7 @@
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccUtils;
@@ -68,6 +69,7 @@
*/
public class UiccCarrierPrivilegeRules extends Handler {
private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
+ private static final boolean DBG = false;
private static final String AID = "A00000015141434C00";
private static final int CLA = 0x80;
@@ -117,6 +119,10 @@
private static final int STATE_LOADED = 1;
private static final int STATE_ERROR = 2;
+ // Max number of retries for open logical channel, interval is 10s.
+ private static final int MAX_RETRY = 1;
+ private static final int RETRY_INTERVAL_MS = 10000;
+
// Describes a single rule.
private static class AccessRule {
public byte[] certificateHash;
@@ -169,12 +175,12 @@
length = Integer.parseInt(data.substring(offset + 2, offset + 2 + numBytes * 2), 16) * 2;
lengthBytes = data.substring(offset, offset + 2 + numBytes * 2);
}
- Rlog.d(LOG_TAG, "TLV parseLength length=" + length + "lenghtBytes: " + lengthBytes);
+ log("TLV parseLength length=" + length + "lenghtBytes: " + lengthBytes);
return lengthBytes;
}
public String parse(String data, boolean shouldConsumeAll) {
- Rlog.d(LOG_TAG, "Parse TLV: " + tag);
+ log("Parse TLV: " + tag);
if (!data.startsWith(tag)) {
throw new IllegalArgumentException("Tags don't match.");
}
@@ -186,7 +192,7 @@
parseLength(data);
index += lengthBytes.length();
- Rlog.d(LOG_TAG, "index="+index+" length="+length+"data.length="+data.length());
+ log("index="+index+" length="+length+"data.length="+data.length());
int remainingLength = data.length() - (index + length);
if (remainingLength < 0) {
throw new IllegalArgumentException("Not enough data.");
@@ -196,7 +202,7 @@
}
value = data.substring(index, index + length);
- Rlog.d(LOG_TAG, "Got TLV: " + tag + "," + length + "," + value);
+ log("Got TLV: " + tag + "," + length + "," + value);
return data.substring(index + length);
}
@@ -209,18 +215,29 @@
private Message mLoadedCallback;
private String mStatusMessage; // Only used for debugging.
private int mChannelId; // Channel Id for communicating with UICC.
+ private int mRetryCount; // Number of retries for open logical channel.
+ private final Runnable mRetryRunnable = new Runnable() {
+ @Override
+ public void run() {
+ openChannel();
+ }
+ };
+
+ private void openChannel() {
+ // Send open logical channel request.
+ mUiccCard.iccOpenLogicalChannel(AID,
+ obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+ }
public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
- Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
+ log("Creating UiccCarrierPrivilegeRules");
mUiccCard = uiccCard;
mState = new AtomicInteger(STATE_LOADING);
mStatusMessage = "Not loaded.";
mLoadedCallback = loadedCallback;
mRules = "";
- // Start loading the rules.
- mUiccCard.iccOpenLogicalChannel(AID,
- obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+ openChannel();
}
/**
@@ -238,29 +255,25 @@
* @return Access status.
*/
public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
- Rlog.d(LOG_TAG, "hasCarrierPrivileges: " + signature + " : " + packageName);
+ log("hasCarrierPrivileges: " + signature + " : " + packageName);
int state = mState.get();
if (state == STATE_LOADING) {
- Rlog.d(LOG_TAG, "Rules not loaded.");
+ log("Rules not loaded.");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
} else if (state == STATE_ERROR) {
- Rlog.d(LOG_TAG, "Error loading rules.");
+ log("Error loading rules.");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
}
// SHA-1 is for backward compatible support only, strongly discouraged for new use.
byte[] certHash = getCertHash(signature, "SHA-1");
byte[] certHash256 = getCertHash(signature, "SHA-256");
- Rlog.d(LOG_TAG, "Checking SHA1: " + IccUtils.bytesToHexString(certHash) + " : " + packageName);
- Rlog.d(LOG_TAG, "Checking SHA256: " + IccUtils.bytesToHexString(certHash256) + " : " + packageName);
for (AccessRule ar : mAccessRules) {
if (ar.matches(certHash, packageName) || ar.matches(certHash256, packageName)) {
- Rlog.d(LOG_TAG, "Match found!");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
}
- Rlog.d(LOG_TAG, "No matching rule found. Returning false.");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
@@ -273,8 +286,11 @@
*/
public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
try {
+ // Include DISABLED_UNTIL_USED components. This facilitates cases where a carrier app
+ // is disabled by default, and some other component wants to enable it when it has
+ // gained carrier privileges (as an indication that a matching SIM has been inserted).
PackageInfo pInfo = packageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES);
+ PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
Signature[] signatures = pInfo.signatures;
for (Signature sig : signatures) {
int accessStatus = getCarrierPrivilegeStatus(sig, pInfo.packageName);
@@ -361,19 +377,29 @@
switch (msg.what) {
case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
- Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
+ log("EVENT_OPEN_LOGICAL_CHANNEL_DONE");
ar = (AsyncResult) msg.obj;
if (ar.exception == null && ar.result != null) {
mChannelId = ((int[]) ar.result)[0];
mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3, DATA,
obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
} else {
- updateState(STATE_ERROR, "Error opening channel");
+ // MISSING_RESOURCE could be due to logical channels temporarily unavailable,
+ // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
+ if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY &&
+ ((CommandException) (ar.exception)).getCommandError() ==
+ CommandException.Error.MISSING_RESOURCE) {
+ mRetryCount++;
+ removeCallbacks(mRetryRunnable);
+ postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
+ } else {
+ updateState(STATE_ERROR, "Error opening channel: " + ar.exception);
+ }
}
break;
case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
- Rlog.d(LOG_TAG, "EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
+ log("EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
ar = (AsyncResult) msg.obj;
if (ar.exception == null && ar.result != null) {
IccIoResult response = (IccIoResult) ar.result;
@@ -409,7 +435,7 @@
break;
case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
- Rlog.d(LOG_TAG, "EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
+ log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
break;
default:
@@ -422,17 +448,17 @@
* For long payload, we need to fetch it repeatly before start parsing it.
*/
private boolean isDataComplete() {
- Rlog.d(LOG_TAG, "isDataComplete mRules:" + mRules);
+ log("isDataComplete mRules:" + mRules);
if (mRules.startsWith(TAG_ALL_REF_AR_DO)) {
TLV allRules = new TLV(TAG_ALL_REF_AR_DO);
String lengthBytes = allRules.parseLength(mRules);
- Rlog.d(LOG_TAG, "isDataComplete lengthBytes: " + lengthBytes);
+ log("isDataComplete lengthBytes: " + lengthBytes);
if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
allRules.length) {
- Rlog.d(LOG_TAG, "isDataComplete yes");
+ log("isDataComplete yes");
return true;
} else {
- Rlog.d(LOG_TAG, "isDataComplete no");
+ log("isDataComplete no");
return false;
}
} else {
@@ -444,7 +470,7 @@
* Parses the rules from the input string.
*/
private static List<AccessRule> parseRules(String rules) {
- Rlog.d(LOG_TAG, "Got rules: " + rules);
+ log("Got rules: " + rules);
TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40
allRefArDo.parse(rules, true);
@@ -468,7 +494,7 @@
* Parses a single rule.
*/
private static AccessRule parseRefArdo(String rule) {
- Rlog.d(LOG_TAG, "Got rule: " + rule);
+ log("Got rule: " + rule);
String certificateHash = null;
String packageName = null;
@@ -510,18 +536,14 @@
TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
permDo.parse(arDo.value, true);
- Rlog.e(LOG_TAG, permDo.value);
} else {
// Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
throw new RuntimeException("Invalid Rule type");
}
}
- Rlog.e(LOG_TAG, "Adding: " + certificateHash + " : " + packageName + " : " + accessType);
-
AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash),
packageName, accessType);
- Rlog.e(LOG_TAG, "Parsed rule: " + accessRule);
return accessRule;
}
@@ -548,7 +570,10 @@
}
mStatusMessage = statusMessage;
- Rlog.e(LOG_TAG, mStatusMessage);
+ }
+
+ private static void log(String msg) {
+ if (DBG) Rlog.d(LOG_TAG, msg);
}
/**
diff --git a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
index 913aa7c..bd3357e 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
@@ -36,6 +36,7 @@
protected String getEFPath(int efid) {
switch(efid) {
case EF_SMS:
+ case EF_EXT5:
case EF_EXT6:
case EF_MWIS:
case EF_MBI:
@@ -58,6 +59,7 @@
case EF_INFO_CPHS:
case EF_CSP_CPHS:
case EF_GID1:
+ case EF_GID2:
case EF_LI:
return MF_SIM + DF_ADF;
diff --git a/src/java/com/google/android/mms/pdu/PduParser.java b/src/java/com/google/android/mms/pdu/PduParser.java
index acd1d04..9fbdb66 100755
--- a/src/java/com/google/android/mms/pdu/PduParser.java
+++ b/src/java/com/google/android/mms/pdu/PduParser.java
@@ -1774,6 +1774,9 @@
return THE_FIRST_PART;
}
}
+ // This is not the first part, so append to end (keeping the original order)
+ // Check b/19607294 for details of this change
+ return THE_LAST_PART;
}
/* check part's content-type */
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..2027fef
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Include subdirectory makefiles
+# ============================================================
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 13363dc..96c5a9d 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -8,6 +8,7 @@
#LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
similarity index 95%
rename from tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
index 6e40760..4e5419a 100755
--- a/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
@@ -58,7 +58,7 @@
assertEquals(a1.types[i], a2.types[i]);
}
assertEquals(a1.carrierEnabled, a2.carrierEnabled);
- assertEquals(a1.bearer, a2.bearer);
+ assertEquals(a1.bearerBitmask, a2.bearerBitmask);
assertEquals(a1.profileId, a2.profileId);
assertEquals(a1.modemCognitive, a2.modemCognitive);
assertEquals(a1.maxConns, a2.maxConns);
@@ -81,7 +81,7 @@
testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
expectedApn = new ApnSetting(
-1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
- "", "", "", "", "", 0, dunTypes, "IP", "IP",true, 0,
+ "", "", "", "", "", 0, dunTypes, "IP", "IP",true, 0, 0,
0, false, 0, 0, 0, 0, "", "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
@@ -89,7 +89,7 @@
testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
expectedApn = new ApnSetting(
-1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14,
+ "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
0, false, 0, 0, 0, 0, "", "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
@@ -97,7 +97,7 @@
testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV4V6, IP,true,14";
expectedApn = new ApnSetting(
-1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP", true, 14,
+ "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP", true, 14, 0,
0, false, 0, 0, 0, 0, "", "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
@@ -105,7 +105,7 @@
testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
expectedApn = new ApnSetting(
-1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
- "IP",true, 14, 0, false, 0, 0, 0, 0, "spn", "testSpn");
+ "IP",true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testSpn");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// Return no apn if insufficient fields given.
@@ -141,7 +141,7 @@
ApnSetting apn = new ApnSetting(
99, "12345", "Name", "apn", "proxy", "port",
"mmsc", "mmsproxy", "mmsport", "user", "password", 0,
- types, "IPV4V6", "IP", true, 14, 0, false, 0, 0, 0, 0, "", "");
+ types, "IPV4V6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
String expected = "[ApnSettingV2] Name, 99, 12345, apn, proxy, " +
"mmsc, mmsproxy, mmsport, port, 0, default | *, " +
"IPV4V6, IP, true, 14, 0, false, 0, 0, 0, 0, , ";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
new file mode 100644
index 0000000..61e5b4f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.telephony.TelephonyManager;
+import android.test.InstrumentationTestCase;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class CarrierAppUtilsTest extends InstrumentationTestCase {
+ private static final String CARRIER_APP = "com.example.carrier";
+ private static final String[] CARRIER_APPS = new String[] { CARRIER_APP };
+ private static final int USER_ID = 12345;
+ private static final String CALLING_PACKAGE = "phone";
+
+ @Mock private IPackageManager mPackageManager;
+ @Mock private TelephonyManager mTelephonyManager;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ System.setProperty("dexmaker.dexcache",
+ getInstrumentation().getTargetContext().getCacheDir().getPath());
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /** No apps configured - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_EmptyList() {
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, new String[0]);
+ Mockito.verifyNoMoreInteractions(mPackageManager, mTelephonyManager);
+ }
+
+ /** Configured app is missing - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_MissingApp() throws Exception {
+ Mockito.when(mPackageManager.getApplicationInfo("com.example.missing.app",
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(null);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, new String[] { "com.example.missing.app" });
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(Mockito.any(String[].class),
+ Mockito.anyInt());
+ Mockito.verifyNoMoreInteractions(mTelephonyManager);
+ }
+
+ /** Configured app is not bundled with the system - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NonSystemApp() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ Mockito.verifyNoMoreInteractions(mTelephonyManager);
+ }
+
+ /**
+ * Configured app has privileges, but was disabled by the user - should only grant
+ * permissions.
+ */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_DisabledUser()
+ throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured app has privileges, but was disabled - should only grant permissions. */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Disabled() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured app has privileges, and is already enabled - should only grant permissions. */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Enabled() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured /data app has privileges - should only grant permissions. */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_UpdatedApp() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured app has privileges, and is in the default state - should enable. */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Default() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+ CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured app has privileges, and is disabled until used - should enable. */
+ public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_DisabledUntilUsed()
+ throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+ CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+ Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+ new String[] {appInfo.packageName}, USER_ID);
+ }
+
+ /** Configured app has no privileges, and was disabled by the user - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUser() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+
+ /** Configured app has no privileges, and was disabled - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Disabled() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+
+ /** Configured app has no privileges, and is explicitly enabled - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Enabled() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+
+ /** Configured /data app has no privileges - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_UpdatedApp() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+
+ /** Configured app has no privileges, and is in the default state - should disable until use. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Default() throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+ CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
+ CALLING_PACKAGE);
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+
+ /** Configured app has no privileges, and is disabled until used - should do nothing. */
+ public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUntilUsed()
+ throws Exception {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = CARRIER_APP;
+ appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+ Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+ Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+ mTelephonyManager, USER_ID, CARRIER_APPS);
+ Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+ Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+ Mockito.anyString());
+ Mockito.verify(mPackageManager, Mockito.never())
+ .grantDefaultPermissionsToEnabledCarrierApps(
+ Mockito.any(String[].class), Mockito.anyInt());
+ }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
index 8b9fd2e..7dc5777 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
@@ -34,7 +34,9 @@
ISms sms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
assertNotNull(sms);
- List<SmsRawData> records = sms.getAllMessagesFromIccEf(ActivityThread.currentPackageName());
+ int preferredSmsSubscription = sms.getPreferredSmsSubscription();
+ List<SmsRawData> records = sms.getAllMessagesFromIccEfForSubscriber(
+ preferredSmsSubscription, ActivityThread.currentPackageName());
assertNotNull(records);
assertTrue(records.size() >= 0);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java b/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
rename to tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken